From e7519e552465d23fd568615287853c81673194c1 Mon Sep 17 00:00:00 2001 From: yexb <553699424@qq.com> Date: Tue, 16 Jun 2026 13:26:33 +0800 Subject: [PATCH] =?UTF-8?q?feat(mmsMapping):=20=E5=AE=8C=E5=96=84ICD?= =?UTF-8?q?=E8=B7=AF=E5=BE=84=E6=A0=A1=E9=AA=8C=E5=8A=9F=E8=83=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 添加ICD路径校验契约测试用例验证功能完整性 - 实现ICD校验一致性检查仅在需要时注入标准映射JSON - 添加ICD校验动作按钮样式与主要操作按钮保持一致 - 实现ICD激活前验证映射JSON存在性确保数据完整 - 添加ICD页面独立获取活动标准记录功能避免过滤影响 - 实现上传文件名自动填充缺失路径提升用户体验 - 添加非标准和上游ICD类型一致性校验动作显示 - 实现ICD校验结果成功失败状态指示器显示 - 分离JSON映射问题与ICD一致性问题分别处理 - 使用JSON就绪状态替代保存就绪进行校验判断 - 添加XML映射完成前保存校验结果前置条件验证 - 重构ICD校验对话框保存步骤逻辑优化流程 - 扩展ICD校验消息类型支持对象格式详情展示 - 添加校验结果中解析ICD文档回写到路径记录 - 修复映射结果面板国际化文本乱码问题 - 添加ICD一致性问题列表单独弹窗显示功能 - 实现ICD校验状态指示器交互式问题查看 - 更新序列配置界面国际化文本提升可用性 - 重构问题列表标签文本支持动态计数显示 - 优化JSON映射序列配置搜索和展示功能 - 修正ICD校验保存逻辑确保数据验证完整性 --- .../api/tools/mmsmapping/interface/index.ts | 18 +- .../components/IcdPathCheckDialog.vue | 29 +++- .../components/IcdPathFormDialog.vue | 10 +- .../contracts/check-flow-contract.mjs | 12 +- .../check-icd-path-page-contract.mjs | 81 ++++++++- .../check-result-panel-labels-contract.mjs | 38 +++++ frontend/src/views/tools/mmsMapping/index.vue | 35 +++- .../mmsMapping/utils/useMmsMappingFlow.ts | 58 +++++-- .../components/MappingResultPanel.vue | 157 +++++++++++++++--- 9 files changed, 390 insertions(+), 48 deletions(-) create mode 100644 frontend/src/views/tools/mmsMapping/contracts/check-result-panel-labels-contract.mjs diff --git a/frontend/src/api/tools/mmsmapping/interface/index.ts b/frontend/src/api/tools/mmsmapping/interface/index.ts index b64bd5d..eb250c7 100644 --- a/frontend/src/api/tools/mmsmapping/interface/index.ts +++ b/frontend/src/api/tools/mmsmapping/interface/index.ts @@ -148,6 +148,14 @@ export namespace MmsMapping { canCheckPqdif?: boolean } + export interface IcdCheckMsg { + summary?: string + details?: string[] + issuesJson?: string + correctedJson?: string + [key: string]: unknown + } + export interface CreateDeviceTypeRequest { name: string icd?: string @@ -164,10 +172,12 @@ export namespace MmsMapping { } export interface SaveIcdCheckResultRequest { + icd?: string + icdDocument?: IcdDocument mappingJson?: string xml?: string result: number - msg?: string + msg?: IcdCheckMsg } export interface IcdPathRecord { @@ -180,7 +190,7 @@ export namespace MmsMapping { jsonStr?: string xmlStr?: string result?: number - msg?: string + msg?: IcdCheckMsg | string type?: number referenceIcdId?: string createBy?: string @@ -218,10 +228,12 @@ export namespace MmsMapping { } export interface SaveIcdPathCheckResultRequest { + icd?: string + icdDocument?: IcdDocument mappingJson?: string xml?: string result?: number - msg?: string + msg?: IcdCheckMsg } export interface IcdJsonConsistencyCheckRequest { diff --git a/frontend/src/views/tools/mmsMapping/components/IcdPathCheckDialog.vue b/frontend/src/views/tools/mmsMapping/components/IcdPathCheckDialog.vue index c432514..417af20 100644 --- a/frontend/src/views/tools/mmsMapping/components/IcdPathCheckDialog.vue +++ b/frontend/src/views/tools/mmsMapping/components/IcdPathCheckDialog.vue @@ -66,6 +66,8 @@ :problem-tab-label="problemTabLabel" :problem-list="problemList" :problem-empty-text="problemEmptyText" + :icd-consistency-problem-list="icdConsistencyProblemList" + :icd-consistency-problem-empty-text="icdConsistencyProblemEmptyText" :method-describe="methodDescribe" :can-export-json-mapping="canExportJsonMapping" :can-export-xml-mapping="canExportXmlMapping" @@ -76,9 +78,11 @@ :sequence-dialog-visible="sequenceDialogVisible" :show-save-icd-check-result="showSaveIcdCheckResult" :show-icd-check-action="showConsistencyCheck" + :can-run-icd-consistency-check="canRunIcdConsistencyCheck" :can-save-icd-check-result="canSaveIcdCheckResult" :is-saving-icd-check-result="isSavingIcdCheckResult" :save-icd-check-result-text="saveIcdCheckResultText" + :icd-consistency-status="icdConsistencyStatus" :show-description="false" @export-mapping="handleExportMapping" @generate-xml-mapping="handleGenerateXmlMapping" @@ -130,7 +134,7 @@ const emit = defineEmits<{ }>() const dialogTitle = computed(() => (props.icdPathName ? `ICD校验:${props.icdPathName}` : 'ICD校验')) -const showConsistencyCheck = computed(() => props.icdPathType === 1 || props.icdPathType === 3) +const showConsistencyCheck = computed(() => props.icdPathType === 2 || props.icdPathType === 3) const saveIcdCheckResult = (params: MmsMapping.SaveIcdCheckResultRequest): Promise | boolean> => { return saveIcdPathCheckResultApi(props.icdPathId, params) @@ -163,6 +167,8 @@ const { problemTabLabel, problemList, problemEmptyText, + icdConsistencyProblemList, + icdConsistencyProblemEmptyText, methodDescribe, canExportJsonMapping, canExportXmlMapping, @@ -172,10 +178,12 @@ const { showXmlMappingTab, sequenceDialogVisible, showSaveIcdCheckResult, + canRunIcdConsistencyCheck, canSaveIcdCheckResult, isSavingIcdCheckResult, saveIcdCheckResultText, hasIcdConsistencyCheckResult, + icdConsistencyStatus, confirmDialogVisible, isConfirmingSelection, handleIcdFileChange, @@ -192,8 +200,9 @@ const { } = useMmsMappingFlow({ deviceTypeCheckId: toRef(props, 'icdPathId'), deviceTypeCheckName: toRef(props, 'icdPathName'), - standardMappingJson: computed(() => props.activeIcdPathRecord?.jsonStr?.trim() || ''), - standardMappingName: computed(() => props.activeIcdPathRecord?.name?.trim() || ''), + // 关键业务节点:只有需要一致性校验的 ICD 类型才注入标准 JSON,避免隐藏校验按钮后保存仍被校验前置条件卡住。 + standardMappingJson: computed(() => (showConsistencyCheck.value ? props.activeIcdPathRecord?.jsonStr?.trim() || '' : '')), + standardMappingName: computed(() => (showConsistencyCheck.value ? props.activeIcdPathRecord?.name?.trim() || '' : '')), saveIcdCheckResult, onIcdCheckSaved: () => { emit('saved') @@ -233,6 +242,10 @@ const activeStepIndex = computed(() => { return 0 }) +const canReachSaveStep = computed(() => + showConsistencyCheck.value ? hasIcdConsistencyCheckResult.value : hasXmlMapping.value +) + const icdCheckFlowSteps = computed(() => { const steps = [ createFlowStep( @@ -284,8 +297,14 @@ const icdCheckFlowSteps = computed(() => { steps.push( createFlowStep( '保存', - isSavingIcdCheckResult.value ? '正在保存校验结果' : hasXmlMapping.value ? '可保存校验结果' : '等待XML映射', - isSavingIcdCheckResult.value ? 'process' : 'wait' + isSavingIcdCheckResult.value + ? '正在保存校验结果' + : canReachSaveStep.value + ? '可保存校验结果' + : showConsistencyCheck.value && hasXmlMapping.value + ? '等待执行JSON一致性校验' + : '等待XML映射', + isSavingIcdCheckResult.value ? 'process' : canReachSaveStep.value ? 'process' : 'wait' ) ) diff --git a/frontend/src/views/tools/mmsMapping/components/IcdPathFormDialog.vue b/frontend/src/views/tools/mmsMapping/components/IcdPathFormDialog.vue index e71e393..8ecde90 100644 --- a/frontend/src/views/tools/mmsMapping/components/IcdPathFormDialog.vue +++ b/frontend/src/views/tools/mmsMapping/components/IcdPathFormDialog.vue @@ -139,10 +139,11 @@ const icdTypeOptions = [ { label: '手动录入的非标准', value: 2 }, { label: '上游解析传递', value: 3 } ] -const formRules: FormRules = { +const pathRequired = computed(() => !selectedIcdFile.value) +const formRules = computed>(() => ({ name: [{ required: true, message: '请输入 ICD 名称', trigger: 'blur' }], - path: [{ required: true, message: '请输入 ICD 存储路径', trigger: 'blur' }] -} + path: pathRequired.value ? [{ required: true, message: '请输入 ICD 存储路径', trigger: 'blur' }] : [] +})) const dialogTitle = computed(() => (props.mode === 'create' ? '新增ICD记录' : '编辑ICD记录')) function unwrapApiPayload(response: ResultData | T): T { @@ -190,7 +191,7 @@ watch( const buildSavePayload = (): MmsMapping.CreateIcdPathRequest => ({ name: formModel.name.trim(), - path: formModel.path.trim(), + path: formModel.path.trim() || selectedIcdFile.value?.name || '', angle: formModel.angle, usePhaseIndex: formModel.usePhaseIndex, type: formModel.type @@ -202,6 +203,7 @@ const handleClose = () => { const handleIcdFileChange = (uploadFile: UploadFile) => { selectedIcdFile.value = uploadFile.raw || null + formRef.value?.clearValidate('path') } const handleIcdFileRemove = () => { diff --git a/frontend/src/views/tools/mmsMapping/contracts/check-flow-contract.mjs b/frontend/src/views/tools/mmsMapping/contracts/check-flow-contract.mjs index 13a6638..3d18e35 100644 --- a/frontend/src/views/tools/mmsMapping/contracts/check-flow-contract.mjs +++ b/frontend/src/views/tools/mmsMapping/contracts/check-flow-contract.mjs @@ -29,7 +29,17 @@ const checks = [ / /saveIcdCheckResultApi/.test(flowSource)], - ['flow supports saved callback for dialog host', () => /onIcdCheckSaved/.test(flowSource) && /options\.onIcdCheckSaved\?\.\(\)/.test(flowSource)] + ['flow supports saved callback for dialog host', () => /onIcdCheckSaved/.test(flowSource) && /options\.onIcdCheckSaved\?\.\(\)/.test(flowSource)], + [ + 'ICD path check dialog only requires standard mapping when consistency check is visible', + () => + /standardMappingJson:\s*computed\(\(\)\s*=>\s*\(?showConsistencyCheck\.value\s*\?\s*props\.activeIcdPathRecord\?\.jsonStr\?\.trim\(\)\s*\|\|\s*''\s*:\s*''\)?\)/.test( + checkDialogSource + ) && + /standardMappingName:\s*computed\(\(\)\s*=>\s*\(?showConsistencyCheck\.value\s*\?\s*props\.activeIcdPathRecord\?\.name\?\.trim\(\)\s*\|\|\s*''\s*:\s*''\)?\)/.test( + checkDialogSource + ) + ] ] const failures = checks.filter(([, check]) => !check()).map(([name]) => name) diff --git a/frontend/src/views/tools/mmsMapping/contracts/check-icd-path-page-contract.mjs b/frontend/src/views/tools/mmsMapping/contracts/check-icd-path-page-contract.mjs index cc61e73..24ced36 100644 --- a/frontend/src/views/tools/mmsMapping/contracts/check-icd-path-page-contract.mjs +++ b/frontend/src/views/tools/mmsMapping/contracts/check-icd-path-page-contract.mjs @@ -78,7 +78,13 @@ const checks = [ ['ICD path type search uses select options', () => /prop:\s*'type'[\s\S]*enum:\s*icdTypeOptions[\s\S]*search:\s*\{[\s\S]*el:\s*'select'/.test(pageSource)], ['ICD path form uses select for type', () => / /type:\s*3/.test(formDialogSource) && /formModel\.type\s*=\s*3/.test(formDialogSource)], - ['ICD dialog action buttons match parse ICD primary style', () => isPrimarySolidButton(requestPanelSource, '选择 ICD') && isPrimarySolidButton(resultPanelSource, 'ICD校验') && isPrimarySolidButton(resultPanelSource, 'saveIcdCheckResultText')], + [ + 'ICD dialog action buttons match parse ICD primary style', + () => + isPrimarySolidButton(requestPanelSource, '选择 ICD') && + / @@ -87,17 +93,86 @@ const checks = [ ) && /renderActivationStatus[\s\S]*handleActivateIcdPath\(row\)/.test(pageSource) ], ['ICD activation updates current record to standard type', () => /handleActivateIcdPath/.test(pageSource) && /type:\s*1/.test(pageSource) && /updateIcdPathApi/.test(pageSource)], + ['ICD activation requires mapping JSON before setting standard type', () => /handleActivateIcdPath[\s\S]*row\.jsonStr\?\.trim\(\)[\s\S]*不能激活/.test(pageSource)], + ['ICD page fetches active standard record independent of current table filters', () => /refreshActiveIcdPathRecord/.test(pageSource) && /listIcdPathsApi\(\{\s*type:\s*1\s*\}\)/.test(pageSource)], ['ICD path form supports multipart file save', () => / /pathRequired/.test(formDialogSource) && /selectedIcdFile\.value\?\.name/.test(formDialogSource)], ['ICD check dialog receives ICD type', () => /:icd-path-type="currentIcdCheckRecord\.type"/.test(pageSource) && /icdPathType:\s*number/.test(checkDialogSource)], ['ICD check dialog receives active record JSON as standard mapping', () => /:active-icd-path-record="activeIcdPathRecord"/.test(pageSource) && /activeIcdPathRecord:\s*MmsMapping\.IcdPathRecord\s*\|\s*null/.test(checkDialogSource) && /standardMappingJson/.test(flowSource)], [ - 'standard and upstream ICD types show consistency check action', + 'non-standard and upstream ICD types show consistency check action', () => /showConsistencyCheck/.test(checkDialogSource) && - /props\.icdPathType\s*===\s*1[\s\S]*props\.icdPathType\s*===\s*3/.test(checkDialogSource) && + /props\.icdPathType\s*===\s*2[\s\S]*props\.icdPathType\s*===\s*3/.test(checkDialogSource) && + !/props\.icdPathType\s*===\s*1/.test(checkDialogSource) && /:show-icd-check-action="showConsistencyCheck"/.test(checkDialogSource) ], ['ICD check action calls backend consistency check before save', () => /@icd-check="handleIcdConsistencyCheck"/.test(checkDialogSource) && /handleIcdConsistencyCheck/.test(flowSource) && /checkIcdJsonConsistencyApi/.test(flowSource) && /lastIcdConsistencyCheckResult/.test(flowSource)], + [ + 'ICD check result renders success and failure status indicator', + () => + /icdConsistencyStatus/.test(flowSource) && + /:icd-consistency-status="icdConsistencyStatus"/.test(checkDialogSource) && + /icd-consistency-indicator/.test(resultPanelSource) && + /icdConsistencyProblemDialogVisible\s*=\s*true/.test(resultPanelSource) + ], + [ + 'ICD check issues are separated from JSON mapping problems', + () => + /const\s+jsonMappingProblemList\s*=\s*computed\(\(\)\s*=>\s*\[/.test(flowSource) && + /const\s+icdConsistencyProblemList\s*=\s*computed\(\(\)\s*=>\s*lastIcdConsistencyCheckResult\.value\?\.issues/.test( + flowSource + ) && + !/const\s+problemList\s*=\s*computed\(\(\)\s*=>\s*\[[\s\S]*lastIcdConsistencyCheckResult\.value\?\.issues/.test( + flowSource + ) && + /:icd-consistency-problem-list="icdConsistencyProblemList"/.test(checkDialogSource) && + /ICD_CONSISTENCY_PROBLEM_LABEL/.test(resultPanelSource) + ], + ['ICD check action uses JSON readiness instead of save readiness', () => /canRunIcdConsistencyCheck/.test(flowSource) && /:can-run-icd-consistency-check="canRunIcdConsistencyCheck"/.test(checkDialogSource) && /:disabled="!canRunIcdConsistencyCheck"/.test(resultPanelSource)], + [ + 'ICD check result save requires XML mapping before save', + () => + /canSaveIcdCheckResult[\s\S]*xmlContentForExport\.value/.test(flowSource) && + /handleSaveIcdCheckResult[\s\S]*xmlContentForExport\.value[\s\S]*请先完成 XML 映射后再保存/.test(flowSource) && + /xml:\s*xmlContentForExport\.value/.test(flowSource) + ], + [ + 'ICD check dialog keeps save after XML and optional consistency check', + () => + /canReachSaveStep[\s\S]*showConsistencyCheck\.value\s*\?\s*hasIcdConsistencyCheckResult\.value\s*:\s*hasXmlMapping\.value/.test( + checkDialogSource + ) && + /steps\.push\([\s\S]*canReachSaveStep\.value[\s\S]*'可保存校验结果'[\s\S]*showConsistencyCheck\.value\s*&&\s*hasXmlMapping\.value[\s\S]*'等待执行JSON一致性校验'/.test( + checkDialogSource + ) + ], + [ + 'ICD path check msg uses JSON object payload and readable table render', + () => + /interface\s+IcdCheckMsg[\s\S]*summary\?:\s*string[\s\S]*details\?:\s*string\[\][\s\S]*\[key:\s*string\]:\s*unknown/.test( + typeSource + ) && + /SaveIcdPathCheckResultRequest[\s\S]*msg\?:\s*IcdCheckMsg/.test(typeSource) && + /IcdPathRecord[\s\S]*msg\?:\s*IcdCheckMsg\s*\|\s*string/.test(typeSource) && + /buildIcdCheckMsg/.test(flowSource) && + /summary:\s*summaryMessage/.test(flowSource) && + /details:\s*checkResult\?\.issues/.test(flowSource) && + /issuesJson:\s*checkResult\?\.issuesJson/.test(flowSource) && + /correctedJson:\s*checkResult\?\.correctedJson/.test(flowSource) && + /renderIcdCheckMsg/.test(pageSource) && + /JSON\.stringify\(value\)/.test(pageSource) + ], + [ + 'ICD path check save writes parsed ICD document back to path record', + () => + /SaveIcdCheckResultRequest[\s\S]*icdDocument\?:\s*IcdDocument/.test(typeSource) && + /SaveIcdPathCheckResultRequest[\s\S]*icdDocument\?:\s*IcdDocument/.test(typeSource) && + /const\s+parsedIcdDocument\s*=\s*ref\(null\)/.test(flowSource) && + /parsedIcdDocument\.value\s*=\s*payload\.icdDocument\s*\|\|\s*null/.test(flowSource) && + /icdDocument:\s*parsedIcdDocument\.value\s*\|\|\s*responsePayload\.value\?\.icdDocument/.test(flowSource) + ], + ['mapping result panel has no mojibake text', () => !/[鏄搴褰鍙纭鏍閰獙][\s\S]*[犲垪撳栨疆]/.test(resultPanelSource)], ['flow supports injected ICD check save handler', () => /saveIcdCheckResult\?:/.test(flowSource) && /options\.saveIcdCheckResult/.test(flowSource)], ['form dialog follows ICD check dialog visual shell', () => /class="icd-check-dialog"/.test(formDialogSource) && /class="icd-check-dialog__body"/.test(formDialogSource)], ['check dialog reuses shared MMS mapping flow panels', () => / /problemButtonText/.test(source)], + ['Old generic problem dialog title is removed', () => !source.includes('title="问题列表"')], + ['XML match result action opens dedicated XML match dialog', () => /@click="matchResultDialogVisible = true"/.test(source)], + ['Old XML match result display text is removed', () => !source.includes('匹配结果展示')], + [ + 'ICD consistency status is placed in preview section, not panel actions', + () => + /class="panel-section result-card grow-card preview-tab-section"[\s\S]*class="icd-consistency-status"/.test( + source + ) && + !/class="panel-actions"[\s\S]*class="icd-consistency-status"[\s\S]*<\/div>\s*<\/div>\s*
import { Connection, Delete, Edit, Plus } from '@element-plus/icons-vue' import { ElMessage, ElMessageBox, type TagProps } from 'element-plus' -import { reactive, ref } from 'vue' +import { nextTick, reactive, ref } from 'vue' import type { ResultData } from '@/api/interface' import ProTable from '@/components/ProTable/index.vue' import type { ColumnProps, ProTableInstance } from '@/components/ProTable/interface' @@ -136,6 +136,18 @@ const icdTypeOptions = [ const getIcdTypeText = (value?: number) => icdTypeOptions.find(option => option.value === value)?.label || '未知类型' +const renderIcdCheckMsg = (value: MmsMapping.IcdPathRecord['msg']) => { + if (!value) return '' + if (typeof value === 'string') return value + if (typeof value.summary === 'string' && value.summary.trim()) return value.summary.trim() + + try { + return JSON.stringify(value) + } catch { + return '' + } +} + const renderActivationStatus = (row: MmsMapping.IcdPathRecord) => { if (row.type === 1) { return ( @@ -225,7 +237,7 @@ const columns = reactive[]>([ order: 3 } }, - { prop: 'msg', label: '结论描述', minWidth: 220, isShow: false }, + { prop: 'msg', label: '结论描述', minWidth: 220, isShow: false, render: scope => renderIcdCheckMsg(scope.row.msg) }, { prop: 'referenceIcdId', label: '标准ICD引用', minWidth: 170, isShow: false }, { prop: 'createTime', label: '创建时间', minWidth: 170 }, { prop: 'updateTime', label: '更新时间', minWidth: 170, isShow: false }, @@ -255,7 +267,7 @@ const getTableList = async (params: IcdPathTableParams = {}) => { const pageSize = params.pageSize || 10 const startIndex = (pageNum - 1) * pageSize - activeIcdPathRecord.value = records.find(record => record.type === 1 && record.state !== 0) || null + activeIcdPathRecord.value = records.find(record => record.type === 1 && record.state !== 0) || activeIcdPathRecord.value // ICD 路径接口当前返回列表数据;这里统一包装为 ProTable 分页结构,保持和设备类型管理页一致。 return { @@ -268,9 +280,18 @@ const getTableList = async (params: IcdPathTableParams = {}) => { } } +const refreshActiveIcdPathRecord = async () => { + // 关键业务节点:标准 ICD 不能依赖当前表格筛选结果,否则校验弹窗可能拿不到已激活记录的 JSON。 + const response = await listIcdPathsApi({ type: 1 }) + const records = unwrapApiPayload(response) || [] + + activeIcdPathRecord.value = records.find(record => record.type === 1 && record.state !== 0) || null +} + const refreshIcdPaths = () => { proTable.value?.clearSelection() proTable.value?.getTableList() + refreshActiveIcdPathRecord() } const handleTableRequestError = (error: unknown) => { @@ -333,6 +354,11 @@ const handleActivateIcdPath = async (row: MmsMapping.IcdPathRecord) => { return } + if (!row.jsonStr?.trim()) { + ElMessage.warning('当前 ICD 记录缺少 JSON 映射结果,不能激活,请先完成 ICD 校验并保存') + return + } + try { await ElMessageBox.confirm('激活后该 ICD 将作为标准 ICD 参与一致性校验,是否继续?', '激活ICD记录', { confirmButtonText: '确定', @@ -381,8 +407,11 @@ const handleIcdCheck = (row: MmsMapping.IcdPathRecord) => { currentIcdCheckRecord.id = row.id currentIcdCheckRecord.name = row.name || '' currentIcdCheckRecord.type = row.type ?? 3 + refreshActiveIcdPathRecord() icdCheckDialogVisible.value = true } + +nextTick(refreshActiveIcdPathRecord)