diff --git a/DESIGN.md b/DESIGN.md index 452df1d..edd988b 100644 --- a/DESIGN.md +++ b/DESIGN.md @@ -815,6 +815,16 @@ features/agent/components The goal is to avoid repeating interaction and accessibility work while keeping full control over the visual language defined here. +## 工况详情状态强调 + +生命周期和风险等级是独立语义:完成、执行中属于生命周期,正常、关注、高风险属于风险等级,二者不得通过同一套容器色相互继承。状态必须同时包含文字或图标,且只在最有用的层级表达一次。 + +禁止使用彩色侧边条、整卡状态底色和继承式彩色圆点。正常状态保持中性,仅用小型绿色完成标识表达生命周期结束;关注和高风险只在直接表达风险的徽标、图标或 KPI 表面使用橙色和红色。 + +完成报告按标题、生命周期徽标、结论、时间、关键指标和报告章节的顺序组织。报告、判断摘要和章节使用中性容器及列表符号,KPI 保留业务原始顺序,并以中性、浅橙、浅红表面对应正常、关注、高风险。 + +任务步骤只按流程状态着色:当前蓝色,完成绿色,待处理灰色,待人工确认橙色。主要操作使用蓝色,提交完成后使用绿色。 + ## Known Open Decisions - Dark mode is not specified in this version. A future night-operations mode should be designed deliberately rather than generated by inverting colors. diff --git a/features/workbench/components/condition-report-risk.test.ts b/features/workbench/components/condition-report-risk.test.ts new file mode 100644 index 0000000..6e7730d --- /dev/null +++ b/features/workbench/components/condition-report-risk.test.ts @@ -0,0 +1,16 @@ +import { describe, expect, it } from "vitest"; + +import { getConditionReportRiskLevel } from "./condition-report-risk"; + +describe("getConditionReportRiskLevel", () => { + it("maps error and warning statuses to their operational risk levels", () => { + expect(getConditionReportRiskLevel("error", "normal")).toBe("critical"); + expect(getConditionReportRiskLevel("warning", "normal")).toBe("attention"); + }); + + it("preserves the explicit risk level for lifecycle-only statuses", () => { + expect(getConditionReportRiskLevel("completed", "normal")).toBe("normal"); + expect(getConditionReportRiskLevel("completed", "attention")).toBe("attention"); + expect(getConditionReportRiskLevel("running", "critical")).toBe("critical"); + }); +}); diff --git a/features/workbench/components/condition-report-risk.ts b/features/workbench/components/condition-report-risk.ts new file mode 100644 index 0000000..e01779f --- /dev/null +++ b/features/workbench/components/condition-report-risk.ts @@ -0,0 +1,19 @@ +import type { + ScheduledConditionRiskLevel, + ScheduledConditionStatus +} from "@/features/workbench/types"; + +export function getConditionReportRiskLevel( + status: ScheduledConditionStatus, + riskLevel: ScheduledConditionRiskLevel +): ScheduledConditionRiskLevel { + if (status === "error") { + return "critical"; + } + + if (status === "warning") { + return "attention"; + } + + return riskLevel; +} diff --git a/features/workbench/components/scheduled-condition-detail-panel.tsx b/features/workbench/components/scheduled-condition-detail-panel.tsx index c9e7f36..2477bfe 100644 --- a/features/workbench/components/scheduled-condition-detail-panel.tsx +++ b/features/workbench/components/scheduled-condition-detail-panel.tsx @@ -32,22 +32,32 @@ import type { ScheduledConditionItem, ScheduledConditionKpi, ScheduledConditionRecord, + ScheduledConditionRiskLevel, ScheduledWorkOrderItem } from "@/features/workbench/types"; import { formatDuration, formatTime, formatTimeRange, - getDetailAccentClassName, - getKpiStatusClassName, getRiskLabel, getWorkOrderCurrentStage, getWorkOrderStagePillClassName, statusClassNames, statusLabels } from "./scheduled-condition-feed-utils"; +import { getConditionReportRiskLevel } from "./condition-report-risk"; const RUNNING_WORKFLOW_TICK_MS = 1_000; +const STATUS_PILL_CLASS_NAME = "rounded-full border px-2 py-0.5 text-xs font-semibold leading-4"; +const NEUTRAL_SURFACE_CLASS_NAME = "rounded-xl border border-slate-200/80 bg-white/95"; +const INSET_SURFACE_CLASS_NAME = "rounded-xl border border-slate-200/70 bg-slate-50/80"; +const REPORT_META_TILE_CLASS_NAME = "rounded-lg border border-slate-200/70 bg-slate-50/80 px-2 py-1.5"; + +const riskIndicatorClassNames: Record = { + normal: "border-slate-200 bg-slate-50 text-slate-600", + attention: "border-orange-200 bg-orange-50 text-orange-700", + critical: "border-red-200 bg-red-50 text-red-700" +}; type IncidentOperation = { target: string; @@ -64,7 +74,9 @@ type SubmittedIncidentWorkOrder = { operations: IncidentOperation[]; }; -type ExecutionStepStatus = "done" | "current" | "pending"; +type ExecutionStepStatus = "done" | "current" | "pending" | "confirmation"; + +type ExecutionBadgeTone = "active" | "confirmation" | "completed" | "pending"; type ExecutionStepMeta = { label: string; @@ -97,7 +109,7 @@ export function StatusPill({ return ( ; - } else { - content = ; - } + const content = + condition.kind === "work_order" ? ( + + ) : ( + + ); return ( @@ -146,6 +157,7 @@ function ConditionRecordDetailPanel({ const insight = getConditionAnalysisInsight(condition); const [submittedWorkOrder, setSubmittedWorkOrder] = useState(null); const header = getConditionDetailHeader(condition); + const riskLevel = getConditionReportRiskLevel(condition.status, condition.riskLevel); const bodyKey = `${condition.id}-${condition.status}-${condition.riskLevel}`; function handleSubmitIncidentWorkOrder() { @@ -161,7 +173,7 @@ function ConditionRecordDetailPanel({ return ( - + {condition.status === "running" ? ( - ) : condition.status === "error" ? ( - <> - - - - - - - ) : condition.status === "warning" || condition.riskLevel === "attention" ? ( - <> - - - - - ) : ( <> - - - + + {riskLevel === "critical" ? ( + <> + + + + + + ) : riskLevel === "attention" ? ( + <> + + + + + ) : ( + <> + + + + )} )} @@ -249,7 +263,7 @@ function WorkOrderDetailPanel({ condition }: { condition: ScheduledWorkOrderItem return ( - + -