feat(checkin): 添加打卡功能并优化定位处理
在详情页添加打卡按钮,实现打卡功能并验证用户定位范围 将用户经纬度信息传递到详情页,优化路由跳转参数
Showing
2 changed files
with
102 additions
and
20 deletions
| 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 { | ... | ... |
-
Please register or login to post a comment