From 5e96008ac46bba43247305a6d48b434d8eb3cf83 Mon Sep 17 00:00:00 2001 From: jacy-dukang Date: Thu, 9 Jul 2026 21:12:28 +0800 Subject: [PATCH] =?UTF-8?q?=E9=A6=96=E9=A1=B5=E5=AE=9A=E4=BD=8D=E9=97=AE?= =?UTF-8?q?=E9=A2=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- apps/h5-user/src/components/RegionPicker.tsx | 73 +++++++++++++++++--- apps/h5-user/src/lib/region-data.ts | 19 +++++ apps/h5-user/src/pages/StoreListPage.tsx | 17 +++-- 3 files changed, 90 insertions(+), 19 deletions(-) diff --git a/apps/h5-user/src/components/RegionPicker.tsx b/apps/h5-user/src/components/RegionPicker.tsx index c046a9c..d47ac48 100644 --- a/apps/h5-user/src/components/RegionPicker.tsx +++ b/apps/h5-user/src/components/RegionPicker.tsx @@ -7,6 +7,7 @@ import { getDistrictsForPicker, getProvincesForPicker, normalizeRegionSelection, + toCityLevelRegion, type RegionSelection, } from '../lib/region-data'; @@ -15,26 +16,57 @@ type RegionPickerProps = { value: RegionSelection; onClose: () => void; onConfirm: (region: RegionSelection) => void; + /** 2 = 仅省/市(门店列表);3 = 省/市/区(地址等) */ + levels?: 2 | 3; }; type PickerLevel = 'province' | 'city' | 'district'; -const TABS: Array<{ key: PickerLevel; label: string }> = [ +const ALL_TABS: Array<{ key: PickerLevel; label: string }> = [ { key: 'province', label: '省份' }, { key: 'city', label: '城市' }, { key: 'district', label: '区县' }, ]; -export default function RegionPicker({ open, value, onClose, onConfirm }: RegionPickerProps) { +function initialTab(value: RegionSelection, levels: 2 | 3): PickerLevel { + const normalized = levels === 2 ? toCityLevelRegion(value) : normalizeRegionSelection(value); + if (levels === 2) { + return normalized.province && normalized.province !== REGION_ALL ? 'city' : 'province'; + } + if (normalized.district && normalized.district !== REGION_ALL) return 'district'; + if (normalized.city && normalized.city !== REGION_ALL) return 'city'; + return 'province'; +} + +function tabLabel(tab: PickerLevel, draft: RegionSelection, fallback: string) { + if (tab === 'province') { + return draft.province && draft.province !== REGION_ALL ? draft.province : fallback; + } + if (tab === 'city') { + return draft.city && draft.city !== REGION_ALL ? draft.city : fallback; + } + return draft.district && draft.district !== REGION_ALL ? draft.district : fallback; +} + +export default function RegionPicker({ + open, + value, + onClose, + onConfirm, + levels = 3, +}: RegionPickerProps) { const [draft, setDraft] = useState(value); const [activeTab, setActiveTab] = useState('province'); const listRef = useRef(null); + const tabs = levels === 2 ? ALL_TABS.slice(0, 2) : ALL_TABS; + useEffect(() => { if (!open) return; - setDraft(normalizeRegionSelection(value)); - setActiveTab('province'); - }, [open, value]); + const normalized = levels === 2 ? toCityLevelRegion(value) : normalizeRegionSelection(value); + setDraft(normalized); + setActiveTab(initialTab(value, levels)); + }, [open, value, levels]); const listItems = useMemo(() => { if (activeTab === 'province') return getProvincesForPicker(); @@ -45,7 +77,10 @@ export default function RegionPicker({ open, value, onClose, onConfirm }: Region const selectedValue = activeTab === 'province' ? draft.province : activeTab === 'city' ? draft.city : draft.district; - const canConfirm = Boolean(draft.province && draft.city && draft.district); + const canConfirm = + levels === 2 + ? Boolean(draft.province && draft.city) + : Boolean(draft.province && draft.city && draft.district); useEffect(() => { if (!open) return; @@ -68,6 +103,15 @@ export default function RegionPicker({ open, value, onClose, onConfirm }: Region } const nextCities = getCities(province); const city = nextCities[0] ?? ''; + if (levels === 2) { + setDraft({ + province, + city, + district: REGION_ALL, + }); + setActiveTab('city'); + return; + } const nextDistricts = getDistricts(province, city); setDraft({ province, @@ -80,7 +124,15 @@ export default function RegionPicker({ open, value, onClose, onConfirm }: Region function selectCity(city: string) { if (city === REGION_ALL) { setDraft({ ...draft, city: REGION_ALL, district: REGION_ALL }); - setActiveTab('district'); + if (levels === 3) setActiveTab('district'); + return; + } + if (levels === 2) { + setDraft({ + ...draft, + city, + district: REGION_ALL, + }); return; } const nextDistricts = getDistricts(draft.province, city); @@ -110,7 +162,8 @@ export default function RegionPicker({ open, value, onClose, onConfirm }: Region function handleConfirm() { if (!canConfirm) return; - onConfirm(normalizeRegionSelection(draft)); + const next = levels === 2 ? toCityLevelRegion(draft) : normalizeRegionSelection(draft); + onConfirm(next); } return ( @@ -123,7 +176,7 @@ export default function RegionPicker({ open, value, onClose, onConfirm }: Region >
- {TABS.map((tab) => { + {tabs.map((tab) => { const disabled = (tab.key === 'city' && !draft.province) || (tab.key === 'district' && (!draft.province || !draft.city)); @@ -137,7 +190,7 @@ export default function RegionPicker({ open, value, onClose, onConfirm }: Region className={`region-picker-tab${activeTab === tab.key ? ' active' : ''}`} onClick={() => onTabClick(tab.key)} > - {tab.label} + {tabLabel(tab.key, draft, tab.label)} ); })} diff --git a/apps/h5-user/src/lib/region-data.ts b/apps/h5-user/src/lib/region-data.ts index 55dc928..7e3959c 100644 --- a/apps/h5-user/src/lib/region-data.ts +++ b/apps/h5-user/src/lib/region-data.ts @@ -58,6 +58,25 @@ export function formatRegion(province: string, city: string, district: string): return `${province} ${city} ${district}`; } +/** 仅展示省、市两级(门店列表等场景) */ +export function formatRegionCity(province: string, city: string): string { + if (!province) return ''; + if (province === REGION_ALL) return REGION_ALL; + if (city === REGION_ALL) return `${province} ${REGION_ALL}`; + if (!city) return province; + return `${province} ${city}`; +} + +/** 门店筛选:固定为市级,不按区县过滤 */ +export function toCityLevelRegion(selection: RegionSelection): RegionSelection { + const normalized = normalizeRegionSelection(selection); + return { + province: normalized.province, + city: normalized.city, + district: REGION_ALL, + }; +} + export type RegionSelection = { province: string; city: string; diff --git a/apps/h5-user/src/pages/StoreListPage.tsx b/apps/h5-user/src/pages/StoreListPage.tsx index 28627af..0775e33 100644 --- a/apps/h5-user/src/pages/StoreListPage.tsx +++ b/apps/h5-user/src/pages/StoreListPage.tsx @@ -8,8 +8,9 @@ import RegionPicker from '../components/RegionPicker'; import { DEFAULT_REGION, FALLBACK_CITY_REGION, - formatRegion, + formatRegionCity, REGION_ALL, + toCityLevelRegion, type RegionSelection, } from '../lib/region-data'; import { FALLBACK_CITY_CODE, resolveUserCity } from '../lib/wechat-location'; @@ -62,7 +63,7 @@ export default function StoreListPage() { const [loading, setLoading] = useState(false); const [categoryTab, setCategoryTab] = useState('全部'); const [keyword, setKeyword] = useState(''); - const [region, setRegion] = useState(DEFAULT_REGION); + const [region, setRegion] = useState(() => toCityLevelRegion(DEFAULT_REGION)); const [regionPickerOpen, setRegionPickerOpen] = useState(false); const [filterMode, setFilterMode] = useState<'auto' | 'manual'>('auto'); const [usedFallback, setUsedFallback] = useState(false); @@ -82,7 +83,7 @@ export default function StoreListPage() { if (filterMode !== 'auto' || geoReady) return; resolveUserCity().then((resolved) => { if (resolved) { - setRegion(resolved.region); + setRegion(toCityLevelRegion(resolved.region)); } setGeoReady(true); }); @@ -103,7 +104,7 @@ export default function StoreListPage() { code !== FALLBACK_CITY_CODE ) { setUsedFallback(true); - setRegion(FALLBACK_CITY_REGION); + setRegion(toCityLevelRegion(FALLBACK_CITY_REGION)); return null; } return data; @@ -138,11 +139,8 @@ export default function StoreListPage() { const city = s.cityName ?? '郑州市'; if (region.province !== REGION_ALL && province !== region.province) return false; if (region.city !== REGION_ALL && city !== region.city) return false; - if (region.district !== REGION_ALL && s.district !== region.district) return false; return true; }); - } else if (region.district !== REGION_ALL) { - list = list.filter((s) => s.district === region.district); } const q = keyword.trim().toLowerCase(); if (q) { @@ -156,7 +154,7 @@ export default function StoreListPage() { return list; }, [stores, categoryTab, keyword, region, cityCode]); - const regionLabel = formatRegion(region.province, region.city, region.district); + const regionLabel = formatRegionCity(region.province, region.city); const emptyMessage = filterMode === 'manual' && filtered.length === 0 ? '未找到匹配门店' @@ -271,10 +269,11 @@ export default function StoreListPage() { setRegionPickerOpen(false)} onConfirm={(next) => { setFilterMode('manual'); - setRegion(next); + setRegion(toCityLevelRegion(next)); setRegionPickerOpen(false); }} />