fix(user): 修复地图选点打开失败及定位权限处理问题
- 移除旧的定位权限拒绝检测方法,改用微信隐私授权新接口保证兼容 - 新增确保微信隐私授权的函数,适配新版基础库及旧版本放行逻辑 - 增加定位权限预检和引导用户开启权限流程,支持设置页跳转回读授权状态 - 在调用 chooseLocation 时仅传入有效经纬度,避免部分机型失败 - 地图打开失败时增加弹窗提示,支持用户手动选择地区或重试打开地图 - 定位失败且已锁定地区时取消误触发地区选择器,提升用户体验
This commit is contained in:
@@ -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<ShopUserAddress> | null) => {
|
||||
return true
|
||||
}
|
||||
|
||||
/** 确保已通过微信隐私授权(定位类接口前置要求);旧基础库无该接口时直接放行 */
|
||||
const ensurePrivacyAuthorized = (): Promise<void> => {
|
||||
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<boolean> => {
|
||||
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)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user