From 23203f6ebbe59bb44bc4a41a566cb4be1b49bea6 Mon Sep 17 00:00:00 2001 From: "2569718930@qq.com" <2569718930@qq.com> Date: Tue, 26 May 2026 08:30:33 +0800 Subject: [PATCH] =?UTF-8?q?=E5=9B=BE=E8=A1=A8=20stats=20bar=20=E6=B8=A9?= =?UTF-8?q?=E5=BA=A6=E6=95=B0=E5=AD=97=2060=20=E7=A7=92=E7=BA=A7=E8=BD=BB?= =?UTF-8?q?=E9=87=8F=E8=BD=AE=E8=AF=A2?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit liveTemp state 独立于 hourly 缓存,每 60s fetch /api/city/{city}/summary。 只取 current.temp,不触发 _analyze() 重算。 图表曲线保持 5min TTL,顶部数字最快 1min 级更新。 --- .../LiveTemperatureThresholdChart.tsx | 24 +++++++++++++++++-- .../__tests__/refreshCadencePolicy.test.ts | 5 +++- 2 files changed, 26 insertions(+), 3 deletions(-) diff --git a/frontend/components/dashboard/scan-terminal/LiveTemperatureThresholdChart.tsx b/frontend/components/dashboard/scan-terminal/LiveTemperatureThresholdChart.tsx index c7aa24de..27d1e643 100644 --- a/frontend/components/dashboard/scan-terminal/LiveTemperatureThresholdChart.tsx +++ b/frontend/components/dashboard/scan-terminal/LiveTemperatureThresholdChart.tsx @@ -1117,6 +1117,7 @@ export function LiveTemperatureThresholdChart({ const city = String(row?.city || "").toLowerCase().trim(); const [timeframe, setTimeframe] = useState<"1D" | "3D">("1D"); const [userToggledKeys, setUserToggledKeys] = useState>({}); + const [liveTemp, setLiveTemp] = useState(null); useEffect(() => { setUserToggledKeys({}); @@ -1167,6 +1168,24 @@ export function LiveTemperatureThresholdChart({ }; }, [city, row, isActive, slotIndex]); + // ── 60s lightweight live-temp poll ── + useEffect(() => { + if (!city) return; + const fetchLiveTemp = () => { + fetch(`/api/city/${encodeURIComponent(city)}/summary`) + .then((res) => (res.ok ? res.json() : null)) + .then((payload) => { + if (!payload) return; + const temp = validNumber(payload?.current?.temp); + if (temp !== null) setLiveTemp(temp); + }) + .catch(() => {}); + }; + fetchLiveTemp(); + const id = setInterval(fetchLiveTemp, 60_000); + return () => clearInterval(id); + }, [city]); + const { data, series } = useMemo(() => { if (timeframe === "3D") { return build3DayChartData(row, hourly); @@ -1247,6 +1266,7 @@ export function LiveTemperatureThresholdChart({ () => getObservationDisplayMetrics(row, hourly, settlementPlate), [row, hourly, settlementPlate], ); + const displayRunwayTemp = liveTemp ?? currentRunwayTemp; const wundergroundDailyHigh = validNumber(hourly?.airportCurrent?.max_so_far ?? hourly?.airportPrimary?.max_so_far) ?? null; const modelValues = Object.values(row?.model_cluster_sources || {}) @@ -1412,7 +1432,7 @@ export function LiveTemperatureThresholdChart({
{isEn ? "Runway" : runwayHeaderLabel}:{" "} - {temp(currentRunwayTemp)} + {temp(displayRunwayTemp)} | @@ -1454,7 +1474,7 @@ export function LiveTemperatureThresholdChart({ {isEn ? "Runway Live (1m)" : `${runwayHeaderLabel}`} - {temp(currentRunwayTemp)} + {temp(displayRunwayTemp)}
diff --git a/frontend/components/dashboard/scan-terminal/__tests__/refreshCadencePolicy.test.ts b/frontend/components/dashboard/scan-terminal/__tests__/refreshCadencePolicy.test.ts index c39f5fe4..861f0308 100644 --- a/frontend/components/dashboard/scan-terminal/__tests__/refreshCadencePolicy.test.ts +++ b/frontend/components/dashboard/scan-terminal/__tests__/refreshCadencePolicy.test.ts @@ -34,10 +34,13 @@ export function runTests() { ); assert( chartSource.includes("DASHBOARD_REFRESH_POLICY_MS.metar") && - !chartSource.includes("setInterval(") && !chartSource.includes("window.setInterval"), "selected city detail chart cache should align with 5-minute scan/metar cadence", ); + assert( + chartSource.includes("setInterval(fetchLiveTemp, 60_000)"), + "selected city chart should poll live temperature every 60 seconds via lightweight summary endpoint", + ); assert( chartSource.includes("_hourlyRequestCache") && chartSource.includes("seedHourlyForecastFromRow") &&