小程序修改

This commit is contained in:
2026-07-12 19:44:36 +08:00
parent 3b4833b51a
commit e2dfb08de3
45 changed files with 2028 additions and 347 deletions
+163
View File
@@ -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;
}