refactor(ui): consolidate state maps + colors to tokens (audit P1)
UI/UX 盘点 P1 一致性修复: - 收敛重复状态映射:ExplorationSection 复用 SESSION_STATUS、TaskQueueMonitor 复用 EVAL_STATUS(消除 blue/green 与 processing/success 颜色漂移)、 Campaigns 复用 SEVERITY_META(均来自 intelligent_eval/status.ts) - 硬编码颜色走 token:Reports 通过率用 passRateColor、通过/失败/delta 用 statusColors;IntelligentEvals/TaskQueueMonitor/EvalReport/PeriodComparison 的 #52c41a/#ff4d4f 用 statusColors.completed/failed;RunList/RuleOverview 的 #faad14 用 colors.warning;Home 的 #1677ff 用 colors.primary tsc 0 错误 vitest 19 passed
This commit is contained in:
parent
3626ab288b
commit
fa5e3b8d5d
@ -9,13 +9,7 @@ import {
|
|||||||
} from '../api'
|
} from '../api'
|
||||||
import { colors } from '../tokens'
|
import { colors } from '../tokens'
|
||||||
import { shortDateTime } from '../utils/date'
|
import { shortDateTime } from '../utils/date'
|
||||||
|
import { SESSION_STATUS } from './intelligent_eval/status'
|
||||||
const SESSION_STATUS: Record<string, { label: string; color: string }> = {
|
|
||||||
running: { label: '进行中', color: 'processing' },
|
|
||||||
completed: { label: '已完成', color: 'success' },
|
|
||||||
failed: { label: '失败', color: 'error' },
|
|
||||||
expired: { label: '已过期', color: 'default' },
|
|
||||||
}
|
|
||||||
|
|
||||||
const EMOTION_LABELS: Record<string, string> = {
|
const EMOTION_LABELS: Record<string, string> = {
|
||||||
positive: '满意',
|
positive: '满意',
|
||||||
@ -201,7 +195,7 @@ export default function ExplorationSection({ campaignId, summary }: {
|
|||||||
size="small"
|
size="small"
|
||||||
onChange={onExpand}
|
onChange={onExpand}
|
||||||
items={sessions.map((s) => {
|
items={sessions.map((s) => {
|
||||||
const meta = SESSION_STATUS[s.status] ?? SESSION_STATUS.running
|
const meta = SESSION_STATUS[s.status as keyof typeof SESSION_STATUS] ?? SESSION_STATUS.running
|
||||||
const personaName = typeof s.persona?.name === 'string' ? s.persona.name : s.id.slice(0, 8)
|
const personaName = typeof s.persona?.name === 'string' ? s.persona.name : s.id.slice(0, 8)
|
||||||
return {
|
return {
|
||||||
key: s.id,
|
key: s.id,
|
||||||
|
|||||||
@ -5,7 +5,7 @@ import {
|
|||||||
type MetricDiff, type ModelConfig,
|
type MetricDiff, type ModelConfig,
|
||||||
} from '../api'
|
} from '../api'
|
||||||
import { shortDateTime } from '../utils/date'
|
import { shortDateTime } from '../utils/date'
|
||||||
import { colors } from '../tokens'
|
import { colors, statusColors } from '../tokens'
|
||||||
|
|
||||||
const TREND_META: Record<string, { label: string; color: string }> = {
|
const TREND_META: Record<string, { label: string; color: string }> = {
|
||||||
improving: { label: '改善', color: 'green' },
|
improving: { label: '改善', color: 'green' },
|
||||||
@ -119,7 +119,7 @@ export default function PeriodComparisonSection({
|
|||||||
const good = metric.goodDirection === 'down' ? (pair.delta ?? 0) < 0 : (pair.delta ?? 0) > 0
|
const good = metric.goodDirection === 'down' ? (pair.delta ?? 0) < 0 : (pair.delta ?? 0) > 0
|
||||||
const deltaColor = pair.delta == null || pair.delta === 0
|
const deltaColor = pair.delta == null || pair.delta === 0
|
||||||
? colors.textMuted
|
? colors.textMuted
|
||||||
: good ? '#52c41a' : '#ff4d4f'
|
: good ? statusColors.completed : statusColors.failed
|
||||||
return (
|
return (
|
||||||
<Space size={4}>
|
<Space size={4}>
|
||||||
<span style={{ color: colors.textMuted }}>
|
<span style={{ color: colors.textMuted }}>
|
||||||
|
|||||||
@ -143,7 +143,7 @@ function ratioColor(pass: number, total: number): string {
|
|||||||
if (!total) return colors.textMuted
|
if (!total) return colors.textMuted
|
||||||
const r = pass / total
|
const r = pass / total
|
||||||
if (r >= 0.8) return statusColors.completed
|
if (r >= 0.8) return statusColors.completed
|
||||||
if (r >= 0.5) return '#faad14'
|
if (r >= 0.5) return colors.warning
|
||||||
return statusColors.failed
|
return statusColors.failed
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -233,7 +233,7 @@ function RunRow({ r, selected, targetName, scenarioName, onSelect, onOpenReport,
|
|||||||
const passRate = r.summary?.pass_rate
|
const passRate = r.summary?.pass_rate
|
||||||
if (passRate != null) {
|
if (passRate != null) {
|
||||||
const pct = Math.round(passRate * 100)
|
const pct = Math.round(passRate * 100)
|
||||||
const color = pct >= 80 ? statusColors.completed : pct >= 50 ? '#faad14' : statusColors.failed
|
const color = pct >= 80 ? statusColors.completed : pct >= 50 ? colors.warning : statusColors.failed
|
||||||
return { pct, color, label: `${pct}%`, active: false }
|
return { pct, color, label: `${pct}%`, active: false }
|
||||||
}
|
}
|
||||||
return { pct: 100, color: colors.textMuted, label: '完成', active: false }
|
return { pct: 100, color: colors.textMuted, label: '完成', active: false }
|
||||||
|
|||||||
@ -16,7 +16,7 @@ import {
|
|||||||
type IntelligentEvalSession,
|
type IntelligentEvalSession,
|
||||||
type ReportEvidence,
|
type ReportEvidence,
|
||||||
} from '../../api'
|
} from '../../api'
|
||||||
import { colors } from '../../tokens'
|
import { colors, statusColors } from '../../tokens'
|
||||||
import { shortDateTime } from '../../utils/date'
|
import { shortDateTime } from '../../utils/date'
|
||||||
import { SESSION_STATUS, severityOf } from './status'
|
import { SESSION_STATUS, severityOf } from './status'
|
||||||
|
|
||||||
@ -143,8 +143,8 @@ export default function EvalReport({ ev, onBack }: EvalReportProps) {
|
|||||||
: null
|
: null
|
||||||
const overallNum = overall != null ? Number(overall) : null
|
const overallNum = overall != null ? Number(overall) : null
|
||||||
const overallColor = overallNum == null ? colors.textMuted
|
const overallColor = overallNum == null ? colors.textMuted
|
||||||
: overallNum >= 0.8 ? '#52c41a'
|
: overallNum >= 0.8 ? statusColors.completed
|
||||||
: overallNum >= 0.6 ? colors.warning : '#ff4d4f'
|
: overallNum >= 0.6 ? colors.warning : statusColors.failed
|
||||||
const sevCounts = findings.reduce((acc, f) => {
|
const sevCounts = findings.reduce((acc, f) => {
|
||||||
acc[f.severity] = (acc[f.severity] ?? 0) + 1
|
acc[f.severity] = (acc[f.severity] ?? 0) + 1
|
||||||
return acc
|
return acc
|
||||||
@ -210,7 +210,7 @@ export default function EvalReport({ ev, onBack }: EvalReportProps) {
|
|||||||
</Col>
|
</Col>
|
||||||
<Col xs={12} md={6}>
|
<Col xs={12} md={6}>
|
||||||
<div style={summaryCard}>
|
<div style={summaryCard}>
|
||||||
<div style={summaryIcon('#ff4d4f')}><WarningOutlined /></div>
|
<div style={summaryIcon(statusColors.failed)}><WarningOutlined /></div>
|
||||||
<div>
|
<div>
|
||||||
<div style={{ fontSize: 12, color: colors.textMuted }}>问题发现</div>
|
<div style={{ fontSize: 12, color: colors.textMuted }}>问题发现</div>
|
||||||
<div style={{ fontSize: 26, fontWeight: 700, color: colors.text, lineHeight: 1.2 }}>
|
<div style={{ fontSize: 26, fontWeight: 700, color: colors.text, lineHeight: 1.2 }}>
|
||||||
|
|||||||
@ -6,8 +6,9 @@ import type { ColumnsType } from 'antd/es/table'
|
|||||||
import { ReloadOutlined } from '@ant-design/icons'
|
import { ReloadOutlined } from '@ant-design/icons'
|
||||||
import { intelligentEvalsApi, type TaskQueueItem, type TaskQueueStatus } from '../../api'
|
import { intelligentEvalsApi, type TaskQueueItem, type TaskQueueStatus } from '../../api'
|
||||||
import { usePolling } from '../../hooks/usePolling'
|
import { usePolling } from '../../hooks/usePolling'
|
||||||
import { colors } from '../../tokens'
|
import { colors, statusColors } from '../../tokens'
|
||||||
import { formatDateTime } from '../../utils/date'
|
import { formatDateTime } from '../../utils/date'
|
||||||
|
import { EVAL_STATUS } from './status'
|
||||||
|
|
||||||
const STATUS_META: Record<TaskQueueStatus, { label: string; color: string }> = {
|
const STATUS_META: Record<TaskQueueStatus, { label: string; color: string }> = {
|
||||||
pending: { label: '待认领', color: 'orange' },
|
pending: { label: '待认领', color: 'orange' },
|
||||||
@ -16,13 +17,6 @@ const STATUS_META: Record<TaskQueueStatus, { label: string; color: string }> = {
|
|||||||
failed: { label: '失败', color: 'red' },
|
failed: { label: '失败', color: 'red' },
|
||||||
}
|
}
|
||||||
|
|
||||||
const EVAL_STATUS_META: Record<string, { label: string; color: string }> = {
|
|
||||||
executing: { label: '执行中', color: 'blue' },
|
|
||||||
completed: { label: '已完成', color: 'green' },
|
|
||||||
cancelled: { label: '已取消', color: 'default' },
|
|
||||||
failed: { label: '失败', color: 'red' },
|
|
||||||
}
|
|
||||||
|
|
||||||
type FilterKey = 'all' | 'unresolved' | TaskQueueStatus
|
type FilterKey = 'all' | 'unresolved' | TaskQueueStatus
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -67,8 +61,8 @@ export default function TaskQueueMonitor() {
|
|||||||
{ key: 'unresolved', label: '待处理', color: colors.warning, get: () => stats?.unresolved ?? 0 },
|
{ key: 'unresolved', label: '待处理', color: colors.warning, get: () => stats?.unresolved ?? 0 },
|
||||||
{ key: 'pending', label: '待认领', get: () => stats?.pending ?? 0 },
|
{ key: 'pending', label: '待认领', get: () => stats?.pending ?? 0 },
|
||||||
{ key: 'assigned', label: '执行中', color: colors.primary, get: () => stats?.assigned ?? 0 },
|
{ key: 'assigned', label: '执行中', color: colors.primary, get: () => stats?.assigned ?? 0 },
|
||||||
{ key: 'completed', label: '已完成', color: '#52c41a', get: () => stats?.completed ?? 0 },
|
{ key: 'completed', label: '已完成', color: statusColors.completed, get: () => stats?.completed ?? 0 },
|
||||||
{ key: 'failed', label: '失败', color: '#ff4d4f', get: () => stats?.failed ?? 0 },
|
{ key: 'failed', label: '失败', color: statusColors.failed, get: () => stats?.failed ?? 0 },
|
||||||
]
|
]
|
||||||
|
|
||||||
const columns: ColumnsType<TaskQueueItem> = [
|
const columns: ColumnsType<TaskQueueItem> = [
|
||||||
@ -81,9 +75,9 @@ export default function TaskQueueMonitor() {
|
|||||||
render: (_, t) => (
|
render: (_, t) => (
|
||||||
<Space size={6}>
|
<Space size={6}>
|
||||||
<span style={{ fontWeight: 500 }}>{t.eval_name ?? t.eval_id.slice(0, 8)}</span>
|
<span style={{ fontWeight: 500 }}>{t.eval_name ?? t.eval_id.slice(0, 8)}</span>
|
||||||
{t.eval_status && EVAL_STATUS_META[t.eval_status] && (
|
{t.eval_status && EVAL_STATUS[t.eval_status as keyof typeof EVAL_STATUS] && (
|
||||||
<Tag color={EVAL_STATUS_META[t.eval_status].color} style={{ margin: 0, fontSize: 11 }}>
|
<Tag color={EVAL_STATUS[t.eval_status as keyof typeof EVAL_STATUS].color} style={{ margin: 0, fontSize: 11 }}>
|
||||||
{EVAL_STATUS_META[t.eval_status].label}
|
{EVAL_STATUS[t.eval_status as keyof typeof EVAL_STATUS].label}
|
||||||
</Tag>
|
</Tag>
|
||||||
)}
|
)}
|
||||||
</Space>
|
</Space>
|
||||||
@ -108,7 +102,7 @@ export default function TaskQueueMonitor() {
|
|||||||
title: '完成时间', dataIndex: 'completed_at', key: 'completed_at', width: 165,
|
title: '完成时间', dataIndex: 'completed_at', key: 'completed_at', width: 165,
|
||||||
render: (v: string | null, t) => {
|
render: (v: string | null, t) => {
|
||||||
if (t.status === 'failed' && t.error) {
|
if (t.status === 'failed' && t.error) {
|
||||||
return <Tooltip title={t.error}><span style={{ color: '#ff4d4f' }}>失败</span></Tooltip>
|
return <Tooltip title={t.error}><span style={{ color: statusColors.failed }}>失败</span></Tooltip>
|
||||||
}
|
}
|
||||||
return v ? formatDateTime(v) : <span style={{ color: colors.textMuted }}>—</span>
|
return v ? formatDateTime(v) : <span style={{ color: colors.textMuted }}>—</span>
|
||||||
},
|
},
|
||||||
|
|||||||
@ -26,6 +26,7 @@ import WindowTimeline, { type TimelineMarker } from '../components/WindowTimelin
|
|||||||
import CampaignRunTimeline from '../components/CampaignRunTimeline'
|
import CampaignRunTimeline from '../components/CampaignRunTimeline'
|
||||||
import PeriodComparisonSection from '../components/PeriodComparisonSection'
|
import PeriodComparisonSection from '../components/PeriodComparisonSection'
|
||||||
import ExplorationSection from '../components/ExplorationSection'
|
import ExplorationSection from '../components/ExplorationSection'
|
||||||
|
import { SEVERITY_META } from '../components/intelligent_eval/status'
|
||||||
import { useResource } from '../hooks/useResource'
|
import { useResource } from '../hooks/useResource'
|
||||||
import { useCampaignReport } from '../hooks/useCampaignReport'
|
import { useCampaignReport } from '../hooks/useCampaignReport'
|
||||||
import { useTabStore } from '../stores/tabStore'
|
import { useTabStore } from '../stores/tabStore'
|
||||||
@ -39,12 +40,6 @@ const CAMPAIGN_STATUS: Record<string, { label: string; color: string }> = {
|
|||||||
failed: { label: '失败', color: 'error' },
|
failed: { label: '失败', color: 'error' },
|
||||||
}
|
}
|
||||||
|
|
||||||
const SEVERITY_META: Record<string, { label: string; color: string }> = {
|
|
||||||
high: { label: '高', color: 'red' },
|
|
||||||
medium: { label: '中', color: 'orange' },
|
|
||||||
low: { label: '低', color: 'blue' },
|
|
||||||
}
|
|
||||||
|
|
||||||
const WINDOW_OPTIONS = [6, 12, 24, 48, 72].map((h) => ({ label: `${h} 小时`, value: h * 3600 }))
|
const WINDOW_OPTIONS = [6, 12, 24, 48, 72].map((h) => ({ label: `${h} 小时`, value: h * 3600 }))
|
||||||
|
|
||||||
const UNIT_OPTIONS = [
|
const UNIT_OPTIONS = [
|
||||||
|
|||||||
@ -48,7 +48,7 @@ export default function HomePage() {
|
|||||||
height: 240,
|
height: 240,
|
||||||
yAxis: { label: { formatter: (v: string) => `${v}%` }, min: 0, max: 100 },
|
yAxis: { label: { formatter: (v: string) => `${v}%` }, min: 0, max: 100 },
|
||||||
point: { size: 3 },
|
point: { size: 3 },
|
||||||
color: '#1677ff',
|
color: colors.primary,
|
||||||
tooltip: {
|
tooltip: {
|
||||||
formatter: (datum: TrendPoint) => ({
|
formatter: (datum: TrendPoint) => ({
|
||||||
name: '通过率',
|
name: '通过率',
|
||||||
@ -95,7 +95,7 @@ export default function HomePage() {
|
|||||||
color={(stats?.running_count ?? 0) > 0 ? 'blue' : 'green'}
|
color={(stats?.running_count ?? 0) > 0 ? 'blue' : 'green'}
|
||||||
title="运行中"
|
title="运行中"
|
||||||
value={stats?.running_count ?? 0}
|
value={stats?.running_count ?? 0}
|
||||||
valueStyle={(stats?.running_count ?? 0) > 0 ? { color: '#1677ff' } : undefined}
|
valueStyle={(stats?.running_count ?? 0) > 0 ? { color: colors.primary } : undefined}
|
||||||
/>
|
/>
|
||||||
</Col>
|
</Col>
|
||||||
<Col xs={12} sm={8} xl={4}>
|
<Col xs={12} sm={8} xl={4}>
|
||||||
|
|||||||
@ -21,7 +21,7 @@ import {
|
|||||||
type CreateIntelligentEvalPayload, type IntelligentEval, type Target,
|
type CreateIntelligentEvalPayload, type IntelligentEval, type Target,
|
||||||
} from '../api'
|
} from '../api'
|
||||||
import { useTabStore, type TabItem } from '../stores/tabStore'
|
import { useTabStore, type TabItem } from '../stores/tabStore'
|
||||||
import { colors } from '../tokens'
|
import { colors, statusColors } from '../tokens'
|
||||||
import { formatDateTime } from '../utils/date'
|
import { formatDateTime } from '../utils/date'
|
||||||
import { useIntelligentEvalRead } from '../read/useIntelligentEvalRead'
|
import { useIntelligentEvalRead } from '../read/useIntelligentEvalRead'
|
||||||
|
|
||||||
@ -47,8 +47,8 @@ const STAT_CHIPS: { key: string; label: string; color?: string }[] = [
|
|||||||
{ key: 'planning', label: '规划中' },
|
{ key: 'planning', label: '规划中' },
|
||||||
{ key: 'pending_approval', label: '待审批' },
|
{ key: 'pending_approval', label: '待审批' },
|
||||||
{ key: 'executing', label: '执行中' },
|
{ key: 'executing', label: '执行中' },
|
||||||
{ key: 'completed', label: '已完成', color: '#52c41a' },
|
{ key: 'completed', label: '已完成', color: statusColors.completed },
|
||||||
{ key: 'failed', label: '失败', color: '#ff4d4f' },
|
{ key: 'failed', label: '失败', color: statusColors.failed },
|
||||||
]
|
]
|
||||||
|
|
||||||
export default function IntelligentEvalsPage() {
|
export default function IntelligentEvalsPage() {
|
||||||
|
|||||||
@ -11,8 +11,9 @@ import {
|
|||||||
} from '@ant-design/icons'
|
} from '@ant-design/icons'
|
||||||
import { reportsApi, runsApi, type Run } from '../api'
|
import { reportsApi, runsApi, type Run } from '../api'
|
||||||
import PageWrapper from '../components/PageWrapper'
|
import PageWrapper from '../components/PageWrapper'
|
||||||
import { colors, triggerColors, triggerLabels } from '../tokens'
|
import { colors, statusColors, triggerColors, triggerLabels } from '../tokens'
|
||||||
import { formatDateTime } from '../utils/date'
|
import { formatDateTime } from '../utils/date'
|
||||||
|
import { passRateColor } from '../utils/colors'
|
||||||
import { useResource } from '../hooks/useResource'
|
import { useResource } from '../hooks/useResource'
|
||||||
|
|
||||||
interface TurnData {
|
interface TurnData {
|
||||||
@ -196,7 +197,7 @@ export default function ReportsPage() {
|
|||||||
<span style={{ color: colors.textMuted }}>· {target}</span>
|
<span style={{ color: colors.textMuted }}>· {target}</span>
|
||||||
<span style={{ color: colors.textMuted, fontSize: 12 }}>{time}</span>
|
<span style={{ color: colors.textMuted, fontSize: 12 }}>{time}</span>
|
||||||
<span style={{
|
<span style={{
|
||||||
color: passRate != null && passRate >= 0.8 ? '#3f8600' : '#cf1322',
|
color: passRateColor(passRate ?? 0),
|
||||||
fontSize: 12, fontWeight: 600,
|
fontSize: 12, fontWeight: 600,
|
||||||
}}>{pct}</span>
|
}}>{pct}</span>
|
||||||
<Tag color={triggerColors[trigger] ?? 'default'} style={{ marginRight: 0, fontSize: 10, lineHeight: '16px', padding: '0 4px' }}>
|
<Tag color={triggerColors[trigger] ?? 'default'} style={{ marginRight: 0, fontSize: 10, lineHeight: '16px', padding: '0 4px' }}>
|
||||||
@ -352,19 +353,19 @@ function SingleReportView({ report }: { report: Report | null }) {
|
|||||||
<Col xs={24} sm={12} md={6}>
|
<Col xs={24} sm={12} md={6}>
|
||||||
<Card>
|
<Card>
|
||||||
<Statistic title="通过用例" value={report.summary.passed_cases}
|
<Statistic title="通过用例" value={report.summary.passed_cases}
|
||||||
valueStyle={{ color: '#3f8600' }} prefix={<CheckCircleOutlined />} />
|
valueStyle={{ color: statusColors.completed }} prefix={<CheckCircleOutlined />} />
|
||||||
</Card>
|
</Card>
|
||||||
</Col>
|
</Col>
|
||||||
<Col xs={24} sm={12} md={6}>
|
<Col xs={24} sm={12} md={6}>
|
||||||
<Card>
|
<Card>
|
||||||
<Statistic title="失败用例" value={report.summary.failed_cases}
|
<Statistic title="失败用例" value={report.summary.failed_cases}
|
||||||
valueStyle={{ color: '#cf1322' }} prefix={<CloseCircleOutlined />} />
|
valueStyle={{ color: statusColors.failed }} prefix={<CloseCircleOutlined />} />
|
||||||
</Card>
|
</Card>
|
||||||
</Col>
|
</Col>
|
||||||
<Col xs={24} sm={12} md={6}>
|
<Col xs={24} sm={12} md={6}>
|
||||||
<Card>
|
<Card>
|
||||||
<Statistic title="通过率" value={report.summary.pass_rate * 100} precision={1} suffix="%"
|
<Statistic title="通过率" value={report.summary.pass_rate * 100} precision={1} suffix="%"
|
||||||
valueStyle={{ color: report.summary.pass_rate >= 0.8 ? '#3f8600' : '#cf1322' }} />
|
valueStyle={{ color: passRateColor(report.summary.pass_rate) }} />
|
||||||
</Card>
|
</Card>
|
||||||
</Col>
|
</Col>
|
||||||
</Row>
|
</Row>
|
||||||
@ -475,7 +476,7 @@ function CompareView({ result }: { result: CompareResult | null }) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const { run_a, run_b, delta, cases, changed_cases } = result
|
const { run_a, run_b, delta, cases, changed_cases } = result
|
||||||
const deltaColor = (v: number) => v > 0 ? '#3f8600' : v < 0 ? '#cf1322' : colors.textMuted
|
const deltaColor = (v: number) => v > 0 ? statusColors.completed : v < 0 ? statusColors.failed : colors.textMuted
|
||||||
const deltaSign = (v: number) => v > 0 ? `+${v}` : String(v)
|
const deltaSign = (v: number) => v > 0 ? `+${v}` : String(v)
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user