From 3db84a7fb24fe71b534c0874952d240a23495345 Mon Sep 17 00:00:00 2001 From: lipan Date: Thu, 13 Aug 2026 16:51:20 +0800 Subject: [PATCH] feat: refine demo flow measurement results --- client/src/components/AnalysisPanel.tsx | 6 +-- .../components/demoFlowAggregation.test.ts | 43 ++++++++++++++++++- client/src/components/demoFlowAggregation.ts | 26 ++++++++++- 3 files changed, 70 insertions(+), 5 deletions(-) diff --git a/client/src/components/AnalysisPanel.tsx b/client/src/components/AnalysisPanel.tsx index b3b90fd..17eb807 100644 --- a/client/src/components/AnalysisPanel.tsx +++ b/client/src/components/AnalysisPanel.tsx @@ -2,8 +2,8 @@ import { useState, useEffect, useMemo, useRef } from 'react'; import { trpc } from '@/lib/trpc'; import { useAuth } from '@/_core/hooks/useAuth'; import { BarChart2, Ruler, Zap, Play, Trash2, ChevronDown, ChevronRight, RefreshCw, Flame, Box, Layers, Crosshair, Cpu, Timer, Square, Maximize2, FileSpreadsheet, Image as ImageIcon } from 'lucide-react'; -import GenericResultView, { filterVisibleMeasurements, formatValue, normalizeOverlay3D, numberArrayFromUnknown, statusClass, statusLabel, type Overlay3D } from './GenericResultView'; -import { aggregateDemoFlowResults, type DemoFlowArtifactGroup, type DemoFlowStatusNotice } from './demoFlowAggregation'; +import GenericResultView, { filterVisibleMeasurements, formatValue, normalizeOverlay3D, numberArrayFromUnknown, statusClass, type Overlay3D } from './GenericResultView'; +import { aggregateDemoFlowResults, demoFlowStatusLabel, type DemoFlowArtifactGroup, type DemoFlowStatusNotice } from './demoFlowAggregation'; import { Tooltip, TooltipContent, TooltipTrigger } from './ui/tooltip'; import { Dialog, DialogContent, DialogHeader, DialogTitle } from './ui/dialog'; @@ -937,7 +937,7 @@ export default function AnalysisPanel({ taskId, onReplay, onViewSmoothHeatmap, o } - {row.measurement.status === 'info' ? '参考' : statusLabel(row.measurement)} + {demoFlowStatusLabel(row.measurement)} diff --git a/client/src/components/demoFlowAggregation.test.ts b/client/src/components/demoFlowAggregation.test.ts index fb2b075..a917416 100644 --- a/client/src/components/demoFlowAggregation.test.ts +++ b/client/src/components/demoFlowAggregation.test.ts @@ -1,5 +1,5 @@ import { describe, expect, it } from 'vitest'; -import { aggregateDemoFlowResults, type DemoFlowSourceResult } from './demoFlowAggregation'; +import { aggregateDemoFlowResults, demoFlowStatusLabel, type DemoFlowSourceResult } from './demoFlowAggregation'; function result(id: number, index: number, status: string, resultData: Record): DemoFlowSourceResult { return { id, type: 'plugin', pluginId: `plugin-${id}`, status, resultData: { demoFlowIndex: index, ...resultData } }; @@ -65,6 +65,47 @@ describe('aggregateDemoFlowResults', () => { expect(model.tableRows).toHaveLength(4); }); + it('过滤全流程指定的中间指标,同时保持其他有效测量项的原顺序', () => { + const model = aggregateDemoFlowResults([ + result(10, 1, 'completed', { + measurements: [ + { name: '车体长度', value: 100, status: 'pass' }, + { name: '前后端平移差', value: 0.1, status: 'pass' }, + { name: '车体宽度', value: 20, status: 'warning' }, + { name: 'ICP fitness', value: 0.98, status: 'pass' }, + { name: '配准 RMSE', value: 0.02, status: 'pass' }, + { name: '车体高度', value: 30, status: 'fail' }, + ], + }), + ], nameOf); + + expect(model.measurementRows.map((row) => row.measurement.name)).toEqual([ + '车体长度', '车体宽度', '车体高度', + ]); + }); + + it('全流程结果使用统一结论文案,不采用算法自定义文案', () => { + expect(demoFlowStatusLabel({ status: 'pass' })).toBe('合格'); + expect(demoFlowStatusLabel({ status: 'warning' })).toBe('警告'); + expect(demoFlowStatusLabel({ status: 'fail' })).toBe('超差'); + expect(demoFlowStatusLabel({ status: 'info' })).toBe('参考'); + expect(demoFlowStatusLabel({})).toBe('参考'); + + const model = aggregateDemoFlowResults([ + result(10, 1, 'completed', { + measurements: [ + { name: '通过项', value: 1, status: 'pass', passLabel: '通过' }, + { name: '警告项', value: 2, status: 'warning', warningLabel: '部分成功' }, + { name: '失败项', value: 3, status: 'fail', failLabel: '失败' }, + ], + }), + ], nameOf); + + expect(model.measurementRows.map((row) => demoFlowStatusLabel(row.measurement))).toEqual([ + '合格', '警告', '超差', + ]); + }); + it('全部为参考项时进入空测量结果状态,且不生成算法分组行', () => { const model = aggregateDemoFlowResults([ result(10, 1, 'completed', { diff --git a/client/src/components/demoFlowAggregation.ts b/client/src/components/demoFlowAggregation.ts index ea7765d..38fb0ad 100644 --- a/client/src/components/demoFlowAggregation.ts +++ b/client/src/components/demoFlowAggregation.ts @@ -1,5 +1,11 @@ import { filterVisibleMeasurements, type Measurement, type PluginResultLike } from './GenericResultView'; +const DEMO_FLOW_HIDDEN_MEASUREMENT_NAMES = new Set([ + '前后端平移差', + 'ICP fitness', + '配准 RMSE', +]); + export type DemoFlowSourceResult = { id: number; type: string; @@ -96,6 +102,24 @@ function hasOverlay(value: PluginResultLike['overlay3d']) { return Boolean(value && value.kind !== 'none'); } +export function filterDemoFlowMeasurements(measurements: readonly Measurement[]): Measurement[] { + return filterVisibleMeasurements(measurements) + .filter((measurement) => !DEMO_FLOW_HIDDEN_MEASUREMENT_NAMES.has(measurement.name)); +} + +export function demoFlowStatusLabel(measurement: Pick): string { + switch (measurement.status) { + case 'pass': + return '合格'; + case 'warning': + return '警告'; + case 'fail': + return '超差'; + default: + return '参考'; + } +} + export function aggregateDemoFlowResults( results: DemoFlowSourceResult[], getAlgorithmName: (result: DemoFlowSourceResult) => string, @@ -113,7 +137,7 @@ export function aggregateDemoFlowResults( const data = (asRecord(result.resultData) ?? {}) as PluginResultLike & Record; const sourceAlgorithmId = result.pluginId ?? (typeof data.pluginId === 'string' ? data.pluginId : result.type); const algorithmName = getAlgorithmName(result); - const measurements = filterVisibleMeasurements(Array.isArray(data.measurements) ? data.measurements : []); + const measurements = filterDemoFlowMeasurements(Array.isArray(data.measurements) ? data.measurements : []); if (measurements.length > 0 && options.includeAlgorithmGroups) { tableRows.push({ kind: 'algorithm', sourceAlgorithmId, algorithmName, resultId: result.id, flowIndex: index });