小程序修改
This commit is contained in:
@@ -0,0 +1,163 @@
|
||||
import Taro from '@tarojs/taro';
|
||||
import { request } from './api';
|
||||
import { DEFAULT_REGION, regionFromGeo, type RegionSelection } from './region-data';
|
||||
import { FALLBACK_CITY_CODE } from './product-images';
|
||||
|
||||
export const GPS_CITY_STORAGE_KEY = 'dukang_gps_city';
|
||||
|
||||
export type ResolvedUserCity = {
|
||||
province: string;
|
||||
city: string;
|
||||
district: string;
|
||||
cityCode?: string;
|
||||
cityName?: string;
|
||||
openCity: boolean;
|
||||
region: RegionSelection;
|
||||
displayCity: string;
|
||||
};
|
||||
|
||||
type GpsCityCache = ResolvedUserCity & { timestamp: number };
|
||||
|
||||
const FALLBACK_CITY: ResolvedUserCity = {
|
||||
province: DEFAULT_REGION.province,
|
||||
city: DEFAULT_REGION.city,
|
||||
district: DEFAULT_REGION.district,
|
||||
cityCode: FALLBACK_CITY_CODE,
|
||||
cityName: '郑州市',
|
||||
openCity: true,
|
||||
region: DEFAULT_REGION,
|
||||
displayCity: '郑州市',
|
||||
};
|
||||
|
||||
let locationPrompted = false;
|
||||
|
||||
function readCache(): GpsCityCache | null {
|
||||
try {
|
||||
const raw = Taro.getStorageSync(GPS_CITY_STORAGE_KEY);
|
||||
if (!raw) return null;
|
||||
const parsed = JSON.parse(String(raw)) as GpsCityCache;
|
||||
if (Date.now() - parsed.timestamp > 30 * 60 * 1000) return null;
|
||||
return parsed;
|
||||
} catch {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
function writeCache(data: ResolvedUserCity) {
|
||||
try {
|
||||
Taro.setStorageSync(
|
||||
GPS_CITY_STORAGE_KEY,
|
||||
JSON.stringify({ ...data, timestamp: Date.now() } satisfies GpsCityCache),
|
||||
);
|
||||
} catch {
|
||||
/* ignore */
|
||||
}
|
||||
}
|
||||
|
||||
async function reportLocationToServer(payload: {
|
||||
latitude?: number;
|
||||
longitude?: number;
|
||||
sdk: 'jssdk' | 'geolocation';
|
||||
status: 'success' | 'fail';
|
||||
errMsg?: string;
|
||||
}) {
|
||||
return request<{
|
||||
province?: string;
|
||||
city?: string;
|
||||
district?: string;
|
||||
cityCode?: string;
|
||||
cityName?: string;
|
||||
openCity?: boolean;
|
||||
}>('/common/wechat/location', {
|
||||
method: 'POST',
|
||||
data: payload,
|
||||
});
|
||||
}
|
||||
|
||||
function toResolved(data: {
|
||||
province?: string;
|
||||
city?: string;
|
||||
district?: string;
|
||||
cityCode?: string;
|
||||
cityName?: string;
|
||||
openCity?: boolean;
|
||||
}): ResolvedUserCity | null {
|
||||
if (!data.province || !data.city) return null;
|
||||
const region = regionFromGeo(data.province, data.city, data.district);
|
||||
const displayCity = data.cityName ?? (data.city.endsWith('市') ? data.city : `${data.city}市`);
|
||||
return {
|
||||
province: data.province,
|
||||
city: data.city,
|
||||
district: data.district ?? '',
|
||||
cityCode: data.cityCode,
|
||||
cityName: data.cityName,
|
||||
openCity: !!data.openCity,
|
||||
region,
|
||||
displayCity,
|
||||
};
|
||||
}
|
||||
|
||||
async function promptLocationAuth() {
|
||||
if (locationPrompted) return;
|
||||
locationPrompted = true;
|
||||
await Taro.showModal({
|
||||
title: '位置授权',
|
||||
content: '需要获取您的位置以展示所在城市的商品与门店',
|
||||
confirmText: '去授权',
|
||||
showCancel: true,
|
||||
}).catch(() => {});
|
||||
}
|
||||
|
||||
function getLocation(): Promise<Taro.getLocation.SuccessCallbackResult> {
|
||||
return new Promise((resolve, reject) => {
|
||||
Taro.getLocation({
|
||||
type: 'gcj02',
|
||||
success: resolve,
|
||||
fail: reject,
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
/** 获取并解析用户当前城市;失败返回郑州市兜底 */
|
||||
export async function resolveUserCity(force = false): Promise<ResolvedUserCity> {
|
||||
if (!force) {
|
||||
const cached = readCache();
|
||||
if (cached) return cached;
|
||||
}
|
||||
|
||||
if (process.env.TARO_ENV !== 'weapp') {
|
||||
return FALLBACK_CITY;
|
||||
}
|
||||
|
||||
try {
|
||||
const loc = await getLocation();
|
||||
const data = await reportLocationToServer({
|
||||
latitude: loc.latitude,
|
||||
longitude: loc.longitude,
|
||||
sdk: 'jssdk',
|
||||
status: 'success',
|
||||
});
|
||||
const resolved = toResolved(data);
|
||||
if (resolved) {
|
||||
writeCache(resolved);
|
||||
return resolved;
|
||||
}
|
||||
} catch (err) {
|
||||
const errMsg = err instanceof Error ? err.message : String(err);
|
||||
const denied = /auth deny|authorize|permission|拒绝/i.test(errMsg);
|
||||
if (denied) {
|
||||
await promptLocationAuth();
|
||||
}
|
||||
await reportLocationToServer({
|
||||
sdk: 'jssdk',
|
||||
status: 'fail',
|
||||
errMsg: errMsg.slice(0, 200),
|
||||
}).catch(() => {});
|
||||
}
|
||||
|
||||
return FALLBACK_CITY;
|
||||
}
|
||||
|
||||
export function getCityCodeForCatalog(resolved: ResolvedUserCity): string {
|
||||
return resolved.openCity && resolved.cityCode ? resolved.cityCode : FALLBACK_CITY_CODE;
|
||||
}
|
||||
Reference in New Issue
Block a user