UX 审查收尾:AI 解读过渡标记 + 极端温度视觉强化
P2-17:AI 快速判断→完整解读过渡标记 - AiEvidencePanel 新增 useTransitionMarker hook - AI 从 loading→ready 后显示"✓ Updated/已更新"徽标(4 秒后消失) - 绿色动画徽标 fadeUpIn,避免用户以为信息没变 P2-18:极端温度视觉强化 - globals.css 新增 temp-extreme-hot(橙红辉光 ≥40°C/≥104°F) - globals.css 新增 temp-extreme-cold(冰蓝辉光 ≤-5°C/≤23°F) - PanelSections Hero 温度值自动应用极端温度样式 Tested: npx tsc --noEmit
This commit is contained in:
@@ -1027,6 +1027,18 @@
|
|||||||
color: var(--color-accent-primary);
|
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) {
|
.root :global(.scan-ai-raw-metar) {
|
||||||
margin-top: 12px;
|
margin-top: 12px;
|
||||||
padding-top: 12px;
|
padding-top: 12px;
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
"use client";
|
"use client";
|
||||||
|
|
||||||
|
import { useEffect, useState } from "react";
|
||||||
import type {
|
import type {
|
||||||
AiCityForecastPayload,
|
AiCityForecastPayload,
|
||||||
AiCityForecastState,
|
AiCityForecastState,
|
||||||
@@ -13,6 +14,18 @@ function confidenceBadge(confidence: string | null, isEn: boolean) {
|
|||||||
return { label: isEn ? "Low" : "低", tone: "low" };
|
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({
|
export function AiEvidencePanel({
|
||||||
aiBullets,
|
aiBullets,
|
||||||
aiCityForecast,
|
aiCityForecast,
|
||||||
@@ -62,6 +75,8 @@ export function AiEvidencePanel({
|
|||||||
? `${formatTemperatureValue(aiRangeLow, tempSymbol, { digits: 0 })} ~ ${formatTemperatureValue(aiRangeHigh, tempSymbol, { digits: 0 })}`
|
? `${formatTemperatureValue(aiRangeLow, tempSymbol, { digits: 0 })} ~ ${formatTemperatureValue(aiRangeHigh, tempSymbol, { digits: 0 })}`
|
||||||
: null;
|
: null;
|
||||||
|
|
||||||
|
const showUpdatedBadge = useTransitionMarker(aiForecast.status, Boolean(aiCityForecast));
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<section className="scan-ai-city-section scan-ai-city-ai-read">
|
<section className="scan-ai-city-section scan-ai-city-ai-read">
|
||||||
<div className="scan-ai-city-section-title">
|
<div className="scan-ai-city-section-title">
|
||||||
@@ -143,6 +158,11 @@ export function AiEvidencePanel({
|
|||||||
</>
|
</>
|
||||||
) : aiForecast.status === "ready" && aiCityForecast ? (
|
) : aiForecast.status === "ready" && aiCityForecast ? (
|
||||||
<>
|
<>
|
||||||
|
{showUpdatedBadge ? (
|
||||||
|
<span className="scan-ai-updated-badge">
|
||||||
|
{isEn ? "✓ Updated" : "✓ 已更新"}
|
||||||
|
</span>
|
||||||
|
) : null}
|
||||||
<p className="scan-ai-weather-summary">
|
<p className="scan-ai-weather-summary">
|
||||||
{aiRuleEvidenceMode ? aiRuleEvidenceText : aiReadCompleteText}
|
{aiRuleEvidenceMode ? aiRuleEvidenceText : aiReadCompleteText}
|
||||||
</p>
|
</p>
|
||||||
|
|||||||
Reference in New Issue
Block a user