Files
frontend-miniapp/src/pages/open-location/index.vue
lyf f9fefa11ef
Some checks failed
CI / verify (push) Has been cancelled
修复小程序内来馆地图导航
2026-07-11 17:24:45 +08:00

200 lines
4.8 KiB
Vue

<template>
<view class="open-location-page">
<view class="open-location-panel">
<text class="open-location-title">{{ title }}</text>
<text class="open-location-desc">{{ description }}</text>
<view class="open-location-actions">
<view class="open-location-button primary" @tap="openLocation">
<text class="open-location-button-text">重新打开</text>
</view>
<view class="open-location-button" @tap="goBack">
<text class="open-location-button-text">返回导览</text>
</view>
</view>
</view>
</view>
</template>
<script setup lang="ts">
import { onLoad, onUnload } from '@dcloudio/uni-app'
import { computed, ref } from 'vue'
import { openWechatHostLocation } from '@/services/WechatOpenLocationService'
import {
parseWechatOpenLocationPageOptions,
type WechatOpenLocationPageOptions
} from '@/utils/wechatOpenLocationProtocol'
const latitude = ref<number | null>(null)
const longitude = ref<number | null>(null)
const locationName = ref('')
const locationAddress = ref('')
const hasOpened = ref(false)
const hasError = ref(false)
const isOpening = ref(false)
const errorMessage = ref('')
let isPageActive = true
let autoBackTimer: ReturnType<typeof setTimeout> | null = null
const title = computed(() => {
if (hasError.value) return '地图打开失败'
if (hasOpened.value) return '已打开地图'
return '正在打开地图'
})
const description = computed(() => {
if (hasError.value) return errorMessage.value || '请返回导览后重试,或检查小程序位置权限。'
if (locationName.value) return locationName.value
return '正在为你唤起地图位置页。'
})
const goBack = () => {
if (!isPageActive) return
isPageActive = false
if (autoBackTimer) {
clearTimeout(autoBackTimer)
autoBackTimer = null
}
uni.navigateBack({
delta: 1,
fail: () => {
uni.redirectTo({
url: '/pages/index/index?tab=guide'
})
}
})
}
const openLocation = async () => {
if (isOpening.value) return
if (latitude.value === null || longitude.value === null) {
hasError.value = true
errorMessage.value = '地图参数无效,请返回导览后重试。'
uni.showToast({
title: '缺少地图坐标',
icon: 'none'
})
return
}
isOpening.value = true
const result = await openWechatHostLocation({
latitude: latitude.value,
longitude: longitude.value,
name: locationName.value,
address: locationAddress.value
})
if (!isPageActive) return
isOpening.value = false
if (result.status === 'opened') {
hasOpened.value = true
hasError.value = false
autoBackTimer = setTimeout(goBack, 300)
return
}
hasError.value = true
errorMessage.value = result.reason === 'timeout'
? '微信地图响应超时,请重试或返回导览。'
: '微信地图能力调用失败,请检查权限后重试。'
uni.showToast({
title: result.reason === 'timeout' ? '微信地图响应超时' : '无法打开地图',
icon: 'none'
})
}
onLoad((options?: WechatOpenLocationPageOptions) => {
isPageActive = true
const result = parseWechatOpenLocationPageOptions(options)
if (!result.ok) {
hasError.value = true
errorMessage.value = result.message
uni.showToast({
title: result.message,
icon: 'none'
})
return
}
latitude.value = result.value.latitude
longitude.value = result.value.longitude
locationName.value = result.value.name
locationAddress.value = result.value.address || ''
void openLocation()
})
onUnload(() => {
isPageActive = false
if (autoBackTimer) clearTimeout(autoBackTimer)
autoBackTimer = null
})
</script>
<style scoped lang="scss">
.open-location-page {
min-height: 100vh;
padding: 28px 20px;
box-sizing: border-box;
display: flex;
align-items: center;
justify-content: center;
background: #f5f5ed;
}
.open-location-panel {
width: 100%;
max-width: 360px;
padding: 24px 20px;
box-sizing: border-box;
display: flex;
flex-direction: column;
gap: 12px;
background: #ffffff;
border: 1px solid rgba(31, 35, 41, 0.08);
border-radius: 8px;
}
.open-location-title {
font-size: 20px;
line-height: 28px;
font-weight: 700;
color: #262421;
}
.open-location-desc {
font-size: 14px;
line-height: 22px;
color: #424754;
}
.open-location-actions {
margin-top: 8px;
display: flex;
gap: 10px;
}
.open-location-button {
min-width: 0;
flex: 1;
height: 42px;
display: flex;
align-items: center;
justify-content: center;
border: 1px solid rgba(31, 35, 41, 0.12);
border-radius: 8px;
background: #ffffff;
}
.open-location-button.primary {
border-color: #d7d800;
background: #e0e100;
}
.open-location-button-text {
font-size: 14px;
line-height: 20px;
font-weight: 600;
color: #262421;
}
</style>