diff --git a/client/src/components/AlgorithmRunPanel.tsx b/client/src/components/AlgorithmRunPanel.tsx index 16482b4..68c0bc7 100644 --- a/client/src/components/AlgorithmRunPanel.tsx +++ b/client/src/components/AlgorithmRunPanel.tsx @@ -208,6 +208,7 @@ export default function AlgorithmRunPanel({ taskId, modelId, pointCloudLoaded: _ const demoPluginIds = new Set(demoAllPluginIds); const runningDemoResults = analysisResults.filter((r) => (r.status === 'processing' || r.status === 'pending') && r.type === 'plugin' && r.pluginId && demoPluginIds.has(r.pluginId)); const demoFlowRunning = runningDemoResults.length > 0; + const demoFlowActive = demoFlowRunning || runDemoAll.isPending; const selectedPcCount = selectedPcFileIds?.length ?? 0; const selectedCadCount = selectedCadFileIds?.length ?? 0; @@ -490,7 +491,7 @@ export default function AlgorithmRunPanel({ taskId, modelId, pointCloudLoaded: _ {/* 未配准提示:有点云文件且选了测量算法,但所选点云均未配准 */} - {selectedMeasIds.length > 0 && !hasRegisteredCloud && (taskPcFileCount ?? 0) > 0 && ( + {selectedMeasIds.length > 0 && !hasRegisteredCloud && (taskPcFileCount ?? 0) > 0 && !demoFlowActive && (
diff --git a/client/src/components/AnalysisPanel.tsx b/client/src/components/AnalysisPanel.tsx index 5622ac0..5e730f7 100644 --- a/client/src/components/AnalysisPanel.tsx +++ b/client/src/components/AnalysisPanel.tsx @@ -55,7 +55,13 @@ interface Props { /** 外部分析任务是否正在提交中 */ externalPending?: boolean; /** 插件叠加层:用户点击「在 3D 中查看叠加层」时回调 Home → PointCloudViewer */ - onViewPluginOverlay?: (args: { taskId: number; resultId: number; overlay: Overlay3D }) => void; + onViewPluginOverlay?: (args: { + taskId: number; + resultId: number; + overlay: Overlay3D; + inputPointCloudFileIds?: number[]; + inputCadFileIds?: number[]; + }) => void; } type Measurement = { name: string; value: number; unit: string; tolerance: number; status: string }; @@ -226,6 +232,8 @@ type DemoFlowMeta = { total?: number; }; +const SHOW_LEGACY_ANALYSIS_ACTIONS = false; + type AnalysisListItem = | { kind: 'result'; result: AnalysisResultRow } | { kind: 'demoFlow'; runId: string; results: AnalysisResultRow[] }; @@ -752,36 +760,44 @@ export default function AnalysisPanel({ taskId, onReplay, onViewSmoothHeatmap, o > - - {onRunAssembly && ( - - )} - {onRunDeviation && ( - + {/* 产品策略:旧分析入口暂不在标题栏展示,保留实现便于后续恢复。 + 误差分析当前后端仍是模拟统计结果,不作为正式入口展示。 + 装配检测仍有 legacy 后台任务,但暂不作为主流程按钮展示。 + 偏差热力依赖 STL/OBJ,不适合当前以 STEP 为主的流程,暂时隐藏。 */} + {SHOW_LEGACY_ANALYSIS_ACTIONS && ( + <> + + {onRunAssembly && ( + + )} + {onRunDeviation && ( + + )} + )}
diff --git a/client/src/components/GenericResultView.tsx b/client/src/components/GenericResultView.tsx index b61abe2..af64684 100644 --- a/client/src/components/GenericResultView.tsx +++ b/client/src/components/GenericResultView.tsx @@ -38,7 +38,11 @@ interface ErrorStats { type Overlay3D = | { kind: 'none' } | { kind: 'vertexColors'; file: string; vertexCount: number; dispMin: number; dispMax: number } - | { kind: 'ply'; file: string }; + | { kind: 'ply'; file: string; files?: string[] }; + +type RawOverlay3D = + | Overlay3D + | { kind: 'vertexColors'; file?: string; url?: string; vertexCount?: number; dispMin?: number; dispMax?: number }; interface PluginResultLike { pluginId?: string; @@ -46,7 +50,9 @@ interface PluginResultLike { message?: string; measurements?: Measurement[]; errorStats?: ErrorStats | null; - overlay3d?: Overlay3D; + overlay3d?: RawOverlay3D; + inputPointCloudFileIds?: unknown; + inputCadFileIds?: unknown; images?: string[]; /** outDir 下的报表产物(相对路径,如 report.xlsx),前端展示下载入口 */ files?: (string | { name?: string; path?: string })[]; @@ -61,7 +67,13 @@ interface Props { pluginName?: string; data: PluginResultLike; /** 用户点击「在 3D 中查看」时回调,把叠加层信息传给 Home → PointCloudViewer */ - onViewOverlay?: (overlay: { taskId: number; resultId: number; overlay: Overlay3D }) => void; + onViewOverlay?: (overlay: { + taskId: number; + resultId: number; + overlay: Overlay3D; + inputPointCloudFileIds?: number[]; + inputCadFileIds?: number[]; + }) => void; } function statusClass(s?: Measurement['status']) { @@ -97,6 +109,48 @@ function formatValue(v: number): string { return v.toFixed(3); } +function numberArrayFromUnknown(value: unknown): number[] | undefined { + if (!Array.isArray(value)) return undefined; + const ids = value.filter((id): id is number => typeof id === 'number' && Number.isInteger(id)); + return ids.length > 0 ? ids : undefined; +} + +function normalizeOverlay3D(overlay?: RawOverlay3D): Overlay3D { + if (!overlay || overlay.kind === 'none') return { kind: 'none' }; + if (overlay.kind === 'ply') { + const files = Array.isArray(overlay.files) + ? overlay.files.filter((f): f is string => typeof f === 'string' && f.length > 0) + : undefined; + return files && files.length > 0 + ? { kind: 'ply', file: overlay.file, files } + : { kind: 'ply', file: overlay.file }; + } + + const legacyUrl = 'url' in overlay ? overlay.url : undefined; + const file = typeof overlay.file === 'string' && overlay.file.length > 0 + ? overlay.file + : typeof legacyUrl === 'string' && legacyUrl.length > 0 + ? legacyUrl + : undefined; + if (!file) return { kind: 'none' }; + + if ( + typeof overlay.vertexCount === 'number' && + typeof overlay.dispMin === 'number' && + typeof overlay.dispMax === 'number' + ) { + return { + kind: 'vertexColors', + file, + vertexCount: overlay.vertexCount, + dispMin: overlay.dispMin, + dispMax: overlay.dispMax, + }; + } + + return { kind: 'ply', file }; +} + export default function GenericResultView({ taskId, resultId, @@ -112,7 +166,9 @@ export default function GenericResultView({ const files = (data.files ?? []).map((f) => typeof f === 'string' ? f : (f as { name?: string; path?: string }).name ?? String(f) ); - const overlay = data.overlay3d ?? { kind: 'none' }; + const overlay = normalizeOverlay3D(data.overlay3d); + const inputPointCloudFileIds = numberArrayFromUnknown(data.inputPointCloudFileIds); + const inputCadFileIds = numberArrayFromUnknown(data.inputCadFileIds); const displayPluginName = pluginName ?? pluginId ?? data.pluginId; return ( @@ -191,7 +247,7 @@ export default function GenericResultView({ -
- - - {pcLoading ? `加载点云 ${pcProgress}%` : - cadLoading ? `加载 CAD ${cadLoadProgress}%` : - '待机'} - -
+ {(pcLoading || cadLoading) && ( +
+ + + {pcLoading ? `加载点云 ${pcProgress}%` : `加载 CAD ${cadLoadProgress}%`} + +
+ )} {loading ? (
- ) : isAuthenticated ? ( -
- {user?.name || '用户'} - -
- ) : ( + ) : !isAuthenticated ? ( - )} + ) : null}
@@ -439,6 +463,7 @@ export default function Home() { pointCloudUrls={pointCloudUrlsStable} batchTaskId={batchTaskId ?? undefined} loadKey={pcLoadKey} + showPointCloud={showPointCloud} selectedPcFileIds={selectedPcFileIds.length > 0 ? selectedPcFileIds : undefined} onCadProgress={handleCadProgress} onCadLoaded={handleCadLoaded} @@ -474,13 +499,9 @@ export default function Home() { {/* 操作提示 */}
- {errorPickEnabled && pointCloudRenderMode === 'smoothHeatmap' && ( - 平滑模式不支持选点,请切回「点云」 - )} - {errorPickEnabled && pointCloudRenderMode === 'points' - ? <>左键点击选点误差右键平移滚轮缩放 - : <>左键旋转右键平移滚轮缩放 - } + 左键旋转 + 右键平移 + 滚轮缩放
{/* 选中点信息 */} @@ -522,15 +543,21 @@ export default function Home() {
{heatmapViewResultId != null && pointCloudRenderMode === 'smoothHeatmap' && ( @@ -538,26 +565,12 @@ export default function Home() { )} - -
@@ -584,7 +597,7 @@ export default function Home() { setRenderPointBudget(Number(e.target.value))} diff --git a/server/algorithmJobRunner.ts b/server/algorithmJobRunner.ts index d6997e5..61c1b2a 100644 --- a/server/algorithmJobRunner.ts +++ b/server/algorithmJobRunner.ts @@ -122,6 +122,8 @@ async function writeInputJson( params: Record, pointCloudPaths: string[], cadPath: string | null, + inputPointCloudFileIds: number[], + inputCadFileIds: number[], ) { const inputJson = { schemaVersion: 1, @@ -133,7 +135,9 @@ async function writeInputJson( outDir: workDir, inputs: { pointCloudPaths, + pointCloudFileIds: inputPointCloudFileIds, cadPath, + cadFileIds: inputCadFileIds, config: params, }, }; @@ -331,6 +335,9 @@ export async function runAlgorithmJob( pointCloudFileIds, cadFileIds, ); + const inputPointCloudFileIds = pointCloudRecords.map((r) => r.id); + const inputCadFileIds = Array.isArray(cadFileIds) ? cadFileIds.filter((id) => Number.isInteger(id)) : []; + const inputJsonPath = await writeInputJson( workDir, taskId, @@ -339,6 +346,8 @@ export async function runAlgorithmJob( params, pointCloudPaths, cadPath, + inputPointCloudFileIds, + inputCadFileIds, ); let exitCode: number | null = 0; @@ -363,6 +372,8 @@ export async function runAlgorithmJob( progress: finalStatus === 'completed' ? 1 : undefined, stage: finalStatus === 'completed' ? '完成' : '失败', exitCode, + inputPointCloudFileIds, + inputCadFileIds, // 把 result.json 内容平展到 resultData 里,方便前端直接使用 schemaVersion: result?.schemaVersion ?? manifest.schemaVersion, message: result?.message, diff --git a/server/plugins/adapters/flatness_suite_run.py b/server/plugins/adapters/flatness_suite_run.py index b5d8e2a..289b165 100644 --- a/server/plugins/adapters/flatness_suite_run.py +++ b/server/plugins/adapters/flatness_suite_run.py @@ -394,7 +394,7 @@ def main() -> int: overlay3d: dict = {"kind": "none"} for ply_key in ("contourPly", "curvaturePly"): if ply_key in extra: - overlay3d = {"kind": "vertexColors", "url": extra[ply_key]} + overlay3d = {"kind": "ply", "file": extra[ply_key]} break if skipped: diff --git a/server/plugins/types.ts b/server/plugins/types.ts index 449f803..d689132 100644 --- a/server/plugins/types.ts +++ b/server/plugins/types.ts @@ -141,6 +141,8 @@ export type PluginOverlay3D = /** 着色 PLY 叠加文件(同样相对 outDir) */ kind: 'ply'; file: string; + /** 批量结果可包含多个 PLY 叠加文件 */ + files?: string[]; }; /** 算法写出的 result.json(与 INTEGRATION.md 描述对齐) */ @@ -177,4 +179,8 @@ export interface PluginResultData extends Partial { error?: string; /** 子进程已结束时的退出码 */ exitCode?: number | null; + /** 本次算法运行实际使用的任务点云文件 ID,用于前端叠加层匹配校验 */ + inputPointCloudFileIds?: number[]; + /** 本次算法运行前端选择的任务 CAD 文件 ID,用于后续匹配/追溯 */ + inputCadFileIds?: number[]; }