diff --git a/apps/web-antd/src/api/store-delivery/index.ts b/apps/web-antd/src/api/store-delivery/index.ts index 060a635..e28c4f4 100644 --- a/apps/web-antd/src/api/store-delivery/index.ts +++ b/apps/web-antd/src/api/store-delivery/index.ts @@ -27,6 +27,7 @@ export interface PolygonZoneDto { id: string; minOrderAmount: number; name: string; + polygonGeoJson: string; priority: number; } @@ -46,6 +47,10 @@ export interface DeliveryGeneralSettingsDto { export interface StoreDeliverySettingsDto { generalSettings: DeliveryGeneralSettingsDto; mode: DeliveryMode; + /** 半径配送中心点纬度 */ + radiusCenterLatitude: null | number; + /** 半径配送中心点经度 */ + radiusCenterLongitude: null | number; polygonZones: PolygonZoneDto[]; radiusTiers: RadiusTierDto[]; storeId: string; @@ -60,6 +65,13 @@ export interface CopyStoreDeliverySettingsParams { targetStoreIds: string[]; } +/** 地址地理编码结果 */ +export interface StoreDeliveryGeocodeDto { + address: string; + latitude: null | number; + longitude: null | number; +} + /** 获取门店配送设置 */ export async function getStoreDeliverySettingsApi(storeId: string) { return requestClient.get('/store/delivery', { @@ -80,3 +92,10 @@ export async function copyStoreDeliverySettingsApi( ) { return requestClient.post('/store/delivery/copy', data); } + +/** 地址地理编码(后端签名代理) */ +export async function geocodeStoreDeliveryAddressApi(address: string) { + return requestClient.get('/store/delivery/geocode', { + params: { address }, + }); +} diff --git a/apps/web-antd/src/views/store/delivery/composables/useTencentGeocoder.ts b/apps/web-antd/src/views/store/delivery/composables/useTencentGeocoder.ts new file mode 100644 index 0000000..a5aebb7 --- /dev/null +++ b/apps/web-antd/src/views/store/delivery/composables/useTencentGeocoder.ts @@ -0,0 +1,62 @@ +/** + * 文件职责:封装配送页地址地理编码能力。 + * 1. 通过 TenantApi 后端代理调用腾讯地图 WebService,避免前端暴露 SK。 + * 2. 内置结果缓存,减少重复请求与抖动。 + */ +import type { LngLatTuple } from './delivery-page/geojson'; + +import { geocodeStoreDeliveryAddressApi } from '#/api/store-delivery'; + +const geocodeCache = new Map(); +const geocodeFailedAddressSet = new Set(); + +function toTuple( + latitude: null | number, + longitude: null | number, +): LngLatTuple | null { + const lng = Number(longitude); + const lat = Number(latitude); + if (!Number.isFinite(lng) || !Number.isFinite(lat)) return null; + if (lng < -180 || lng > 180 || lat < -90 || lat > 90) return null; + if (lng === 0 && lat === 0) return null; + return [lng, lat]; +} + +/** + * 将地址解析为经纬度中心点。 + */ +export async function geocodeAddressToLngLat(rawAddress: string) { + const address = rawAddress?.trim() ?? ''; + if (!address) return null; + + if (geocodeCache.has(address)) { + const cached = geocodeCache.get(address); + if (cached) { + return cached; + } + } + if (geocodeFailedAddressSet.has(address)) { + return null; + } + + try { + const response = await geocodeStoreDeliveryAddressApi(address); + const parsed = toTuple( + response?.latitude ?? null, + response?.longitude ?? null, + ); + if (!parsed) { + geocodeFailedAddressSet.add(address); + return null; + } + + geocodeCache.set(address, parsed); + return parsed; + } catch (error) { + geocodeFailedAddressSet.add(address); + if (import.meta.env.DEV) { + console.warn('地址地理编码失败', error); + } + return null; + } +}