feat(admin,partner): Tencent map location picker and expose LBS key

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>
This commit is contained in:
2026-07-27 23:19:40 +08:00
parent d36fe13bcd
commit 317f910f3c
13 changed files with 571 additions and 52 deletions
+68 -13
View File
@@ -1,5 +1,5 @@
import { useEffect, useMemo, useState } from 'react';
import { View, Text } from '@tarojs/components';
import { View, Text, Image } from '@tarojs/components';
import Taro, { usePageScroll, useRouter, useShareAppMessage, useShareTimeline } from '@tarojs/taro';
import PageShell from '../../components/PageShell';
import PageNavBar from '../../components/PageNavBar';
@@ -13,6 +13,12 @@ import {
toWeappShareMessage,
} from '../../lib/wechat-share';
type StoreMedia = {
url?: string | null;
bizType?: string | null;
mediaType?: string | null;
};
type Store = {
id: string;
name: string;
@@ -25,6 +31,7 @@ type Store = {
intro?: string | null;
coverUrl?: string | null;
carouselUrls?: string[] | null;
media?: StoreMedia[] | null;
openTime?: string | null;
closeTime?: string | null;
openTime2?: string | null;
@@ -35,6 +42,26 @@ type Store = {
category?: { name: string } | null;
};
function uniqueUrls(urls: Array<string | null | undefined>) {
const seen = new Set<string>();
const out: string[] = [];
for (const raw of urls) {
const url = String(raw || '').trim();
if (!url || seen.has(url)) continue;
seen.add(url);
out.push(url);
}
return out;
}
function envPhotoUrls(store: Store) {
return uniqueUrls(
(store.media || [])
.filter((m) => !m.bizType || m.bizType === 'ENV')
.map((m) => m.url),
);
}
function fullAddress(store: Store) {
const city = store.cityName || store.city || '';
return `${store.province || ''}${city}${store.district || ''}${store.address || ''}`.trim();
@@ -66,12 +93,15 @@ export default function StoreDetailPage() {
}, [storeId]);
const sharePayload = useMemo(
() => ({
title: store?.name || DEFAULT_SHARE_TITLE,
desc: store?.intro?.trim() || store?.address || DEFAULT_SHARE_DESC,
path: `/pages/store-detail/index?id=${storeId}`,
imgUrl: store?.coverUrl || store?.carouselUrls?.[0] || undefined,
}),
() => {
const envFirst = store ? envPhotoUrls(store)[0] : undefined;
return {
title: store?.name || DEFAULT_SHARE_TITLE,
desc: store?.intro?.trim() || store?.address || DEFAULT_SHARE_DESC,
path: `/pages/store-detail/index?id=${storeId}`,
imgUrl: store?.coverUrl || envFirst || store?.carouselUrls?.[0] || undefined,
};
},
[store, storeId],
);
@@ -135,15 +165,23 @@ export default function StoreDetailPage() {
);
}
const images =
(store.carouselUrls && store.carouselUrls.length > 0
? store.carouselUrls
: store.coverUrl
? [store.coverUrl]
: []) as string[];
const envPhotos = envPhotoUrls(store);
const images = uniqueUrls([
store.coverUrl,
...(store.carouselUrls || []),
...envPhotos,
]);
const intro = store.intro?.trim() || '';
function previewEnv(index: number) {
if (!envPhotos.length) return;
Taro.previewImage({
current: envPhotos[index],
urls: envPhotos,
}).catch(() => toast('无法预览图片'));
}
return (
<PageShell variant="scroll" className="store-detail-page" hasFixedFooter>
<WechatShareReady payload={sharePayload} />
@@ -210,6 +248,23 @@ export default function StoreDetailPage() {
</View>
) : null}
{envPhotos.length > 0 ? (
<View className="store-detail-section">
<Text className="store-detail-section-title"></Text>
<View className="store-detail-env-grid">
{envPhotos.map((url, index) => (
<View
key={`${url}-${index}`}
className="store-detail-env-item"
onClick={() => previewEnv(index)}
>
<Image className="store-detail-env-img" src={url} mode="aspectFill" />
</View>
))}
</View>
</View>
) : null}
<View className="store-detail-bar">
<View
className="u-btn u-btn--block"
@@ -144,6 +144,27 @@
color: var(--color-on-surface);
}
.store-detail-env-grid {
display: grid;
grid-template-columns: repeat(3, 1fr);
gap: 8px;
}
.store-detail-env-item {
position: relative;
width: 100%;
aspect-ratio: 1;
border-radius: var(--radius-md, 8px);
overflow: hidden;
background: var(--color-surface-container);
}
.store-detail-env-img {
width: 100%;
height: 100%;
display: block;
}
.store-detail-bar {
position: fixed;
bottom: 0;