UX 审查修复:统一修正术语、图表"现在"标记、专业术语解释、异常行动建议

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
This commit is contained in:
2569718930@qq.com
2026-05-10 17:33:15 +08:00
parent bf70d0b42a
commit d94476f943
7 changed files with 47 additions and 22 deletions
@@ -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"}`,
},
},
},
@@ -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<string, unknown>,
responsive: true,
scales: {
x: {
@@ -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,
@@ -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({
<strong>{isEn ? "Data freshness" : "数据新鲜度"}</strong>
{rows.map((freshness) => (
<span key={freshness.label} className={freshness.tone}>
<b>{freshness.label}{freshnessSeparator}</b>
<b title={freshness.labelTitle}>{freshness.label}{freshnessSeparator}</b>
<em>{freshness.value}</em>
</span>
))}
@@ -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 })
@@ -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."
: "在峰值窗口内继续对照实测与预计高点。";
+1
View File
@@ -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,