hookehuyr

feat(checkin): 添加打卡功能并优化定位处理

在详情页添加打卡按钮,实现打卡功能并验证用户定位范围
将用户经纬度信息传递到详情页,优化路由跳转参数
1 <!-- 1 <!--
2 * @Date: 2024-09-15 22:08:49 2 * @Date: 2024-09-15 22:08:49
3 * @LastEditors: hookehuyr hookehuyr@gmail.com 3 * @LastEditors: hookehuyr hookehuyr@gmail.com
4 - * @LastEditTime: 2025-03-22 21:33:41 4 + * @LastEditTime: 2025-08-26 17:21:24
5 - * @FilePath: /map-demo/src/views/by/info.vue 5 + * @FilePath: /map-demo/src/views/checkin/info.vue
6 * @Description: 文件描述 6 * @Description: 文件描述
7 --> 7 -->
8 <template> 8 <template>
...@@ -30,6 +30,7 @@ ...@@ -30,6 +30,7 @@
30 <van-icon v-if="!audio_list_height" name="https://cdn.ipadbiz.cn/bieyuan/map/icon/%E8%AF%AD%E9%9F%B31@3x.png" size="1.65rem" /> 30 <van-icon v-if="!audio_list_height" name="https://cdn.ipadbiz.cn/bieyuan/map/icon/%E8%AF%AD%E9%9F%B31@3x.png" size="1.65rem" />
31 <van-icon v-else name="https://cdn.ipadbiz.cn/bieyuan/map/icon/%E8%AF%AD%E9%9F%B32@3x.png" size="1.65rem" /> 31 <van-icon v-else name="https://cdn.ipadbiz.cn/bieyuan/map/icon/%E8%AF%AD%E9%9F%B32@3x.png" size="1.65rem" />
32 </div> 32 </div>
33 + <div @click="checkIn()" class="info-btn" style="margin-right: 0.75rem;">打卡</div>
33 <div v-if="page_details.path?.length > 1" @click="goTo()" class="info-btn">前往</div> 34 <div v-if="page_details.path?.length > 1" @click="goTo()" class="info-btn">前往</div>
34 <div @click="goToWalk()" class="info-btn">前往</div> 35 <div @click="goToWalk()" class="info-btn">前往</div>
35 </div> 36 </div>
...@@ -88,12 +89,13 @@ ...@@ -88,12 +89,13 @@
88 <script setup> 89 <script setup>
89 import { ref, watch, watchEffect } from 'vue' 90 import { ref, watch, watchEffect } from 'vue'
90 import { useRoute, useRouter } from 'vue-router' 91 import { useRoute, useRouter } from 'vue-router'
91 -import { showImagePreview } from 'vant'; 92 +import { showImagePreview, showDialog } from 'vant';
92 import { storeToRefs } from 'pinia' 93 import { storeToRefs } from 'pinia'
93 import audioPlayList from '@/components/audioList.vue' 94 import audioPlayList from '@/components/audioList.vue'
94 import { mainStore, useTitle } from '@/utils/generatePackage' 95 import { mainStore, useTitle } from '@/utils/generatePackage'
95 import wx from 'weixin-js-sdk'; 96 import wx from 'weixin-js-sdk';
96 import $ from 'jquery'; 97 import $ from 'jquery';
98 +import AMapLoader from '@amap/amap-jsapi-loader'
97 99
98 import { mapAPI, mapAudioAPI } from '@/api/map.js' 100 import { mapAPI, mapAudioAPI } from '@/api/map.js'
99 101
...@@ -120,7 +122,7 @@ watch( ...@@ -120,7 +122,7 @@ watch(
120 () => props.info, 122 () => props.info,
121 (v) => { 123 (v) => {
122 if (v.details.length) { 124 if (v.details.length) {
123 - page_details.value = { ...v.details[0], position: v.position, path: v.path }; 125 + page_details.value = { ...v.details[0], position: v.position, path: v.path, current_lng: v.current_lng, current_lat: v.current_lat };
124 // 获取浏览器可视范围的高度 126 // 获取浏览器可视范围的高度
125 $('.info-page').height(props.height + 'px'); 127 $('.info-page').height(props.height + 'px');
126 } 128 }
...@@ -197,12 +199,20 @@ onMounted(async () => { ...@@ -197,12 +199,20 @@ onMounted(async () => {
197 const raw_list = data.list[0].list; // 获取标记点列表 199 const raw_list = data.list[0].list; // 获取标记点列表
198 const marker_id = $route.query.marker_id; 200 const marker_id = $route.query.marker_id;
199 const current_marker = raw_list.filter(item => item.id == marker_id)[0]; 201 const current_marker = raw_list.filter(item => item.id == marker_id)[0];
202 + // 定位
203 + const current_lng = $route.query.current_lng;
204 + const current_lat = $route.query.current_lat;
200 // 205 //
201 page_details.value = { ...current_marker.details[0], position: current_marker.position, path: current_marker.path }; 206 page_details.value = { ...current_marker.details[0], position: current_marker.position, path: current_marker.path };
202 // 富文本转义, 分割线样式转换 207 // 富文本转义, 分割线样式转换
203 page_details.value.introduction = page_details.value.introduction?.replace(/\<hr\>/g, '<div class="van-hairline--bottom" style="margin: 1rem 0;"></div>') 208 page_details.value.introduction = page_details.value.introduction?.replace(/\<hr\>/g, '<div class="van-hairline--bottom" style="margin: 1rem 0;"></div>')
204 page_details.value.story = page_details.value.story?.replace(/\<hr\>/g, '<div class="van-hairline--bottom" style="margin: 1rem 0;"></div>') 209 page_details.value.story = page_details.value.story?.replace(/\<hr\>/g, '<div class="van-hairline--bottom" style="margin: 1rem 0;"></div>')
205 page_details.value.experience = page_details.value.experience?.replace(/\<hr\>/g, '<div class="van-hairline--bottom" style="margin: 1rem 0;"></div>') 210 page_details.value.experience = page_details.value.experience?.replace(/\<hr\>/g, '<div class="van-hairline--bottom" style="margin: 1rem 0;"></div>')
211 + // 定位
212 + if (current_lng && current_lat) {
213 + page_details.value.current_lng = current_lng;
214 + page_details.value.current_lat = current_lat;
215 + }
206 // 查询是否有音频列表,打标记点是否有音频 216 // 查询是否有音频列表,打标记点是否有音频
207 const getAudioList = await mapAudioAPI({ mid: $route.query.id, bid: marker_id }); 217 const getAudioList = await mapAudioAPI({ mid: $route.query.id, bid: marker_id });
208 if (getAudioList.data && getAudioList.data.length) { 218 if (getAudioList.data && getAudioList.data.length) {
...@@ -270,6 +280,11 @@ onMounted(async () => { ...@@ -270,6 +280,11 @@ onMounted(async () => {
270 wx.updateTimelineShareData(shareData); 280 wx.updateTimelineShareData(shareData);
271 // 分享到腾讯微博 281 // 分享到腾讯微博
272 wx.onMenuShareWeibo(shareData); 282 wx.onMenuShareWeibo(shareData);
283 + const AMap = await AMapLoader.load({
284 + key: '17b8fc386104b89db88b60b049a6dbce', // 控制台获取
285 + version: '2.0', // 指定API版本
286 + plugins: ['AMap.ElasticMarker','AMap.ImageLayer','AMap.ToolBar','AMap.IndoorMap','AMap.Walking','AMap.Geolocation'] // 必须加载步行导航插件
287 + })
273 }); 288 });
274 289
275 // watchEffect( 290 // watchEffect(
...@@ -302,9 +317,9 @@ const goTo = () => { // 打开标记地图显示 ...@@ -302,9 +317,9 @@ const goTo = () => { // 打开标记地图显示
302 return; 317 return;
303 } 318 }
304 // 319 //
305 - if ($router.currentRoute.value.path === '/by/info') { // 详情页 320 + if ($router.currentRoute.value.path === '/checkin/info') { // 详情页
306 $router.push({ 321 $router.push({
307 - path: '/by', 322 + path: '/checkin',
308 query: { 323 query: {
309 id: $route.query.id, 324 id: $route.query.id,
310 marker_id: $route.query.marker_id 325 marker_id: $route.query.marker_id
...@@ -320,9 +335,9 @@ const goTo = () => { // 打开标记地图显示 ...@@ -320,9 +335,9 @@ const goTo = () => { // 打开标记地图显示
320 335
321 const goToWalk = async () => { // 打开步行导航地图显示 336 const goToWalk = async () => { // 打开步行导航地图显示
322 // 337 //
323 - if ($router.currentRoute.value.path === '/by/info') { // 详情页 338 + if ($router.currentRoute.value.path === '/checkin/info') { // 详情页
324 $router.push({ 339 $router.push({
325 - path: '/by', 340 + path: '/checkin',
326 query: { 341 query: {
327 id: $route.query.id, 342 id: $route.query.id,
328 marker_id: $route.query.marker_id 343 marker_id: $route.query.marker_id
...@@ -343,14 +358,14 @@ const goToWalk = async () => { // 打开步行导航地图显示 ...@@ -343,14 +358,14 @@ const goToWalk = async () => { // 打开步行导航地图显示
343 358
344 const goBack = () => { // 返回首页 359 const goBack = () => { // 返回首页
345 $router.push({ 360 $router.push({
346 - path: '/by', 361 + path: '/checkin',
347 query: { 362 query: {
348 id: $route.query.id, 363 id: $route.query.id,
349 } 364 }
350 }) 365 })
351 } 366 }
352 367
353 -const showBack = computed(() => $router.currentRoute.value.path === '/by/info'); 368 +const showBack = computed(() => $router.currentRoute.value.path === '/checkin/info');
354 369
355 const voicePause = () => { 370 const voicePause = () => {
356 audio.value.pause(); 371 audio.value.pause();
...@@ -450,7 +465,7 @@ const onClickAudioList = () => { ...@@ -450,7 +465,7 @@ const onClickAudioList = () => {
450 if ($('.info-page').height() < $(window).height()) { // 在浮动模式下点击音频列表 465 if ($('.info-page').height() < $(window).height()) { // 在浮动模式下点击音频列表
451 // 打开页面 466 // 打开页面
452 $router.push({ 467 $router.push({
453 - path: '/by/info', 468 + path: '/checkin/info',
454 query: { 469 query: {
455 id: $route.query.id, 470 id: $route.query.id,
456 marker_id: props.info.id, 471 marker_id: props.info.id,
...@@ -478,6 +493,59 @@ const onStatusAudioList = (status) => { // 音频列表组件,状态改变 ...@@ -478,6 +493,59 @@ const onStatusAudioList = (status) => { // 音频列表组件,状态改变
478 // show_audio.value = false; 493 // show_audio.value = false;
479 // } 494 // }
480 } 495 }
496 +
497 +// const checkInRange = (current_lng, current_lat, point_range) => {
498 +// let isPointInRing = AMap.GeometryUtil.isPointInRing([current_lng, current_lat], point_range);
499 +// return isPointInRing
500 +// }
501 +
502 +// 现在只有一个地点的经纬度page_details.value?.position, 我现在需要判断我当前经纬度离这个点的距离是否在1km以内
503 +const checkInRange = (current_lng, current_lat, point_range) => {
504 + if (!point_range) return false;
505 + let distance = AMap.GeometryUtil.distance([current_lng, current_lat], point_range);
506 + return distance < 1000;
507 +}
508 +
509 +
510 +const check_in_status = ref(false);
511 +const checkIn = async () => { // 打卡
512 + if (check_in_status.value) {
513 + show_toast.value = true;
514 + toast_text.value = '您已打卡';
515 + return;
516 + }
517 +
518 + // 判断用户是否定位
519 + if (!page_details.value.current_lng || !page_details.value.current_lat) {
520 + show_toast.value = true;
521 + toast_text.value = '请先获取定位';
522 + return;
523 + }
524 +
525 + // 判断用户时候在范围内
526 + if (!checkInRange(page_details.value.current_lng, page_details.value.current_lat, page_details.value?.position)) {
527 + show_toast.value = true;
528 + toast_text.value = '您不在打卡范围';
529 + return;
530 + }
531 +
532 + if (page_details.value?.position.length) {
533 + // emit("checkIn", {name: '打卡', point: [+page_details.value?.position[0], +page_details.value?.position[1]]});
534 + // 确认打卡
535 + check_in_status.value = true;
536 + // 提示打卡成功
537 + showDialog({
538 + title: '温馨提示',
539 + message: `恭喜您打卡成功`,
540 + confirmButtonText: '去上传图片',
541 + }).then(() => {
542 + // TODO: 后续上传图片页面
543 + });
544 + } else {
545 + show_toast.value = true;
546 + toast_text.value = '该标记点没有关联导航';
547 + }
548 +}
481 </script> 549 </script>
482 550
483 <style lang="less"> 551 <style lang="less">
......
1 <!-- 1 <!--
2 * @Date: 2023-05-19 14:54:27 2 * @Date: 2023-05-19 14:54:27
3 * @LastEditors: hookehuyr hookehuyr@gmail.com 3 * @LastEditors: hookehuyr hookehuyr@gmail.com
4 - * @LastEditTime: 2025-08-08 14:51:23 4 + * @LastEditTime: 2025-08-26 17:01:40
5 - * @FilePath: /map-demo/src/views/by/map.vue 5 + * @FilePath: /map-demo/src/views/checkin/map.vue
6 * @Description: 公众地图主体页面 6 * @Description: 公众地图主体页面
7 --> 7 -->
8 <template> 8 <template>
...@@ -27,14 +27,14 @@ ...@@ -27,14 +27,14 @@
27 :src="data_logo" 27 :src="data_logo"
28 /> 28 />
29 </div> 29 </div>
30 - <div @click="scanQrcode" style="position: absolute; bottom: 1rem; left: calc(50% - 2.5rem);"> 30 + <!-- <div @click="scanQrcode" style="position: absolute; bottom: 1rem; left: calc(50% - 2.5rem);">
31 <van-image 31 <van-image
32 width="5rem" 32 width="5rem"
33 height="5rem" 33 height="5rem"
34 fit="contain" 34 fit="contain"
35 src="https://cdn.ipadbiz.cn/bieyuan/map/icon/scan@3x.png" 35 src="https://cdn.ipadbiz.cn/bieyuan/map/icon/scan@3x.png"
36 /> 36 />
37 - </div> 37 + </div> -->
38 38
39 <van-config-provider :theme-vars="themeVars"> 39 <van-config-provider :theme-vars="themeVars">
40 <van-floating-panel v-model:height="info_height" :anchors="anchors" @height-change="onHeightChange"> 40 <van-floating-panel v-model:height="info_height" :anchors="anchors" @height-change="onHeightChange">
...@@ -99,7 +99,7 @@ import $ from 'jquery'; ...@@ -99,7 +99,7 @@ import $ from 'jquery';
99 import { useRect } from '@vant/use'; 99 import { useRect } from '@vant/use';
100 import { mapAPI } from '@/api/map.js' 100 import { mapAPI } from '@/api/map.js'
101 import wx from 'weixin-js-sdk' 101 import wx from 'weixin-js-sdk'
102 -import pageInfo from '@/views/by/info.vue' 102 +import pageInfo from '@/views/checkin/info.vue'
103 import audioBackground1 from '@/components/audioBackground1.vue' 103 import audioBackground1 from '@/components/audioBackground1.vue'
104 import { mapState, mapActions } from 'pinia' 104 import { mapState, mapActions } from 'pinia'
105 import { mainStore } from '@/store' 105 import { mainStore } from '@/store'
...@@ -485,6 +485,18 @@ export default { ...@@ -485,6 +485,18 @@ export default {
485 await this.$nextTick(); 485 await this.$nextTick();
486 this.itemInfo = updatedInfo; 486 this.itemInfo = updatedInfo;
487 487
488 + // 写入用户经纬度
489 + if (this.current_lng && this.current_lat) {
490 + this.itemInfo.current_lng = this.current_lng;
491 + this.itemInfo.current_lat = this.current_lat;
492 + } else {
493 + this.itemInfo.current_lng = '';
494 + this.itemInfo.current_lat = '';
495 + // 提示用户获取定位
496 + this.show_toast = true;
497 + this.toast_text = '请先点击左侧按钮获取定位'
498 + }
499 +
488 // 详情为空提示 500 // 详情为空提示
489 if (!this.itemInfo.details.length) { 501 if (!this.itemInfo.details.length) {
490 this.show_toast = true; 502 this.show_toast = true;
...@@ -845,7 +857,7 @@ export default { ...@@ -845,7 +857,7 @@ export default {
845 let marker_id = parseQueryString(result).marker_id; 857 let marker_id = parseQueryString(result).marker_id;
846 // 跳转详情页 858 // 跳转详情页
847 this.$router.push({ 859 this.$router.push({
848 - path: '/by/info', 860 + path: '/checkin/info',
849 query: { 861 query: {
850 id, 862 id,
851 marker_id 863 marker_id
...@@ -855,7 +867,7 @@ export default { ...@@ -855,7 +867,7 @@ export default {
855 }); 867 });
856 // 识别率太低 868 // 识别率太低
857 // this.$router.push({ 869 // this.$router.push({
858 - // path: '/by/scan' 870 + // path: '/checkin/scan'
859 // }) 871 // })
860 }, 872 },
861 onHeightChange ({ height }) { // 监听浮动面板高度变化 873 onHeightChange ({ height }) { // 监听浮动面板高度变化
...@@ -868,10 +880,12 @@ export default { ...@@ -868,10 +880,12 @@ export default {
868 // this.changeAudioStatus('pause'); 880 // this.changeAudioStatus('pause');
869 // 881 //
870 this.$router.push({ 882 this.$router.push({
871 - path: '/by/info', 883 + path: '/checkin/info',
872 query: { 884 query: {
873 id: this.$route.query.id, 885 id: this.$route.query.id,
874 - marker_id: this.itemInfo.id 886 + marker_id: this.itemInfo.id,
887 + current_lng: this.itemInfo.current_lng,
888 + current_lat: this.itemInfo.current_lat,
875 } 889 }
876 }) 890 })
877 } else { 891 } else {
......