feat(frontend): surface scenario version across dropdowns and views
Some checks failed
CI / test (push) Failing after 43s

场景启动下拉、报告页运行下拉(A/B)、场景筛选(版本范围 v1/v2)、
仪表盘最近评测行、场景预览与编辑弹窗标题统一带上考纲版本标识。
This commit is contained in:
sinohqb 2026-07-29 14:20:09 +08:00
parent 770d260750
commit 8e3cf82fcc
4 changed files with 27 additions and 9 deletions

View File

@ -216,6 +216,11 @@ function RecentRunRow({ run, onOpen }: { run: Run; onOpen: () => void }) {
<span style={{ fontWeight: 500 }}>{run.scenario_name || run.scenario_id.slice(0, 8)}</span> <span style={{ fontWeight: 500 }}>{run.scenario_name || run.scenario_id.slice(0, 8)}</span>
<span style={{ color: colors.textMuted }}> · {run.target_name || run.target_id.slice(0, 8)}</span> <span style={{ color: colors.textMuted }}> · {run.target_name || run.target_id.slice(0, 8)}</span>
</div> </div>
{run.scenario_version != null && (
<Tag style={{ margin: 0, fontSize: 10, lineHeight: '16px', padding: '0 4px' }}>
v{run.scenario_version}
</Tag>
)}
{trigger !== 'manual' && ( {trigger !== 'manual' && (
<Tag color={triggerColors[trigger] ?? 'default'} style={{ margin: 0, fontSize: 10, lineHeight: '16px', padding: '0 4px' }}> <Tag color={triggerColors[trigger] ?? 'default'} style={{ margin: 0, fontSize: 10, lineHeight: '16px', padding: '0 4px' }}>
{triggerLabels[trigger] ?? trigger} {triggerLabels[trigger] ?? trigger}

View File

@ -159,15 +159,26 @@ export default function ReportsPage() {
clearSelection() clearSelection()
} }
// 场景筛选项:从 run 列表聚合(含各场景 run 数量 // 场景筛选项:从 run 列表聚合(含各场景 run 数量与出现过的考纲版本
const scenarioOptions = useMemo(() => { const scenarioOptions = useMemo(() => {
const map = new Map<string, { name: string; count: number }>() const map = new Map<string, { name: string; count: number; versions: Set<number> }>()
for (const r of runs) { for (const r of runs) {
const entry = map.get(r.scenario_id) const entry = map.get(r.scenario_id)
if (entry) entry.count += 1 if (entry) {
else map.set(r.scenario_id, { name: r.scenario_name || r.scenario_id.slice(0, 8), count: 1 }) entry.count += 1
entry.versions.add(r.scenario_version ?? 1)
} else {
map.set(r.scenario_id, {
name: r.scenario_name || r.scenario_id.slice(0, 8),
count: 1,
versions: new Set([r.scenario_version ?? 1]),
})
}
} }
return [...map.entries()].map(([id, s]) => ({ value: id, label: `${s.name}${s.count}` })) return [...map.entries()].map(([id, s]) => {
const versions = [...s.versions].sort((a, b) => a - b).map((v) => `v${v}`).join('/')
return { value: id, label: `${s.name}${s.count} · ${versions}` }
})
}, [runs]) }, [runs])
const filteredRuns = useMemo( const filteredRuns = useMemo(
@ -179,15 +190,17 @@ export default function ReportsPage() {
const passRate = (r.summary as Record<string, unknown>)?.pass_rate as number | undefined const passRate = (r.summary as Record<string, unknown>)?.pass_rate as number | undefined
const pct = passRate != null ? `${Math.round(passRate * 100)}%` : '-' const pct = passRate != null ? `${Math.round(passRate * 100)}%` : '-'
const scenario = r.scenario_name || r.scenario_id.slice(0, 8) const scenario = r.scenario_name || r.scenario_id.slice(0, 8)
const version = `v${r.scenario_version ?? 1}`
const target = r.target_name || r.target_id.slice(0, 8) const target = r.target_name || r.target_id.slice(0, 8)
const time = r.started_at ? formatDateTime(r.started_at) : '' const time = r.started_at ? formatDateTime(r.started_at) : ''
const trigger = r.triggered_by ?? 'manual' const trigger = r.triggered_by ?? 'manual'
return { return {
value: r.id, value: r.id,
searchText: `${scenario} ${target} ${time} ${r.id} ${triggerLabels[trigger] ?? trigger}`.toLowerCase(), searchText: `${scenario} ${version} ${target} ${time} ${r.id} ${triggerLabels[trigger] ?? trigger}`.toLowerCase(),
label: ( label: (
<span style={{ display: 'flex', alignItems: 'center', gap: 6 }}> <span style={{ display: 'flex', alignItems: 'center', gap: 6 }}>
<span style={{ fontWeight: 500, overflow: 'hidden', textOverflow: 'ellipsis' }}>{scenario}</span> <span style={{ fontWeight: 500, overflow: 'hidden', textOverflow: 'ellipsis' }}>{scenario}</span>
<Tag color="geekblue" style={{ marginRight: 0, fontSize: 10, lineHeight: '16px', padding: '0 4px' }}>{version}</Tag>
<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={{

View File

@ -163,7 +163,7 @@ export default function RunsPage() {
filterOption={(input, opt) => filterOption={(input, opt) =>
(opt?.label as string ?? '').toLowerCase().includes(input.toLowerCase())} (opt?.label as string ?? '').toLowerCase().includes(input.toLowerCase())}
placeholder="选择评测场景" placeholder="选择评测场景"
options={scenarios.map((s) => ({ label: s.name, value: s.id }))} options={scenarios.map((s) => ({ label: `${s.name}v${s.version ?? 1}`, value: s.id }))}
style={{ flex: 1 }} style={{ flex: 1 }}
size="middle" size="middle"
/> />

View File

@ -248,7 +248,7 @@ export default function ScenariosPage() {
</div> </div>
<Drawer <Drawer
title={editingScenario ? '编辑评测场景' : '新增评测场景'} title={editingScenario ? `编辑评测场景(当前 v${editingScenario.version ?? 1}` : '新增评测场景'}
open={drawerOpen} open={drawerOpen}
onClose={() => setDrawerOpen(false)} onClose={() => setDrawerOpen(false)}
width={720} width={720}
@ -329,7 +329,7 @@ export default function ScenariosPage() {
</Drawer> </Drawer>
<Modal <Modal
title={`场景预览: ${previewData?.name || ''}`} title={`场景预览: ${previewData?.name || ''}${previewData?.version != null ? `v${previewData.version}` : ''}`}
open={previewOpen} open={previewOpen}
onCancel={() => setPreviewOpen(false)} onCancel={() => setPreviewOpen(false)}
footer={null} footer={null}