import { useCallback, useEffect, useMemo, useState } from 'react'; import { Link, useNavigate } from 'react-router-dom'; import AppImage from '@dukang/shared-ui/AppImage'; import { request } from '../lib/api'; import { track } from '../lib/analytics'; import TabMainHeader from '../components/TabMainHeader'; import RegionPicker from '../components/RegionPicker'; import { DEFAULT_REGION, FALLBACK_CITY_REGION, formatRegion, REGION_ALL, type RegionSelection, } from '../lib/region-data'; import { FALLBACK_CITY_CODE, resolveUserCity } from '../lib/wechat-location'; type OpenCity = { code: string; name: string; province: string; }; type StoreItem = { id: string; name: string; province?: string; cityName?: string; district: string; address: string; coverUrl?: string | null; status: string; openTime?: string | null; closeTime?: string | null; category?: { name: string } | null; }; function resolveCityCode(region: RegionSelection, cities: OpenCity[]): string | undefined { if (region.province === REGION_ALL || region.city === REGION_ALL) return undefined; return cities.find((c) => c.province === region.province && c.name === region.city)?.code; } const CATEGORY_TABS = ['全部', '火锅', '地方菜', '高端餐饮', '烧烤烤肉', '西餐'] as const; const MOCK_DISTANCES = ['800m', '1.2km', '3.5km', '1.5km', '2.0km']; function storeCover(store: StoreItem, index: number) { if (store.coverUrl) return String(store.coverUrl); const imgs = ['/images/1.png', '/images/2.png', '/images/3.png']; return imgs[index % imgs.length]; } function formatHours(store: StoreItem) { if (store.openTime && store.closeTime) { return `营业时间: ${store.openTime}-${store.closeTime}`; } return '营业时间: 10:00-22:00'; } export default function StoreListPage() { const navigate = useNavigate(); const [cities, setCities] = useState([]); const [stores, setStores] = useState([]); const [loading, setLoading] = useState(false); const [categoryTab, setCategoryTab] = useState('全部'); const [keyword, setKeyword] = useState(''); const [region, setRegion] = useState(DEFAULT_REGION); const [regionPickerOpen, setRegionPickerOpen] = useState(false); const [filterMode, setFilterMode] = useState<'auto' | 'manual'>('auto'); const [usedFallback, setUsedFallback] = useState(false); const [geoReady, setGeoReady] = useState(false); const cityCode = useMemo(() => resolveCityCode(region, cities), [region, cities]); useEffect(() => { track('store_list_view', { pagePath: '/stores' }); }, []); useEffect(() => { request('USER_H5', '/catalog/cities').then(setCities); }, []); useEffect(() => { if (filterMode !== 'auto' || geoReady) return; resolveUserCity().then((resolved) => { if (resolved) { setRegion(resolved.region); } setGeoReady(true); }); }, [filterMode, geoReady]); const fetchStores = useCallback( async (targetRegion: RegionSelection, allowFallback: boolean) => { const code = resolveCityCode(targetRegion, cities); const qs = code ? `?cityCode=${encodeURIComponent(code)}` : ''; const data = await request('USER_H5', `/stores${qs}`).catch(() => [] as StoreItem[]); if ( allowFallback && filterMode === 'auto' && data.length === 0 && !usedFallback && code && code !== FALLBACK_CITY_CODE ) { setUsedFallback(true); setRegion(FALLBACK_CITY_REGION); return null; } return data; }, [cities, filterMode, usedFallback], ); useEffect(() => { if (filterMode === 'auto' && !geoReady) return; let cancelled = false; setLoading(true); fetchStores(region, true) .then((data) => { if (!cancelled && data) setStores(data); }) .finally(() => { if (!cancelled) setLoading(false); }); return () => { cancelled = true; }; }, [region, cities, filterMode, geoReady, fetchStores, usedFallback]); const filtered = useMemo(() => { let list = stores; if (categoryTab !== '全部') { list = list.filter((s) => s.category?.name === categoryTab); } if (!cityCode) { list = list.filter((s) => { const province = s.province ?? '河南省'; 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) { list = list.filter( (s) => s.name.toLowerCase().includes(q) || s.address.toLowerCase().includes(q) || s.district.toLowerCase().includes(q), ); } return list; }, [stores, categoryTab, keyword, region, cityCode]); const regionLabel = formatRegion(region.province, region.city, region.district); const emptyMessage = filterMode === 'manual' && filtered.length === 0 ? '未找到匹配门店' : '暂无门店'; return (
search { setFilterMode('manual'); setKeyword(e.target.value); }} />
{CATEGORY_TABS.map((tab) => ( ))}
{loading &&
加载门店中...
} {!loading && filtered.map((s, index) => (
navigate(`/stores/${s.id}`)} onKeyDown={(e) => { if (e.key === 'Enter' || e.key === ' ') { e.preventDefault(); navigate(`/stores/${s.id}`); } }} >

{s.name}

{MOCK_DISTANCES[index % MOCK_DISTANCES.length]}
营业中 {formatHours(s)}

{s.district} {s.address}

e.stopPropagation()} > 去核销
))} {!loading && filtered.length === 0 && (
{emptyMessage}
)} {!loading && filtered.length > 0 && (

没有更多门店了

)}
setRegionPickerOpen(false)} onConfirm={(next) => { setFilterMode('manual'); setRegion(next); setRegionPickerOpen(false); }} />
); }