hookehuyr

fix(WeRunAuth): 修复授权状态初始显示问题并添加自动检查控制

refactor(ActivitiesCover): 优化定位授权流程和错误处理

- 将WeRunAuth组件初始状态设为null避免闪烁
- 添加auto-check prop控制自动授权检查
- 重构ActivitiesCover定位逻辑,仅在用户操作时请求授权
- 增加定位超时和失败的错误处理
<template>
<view class="werun-auth-container">
<!-- 未授权状态 -->
<view v-if="!isAuthorized" class="auth-prompt">
<view v-if="isAuthorized === false" class="auth-prompt">
<view class="px-5 py-6 bg-white rounded-xl shadow-md mx-4 mt-4">
<view class="flex flex-col items-center justify-center py-8">
<!-- <view class="mb-4">
......@@ -32,24 +32,38 @@
</view>
<!-- 已授权状态 - 显示步数相关内容 -->
<view v-else>
<view v-else-if="isAuthorized === true">
<slot :isLoading="isLoading" />
</view>
<!-- 初始状态或检查中 - 不显示任何内容,避免闪烁 -->
<view v-else>
<slot :isLoading="true" />
</view>
</view>
</template>
<script setup>
import { ref, onMounted, defineEmits } from 'vue'
import { ref, onMounted, defineEmits, defineProps } from 'vue'
import Taro from '@tarojs/taro'
// import { syncWxStepAPI } from '@/api/points'
import { fetch } from '@/api/fn'
// 定义props
const props = defineProps({
// 是否自动检查授权状态,默认为false,避免页面加载时立即显示授权提示
autoCheck: {
type: Boolean,
default: false
}
})
// 定义事件
const emit = defineEmits(['auth-change', 'steps-update', 'steps-synced', 'sync-failed'])
// 响应式数据
const isAuthorized = ref(false)
const isAuthorized = ref(null) // 初始值为null,避免未检查前显示未授权状态
const isLoading = ref(false)
const showManualUpdate = ref(false)
......@@ -73,6 +87,7 @@ const checkAuthStatus = async (shouldGetData = false) => {
}
} catch (error) {
console.error('检查授权状态失败:', error)
// 检查失败时设置为false,表示未授权
isAuthorized.value = false
emit('auth-change', false)
}
......@@ -220,9 +235,11 @@ defineExpose({
showManualUpdate
})
// 组件挂载时检查授权状态,但不获取步数数据
// 组件挂载时根据props决定是否检查授权状态
onMounted(() => {
checkAuthStatus(false)
if (props.autoCheck) {
checkAuthStatus(false)
}
})
</script>
......
<!--
* @Date: 2022-09-19 14:11:06
* @LastEditors: hookehuyr hookehuyr@gmail.com
* @LastEditTime: 2025-09-06 00:22:56
* @LastEditTime: 2025-09-06 00:32:56
* @FilePath: /lls_program/src/pages/ActivitiesCover/index.vue
* @Description: 活动海报页面 - 展示活动信息并处理定位授权
-->
......@@ -24,8 +24,8 @@
<!-- 底部按钮区域 -->
<view class="bottom-section">
<!-- 未授权定位提示 -->
<view v-if="!hasLocationAuth && !locationError" class="location-tip" @click="retryGetLocation">
<!-- 未授权定位提示 - 仅在用户点击参加活动且未授权时显示 -->
<view v-if="showLocationPrompt && !hasLocationAuth && !locationError" class="location-tip" @click="retryGetLocation">
<view class="tip-content">
<view class="tip-icon">📍</view>
<view class="tip-text">需要获取您的位置信息来参与活动</view>
......@@ -116,6 +116,7 @@ const locationError = ref(false) // 位置获取是否失败
const isJoining = ref(false) // 是否正在加入活动
const userLocation = ref({ lng: null, lat: null }) // 用户位置信息
const hasJoinedFamily = ref(false);
const showLocationPrompt = ref(false) // 是否显示定位权限提示
// 海报生成相关状态
const show_post = ref(false) // 显示海报预览
......@@ -171,35 +172,41 @@ const checkLocationAuth = async () => {
/**
* 获取用户位置信息
* @param {boolean} skipAuthCheck - 是否跳过授权检查(当调用方已经检查过授权状态时)
*/
const getUserLocation = async () => {
const getUserLocation = async (skipAuthCheck = false) => {
try {
locationError.value = false // 重置错误状态
// 检查是否已有位置权限
const authSetting = await Taro.getSetting()
const hasLocationAuth = authSetting.authSetting['scope.userLocation']
// 如果没有授权,先显示数据用途说明
if (hasLocationAuth !== true) {
const modalRes = await Taro.showModal({
title: '位置权限申请',
content: '为了提供更好的活动体验,我们需要获取您的位置信息:1. 验证您是否在活动区域内 2. 为您推荐附近的打卡点 3. 记录活动轨迹和完成情况 4. 提供基于位置的个性化服务, 我们承诺严格保护您的位置隐私,仅用于活动相关功能。',
confirmText: '同意授权',
cancelText: '暂不授权'
})
if (!modalRes.confirm) {
Taro.showToast({
title: '需要位置权限才能参与活动',
icon: 'none'
// 如果没有跳过授权检查,则检查权限状态
if (!skipAuthCheck) {
const authSetting = await Taro.getSetting()
const hasLocationAuth = authSetting.authSetting['scope.userLocation']
// 如果没有授权,先显示数据用途说明
if (hasLocationAuth !== true) {
const modalRes = await Taro.showModal({
title: '位置权限申请',
content: '为了提供更好的活动体验,我们需要获取您的位置信息:1. 验证您是否在活动区域内 2. 为您推荐附近的打卡点 3. 记录活动轨迹和完成情况 4. 提供基于位置的个性化服务, 我们承诺严格保护您的位置隐私,仅用于活动相关功能。',
confirmText: '同意授权',
cancelText: '暂不授权'
})
return false
if (!modalRes.confirm) {
Taro.showToast({
title: '需要位置权限才能参与活动',
icon: 'none'
})
return false
}
}
}
const location = await Taro.getLocation({
type: 'gcj02'
type: 'gcj02',
altitude: false, // 不需要海拔信息,提高获取速度
isHighAccuracy: true, // 开启高精度定位
highAccuracyExpireTime: 4000 // 高精度定位超时时间
})
userLocation.value = {
......@@ -208,6 +215,9 @@ const getUserLocation = async () => {
}
console.log('获取到用户位置:', userLocation.value)
// 获取位置成功后隐藏提示
showLocationPrompt.value = false
hasLocationAuth.value = true
return true
} catch (error) {
console.error('获取位置失败:', error)
......@@ -226,11 +236,28 @@ const getUserLocation = async () => {
}
}
})
} else if (error.errMsg && error.errMsg.includes('timeout')) {
// 定位超时
locationError.value = true
Taro.showToast({
title: '定位超时,请检查网络或GPS',
icon: 'none',
duration: 3000
})
} else if (error.errMsg && error.errMsg.includes('fail')) {
// 定位失败,可能是GPS关闭或网络问题
locationError.value = true
await Taro.showModal({
title: '定位失败',
content: '请确保已开启GPS定位服务,并检查网络连接是否正常',
showCancel: false,
confirmText: '我知道了'
})
} else {
// 网络或其他问题导致的获取失败
// 其他未知错误
locationError.value = true
Taro.showToast({
title: '获取位置失败',
title: '获取位置失败,请重试',
icon: 'none'
})
}
......@@ -269,7 +296,7 @@ const checkFamilyStatusAndJoinActivity = async () => {
*/
const retryGetLocation = async () => {
try {
const success = await getUserLocation()
const success = await getUserLocation(false) // 不跳过授权检查,重新处理授权逻辑
if (success) {
hasLocationAuth.value = true
locationError.value = false
......@@ -290,17 +317,45 @@ const handleJoinActivity = async () => {
isJoining.value = true
try {
// 如果没有定位授权,先获取授权和位置信息
if (!hasLocationAuth.value) {
const success = await getUserLocation()
// 首先检查用户是否已加入家庭
if (!hasJoinedFamily.value) {
Taro.showModal({
title: '提示',
content: '没有加入家庭是无法参加活动的',
cancelText: '关闭',
confirmText: '前往加入',
success: (res) => {
if (res.confirm) {
Taro.navigateTo({
url: '/pages/Welcome/index',
});
}
},
});
isJoining.value = false
return
}
// 检查定位授权状态
const authSetting = await Taro.getSetting()
const hasLocationPermission = authSetting.authSetting['scope.userLocation']
if (hasLocationPermission === false) {
// 用户之前拒绝过授权,显示提示让用户手动开启
showLocationPrompt.value = true
isJoining.value = false
return
} else if (hasLocationPermission === undefined) {
// 未请求过授权,直接尝试获取位置(会触发授权弹窗)
const success = await getUserLocation(false) // 不跳过授权检查,让getUserLocation处理授权逻辑
if (!success) {
showLocationPrompt.value = true
isJoining.value = false
return
}
hasLocationAuth.value = true
} else {
// 已有授权,直接获取位置
const success = await getUserLocation()
const success = await getUserLocation(true) // 跳过授权检查,直接获取位置
if (!success) {
isJoining.value = false
return
......@@ -678,8 +733,7 @@ const initPageData = async () => {
hasJoinedFamily.value = true
}
}
// 检查定位授权弹窗
checkLocationAuth()
// 移除页面初始化时的定位权限检查,改为在用户点击参加活动时检查
}
// 处理页面加载时的授权检查
......
......@@ -16,6 +16,7 @@
<!-- 微信步数授权组件 -->
<WeRunAuth
ref="weRunAuthRef"
:auto-check="false"
@auth-change="handleAuthChange"
@steps-synced="handleStepsSynced"
@sync-failed="handleSyncFailed"
......