diff --git a/apps/h5-partner/src/components/TencentLocPickerOverlay.tsx b/apps/h5-partner/src/components/TencentLocPickerOverlay.tsx index 1ac0b05..882020f 100644 --- a/apps/h5-partner/src/components/TencentLocPickerOverlay.tsx +++ b/apps/h5-partner/src/components/TencentLocPickerOverlay.tsx @@ -1,8 +1,9 @@ -import { useEffect, useRef, useState } from 'react'; +import { useEffect, useMemo, useState } from 'react'; +import type { ClientRuntimeConfig } from '@dukang/shared-types'; import { request } from '../lib/api'; import { - placeToPicked, - type LbsPlaceItem, + buildTencentLocPickerUrl, + parseTencentLocPickerMessage, type TencentPickedLocation, } from '../lib/tencentLocPicker'; @@ -12,117 +13,79 @@ type Props = { onPick: (loc: TencentPickedLocation) => void; latitude?: number | null; longitude?: number | null; - region?: string | null; }; +let cachedKey: string | null | undefined; + +async function loadTencentLbsKey(): Promise { + if (cachedKey) return cachedKey; + const cfg = await request('PARTNER_H5', '/common/client-config', { + silent: true, + }); + const key = (cfg.tencentLbsKey || '').trim(); + if (!key) { + throw new Error('未配置腾讯位置服务 Key,请联系管理员在系统设置中配置'); + } + cachedKey = key; + return key; +} + export default function TencentLocPickerOverlay({ open, onClose, onPick, latitude, longitude, - region, }: Props) { - const [keyword, setKeyword] = useState(''); - const [items, setItems] = useState([]); + const [key, setKey] = useState(cachedKey ?? null); const [loading, setLoading] = useState(false); + const [error, setError] = useState(''); const [pending, setPending] = useState(null); - const [hint, setHint] = useState('输入地点名称搜索'); - const seqRef = useRef(0); useEffect(() => { if (!open) { - setKeyword(''); - setItems([]); setPending(null); - setHint('输入地点名称搜索'); return; } - const lat = latitude != null ? Number(latitude) : NaN; - const lng = longitude != null ? Number(longitude) : NaN; - if (Number.isFinite(lat) && Number.isFinite(lng) && !(lat === 0 && lng === 0)) { - void loadNearby(lat, lng); + let cancelled = false; + setError(''); + if (key) return; + setLoading(true); + void loadTencentLbsKey() + .then((k) => { + if (!cancelled) setKey(k); + }) + .catch((e) => { + if (!cancelled) setError(e instanceof Error ? e.message : '加载地图配置失败'); + }) + .finally(() => { + if (!cancelled) setLoading(false); + }); + return () => { + cancelled = true; + }; + }, [open, key]); + + useEffect(() => { + if (!open) return; + function onMessage(event: MessageEvent) { + const picked = parseTencentLocPickerMessage(event.data); + if (!picked) return; + setPending(picked); } - // eslint-disable-next-line react-hooks/exhaustive-deps + window.addEventListener('message', onMessage); + return () => window.removeEventListener('message', onMessage); }, [open]); - async function loadNearby(lat: number, lng: number) { - const seq = ++seqRef.current; - setLoading(true); - setHint('正在加载附近地点…'); - try { - const res = await request<{ items: LbsPlaceItem[] }>( - 'PARTNER_H5', - `/common/lbs/nearby?lat=${encodeURIComponent(String(lat))}&lng=${encodeURIComponent(String(lng))}`, - { silent: true }, - ); - if (seq !== seqRef.current) return; - setItems(res.items ?? []); - setHint(res.items?.length ? `附近 ${res.items.length} 个地点` : '附近暂无地点,请搜索'); - } catch (e) { - if (seq !== seqRef.current) return; - setItems([]); - setHint(e instanceof Error ? e.message : '加载附近地点失败'); - } finally { - if (seq === seqRef.current) setLoading(false); - } - } - - async function runSearch() { - const trimmed = keyword.trim(); - if (!trimmed) { - setHint('请输入搜索关键词'); - return; - } - const seq = ++seqRef.current; - setLoading(true); - setHint('搜索中…'); - try { - const params = new URLSearchParams({ keyword: trimmed }); - if (region?.trim()) params.set('region', region.trim()); - const lat = latitude != null ? Number(latitude) : NaN; - const lng = longitude != null ? Number(longitude) : NaN; - if (Number.isFinite(lat) && Number.isFinite(lng)) { - params.set('lat', String(lat)); - params.set('lng', String(lng)); - } - const res = await request<{ items: LbsPlaceItem[] }>( - 'PARTNER_H5', - `/common/lbs/suggest?${params.toString()}`, - { silent: true }, - ); - if (seq !== seqRef.current) return; - setItems(res.items ?? []); - setHint(res.items?.length ? `找到 ${res.items.length} 个结果` : '无匹配结果'); - } catch (e) { - if (seq !== seqRef.current) return; - setItems([]); - setHint(e instanceof Error ? e.message : '搜索失败'); - } finally { - if (seq === seqRef.current) setLoading(false); - } - } - - function useBrowserLocation() { - if (!navigator.geolocation) { - setHint('当前浏览器不支持定位'); - return; - } - setLoading(true); - navigator.geolocation.getCurrentPosition( - (pos) => { - const lat = pos.coords.latitude; - const lng = pos.coords.longitude; - setPending({ latitude: lat, longitude: lng, name: '当前位置' }); - void loadNearby(lat, lng); - }, - () => { - setLoading(false); - setHint('定位失败,请检查定位权限'); - }, - { enableHighAccuracy: true, timeout: 12000 }, - ); - } + const src = useMemo(() => { + if (!key) return ''; + const lat = latitude != null ? Number(latitude) : undefined; + const lng = longitude != null ? Number(longitude) : undefined; + return buildTencentLocPickerUrl(key, { + latitude: lat != null && Number.isFinite(lat) ? lat : undefined, + longitude: lng != null && Number.isFinite(lng) ? lng : undefined, + }); + }, [key, latitude, longitude]); if (!open) return null; @@ -151,7 +114,6 @@ export default function TencentLocPickerOverlay({ padding: '12px 16px', borderBottom: '1px solid rgba(0,0,0,0.06)', flexShrink: 0, - gap: 8, }} > - -
-
- setKeyword(e.target.value)} - onKeyDown={(e) => { - if (e.key === 'Enter') void runSearch(); - }} - placeholder="输入小区 / 写字楼 / 门店名称" - style={{ - flex: 1, - padding: '10px 12px', - border: '1px solid rgba(0,0,0,0.12)', - borderRadius: 8, - fontSize: 14, - }} + {pending ? ( +

+ 已选 {pending.latitude.toFixed(6)}, {pending.longitude.toFixed(6)} + {pending.name ? ` · ${pending.name}` : ''} +

+ ) : null} +
+ {loading ? ( +

+ 加载地图… +

+ ) : error ? ( +
+

+ {error} +

+

+ 若地图能开但看不到附近地点列表,请确认 Key 已开启 WebServiceAPI,并白名单 + apis.map.qq.com。 +

+
+ ) : src ? ( +