diff --git a/frontend/components/dashboard/ScanTerminalCard.module.css b/frontend/components/dashboard/ScanTerminalCard.module.css index 481566b7..847f910a 100644 --- a/frontend/components/dashboard/ScanTerminalCard.module.css +++ b/frontend/components/dashboard/ScanTerminalCard.module.css @@ -1027,6 +1027,18 @@ color: var(--color-accent-primary); } +.root :global(.scan-ai-updated-badge) { + display: inline-block; + padding: 2px 8px; + border-radius: 999px; + background: rgba(34, 197, 94, 0.12); + color: #4ade80; + font-size: 10px; + font-weight: 700; + margin-bottom: 6px; + animation: fadeUpIn 0.3s ease; +} + .root :global(.scan-ai-raw-metar) { margin-top: 12px; padding-top: 12px; diff --git a/frontend/components/dashboard/scan-terminal/AiEvidencePanel.tsx b/frontend/components/dashboard/scan-terminal/AiEvidencePanel.tsx index ba2ad2b4..fba639a9 100644 --- a/frontend/components/dashboard/scan-terminal/AiEvidencePanel.tsx +++ b/frontend/components/dashboard/scan-terminal/AiEvidencePanel.tsx @@ -1,5 +1,6 @@ "use client"; +import { useEffect, useState } from "react"; import type { AiCityForecastPayload, AiCityForecastState, @@ -13,6 +14,18 @@ function confidenceBadge(confidence: string | null, isEn: boolean) { return { label: isEn ? "Low" : "低", tone: "low" }; } +function useTransitionMarker(status: string, hasContent: boolean) { + const [showUpdated, setShowUpdated] = useState(false); + useEffect(() => { + if (status === "ready" && hasContent) { + setShowUpdated(true); + const id = setTimeout(() => setShowUpdated(false), 4000); + return () => clearTimeout(id); + } + }, [status, hasContent]); + return showUpdated; +} + export function AiEvidencePanel({ aiBullets, aiCityForecast, @@ -62,6 +75,8 @@ export function AiEvidencePanel({ ? `${formatTemperatureValue(aiRangeLow, tempSymbol, { digits: 0 })} ~ ${formatTemperatureValue(aiRangeHigh, tempSymbol, { digits: 0 })}` : null; + const showUpdatedBadge = useTransitionMarker(aiForecast.status, Boolean(aiCityForecast)); + return (
@@ -143,6 +158,11 @@ export function AiEvidencePanel({ ) : aiForecast.status === "ready" && aiCityForecast ? ( <> + {showUpdatedBadge ? ( + + {isEn ? "✓ Updated" : "✓ 已更新"} + + ) : null}

{aiRuleEvidenceMode ? aiRuleEvidenceText : aiReadCompleteText}