import { useEffect, useMemo, useRef, useState } from 'react'; import { REGION_ALL, getCities, getCitiesForPicker, getDistricts, getDistrictsForPicker, getProvincesForPicker, normalizeRegionSelection, toCityLevelRegion, type RegionSelection, } from '../lib/region-data'; type RegionPickerProps = { open: boolean; value: RegionSelection; onClose: () => void; onConfirm: (region: RegionSelection) => void; /** 2 = 仅省/市(门店列表);3 = 省/市/区(地址等) */ levels?: 2 | 3; }; type PickerLevel = 'province' | 'city' | 'district'; const ALL_TABS: Array<{ key: PickerLevel; label: string }> = [ { key: 'province', label: '省份' }, { key: 'city', label: '城市' }, { key: 'district', label: '区县' }, ]; 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; 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(); if (activeTab === 'city') return getCitiesForPicker(draft.province); return getDistrictsForPicker(draft.province, draft.city); }, [activeTab, draft.province, draft.city]); const selectedValue = activeTab === 'province' ? draft.province : activeTab === 'city' ? draft.city : draft.district; const canConfirm = levels === 2 ? Boolean(draft.province && draft.city) : Boolean(draft.province && draft.city && draft.district); useEffect(() => { if (!open) return; scrollActiveIntoView(listRef.current, selectedValue); }, [open, activeTab, selectedValue, listItems.length]); if (!open) return null; function scrollActiveIntoView(container: HTMLDivElement | null, label: string) { if (!container || !label) return; const active = container.querySelector(`[data-label="${CSS.escape(label)}"]`); active?.scrollIntoView({ block: 'nearest' }); } function selectProvince(province: string) { if (province === REGION_ALL) { setDraft({ province: REGION_ALL, city: REGION_ALL, district: REGION_ALL }); setActiveTab('city'); return; } 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, city, district: nextDistricts[0] ?? '', }); setActiveTab('city'); } function selectCity(city: string) { if (city === REGION_ALL) { setDraft({ ...draft, city: REGION_ALL, district: REGION_ALL }); if (levels === 3) setActiveTab('district'); return; } if (levels === 2) { setDraft({ ...draft, city, district: REGION_ALL, }); return; } const nextDistricts = getDistricts(draft.province, city); setDraft({ ...draft, city, district: nextDistricts[0] ?? '', }); setActiveTab('district'); } function selectDistrict(district: string) { setDraft({ ...draft, district }); } function onSelectItem(item: string) { if (activeTab === 'province') selectProvince(item); else if (activeTab === 'city') selectCity(item); else selectDistrict(item); } function onTabClick(tab: PickerLevel) { if (tab === 'city' && !draft.province) return; if (tab === 'district' && (!draft.province || !draft.city)) return; setActiveTab(tab); } function handleConfirm() { if (!canConfirm) return; const next = levels === 2 ? toCityLevelRegion(draft) : normalizeRegionSelection(draft); onConfirm(next); } return (
e.stopPropagation()} >
{tabs.map((tab) => { const disabled = (tab.key === 'city' && !draft.province) || (tab.key === 'district' && (!draft.province || !draft.city)); return ( ); })}
{listItems.map((item) => ( ))}
); }