数据链路 P2 修复:stale-while-revalidate + 扫描数据复用

P2-7 stale-while-revalidate:
- ensureCityDetail 过期缓存不再阻塞等待刷新
- 立即返回缓存数据,后台异步更新
- 用户打开已有缓存的城市时不再看到 loading spinner

P2-8 扫描终端数据复用:
- 新增 store.preloadCityFromRow():从 ScanOpportunityRow 预填充 cityDetails 缓存
- handleSelectRow / handleMapCitySelect / handleOpenDecisionRow 均调用预加载
- 用户从地图/列表/决策卡选城市后,详情面板立即显示缓存数据
- 后台自动拉取完整 detail(stale-while-revalidate)

Tested: npx tsc --noEmit
This commit is contained in:
2569718930@qq.com
2026-05-10 17:00:12 +08:00
parent b3ea8dcfa7
commit 2b2784d811
2 changed files with 49 additions and 26 deletions
@@ -256,10 +256,11 @@ function ScanTerminalScreen() {
setMapSelectedCityName(cityName);
lastMapSelectedCityRef.current = normalizeCityKey(cityName);
const matchedRow = findRowForCity(timeSortedRows, cityName);
if (matchedRow) store.preloadCityFromRow(matchedRow);
setSelectedRowId(matchedRow?.id || null);
addAiPinnedCity(cityName);
setActiveView("analysis");
}, [addAiPinnedCity, timeSortedRows]);
}, [addAiPinnedCity, store, timeSortedRows]);
useEffect(() => {
if (activeView !== "map") return;
@@ -277,6 +278,7 @@ function ScanTerminalScreen() {
const cityName = row.city || row.city_display_name || row.display_name || "";
if (!cityName) return;
setSelectedRowId(row.id);
store.preloadCityFromRow(row);
const selectedCityKey = normalizeCityKey(store.selectedCity);
const rowCityKey = normalizeCityKey(cityName);
const hasCachedDetail =
@@ -297,6 +299,7 @@ function ScanTerminalScreen() {
const cityName = row.city || row.city_display_name || row.display_name || "";
if (!cityName) return;
setSelectedRowId(row.id);
store.preloadCityFromRow(row);
addAiPinnedCity(cityName);
setActiveView("analysis");
void store.selectCity(cityName);