refactor(ui): single-source intelligent-eval label maps in status.ts
All checks were successful
CI / test (push) Successful in 3m13s

架构审查候选⑤:消除标签双轨,状态/决策类型映射单一出口。

- status.ts 新增 DECISION_TYPE_META(12 类型与后端 decision_type 字面量
  对齐)+ decisionTypeOf() 兜底:后端新增类型时前端不再静默显示裸字符串,
  未识别类型显式标注"未识别·xxx"
- DecisionProcess / ExecutionProcess 删除各自手写的 DECISION_TYPE_LABELS
  (后者是审查报告之外的第三处双轨),统一走 decisionTypeOf
- IntelligentEvals 的 STAT_CHIPS 标签从 EVAL_STATUS 派生,不再平行维护

tsc --noEmit 通过,前端 20 tests 全过,标签文本零变化。
This commit is contained in:
sinohqb 2026-08-21 04:00:32 +08:00
parent 2a0bcdd185
commit d87aad5aed
4 changed files with 33 additions and 32 deletions

View File

@ -5,25 +5,11 @@ import {
import type { ColumnsType } from 'antd/es/table' import type { ColumnsType } from 'antd/es/table'
import { DownloadOutlined, ReloadOutlined } from '@ant-design/icons' import { DownloadOutlined, ReloadOutlined } from '@ant-design/icons'
import SectionHeader from '../SectionHeader' import SectionHeader from '../SectionHeader'
import { decisionTypeOf } from './status'
import { intelligentEvalsApi, type DecisionLog } from '../../api' import { intelligentEvalsApi, type DecisionLog } from '../../api'
import { colors } from '../../tokens' import { colors } from '../../tokens'
import { formatDateTime } from '../../utils/date' import { formatDateTime } from '../../utils/date'
const DECISION_TYPE_LABELS: Record<string, { label: string; color: string }> = {
execute_session: { label: '执行会话', color: 'blue' },
wait: { label: '等待', color: 'default' },
start_analysis: { label: '开始分析', color: 'green' },
task_abandoned: { label: '任务放弃', color: 'red' },
session_expired: { label: '会话过期', color: 'orange' },
planner_trigger: { label: '规划触发', color: 'purple' },
planning_gate_failed: { label: '规划超闸判败', color: 'red' },
analyst_nudge: { label: '催促汇总', color: 'purple' },
executing_timeout: { label: '执行超窗判败', color: 'red' },
trigger_failed: { label: '触发失败', color: 'orange' },
trigger_failure_gate: { label: '触发失败判死', color: 'red' },
worker_trigger: { label: '执行触发', color: 'purple' },
}
interface DecisionProcessProps { interface DecisionProcessProps {
evalId: string evalId: string
/** 独立页内作为子视图 tab 使用时可不传tab 切换代替返回)。 */ /** 独立页内作为子视图 tab 使用时可不传tab 切换代替返回)。 */
@ -85,7 +71,7 @@ export default function DecisionProcess({ evalId, onBack }: DecisionProcessProps
key: 'decision_type', key: 'decision_type',
width: 120, width: 120,
render: (val: string) => { render: (val: string) => {
const info = DECISION_TYPE_LABELS[val] ?? { label: val, color: 'default' } const info = decisionTypeOf(val)
return <Tag color={info.color}>{info.label}</Tag> return <Tag color={info.color}>{info.label}</Tag>
}, },
}, },
@ -150,8 +136,8 @@ export default function DecisionProcess({ evalId, onBack }: DecisionProcessProps
children: ( children: (
<div> <div>
<div style={{ marginBottom: 4 }}> <div style={{ marginBottom: 4 }}>
<Tag color={DECISION_TYPE_LABELS[log.decision_type]?.color ?? 'default'}> <Tag color={decisionTypeOf(log.decision_type).color}>
{DECISION_TYPE_LABELS[log.decision_type]?.label ?? log.decision_type} {decisionTypeOf(log.decision_type).label}
</Tag> </Tag>
<span style={{ fontSize: 12, color: colors.textSecondary }}> <span style={{ fontSize: 12, color: colors.textSecondary }}>
{formatDateTime(log.created_at)} {formatDateTime(log.created_at)}

View File

@ -14,7 +14,7 @@ import {
type IntelligentEval, type IntelligentEval,
type TaskQueueItem, type TaskQueueItem,
} from '../../api' } from '../../api'
import { SESSION_STATUS } from './status' import { SESSION_STATUS, decisionTypeOf } from './status'
import { colors, statusColors } from '../../tokens' import { colors, statusColors } from '../../tokens'
import { formatDateTime } from '../../utils/date' import { formatDateTime } from '../../utils/date'
import { usePolling } from '../../hooks/usePolling' import { usePolling } from '../../hooks/usePolling'
@ -28,12 +28,6 @@ const STAGE_CONFIG = [
{ key: 'done', title: '完成', icon: CheckCircleOutlined }, { key: 'done', title: '完成', icon: CheckCircleOutlined },
] ]
const DECISION_TYPE_LABELS: Record<string, string> = {
execute_session: '执行会话',
wait: '等待',
start_analysis: '开始分析',
}
interface ActivityEvent { interface ActivityEvent {
time: string time: string
color: string color: string
@ -93,7 +87,7 @@ function decisionEvents(logs: DecisionLog[]): ActivityEvent[] {
.map((log) => ({ .map((log) => ({
time: log.created_at as string, time: log.created_at as string,
color: log.decision_type === 'start_analysis' ? 'green' : log.decision_type === 'wait' ? 'gray' : 'blue', color: log.decision_type === 'start_analysis' ? 'green' : log.decision_type === 'wait' ? 'gray' : 'blue',
text: `决策·${DECISION_TYPE_LABELS[log.decision_type] ?? log.decision_type}${log.reason}`, text: `决策·${decisionTypeOf(log.decision_type).label}${log.reason}`,
})) }))
} }

View File

@ -26,3 +26,24 @@ export const SEVERITY_META: Record<string, { label: string; color: string; order
export function severityOf(severity: string) { export function severityOf(severity: string) {
return SEVERITY_META[severity?.toLowerCase()] ?? { label: severity, color: 'default', order: 99 } return SEVERITY_META[severity?.toLowerCase()] ?? { label: severity, color: 'default', order: 99 }
} }
// 决策日志类型的标签/颜色单一出口,与后端 decision_type 字面量一一对应。
// 后端新增类型时前端在此补一行;未识别类型由 decisionTypeOf 显式兜底。
export const DECISION_TYPE_META: Record<string, { label: string; color: string }> = {
execute_session: { label: '执行会话', color: 'blue' },
wait: { label: '等待', color: 'default' },
start_analysis: { label: '开始分析', color: 'green' },
task_abandoned: { label: '任务放弃', color: 'red' },
session_expired: { label: '会话过期', color: 'orange' },
planner_trigger: { label: '规划触发', color: 'purple' },
planning_gate_failed: { label: '规划超闸判败', color: 'red' },
analyst_nudge: { label: '催促汇总', color: 'purple' },
executing_timeout: { label: '执行超窗判败', color: 'red' },
trigger_failed: { label: '触发失败', color: 'orange' },
trigger_failure_gate: { label: '触发失败判死', color: 'red' },
worker_trigger: { label: '执行触发', color: 'purple' },
}
export function decisionTypeOf(decisionType: string) {
return DECISION_TYPE_META[decisionType] ?? { label: `未识别·${decisionType}`, color: 'default' }
}

View File

@ -43,14 +43,14 @@ const TASKS_TAB: TabItem = {
} }
// 状态统计条:全部 + 各状态(含 cancelled保证 全部 = 各状态之和draft 为 0 不单列) // 状态统计条:全部 + 各状态(含 cancelled保证 全部 = 各状态之和draft 为 0 不单列)
// 标签从 EVAL_STATUS 单一出口派生,不与 status.ts 平行维护
const STAT_CHIPS: { key: string; label: string; color?: string }[] = [ const STAT_CHIPS: { key: string; label: string; color?: string }[] = [
{ key: 'all', label: '全部评估' }, { key: 'all', label: '全部评估' },
{ key: 'planning', label: '规划中' }, ...(['planning', 'pending_approval', 'executing', 'completed', 'cancelled', 'failed'] as const).map((s) => ({
{ key: 'pending_approval', label: '待审批' }, key: s,
{ key: 'executing', label: '执行中' }, label: EVAL_STATUS[s].label,
{ key: 'completed', label: '已完成', color: statusColors.completed }, color: s === 'completed' ? statusColors.completed : s === 'failed' ? statusColors.failed : undefined,
{ key: 'cancelled', label: '已取消' }, })),
{ key: 'failed', label: '失败', color: statusColors.failed },
] ]
export default function IntelligentEvalsPage() { export default function IntelligentEvalsPage() {