From d94476f9437ff82ec181fa852f07a50dcc09356b Mon Sep 17 00:00:00 2001 From: "2569718930@qq.com" <2569718930@qq.com> Date: Sun, 10 May 2026 17:33:15 +0800 Subject: [PATCH] =?UTF-8?q?UX=20=E5=AE=A1=E6=9F=A5=E4=BF=AE=E5=A4=8D?= =?UTF-8?q?=EF=BC=9A=E7=BB=9F=E4=B8=80=E4=BF=AE=E6=AD=A3=E6=9C=AF=E8=AF=AD?= =?UTF-8?q?=E3=80=81=E5=9B=BE=E8=A1=A8"=E7=8E=B0=E5=9C=A8"=E6=A0=87?= =?UTF-8?q?=E8=AE=B0=E3=80=81=E4=B8=93=E4=B8=9A=E6=9C=AF=E8=AF=AD=E8=A7=A3?= =?UTF-8?q?=E9=87=8A=E3=80=81=E5=BC=82=E5=B8=B8=E8=A1=8C=E5=8A=A8=E5=BB=BA?= =?UTF-8?q?=E8=AE=AE?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit P0-1 术语统一: - WeatherDecisionBand 改用"上修/下修/维持"替代"偏高温/暂不追/等待确认" - 与 AI 后端提示词使用的术语体系一致 P0-9 图表"现在"标记: - chart-utils.ts 导出 currentIndex - AiCityTemperatureChart 在 currentIndex 处绘制竖线(蓝色虚线) P0-13 专业术语解释: - DataFreshnessBar 新增 labelTitle 属性(hover tooltip) - METAR → "机场气象观测报文" / "Meteorological Aerodrome Report" - HKO → "香港天文台官方实测" / "Hong Kong Observatory official readings" P0-16 异常行动建议: - primaryReason 追加行动指引(实测突破→建议关注偏高温区间等待确认 / 峰值已过→建议避免追高 / 观测过旧→建议等待新报文) P1-8 DEB 路径分段样式: - 过去部分实线(已确定),未来部分虚线(预测不确定) P1-11 HistoryChart 单位: - tooltip 使用 temp_symbol 替代硬编码 ° Tested: npx tsc --noEmit --- .../components/dashboard/HistoryChart.tsx | 2 +- .../scan-terminal/AiCityTemperatureChart.tsx | 24 +++++++++++++++++-- .../scan-terminal/AiPinnedCityCard.tsx | 3 +++ .../scan-terminal/DataFreshnessBar.tsx | 3 ++- .../scan-terminal/city-card-decision-utils.ts | 12 +++++----- .../scan-terminal/city-decision-state.ts | 24 +++++++++---------- frontend/lib/chart-utils.ts | 1 + 7 files changed, 47 insertions(+), 22 deletions(-) diff --git a/frontend/components/dashboard/HistoryChart.tsx b/frontend/components/dashboard/HistoryChart.tsx index b5ff2e2b..299ac512 100644 --- a/frontend/components/dashboard/HistoryChart.tsx +++ b/frontend/components/dashboard/HistoryChart.tsx @@ -125,7 +125,7 @@ export function HistoryChart() { titleFont: { family: "Inter", size: 13, weight: 600 }, callbacks: { label: (ctx) => - `${ctx.dataset.label}: ${ctx.parsed.y?.toFixed(1)}°`, + `${ctx.dataset.label}: ${ctx.parsed.y?.toFixed(1)}${store.selectedDetail?.temp_symbol || "°C"}`, }, }, }, diff --git a/frontend/components/dashboard/scan-terminal/AiCityTemperatureChart.tsx b/frontend/components/dashboard/scan-terminal/AiCityTemperatureChart.tsx index 410d5eca..d20575ee 100644 --- a/frontend/components/dashboard/scan-terminal/AiCityTemperatureChart.tsx +++ b/frontend/components/dashboard/scan-terminal/AiCityTemperatureChart.tsx @@ -109,7 +109,6 @@ export function AiCityTemperatureChart({ detail }: { detail: CityDetail }) { >["datasets"] = [ { borderColor: "rgba(100, 116, 139, 0.72)", - borderDash: [6, 4], borderWidth: 1.6, data: chartData.datasets.debPast.map( (value, index) => value ?? chartData.datasets.debFuture[index], @@ -117,6 +116,10 @@ export function AiCityTemperatureChart({ detail }: { detail: CityDetail }) { fill: false, label: forecastLabel, pointRadius: 0, + segment: { + borderDash: (ctx: { p0DataIndex: number }) => + chartData.currentIndex != null && ctx.p0DataIndex < chartData.currentIndex ? [] : [6, 4], + }, spanGaps: true, tension: 0.28, }, @@ -148,6 +151,23 @@ export function AiCityTemperatureChart({ detail }: { detail: CityDetail }) { showLine: false, }); + const ci = chartData.currentIndex; + if (ci != null && ci >= 0 && ci < (chartData.times?.length || 0)) { + datasets.push({ + backgroundColor: "rgba(77, 163, 255, 0.5)", + borderColor: "rgba(77, 163, 255, 0.5)", + borderDash: [3, 4], + borderWidth: 1.2, + data: [ + { x: ci, y: chartData.max }, + { x: ci, y: chartData.min }, + ], + label: "Now", + pointRadius: 0, + showLine: true, + }); + } + return { data: { datasets, @@ -165,7 +185,7 @@ export function AiCityTemperatureChart({ detail }: { detail: CityDetail }) { borderColor: "rgba(77, 163, 255, 0.38)", borderWidth: 1, }, - }, + } as Record, responsive: true, scales: { x: { diff --git a/frontend/components/dashboard/scan-terminal/AiPinnedCityCard.tsx b/frontend/components/dashboard/scan-terminal/AiPinnedCityCard.tsx index 72272da3..16c589db 100644 --- a/frontend/components/dashboard/scan-terminal/AiPinnedCityCard.tsx +++ b/frontend/components/dashboard/scan-terminal/AiPinnedCityCard.tsx @@ -461,6 +461,9 @@ export function AiPinnedCityCard({ const dataFreshnessRows = [ { label: isHkoObservation ? (isEn ? "HKO" : "天文台") : "METAR", + labelTitle: isHkoObservation + ? (isEn ? "Hong Kong Observatory official readings" : "香港天文台官方实测") + : (isEn ? "Meteorological Aerodrome Report — airport weather observation" : "机场气象观测报文"), value: buildObservationFreshnessValue({ detail, displayTime: metarReportTimeDisplay, diff --git a/frontend/components/dashboard/scan-terminal/DataFreshnessBar.tsx b/frontend/components/dashboard/scan-terminal/DataFreshnessBar.tsx index 054c3c63..e9a7dfef 100644 --- a/frontend/components/dashboard/scan-terminal/DataFreshnessBar.tsx +++ b/frontend/components/dashboard/scan-terminal/DataFreshnessBar.tsx @@ -4,6 +4,7 @@ import type { StatusTone } from "@/components/dashboard/scan-terminal/CityStatus export type DataFreshnessRow = { label: string; + labelTitle?: string; value: string; tone: string; }; @@ -26,7 +27,7 @@ export function DataFreshnessBar({ {isEn ? "Data freshness" : "数据新鲜度"} {rows.map((freshness) => ( - {freshness.label}{freshnessSeparator} + {freshness.label}{freshnessSeparator} {freshness.value} ))} diff --git a/frontend/components/dashboard/scan-terminal/city-card-decision-utils.ts b/frontend/components/dashboard/scan-terminal/city-card-decision-utils.ts index 2ed3362f..fe658ac4 100644 --- a/frontend/components/dashboard/scan-terminal/city-card-decision-utils.ts +++ b/frontend/components/dashboard/scan-terminal/city-card-decision-utils.ts @@ -555,15 +555,15 @@ export function buildWeatherDecisionView({ : "等待模型补齐" : paceTone === "warm" ? isEn - ? "Watch hotter range" - : "关注偏高温区间" + ? "Revise upward" + : "预计最高温上修" : paceTone === "cold" ? isEn - ? "Avoid chasing high" - : "暂不追高温" + ? "Revise downward" + : "预计最高温下修" : isEn - ? "Wait for peak-window confirmation" - : "等待峰值窗口确认"; + ? "Stay with model base" + : "维持模型基准"; const expectedHigh = center != null && Number.isFinite(Number(center)) ? formatTemperatureValue(Number(center), tempSymbol, { digits: 1 }) diff --git a/frontend/components/dashboard/scan-terminal/city-decision-state.ts b/frontend/components/dashboard/scan-terminal/city-decision-state.ts index f3a99b37..b4731a6f 100644 --- a/frontend/components/dashboard/scan-terminal/city-decision-state.ts +++ b/frontend/components/dashboard/scan-terminal/city-decision-state.ts @@ -142,28 +142,28 @@ export function buildCityDecisionState({ : "watch"; const primaryReason = observedHighBreak ? isEn - ? "Observation has broken above the model range." - : "实测已突破模型上沿。" + ? "Observation has broken above the model range. Consider the upside scenario; wait for the next bulletin to confirm the breakout." + : "实测已突破模型上沿。建议关注偏高温区间,等待下一报文确认突破是否持续。" : peakHasPassed ? isEn - ? "Peak window has passed; confirm whether a new high can still form." - : "峰值窗口已过,确认是否还会出现新高。" + ? "Peak window has passed; confirm whether a new high can still form. Avoid chasing if no new high prints." + : "峰值窗口已过,确认是否还会出现新高。若无新高,建议避免追高。" : observationStale ? isEn - ? "Observation is stale and needs the next report." - : "观测已过旧,需要下一报文确认。" + ? "Observation is stale and needs the next report. Use only as background reference until fresh data arrives." + : "观测已过旧,需要下一报文确认。当前数据仅作背景参考,建议等待新报文后再做判断。" : marketStatus === "unavailable" ? isEn - ? "Weather evidence is usable, but no tradable quote is available yet." - : "天气证据可参考,但暂无可交易价格。" + ? "Weather evidence is usable, but no tradable quote is available yet. Weather judgment still applies." + : "天气证据可参考,但暂无可交易价格。天气判断仍可参考,无需急于操作。" : modelHighlyConsistent ? isEn - ? "Models are aligned; wait for observation confirmation." - : "模型高度一致,等待实测确认。" + ? "Models are aligned; wait for observation confirmation. A clear direction should emerge after the next report." + : "模型高度一致,等待实测确认。下一报文后方向会更明确。" : needsNextBulletin ? isEn - ? "The next bulletin is more likely to decide direction." - : "下一报文更可能决定方向。" + ? "The next bulletin is more likely to decide direction. Hold until the picture clears." + : "下一报文更可能决定方向。建议等待信号明确后再做决策。" : isEn ? "Compare new observations with the expected high through the peak window." : "在峰值窗口内继续对照实测与预计高点。"; diff --git a/frontend/lib/chart-utils.ts b/frontend/lib/chart-utils.ts index 3d4e039a..5ffbf361 100644 --- a/frontend/lib/chart-utils.ts +++ b/frontend/lib/chart-utils.ts @@ -877,6 +877,7 @@ export function getTemperatureChartData( const xMax = times.length ? chartHmToMinutes(times[times.length - 1]) ?? 24 * 60 : 24 * 60; return { + currentIndex, datasets: { airportMetarPoints, airportMetarSeries,