From b02f0d91b1b9d0cd765c5a967035c8ddcb62d3cc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E8=B5=B5=E5=BF=A0=E6=9E=97?= <170083662@qq.com> Date: Sun, 12 Jul 2026 21:58:52 +0800 Subject: [PATCH] =?UTF-8?q?fix(user):=20=E4=BF=AE=E5=A4=8D=E5=9C=B0?= =?UTF-8?q?=E5=9B=BE=E9=80=89=E7=82=B9=E6=89=93=E5=BC=80=E5=A4=B1=E8=B4=A5?= =?UTF-8?q?=E5=8F=8A=E5=AE=9A=E4=BD=8D=E6=9D=83=E9=99=90=E5=A4=84=E7=90=86?= =?UTF-8?q?=E9=97=AE=E9=A2=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 移除旧的定位权限拒绝检测方法,改用微信隐私授权新接口保证兼容 - 新增确保微信隐私授权的函数,适配新版基础库及旧版本放行逻辑 - 增加定位权限预检和引导用户开启权限流程,支持设置页跳转回读授权状态 - 在调用 chooseLocation 时仅传入有效经纬度,避免部分机型失败 - 地图打开失败时增加弹窗提示,支持用户手动选择地区或重试打开地图 - 定位失败且已锁定地区时取消误触发地区选择器,提升用户体验 --- .workbuddy/memory/2026-07-12.md | 15 ++ src/pages/user/address-edit.tsx | 250 +++++++++++++++++++++----------- 2 files changed, 181 insertions(+), 84 deletions(-) create mode 100644 .workbuddy/memory/2026-07-12.md diff --git a/.workbuddy/memory/2026-07-12.md b/.workbuddy/memory/2026-07-12.md new file mode 100644 index 0000000..b60370b --- /dev/null +++ b/.workbuddy/memory/2026-07-12.md @@ -0,0 +1,15 @@ +# 2026-07-12 + +## address-edit 地图选点「打开地图失败」修复 + +客户反馈部分手机无法通过选择地图填写地址/所在区,提示「打开地图失败,请重试」。 +根因(按概率):①微信隐私授权未处理(新版基础库强制,最契合「部分手机」);②chooseLocation 传 undefined 经纬度;③权限流程不完整;④失败无兜底。 + +已修复 `src/pages/user/address-edit.tsx`(四层): +1. 调 chooseLocation 前 `ensurePrivacyAuthorized()`(wx.getPrivacySetting/requirePrivacyAuthorize,旧基础库无接口则放行)。 +2. 仅当 lat/lng 为有效数字时才传给 chooseLocation,否则 `chooseLocation({})`。 +3. `ensureLocationPermission()`:getSetting 预检 + authorize 申请 + openSetting 引导并回读确认。 +4. 失败兜底:弹窗「手动选择」(打开所在地区选择器)/「再试一次」(chooseLocation({}));regionLocked 时不再误开选择器。 + +注意:项目本地 `tsc --noEmit` / `npm run type-check` 在本机因 Taro 类型入口缺失(`@tarojs/taro` 不在 @types)会报错,与本次改动无关;以 `taro build --type weapp` 为准。 +另:如仍偶发,需确认微信公众平台后台已配置《隐私保护指引》并发布。 diff --git a/src/pages/user/address-edit.tsx b/src/pages/user/address-edit.tsx index 906842b..214f4e4 100644 --- a/src/pages/user/address-edit.tsx +++ b/src/pages/user/address-edit.tsx @@ -13,12 +13,6 @@ definePageConfig({ type SelectedLocation = { lng: string; lat: string; name?: string; address?: string } -/** 检查是否为定位权限拒绝错误 */ -const isLocationDenied = (e: any) => { - const msg = String(e?.errMsg || e?.message || e || '') - return msg.includes('auth deny') || msg.includes('authorize') || msg.includes('permission') || msg.includes('denied') || msg.includes('scope.userLocation') -} - const isUserCancel = (e: any) => { const msg = String(e?.errMsg || e?.message || e || '') return msg.includes('cancel') @@ -33,6 +27,71 @@ const hasValidLngLat = (addr?: Partial | null) => { return true } +/** 确保已通过微信隐私授权(定位类接口前置要求);旧基础库无该接口时直接放行 */ +const ensurePrivacyAuthorized = (): Promise => { + return new Promise((resolve) => { + const wxAny: any = Taro + if (typeof wxAny.requirePrivacyAuthorize !== 'function') { + resolve() + return + } + if (typeof wxAny.getPrivacySetting === 'function') { + wxAny.getPrivacySetting({ + success: (res: any) => { + if (res && res.needAuthorization) { + wxAny.requirePrivacyAuthorize({success: () => resolve(), fail: () => resolve()}) + } else { + resolve() + } + }, + fail: () => resolve(), + }) + } else { + wxAny.requirePrivacyAuthorize({success: () => resolve(), fail: () => resolve()}) + } + }) +} + +/** 预检并申请定位权限;已拒绝则引导去设置,返回最终是否拥有权限 */ +const ensureLocationPermission = async (): Promise => { + try { + const setting: any = await Taro.getSetting() + const auth = setting?.authSetting?.['scope.userLocation'] + if (auth === true) return true + if (auth === false) { + const modal = await Taro.showModal({ + title: '需要定位权限', + content: '选择定位需要开启定位权限,请在设置中开启后重试。', + confirmText: '去设置', + }) + if (modal.confirm) { + await Taro.openSetting() + const setting2: any = await Taro.getSetting() + return setting2?.authSetting?.['scope.userLocation'] === true + } + return false + } + try { + await Taro.authorize({scope: 'scope.userLocation'}) + return true + } catch { + const modal = await Taro.showModal({ + title: '需要定位权限', + content: '选择定位需要开启定位权限,请在设置中开启后重试。', + confirmText: '去设置', + }) + if (modal.confirm) { + await Taro.openSetting() + const setting2: any = await Taro.getSetting() + return setting2?.authSetting?.['scope.userLocation'] === true + } + return false + } + } catch { + return false + } +} + const AddressEditPage: React.FC = () => { const {id, fromWx} = useRouter().params const isEditMode = !!id @@ -171,96 +230,119 @@ const AddressEditPage: React.FC = () => { } } - /** 选择定位 */ - const chooseGeoLocation = async () => { - const applyChosenLocation = (res: any) => { - if (!res) return - if (res.latitude === undefined || res.longitude === undefined) { - Taro.showToast({title: '定位信息获取失败', icon: 'none'}) - return - } - - const next: SelectedLocation = { - lng: String(res.longitude), - lat: String(res.latitude), - name: res.name, - address: res.address, - } - - // 尝试从地图返回的地址文本解析省市区 - const regionResult = res?.provinceName || res?.cityName || res?.adName - ? { - province: String(res.provinceName || ''), - city: String(res.cityName || ''), - region: String(res.adName || '') - } - : parseRegion(String(res.address || '')) - - const province = String(regionResult?.province || '').trim() - const city = String(regionResult?.city || '').trim() - const region = String(regionResult?.region || '').trim() - if (!province || !city || !region) { - Taro.showToast({title: '定位未识别到所在地区,请重新选择定位', icon: 'none'}) - return - } - - setSelectedLocation(next) - setRegionLocked(true) - - // 将地图选点的地址同步到"收货地址"(剥离省市区,避免重复) - const rawAddr = String(res.address || '').trim() - const name = String(res.name || '').trim() - let detail = rawAddr - for (const part of [province, city, region]) { - if (part) detail = detail.replace(part, '') - } - detail = detail.replace(/[,,]+/g, ' ').replace(/\s+/g, ' ').trim() - const base = detail || rawAddr - const nextDetailAddress = (() => { - if (!base && !name) return '' - if (!base) return name - if (!name) return base - return base.includes(name) ? base : `${base} ${name}` - })() - - setFormData(prev => ({ - ...prev, - lng: next.lng, - lat: next.lat, - address: nextDetailAddress || prev.address, - province, - city, - region, - })) - setRegionText(`${province} ${city} ${region}`) + /** 将地图选点结果应用到表单 */ + const applyChosenLocation = (res: any) => { + if (!res) return + if (res.latitude === undefined || res.longitude === undefined) { + Taro.showToast({title: '定位信息获取失败', icon: 'none'}) + return } + const next: SelectedLocation = { + lng: String(res.longitude), + lat: String(res.latitude), + name: res.name, + address: res.address, + } + + // 尝试从地图返回的地址文本解析省市区 + const regionResult = res?.provinceName || res?.cityName || res?.adName + ? { + province: String(res.provinceName || ''), + city: String(res.cityName || ''), + region: String(res.adName || '') + } + : parseRegion(String(res.address || '')) + + const province = String(regionResult?.province || '').trim() + const city = String(regionResult?.city || '').trim() + const region = String(regionResult?.region || '').trim() + if (!province || !city || !region) { + Taro.showToast({title: '定位未识别到所在地区,请重新选择定位', icon: 'none'}) + return + } + + setSelectedLocation(next) + setRegionLocked(true) + + // 将地图选点的地址同步到"收货地址"(剥离省市区,避免重复) + const rawAddr = String(res.address || '').trim() + const name = String(res.name || '').trim() + let detail = rawAddr + for (const part of [province, city, region]) { + if (part) detail = detail.replace(part, '') + } + detail = detail.replace(/[,,]+/g, ' ').replace(/\s+/g, ' ').trim() + const base = detail || rawAddr + const nextDetailAddress = (() => { + if (!base && !name) return '' + if (!base) return name + if (!name) return base + return base.includes(name) ? base : `${base} ${name}` + })() + + setFormData(prev => ({ + ...prev, + lng: next.lng, + lat: next.lat, + address: nextDetailAddress || prev.address, + province, + city, + region, + })) + setRegionText(`${province} ${city} ${region}`) + } + + /** 选择定位 */ + const chooseGeoLocation = async () => { try { + // 1. 微信隐私授权(新版基础库强制,未同意会直接拒绝定位接口) + await ensurePrivacyAuthorized() + + // 2. 定位权限预检 / 申请 + const granted = await ensureLocationPermission() + if (!granted) { + Taro.showToast({title: '未获得定位权限,可手动选择地区', icon: 'none'}) + if (!regionLocked) setRegionPickerVisible(true) + return + } + + // 3. 仅在有有效经纬度时传入初始化参数,避免 undefined 触发部分机型失败 const initLat = selectedLocation?.lat ? Number(selectedLocation.lat) : undefined const initLng = selectedLocation?.lng ? Number(selectedLocation.lng) : undefined const latitude = typeof initLat === 'number' && Number.isFinite(initLat) ? initLat : undefined const longitude = typeof initLng === 'number' && Number.isFinite(initLng) ? initLng : undefined - const res = await Taro.chooseLocation({latitude, longitude}) + const params: any = {} + if (typeof latitude === 'number') params.latitude = latitude + if (typeof longitude === 'number') params.longitude = longitude + + const res = await Taro.chooseLocation(params) applyChosenLocation(res) } catch (e: any) { if (isUserCancel(e)) return - if (isLocationDenied(e)) { - try { - const modal = await Taro.showModal({ - title: '需要定位权限', - content: '选择定位需要开启定位权限,请在设置中开启后重试。', - confirmText: '去设置', - }) - if (modal.confirm) { - await Taro.openSetting() - const res = await Taro.chooseLocation({}) - applyChosenLocation(res) + // 4. 地图失败兜底:引导手动选择地区或重试,避免用户被卡死 + Taro.showModal({ + title: '地图打开失败', + content: '无法调起地图定位,您可以手动选择所在地区并填写详细地址,或稍后重试。', + confirmText: '手动选择', + cancelText: '再试一次', + }).then((modal) => { + if (modal.confirm) { + if (regionLocked) { + Taro.showToast({title: '所在地区已由定位确定,修改请重新选择定位', icon: 'none'}) + } else { + setRegionPickerVisible(true) } - } catch (_e) { /* ignore */ + } else { + Taro.chooseLocation({}) + .then((res: any) => applyChosenLocation(res)) + .catch(() => { + if (!regionLocked) setRegionPickerVisible(true) + }) } - return - } - Taro.showToast({title: '打开地图失败,请重试', icon: 'none'}) + }).catch(() => { + if (!regionLocked) setRegionPickerVisible(true) + }) } }