From b0ff254cd8974e63973d134b8cb61df812d0c86f Mon Sep 17 00:00:00 2001 From: jacy <18049821889@163.com> Date: Wed, 29 Jul 2026 11:31:02 +0800 Subject: [PATCH] fix(partner): use server LBS search instead of locpicker iframe Avoid Tencent locpicker _listTap crash when selecting POI in WeChat WebView. Co-authored-by: Cursor --- .../components/TencentLocPickerOverlay.tsx | 275 ++++++++++++------ apps/h5-partner/src/lib/tencentLocPicker.ts | 80 +---- apps/h5-partner/src/pages/StoreCreatePage.tsx | 31 +- apps/h5-partner/src/pages/StoreDetailPage.tsx | 34 ++- .../common/client-config.controller.ts | 2 +- 5 files changed, 258 insertions(+), 164 deletions(-) diff --git a/apps/h5-partner/src/components/TencentLocPickerOverlay.tsx b/apps/h5-partner/src/components/TencentLocPickerOverlay.tsx index 882020f..1ac0b05 100644 --- a/apps/h5-partner/src/components/TencentLocPickerOverlay.tsx +++ b/apps/h5-partner/src/components/TencentLocPickerOverlay.tsx @@ -1,9 +1,8 @@ -import { useEffect, useMemo, useState } from 'react'; -import type { ClientRuntimeConfig } from '@dukang/shared-types'; +import { useEffect, useRef, useState } from 'react'; import { request } from '../lib/api'; import { - buildTencentLocPickerUrl, - parseTencentLocPickerMessage, + placeToPicked, + type LbsPlaceItem, type TencentPickedLocation, } from '../lib/tencentLocPicker'; @@ -13,79 +12,117 @@ 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 [key, setKey] = useState(cachedKey ?? null); + const [keyword, setKeyword] = useState(''); + const [items, setItems] = useState([]); 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; } - 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); + 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); } - window.addEventListener('message', onMessage); - return () => window.removeEventListener('message', onMessage); + // eslint-disable-next-line react-hooks/exhaustive-deps }, [open]); - 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]); + 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 }, + ); + } if (!open) return null; @@ -114,6 +151,7 @@ export default function TencentLocPickerOverlay({ padding: '12px 16px', borderBottom: '1px solid rgba(0,0,0,0.06)', flexShrink: 0, + gap: 8, }} > - {pending ? ( -

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

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

- 加载地图… -

- ) : error ? ( -
-

- {error} -

-

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

-
- ) : src ? ( -