From 2b85f63e2b4b9a470557aaa3eb97adfc1fd63c0f Mon Sep 17 00:00:00 2001 From: jacy <18049821889@163.com> Date: Wed, 29 Jul 2026 12:30:03 +0800 Subject: [PATCH] =?UTF-8?q?=E7=94=A8=E6=88=B7=E5=B0=8F=E7=A8=8B=E5=BA=8F?= =?UTF-8?q?=E6=89=8B=E6=9C=BA=E5=8F=B7=20=E9=97=A8=E5=BA=97=E9=97=AA?= =?UTF-8?q?=E7=83=81=E9=80=BB=E8=BE=91?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- apps/mini-user/src/pages/stores/index.tsx | 108 ++++++++++++++++------ 1 file changed, 81 insertions(+), 27 deletions(-) diff --git a/apps/mini-user/src/pages/stores/index.tsx b/apps/mini-user/src/pages/stores/index.tsx index c77d443..207cacb 100644 --- a/apps/mini-user/src/pages/stores/index.tsx +++ b/apps/mini-user/src/pages/stores/index.tsx @@ -54,25 +54,43 @@ type Store = { distanceMeters?: number | null; }; -function regionEqual(a: RegionSelection, b: RegionSelection): boolean { - return a.province === b.province && a.city === b.city && a.district === b.district; +/** 筛选城市键(省+市);同城不重复请求 */ +function makeCityKey(region: Pick): string { + return `${region.province}|${region.city}`; } +function sameFilterCity(a: RegionSelection, b: RegionSelection): boolean { + return a.province === b.province && a.city === b.city; +} + +/** 跨 tab 切换 / 页面重建仍复用,避免同城反复打 /stores */ +type StoresListCache = { + cityKey: string; + cityCode: string; + region: RegionSelection; + items: Store[]; +}; +let storesListCache: StoresListCache | null = null; + export default function StoresPage() { - const [stores, setStores] = useState([]); - const [loading, setLoading] = useState(true); + const [stores, setStores] = useState(() => storesListCache?.items ?? []); + const [loading, setLoading] = useState(() => !storesListCache); const [keywordInput, setKeywordInput] = useState(''); const [keyword, setKeyword] = useState(''); - const [region, setRegion] = useState(DEFAULT_REGION); + // 必须与缓存城市对齐,否则 remount 时用默认「郑州」筛掉缓存列表会闪「暂无」 + const [region, setRegion] = useState( + () => storesListCache?.region ?? DEFAULT_REGION, + ); const [regionOpen, setRegionOpen] = useState(false); const [category, setCategory] = useState(EMPTY_CATEGORY); const [categoryOpen, setCategoryOpen] = useState(false); const [categoryTree, setCategoryTree] = useState([]); - /** 上次已用于请求的城市编码;仅换城才再拉列表 */ - const cityCodeRef = useRef(null); + const fetchCityKeyRef = useRef(storesListCache?.cityKey ?? null); const fetchSeqRef = useRef(0); const regionLabel = formatRegionLabel(region); const categoryLabel = formatCategoryLabel(category); + /** 有数据时只显示列表,不用「加载中」盖住(避免闪烁) */ + const showBootLoading = loading && stores.length === 0; const childIdsByParent = useMemo(() => { const map = new Map(); @@ -85,10 +103,13 @@ export default function StoresPage() { return map; }, [categoryTree]); - /** 按城市拉门店;coords 仅用于排序距离,不单独触发二次请求 */ - async function fetchStores(nextCode: string, coords: UserCoords | null) { + async function fetchStores( + nextCode: string, + coords: UserCoords | null, + cityKey: string, + nextRegion: RegionSelection, + ) { const seq = ++fetchSeqRef.current; - setLoading(true); const qs = new URLSearchParams(); if (nextCode) qs.set('cityCode', nextCode); if (coords) { @@ -99,7 +120,15 @@ export default function StoresPage() { try { const list = await request(path); if (seq !== fetchSeqRef.current) return; - setStores(Array.isArray(list) ? list : []); + const items = Array.isArray(list) ? list : []; + setStores(items); + fetchCityKeyRef.current = cityKey; + storesListCache = { + cityKey, + cityCode: nextCode, + region: toCityWideRegion(nextRegion), + items, + }; } catch (e) { if (seq !== fetchSeqRef.current) return; toast(e instanceof Error ? e.message : '加载失败'); @@ -109,26 +138,46 @@ export default function StoresPage() { } /** - * 流程:先定位 → 再请求一次列表(成功用定位城市,失败用默认城市)。 - * 再次进入页且城市未变:不重复请求,避免列表闪烁。 + * 先稳住缓存画面 → 再定位;同城不请求;换城再拉。 */ useDidShow(() => { syncTabBarSelected(1); + + // 同步恢复缓存,避免 await 定位期间 region 不对导致列表被滤空 + if (storesListCache) { + fetchCityKeyRef.current = storesListCache.cityKey; + setStores((prev) => (prev.length > 0 ? prev : storesListCache!.items)); + setRegion((prev) => + sameFilterCity(prev, storesListCache!.region) ? prev : storesListCache!.region, + ); + setLoading(false); + } + void (async () => { const resolved = await resolveUserCity(); const nextCode = getCityCodeForCatalog(resolved); - const nextCoords = readCachedUserCoords(); const nextRegion = toCityWideRegion(resolved.region); - const cityChanged = cityCodeRef.current !== nextCode; + const nextCityKey = makeCityKey(nextRegion); - setRegion((prev) => (regionEqual(prev, nextRegion) ? prev : nextRegion)); - - if (cityCodeRef.current == null || cityChanged) { - cityCodeRef.current = nextCode; - await fetchStores(nextCode, nextCoords); + if ( + fetchCityKeyRef.current === nextCityKey || + storesListCache?.cityKey === nextCityKey + ) { + if (storesListCache?.cityKey === nextCityKey) { + fetchCityKeyRef.current = nextCityKey; + setStores((prev) => (prev.length > 0 ? prev : storesListCache!.items)); + // 同城不覆盖用户已选区县 + setRegion((prev) => (sameFilterCity(prev, nextRegion) ? prev : nextRegion)); + } + setLoading(false); return; } - setLoading(false); + + // 换城:先清空再 loading,避免旧城数据 + 新城筛选交叉闪一下 + setStores([]); + setRegion(nextRegion); + setLoading(true); + await fetchStores(nextCode, readCachedUserCoords(), nextCityKey, nextRegion); })(); }); @@ -144,10 +193,13 @@ export default function StoresPage() { const resolved = await resolveUserCity(true); const nextCode = getCityCodeForCatalog(resolved); const nextRegion = toCityWideRegion(resolved.region); - const coords = readCachedUserCoords(); - cityCodeRef.current = nextCode; + const nextCityKey = makeCityKey(nextRegion); setRegion(nextRegion); - await fetchStores(nextCode, coords); + if (fetchCityKeyRef.current !== nextCityKey) { + setStores([]); + setLoading(true); + } + await fetchStores(nextCode, readCachedUserCoords(), nextCityKey, nextRegion); } catch (e) { toast(e instanceof Error ? e.message : '加载失败'); setLoading(false); @@ -246,9 +298,11 @@ export default function StoresPage() { - {loading ? 加载中… : null} - {!loading && filtered.length === 0 ? 暂无营业中门店 : null} - {!loading && + {showBootLoading ? 加载中… : null} + {!showBootLoading && filtered.length === 0 ? ( + 暂无营业中门店 + ) : null} + {!showBootLoading && filtered.map((s) => (