317f910f3c
Store create/edit on HQ and partner can pick coords via Tencent locpicker; client-config exposes TENCENT_LBS_KEY; seed empty system settings from env. Co-authored-by: Cursor <cursoragent@cursor.com>
134 lines
3.6 KiB
TypeScript
134 lines
3.6 KiB
TypeScript
import { useEffect, useMemo, useState } from 'react';
|
|
import type { ClientRuntimeConfig } from '@dukang/shared-types';
|
|
import { request } from '../lib/api';
|
|
import {
|
|
buildTencentLocPickerUrl,
|
|
parseTencentLocPickerMessage,
|
|
type TencentPickedLocation,
|
|
} from '../lib/tencentLocPicker';
|
|
|
|
type Props = {
|
|
open: boolean;
|
|
onClose: () => void;
|
|
onPick: (loc: TencentPickedLocation) => void;
|
|
latitude?: number | null;
|
|
longitude?: number | null;
|
|
};
|
|
|
|
let cachedKey: string | null | undefined;
|
|
|
|
async function loadTencentLbsKey(): Promise<string> {
|
|
if (cachedKey) return cachedKey;
|
|
const cfg = await request<ClientRuntimeConfig>('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,
|
|
}: Props) {
|
|
const [key, setKey] = useState<string | null>(cachedKey ?? null);
|
|
const [loading, setLoading] = useState(false);
|
|
const [error, setError] = useState('');
|
|
|
|
useEffect(() => {
|
|
if (!open) 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;
|
|
onPick(picked);
|
|
onClose();
|
|
}
|
|
window.addEventListener('message', onMessage);
|
|
return () => window.removeEventListener('message', onMessage);
|
|
}, [open, onPick, onClose]);
|
|
|
|
const src = useMemo(() => {
|
|
if (!key) return '';
|
|
return buildTencentLocPickerUrl(key, {
|
|
latitude: latitude != null ? Number(latitude) : undefined,
|
|
longitude: longitude != null ? Number(longitude) : undefined,
|
|
});
|
|
}, [key, latitude, longitude]);
|
|
|
|
if (!open) return null;
|
|
|
|
return (
|
|
<div
|
|
style={{
|
|
position: 'fixed',
|
|
inset: 0,
|
|
zIndex: 1000,
|
|
background: '#fff',
|
|
display: 'flex',
|
|
flexDirection: 'column',
|
|
}}
|
|
>
|
|
<div
|
|
style={{
|
|
display: 'flex',
|
|
alignItems: 'center',
|
|
justifyContent: 'space-between',
|
|
padding: '12px 16px',
|
|
borderBottom: '1px solid rgba(0,0,0,0.06)',
|
|
flexShrink: 0,
|
|
}}
|
|
>
|
|
<button type="button" className="partner-btn-outline" style={{ padding: '6px 12px' }} onClick={onClose}>
|
|
关闭
|
|
</button>
|
|
<span style={{ fontWeight: 600 }}>地图选点</span>
|
|
<span style={{ width: 52 }} />
|
|
</div>
|
|
<div style={{ flex: 1, minHeight: 0, position: 'relative' }}>
|
|
{loading ? (
|
|
<p className="label-md text-muted" style={{ padding: 24, textAlign: 'center' }}>
|
|
加载地图…
|
|
</p>
|
|
) : error ? (
|
|
<p className="label-md" style={{ padding: 24, color: 'var(--color-heritage-red, #a61d24)' }}>
|
|
{error}
|
|
</p>
|
|
) : src ? (
|
|
<iframe
|
|
title="腾讯地图选点"
|
|
src={src}
|
|
style={{ width: '100%', height: '100%', border: 0, display: 'block' }}
|
|
/>
|
|
) : null}
|
|
</div>
|
|
</div>
|
|
);
|
|
}
|