feat(mmsMapping): 完善ICD路径校验功能

- 添加ICD路径校验契约测试用例验证功能完整性
- 实现ICD校验一致性检查仅在需要时注入标准映射JSON
- 添加ICD校验动作按钮样式与主要操作按钮保持一致
- 实现ICD激活前验证映射JSON存在性确保数据完整
- 添加ICD页面独立获取活动标准记录功能避免过滤影响
- 实现上传文件名自动填充缺失路径提升用户体验
- 添加非标准和上游ICD类型一致性校验动作显示
- 实现ICD校验结果成功失败状态指示器显示
- 分离JSON映射问题与ICD一致性问题分别处理
- 使用JSON就绪状态替代保存就绪进行校验判断
- 添加XML映射完成前保存校验结果前置条件验证
- 重构ICD校验对话框保存步骤逻辑优化流程
- 扩展ICD校验消息类型支持对象格式详情展示
- 添加校验结果中解析ICD文档回写到路径记录
- 修复映射结果面板国际化文本乱码问题
- 添加ICD一致性问题列表单独弹窗显示功能
- 实现ICD校验状态指示器交互式问题查看
- 更新序列配置界面国际化文本提升可用性
- 重构问题列表标签文本支持动态计数显示
- 优化JSON映射序列配置搜索和展示功能
- 修正ICD校验保存逻辑确保数据验证完整性
This commit is contained in:
2026-06-16 13:26:33 +08:00
parent ef80aff151
commit e7519e5524
9 changed files with 390 additions and 48 deletions

View File

@@ -148,6 +148,14 @@ export namespace MmsMapping {
canCheckPqdif?: boolean canCheckPqdif?: boolean
} }
export interface IcdCheckMsg {
summary?: string
details?: string[]
issuesJson?: string
correctedJson?: string
[key: string]: unknown
}
export interface CreateDeviceTypeRequest { export interface CreateDeviceTypeRequest {
name: string name: string
icd?: string icd?: string
@@ -164,10 +172,12 @@ export namespace MmsMapping {
} }
export interface SaveIcdCheckResultRequest { export interface SaveIcdCheckResultRequest {
icd?: string
icdDocument?: IcdDocument
mappingJson?: string mappingJson?: string
xml?: string xml?: string
result: number result: number
msg?: string msg?: IcdCheckMsg
} }
export interface IcdPathRecord { export interface IcdPathRecord {
@@ -180,7 +190,7 @@ export namespace MmsMapping {
jsonStr?: string jsonStr?: string
xmlStr?: string xmlStr?: string
result?: number result?: number
msg?: string msg?: IcdCheckMsg | string
type?: number type?: number
referenceIcdId?: string referenceIcdId?: string
createBy?: string createBy?: string
@@ -218,10 +228,12 @@ export namespace MmsMapping {
} }
export interface SaveIcdPathCheckResultRequest { export interface SaveIcdPathCheckResultRequest {
icd?: string
icdDocument?: IcdDocument
mappingJson?: string mappingJson?: string
xml?: string xml?: string
result?: number result?: number
msg?: string msg?: IcdCheckMsg
} }
export interface IcdJsonConsistencyCheckRequest { export interface IcdJsonConsistencyCheckRequest {

View File

@@ -66,6 +66,8 @@
:problem-tab-label="problemTabLabel" :problem-tab-label="problemTabLabel"
:problem-list="problemList" :problem-list="problemList"
:problem-empty-text="problemEmptyText" :problem-empty-text="problemEmptyText"
:icd-consistency-problem-list="icdConsistencyProblemList"
:icd-consistency-problem-empty-text="icdConsistencyProblemEmptyText"
:method-describe="methodDescribe" :method-describe="methodDescribe"
:can-export-json-mapping="canExportJsonMapping" :can-export-json-mapping="canExportJsonMapping"
:can-export-xml-mapping="canExportXmlMapping" :can-export-xml-mapping="canExportXmlMapping"
@@ -76,9 +78,11 @@
:sequence-dialog-visible="sequenceDialogVisible" :sequence-dialog-visible="sequenceDialogVisible"
:show-save-icd-check-result="showSaveIcdCheckResult" :show-save-icd-check-result="showSaveIcdCheckResult"
:show-icd-check-action="showConsistencyCheck" :show-icd-check-action="showConsistencyCheck"
:can-run-icd-consistency-check="canRunIcdConsistencyCheck"
:can-save-icd-check-result="canSaveIcdCheckResult" :can-save-icd-check-result="canSaveIcdCheckResult"
:is-saving-icd-check-result="isSavingIcdCheckResult" :is-saving-icd-check-result="isSavingIcdCheckResult"
:save-icd-check-result-text="saveIcdCheckResultText" :save-icd-check-result-text="saveIcdCheckResultText"
:icd-consistency-status="icdConsistencyStatus"
:show-description="false" :show-description="false"
@export-mapping="handleExportMapping" @export-mapping="handleExportMapping"
@generate-xml-mapping="handleGenerateXmlMapping" @generate-xml-mapping="handleGenerateXmlMapping"
@@ -130,7 +134,7 @@ const emit = defineEmits<{
}>() }>()
const dialogTitle = computed(() => (props.icdPathName ? `ICD校验${props.icdPathName}` : 'ICD校验')) 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<ResultData<boolean> | boolean> => { const saveIcdCheckResult = (params: MmsMapping.SaveIcdCheckResultRequest): Promise<ResultData<boolean> | boolean> => {
return saveIcdPathCheckResultApi(props.icdPathId, params) return saveIcdPathCheckResultApi(props.icdPathId, params)
@@ -163,6 +167,8 @@ const {
problemTabLabel, problemTabLabel,
problemList, problemList,
problemEmptyText, problemEmptyText,
icdConsistencyProblemList,
icdConsistencyProblemEmptyText,
methodDescribe, methodDescribe,
canExportJsonMapping, canExportJsonMapping,
canExportXmlMapping, canExportXmlMapping,
@@ -172,10 +178,12 @@ const {
showXmlMappingTab, showXmlMappingTab,
sequenceDialogVisible, sequenceDialogVisible,
showSaveIcdCheckResult, showSaveIcdCheckResult,
canRunIcdConsistencyCheck,
canSaveIcdCheckResult, canSaveIcdCheckResult,
isSavingIcdCheckResult, isSavingIcdCheckResult,
saveIcdCheckResultText, saveIcdCheckResultText,
hasIcdConsistencyCheckResult, hasIcdConsistencyCheckResult,
icdConsistencyStatus,
confirmDialogVisible, confirmDialogVisible,
isConfirmingSelection, isConfirmingSelection,
handleIcdFileChange, handleIcdFileChange,
@@ -192,8 +200,9 @@ const {
} = useMmsMappingFlow({ } = useMmsMappingFlow({
deviceTypeCheckId: toRef(props, 'icdPathId'), deviceTypeCheckId: toRef(props, 'icdPathId'),
deviceTypeCheckName: toRef(props, 'icdPathName'), deviceTypeCheckName: toRef(props, 'icdPathName'),
standardMappingJson: computed(() => props.activeIcdPathRecord?.jsonStr?.trim() || ''), // 关键业务节点:只有需要一致性校验的 ICD 类型才注入标准 JSON避免隐藏校验按钮后保存仍被校验前置条件卡住。
standardMappingName: computed(() => props.activeIcdPathRecord?.name?.trim() || ''), standardMappingJson: computed(() => (showConsistencyCheck.value ? props.activeIcdPathRecord?.jsonStr?.trim() || '' : '')),
standardMappingName: computed(() => (showConsistencyCheck.value ? props.activeIcdPathRecord?.name?.trim() || '' : '')),
saveIcdCheckResult, saveIcdCheckResult,
onIcdCheckSaved: () => { onIcdCheckSaved: () => {
emit('saved') emit('saved')
@@ -233,6 +242,10 @@ const activeStepIndex = computed(() => {
return 0 return 0
}) })
const canReachSaveStep = computed(() =>
showConsistencyCheck.value ? hasIcdConsistencyCheckResult.value : hasXmlMapping.value
)
const icdCheckFlowSteps = computed<IcdCheckFlowStep[]>(() => { const icdCheckFlowSteps = computed<IcdCheckFlowStep[]>(() => {
const steps = [ const steps = [
createFlowStep( createFlowStep(
@@ -284,8 +297,14 @@ const icdCheckFlowSteps = computed<IcdCheckFlowStep[]>(() => {
steps.push( steps.push(
createFlowStep( createFlowStep(
'保存', '保存',
isSavingIcdCheckResult.value ? '正在保存校验结果' : hasXmlMapping.value ? '可保存校验结果' : '等待XML映射', isSavingIcdCheckResult.value
isSavingIcdCheckResult.value ? 'process' : 'wait' ? '正在保存校验结果'
: canReachSaveStep.value
? '可保存校验结果'
: showConsistencyCheck.value && hasXmlMapping.value
? '等待执行JSON一致性校验'
: '等待XML映射',
isSavingIcdCheckResult.value ? 'process' : canReachSaveStep.value ? 'process' : 'wait'
) )
) )

View File

@@ -139,10 +139,11 @@ const icdTypeOptions = [
{ label: '手动录入的非标准', value: 2 }, { label: '手动录入的非标准', value: 2 },
{ label: '上游解析传递', value: 3 } { label: '上游解析传递', value: 3 }
] ]
const formRules: FormRules<IcdPathFormModel> = { const pathRequired = computed(() => !selectedIcdFile.value)
const formRules = computed<FormRules<IcdPathFormModel>>(() => ({
name: [{ required: true, message: '请输入 ICD 名称', trigger: 'blur' }], 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记录')) const dialogTitle = computed(() => (props.mode === 'create' ? '新增ICD记录' : '编辑ICD记录'))
function unwrapApiPayload<T>(response: ResultData<T> | T): T { function unwrapApiPayload<T>(response: ResultData<T> | T): T {
@@ -190,7 +191,7 @@ watch(
const buildSavePayload = (): MmsMapping.CreateIcdPathRequest => ({ const buildSavePayload = (): MmsMapping.CreateIcdPathRequest => ({
name: formModel.name.trim(), name: formModel.name.trim(),
path: formModel.path.trim(), path: formModel.path.trim() || selectedIcdFile.value?.name || '',
angle: formModel.angle, angle: formModel.angle,
usePhaseIndex: formModel.usePhaseIndex, usePhaseIndex: formModel.usePhaseIndex,
type: formModel.type type: formModel.type
@@ -202,6 +203,7 @@ const handleClose = () => {
const handleIcdFileChange = (uploadFile: UploadFile) => { const handleIcdFileChange = (uploadFile: UploadFile) => {
selectedIcdFile.value = uploadFile.raw || null selectedIcdFile.value = uploadFile.raw || null
formRef.value?.clearValidate('path')
} }
const handleIcdFileRemove = () => { const handleIcdFileRemove = () => {

View File

@@ -29,7 +29,17 @@ const checks = [
/<MappingConfirmDialog/.test(checkDialogSource) /<MappingConfirmDialog/.test(checkDialogSource)
], ],
['flow still saves ICD check result through shared API', () => /saveIcdCheckResultApi/.test(flowSource)], ['flow still saves ICD check result through shared API', () => /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) const failures = checks.filter(([, check]) => !check()).map(([name]) => name)

View File

@@ -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 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', () => /<el-select\s+v-model="formModel\.type"[\s\S]*<el-option[\s\S]*v-for="option in icdTypeOptions"/.test(formDialogSource)], ['ICD path form uses select for type', () => /<el-select\s+v-model="formModel\.type"[\s\S]*<el-option[\s\S]*v-for="option in icdTypeOptions"/.test(formDialogSource)],
['new ICD path defaults to upstream parsed type', () => /type:\s*3/.test(formDialogSource) && /formModel\.type\s*=\s*3/.test(formDialogSource)], ['new ICD path defaults to upstream parsed 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') &&
/<el-button[\s\S]*type="primary"[\s\S]*@click="emit\('icd-check'\)"/.test(resultPanelSource) &&
isPrimarySolidButton(resultPanelSource, 'saveIcdCheckResultText')
],
[ [
'ICD table activation column calls activation handler before operation', 'ICD table activation column calls activation handler before operation',
() => () =>
@@ -87,17 +93,86 @@ const checks = [
) && /renderActivationStatus[\s\S]*handleActivateIcdPath\(row\)/.test(pageSource) ) && /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 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', () => /<el-upload[\s\S]*:auto-upload="false"[\s\S]*handleIcdFileChange/.test(formDialogSource) && /createIcdPathWithFileApi/.test(formDialogSource) && /updateIcdPathWithFileApi/.test(formDialogSource)], ['ICD path form supports multipart file save', () => /<el-upload[\s\S]*:auto-upload="false"[\s\S]*handleIcdFileChange/.test(formDialogSource) && /createIcdPathWithFileApi/.test(formDialogSource) && /updateIcdPathWithFileApi/.test(formDialogSource)],
['ICD path form allows uploaded file name to fill missing path', () => /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 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)], ['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) && /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) /: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 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<MmsMapping\.IcdDocument\s*\|\s*null>\(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)], ['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)], ['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', () => /<MappingRequestPanel/.test(checkDialogSource) && /<MappingConfigPanel/.test(checkDialogSource) && /<MappingResultPanel/.test(checkDialogSource) && /<MappingConfirmDialog/.test(checkDialogSource)] ['check dialog reuses shared MMS mapping flow panels', () => /<MappingRequestPanel/.test(checkDialogSource) && /<MappingConfigPanel/.test(checkDialogSource) && /<MappingResultPanel/.test(checkDialogSource) && /<MappingConfirmDialog/.test(checkDialogSource)]

View File

@@ -0,0 +1,38 @@
/* eslint-env node */
import fs from 'node:fs'
import path from 'node:path'
import { fileURLToPath } from 'node:url'
const currentDir = path.dirname(fileURLToPath(import.meta.url))
const rootDir = path.resolve(currentDir, '../../../..')
const panelFile = path.resolve(rootDir, 'views/tools/mmsMapping/components/MappingResultPanel.vue')
const source = fs.readFileSync(panelFile, 'utf8')
const checks = [
['JSON problem button uses dedicated JSON mapping issue text', () => /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*<div class="panel-content/.test(
source
)
]
]
const failures = checks.filter(([, check]) => !check()).map(([name]) => name)
if (failures.length) {
console.error('mmsMapping result panel labels contract failed:')
for (const failure of failures) {
console.error(`- ${failure}`)
}
process.exit(1)
}
console.log('mmsMapping result panel labels contract passed')

View File

@@ -51,7 +51,7 @@
<script setup lang="tsx"> <script setup lang="tsx">
import { Connection, Delete, Edit, Plus } from '@element-plus/icons-vue' import { Connection, Delete, Edit, Plus } from '@element-plus/icons-vue'
import { ElMessage, ElMessageBox, type TagProps } from 'element-plus' 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 type { ResultData } from '@/api/interface'
import ProTable from '@/components/ProTable/index.vue' import ProTable from '@/components/ProTable/index.vue'
import type { ColumnProps, ProTableInstance } from '@/components/ProTable/interface' 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 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) => { const renderActivationStatus = (row: MmsMapping.IcdPathRecord) => {
if (row.type === 1) { if (row.type === 1) {
return ( return (
@@ -225,7 +237,7 @@ const columns = reactive<ColumnProps<MmsMapping.IcdPathRecord>[]>([
order: 3 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: 'referenceIcdId', label: '标准ICD引用', minWidth: 170, isShow: false },
{ prop: 'createTime', label: '创建时间', minWidth: 170 }, { prop: 'createTime', label: '创建时间', minWidth: 170 },
{ prop: 'updateTime', label: '更新时间', minWidth: 170, isShow: false }, { prop: 'updateTime', label: '更新时间', minWidth: 170, isShow: false },
@@ -255,7 +267,7 @@ const getTableList = async (params: IcdPathTableParams = {}) => {
const pageSize = params.pageSize || 10 const pageSize = params.pageSize || 10
const startIndex = (pageNum - 1) * pageSize 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 分页结构,保持和设备类型管理页一致。 // ICD 路径接口当前返回列表数据;这里统一包装为 ProTable 分页结构,保持和设备类型管理页一致。
return { return {
@@ -268,9 +280,18 @@ const getTableList = async (params: IcdPathTableParams = {}) => {
} }
} }
const refreshActiveIcdPathRecord = async () => {
// 关键业务节点:标准 ICD 不能依赖当前表格筛选结果,否则校验弹窗可能拿不到已激活记录的 JSON。
const response = await listIcdPathsApi({ type: 1 })
const records = unwrapApiPayload<MmsMapping.IcdPathRecord[]>(response) || []
activeIcdPathRecord.value = records.find(record => record.type === 1 && record.state !== 0) || null
}
const refreshIcdPaths = () => { const refreshIcdPaths = () => {
proTable.value?.clearSelection() proTable.value?.clearSelection()
proTable.value?.getTableList() proTable.value?.getTableList()
refreshActiveIcdPathRecord()
} }
const handleTableRequestError = (error: unknown) => { const handleTableRequestError = (error: unknown) => {
@@ -333,6 +354,11 @@ const handleActivateIcdPath = async (row: MmsMapping.IcdPathRecord) => {
return return
} }
if (!row.jsonStr?.trim()) {
ElMessage.warning('当前 ICD 记录缺少 JSON 映射结果,不能激活,请先完成 ICD 校验并保存')
return
}
try { try {
await ElMessageBox.confirm('激活后该 ICD 将作为标准 ICD 参与一致性校验,是否继续?', '激活ICD记录', { await ElMessageBox.confirm('激活后该 ICD 将作为标准 ICD 参与一致性校验,是否继续?', '激活ICD记录', {
confirmButtonText: '确定', confirmButtonText: '确定',
@@ -381,8 +407,11 @@ const handleIcdCheck = (row: MmsMapping.IcdPathRecord) => {
currentIcdCheckRecord.id = row.id currentIcdCheckRecord.id = row.id
currentIcdCheckRecord.name = row.name || '' currentIcdCheckRecord.name = row.name || ''
currentIcdCheckRecord.type = row.type ?? 3 currentIcdCheckRecord.type = row.type ?? 3
refreshActiveIcdPathRecord()
icdCheckDialogVisible.value = true icdCheckDialogVisible.value = true
} }
nextTick(refreshActiveIcdPathRecord)
</script> </script>
<style scoped lang="scss"> <style scoped lang="scss">

View File

@@ -17,6 +17,7 @@ import { createBaseRequestPayload } from './requestPayload'
type ResultTab = 'json' | 'xml' | 'problem' type ResultTab = 'json' | 'xml' | 'problem'
type TagType = 'success' | 'warning' | 'info' | 'primary' | 'danger' type TagType = 'success' | 'warning' | 'info' | 'primary' | 'danger'
type ExportMappingType = 'json' | 'xml' type ExportMappingType = 'json' | 'xml'
type IcdConsistencyStatus = 'success' | 'failed' | ''
const DEFAULT_REQUEST_FORM: MmsMapping.BaseRequestForm = { const DEFAULT_REQUEST_FORM: MmsMapping.BaseRequestForm = {
version: '1.0', version: '1.0',
@@ -47,6 +48,7 @@ export const useMmsMappingFlow = (options: UseMmsMappingFlowOptions = {}) => {
const isGeneratingXml = ref(false) const isGeneratingXml = ref(false)
const isSavingIcdCheckResult = ref(false) const isSavingIcdCheckResult = ref(false)
const lastIcdConsistencyCheckResult = ref<MmsMapping.IcdJsonConsistencyCheckResponse | null>(null) const lastIcdConsistencyCheckResult = ref<MmsMapping.IcdJsonConsistencyCheckResponse | null>(null)
const parsedIcdDocument = ref<MmsMapping.IcdDocument | null>(null)
const hasSequenceConfigured = ref(false) const hasSequenceConfigured = ref(false)
const sequenceDialogVisible = ref(false) const sequenceDialogVisible = ref(false)
const icdFileAccept = '.icd,.cid,.scd,.xml' const icdFileAccept = '.icd,.cid,.scd,.xml'
@@ -253,7 +255,7 @@ export const useMmsMappingFlow = (options: UseMmsMappingFlowOptions = {}) => {
'' ''
) )
const canConfigureSequence = computed(() => Boolean(mappingJsonPreview.value && !isSubmitting.value)) const canConfigureSequence = computed(() => Boolean(mappingJsonPreview.value && !isSubmitting.value))
const canExportJsonMapping = computed(() => Boolean(xmlContentForExport.value && !isSubmitting.value)) const canExportJsonMapping = computed(() => Boolean(mappingJsonPreview.value && !isSubmitting.value))
const canExportXmlMapping = computed(() => Boolean(xmlContentForExport.value && !isSubmitting.value)) const canExportXmlMapping = computed(() => Boolean(xmlContentForExport.value && !isSubmitting.value))
const canGenerateXmlMapping = computed(() => Boolean(mappingJsonPreview.value && hasSequenceConfigured.value && !isSubmitting.value)) const canGenerateXmlMapping = computed(() => Boolean(mappingJsonPreview.value && hasSequenceConfigured.value && !isSubmitting.value))
const showXmlMappingTab = computed(() => const showXmlMappingTab = computed(() =>
@@ -263,10 +265,20 @@ export const useMmsMappingFlow = (options: UseMmsMappingFlowOptions = {}) => {
const standardMappingJson = computed(() => toValue(options.standardMappingJson)?.trim() || '') const standardMappingJson = computed(() => toValue(options.standardMappingJson)?.trim() || '')
const standardMappingName = computed(() => toValue(options.standardMappingName)?.trim() || '已激活ICD记录') const standardMappingName = computed(() => toValue(options.standardMappingName)?.trim() || '已激活ICD记录')
const showSaveIcdCheckResult = computed(() => Boolean(deviceTypeCheckId.value)) const showSaveIcdCheckResult = computed(() => Boolean(deviceTypeCheckId.value))
const canSaveIcdCheckResult = computed(() => const canRunIcdConsistencyCheck = computed(() =>
Boolean(deviceTypeCheckId.value && xmlContentForExport.value && !isSubmitting.value) Boolean(mappingJsonPreview.value && standardMappingJson.value && !isSubmitting.value)
) )
const canSaveIcdCheckResult = computed(() => {
if (!deviceTypeCheckId.value || !mappingJsonPreview.value || !xmlContentForExport.value || isSubmitting.value) return false
if (standardMappingJson.value && !lastIcdConsistencyCheckResult.value) return false
return true
})
const hasIcdConsistencyCheckResult = computed(() => Boolean(lastIcdConsistencyCheckResult.value)) const hasIcdConsistencyCheckResult = computed(() => Boolean(lastIcdConsistencyCheckResult.value))
const icdConsistencyStatus = computed<IcdConsistencyStatus>(() => {
if (!lastIcdConsistencyCheckResult.value) return ''
return lastIcdConsistencyCheckResult.value.result === 1 ? 'success' : 'failed'
})
const saveIcdCheckResultText = computed(() => '保存') const saveIcdCheckResultText = computed(() => '保存')
const xmlMappingPreview = computed(() => { const xmlMappingPreview = computed(() => {
@@ -307,11 +319,13 @@ export const useMmsMappingFlow = (options: UseMmsMappingFlowOptions = {}) => {
return '请先生成 JSON 映射' return '请先生成 JSON 映射'
}) })
const problemList = computed(() => [ const jsonMappingProblemList = computed(() => [
...(responsePayload.value?.problems?.filter(Boolean) || []), ...(responsePayload.value?.problems?.filter(Boolean) || []),
...(xmlResponsePayload.value?.problems?.filter(Boolean) || []), ...(xmlResponsePayload.value?.problems?.filter(Boolean) || [])
...(lastIcdConsistencyCheckResult.value?.issues?.filter(Boolean) || [])
]) ])
const problemList = jsonMappingProblemList
const icdConsistencyProblemList = computed(() => lastIcdConsistencyCheckResult.value?.issues?.filter(Boolean) || [])
const icdConsistencyProblemEmptyText = '当前 ICD 校验未返回 issues'
const methodDescribe = computed(() => xmlResponsePayload.value?.methodDescribe?.trim() || '') const methodDescribe = computed(() => xmlResponsePayload.value?.methodDescribe?.trim() || '')
@@ -383,6 +397,7 @@ export const useMmsMappingFlow = (options: UseMmsMappingFlowOptions = {}) => {
responsePayload.value = null responsePayload.value = null
xmlResponsePayload.value = null xmlResponsePayload.value = null
lastIcdConsistencyCheckResult.value = null lastIcdConsistencyCheckResult.value = null
parsedIcdDocument.value = null
hasSequenceConfigured.value = false hasSequenceConfigured.value = false
sequenceDialogVisible.value = false sequenceDialogVisible.value = false
parsedCandidates.value = [] parsedCandidates.value = []
@@ -418,6 +433,7 @@ export const useMmsMappingFlow = (options: UseMmsMappingFlowOptions = {}) => {
isParsing.value = true isParsing.value = true
responsePayload.value = null responsePayload.value = null
xmlResponsePayload.value = null xmlResponsePayload.value = null
parsedIcdDocument.value = null
hasSequenceConfigured.value = false hasSequenceConfigured.value = false
sequenceDialogVisible.value = false sequenceDialogVisible.value = false
confirmDialogVisible.value = false confirmDialogVisible.value = false
@@ -443,6 +459,8 @@ export const useMmsMappingFlow = (options: UseMmsMappingFlowOptions = {}) => {
} }
parsedCandidates.value = candidateGroups parsedCandidates.value = candidateGroups
// 关键业务节点:保存 ICD 校验结果时,按后端调试文档回写解析后的 icdDocument 对象。
parsedIcdDocument.value = payload.icdDocument || null
// 关键业务节点:拿到 ICD 候选结果后必须先走 buildIndexConfirmData生成人工确认弹窗所需模型。 // 关键业务节点:拿到 ICD 候选结果后必须先走 buildIndexConfirmData生成人工确认弹窗所需模型。
const confirmResponse = await buildIndexConfirmDataApi(candidateGroups) const confirmResponse = await buildIndexConfirmDataApi(candidateGroups)
@@ -649,6 +667,16 @@ export const useMmsMappingFlow = (options: UseMmsMappingFlowOptions = {}) => {
} }
} }
const buildIcdCheckMsg = (
checkResult: MmsMapping.IcdJsonConsistencyCheckResponse | null,
summaryMessage: string
): MmsMapping.IcdCheckMsg => ({
summary: summaryMessage,
details: checkResult?.issues?.filter(Boolean) || [],
issuesJson: checkResult?.issuesJson || undefined,
correctedJson: checkResult?.correctedJson || undefined
})
const handleSaveIcdCheckResult = async () => { const handleSaveIcdCheckResult = async () => {
if (!deviceTypeCheckId.value) { if (!deviceTypeCheckId.value) {
ElMessage.warning('当前缺少设备类型 ID不能入库校验结果') ElMessage.warning('当前缺少设备类型 ID不能入库校验结果')
@@ -657,7 +685,12 @@ export const useMmsMappingFlow = (options: UseMmsMappingFlowOptions = {}) => {
const mappingJson = responsePayload.value?.mappingJson?.trim() || mappingJsonPreview.value const mappingJson = responsePayload.value?.mappingJson?.trim() || mappingJsonPreview.value
if (!mappingJson || !xmlContentForExport.value) { if (!mappingJson) {
ElMessage.warning('请先生成 JSON 映射后再保存')
return
}
if (!xmlContentForExport.value) {
ElMessage.warning('请先完成 XML 映射后再保存') ElMessage.warning('请先完成 XML 映射后再保存')
return return
} }
@@ -672,14 +705,15 @@ export const useMmsMappingFlow = (options: UseMmsMappingFlowOptions = {}) => {
try { try {
const checkResult = lastIcdConsistencyCheckResult.value const checkResult = lastIcdConsistencyCheckResult.value
const result = checkResult?.result ?? 1 const result = checkResult?.result ?? 1
const msg = const summaryMessage =
checkResult?.message || checkResult?.message ||
(xmlContentForExport.value ? 'ICD一致性校验通过JSON/XML已生成' : 'ICD一致性校验通过JSON已生成') (xmlContentForExport.value ? 'ICD一致性校验通过JSON/XML已生成' : 'ICD一致性校验通过JSON已生成')
const savePayload = { const savePayload = {
icdDocument: parsedIcdDocument.value || responsePayload.value?.icdDocument,
mappingJson, mappingJson,
xml: xmlContentForExport.value || undefined, xml: xmlContentForExport.value,
result, result,
msg msg: buildIcdCheckMsg(checkResult, summaryMessage)
} }
// 关键业务节点ICD 校验流程可由设备类型或 ICD 存储记录承载,保存接口由宿主页注入以避免串写业务表。 // 关键业务节点ICD 校验流程可由设备类型或 ICD 存储记录承载,保存接口由宿主页注入以避免串写业务表。
const response = options.saveIcdCheckResult const response = options.saveIcdCheckResult
@@ -758,6 +792,8 @@ export const useMmsMappingFlow = (options: UseMmsMappingFlowOptions = {}) => {
problemTabLabel, problemTabLabel,
problemList, problemList,
problemEmptyText, problemEmptyText,
icdConsistencyProblemList,
icdConsistencyProblemEmptyText,
methodDescribe, methodDescribe,
canExportJsonMapping, canExportJsonMapping,
canExportXmlMapping, canExportXmlMapping,
@@ -767,10 +803,12 @@ export const useMmsMappingFlow = (options: UseMmsMappingFlowOptions = {}) => {
showXmlMappingTab, showXmlMappingTab,
sequenceDialogVisible, sequenceDialogVisible,
showSaveIcdCheckResult, showSaveIcdCheckResult,
canRunIcdConsistencyCheck,
canSaveIcdCheckResult, canSaveIcdCheckResult,
isSavingIcdCheckResult, isSavingIcdCheckResult,
saveIcdCheckResultText, saveIcdCheckResultText,
hasIcdConsistencyCheckResult, hasIcdConsistencyCheckResult,
icdConsistencyStatus,
confirmDialogVisible, confirmDialogVisible,
isConfirmingSelection, isConfirmingSelection,
handleIcdFileChange, handleIcdFileChange,

View File

@@ -1,11 +1,11 @@
<template> <template>
<section class="mapping-panel"> <section class="mapping-panel">
<div class="panel-header"> <div class="panel-header">
<div> <div>
<div class="panel-title-tabs"> <div class="panel-title-tabs">
<span class="panel-title-tab">映射结果</span> <span class="panel-title-tab">映射结果</span>
</div> </div>
<p v-if="showDescription" class="panel-description">展示和导出JSON与XML的映射结果以及JSON的映射序列配置</p> <p v-if="showDescription" class="panel-description">Mapping result preview and sequence configuration.</p>
</div> </div>
<div class="panel-actions"> <div class="panel-actions">
<el-button <el-button
@@ -29,7 +29,7 @@
v-if="showIcdCheckAction" v-if="showIcdCheckAction"
type="primary" type="primary"
:icon="CircleCheck" :icon="CircleCheck"
:disabled="!canSaveIcdCheckResult" :disabled="!canRunIcdConsistencyCheck"
@click="emit('icd-check')" @click="emit('icd-check')"
> >
ICD校验 ICD校验
@@ -52,6 +52,21 @@
<div class="panel-content panel-content--fixed"> <div class="panel-content panel-content--fixed">
<div class="panel-section result-card grow-card preview-tab-section"> <div class="panel-section result-card grow-card preview-tab-section">
<div v-if="icdConsistencyStatus" class="icd-consistency-status">
<el-tooltip
:content="icdConsistencyStatus === 'success' ? 'ICD校验通过' : 'ICD校验不通过点击查看JSON映射问题列表'"
placement="top"
>
<button
type="button"
:class="['icd-consistency-indicator', `is-${icdConsistencyStatus}`]"
:disabled="icdConsistencyStatus !== 'failed'"
@click="openIcdConsistencyProblems"
>
<el-icon><WarningFilled /></el-icon>
</button>
</el-tooltip>
</div>
<div class="mapping-preview-tabs"> <div class="mapping-preview-tabs">
<button <button
type="button" type="button"
@@ -87,6 +102,16 @@
> >
{{ problemButtonText }} {{ problemButtonText }}
</el-button> </el-button>
<el-button
v-if="icdConsistencyStatus"
type="primary"
plain
size="small"
:icon="WarningFilled"
@click="icdConsistencyProblemDialogVisible = true"
>
{{ icdConsistencyProblemButtonText }}
</el-button>
</template> </template>
<template #trailing-actions> <template #trailing-actions>
<el-button <el-button
@@ -118,7 +143,7 @@
:disabled="!methodDescribe" :disabled="!methodDescribe"
@click="matchResultDialogVisible = true" @click="matchResultDialogVisible = true"
> >
匹配结果展示 XML映射匹配结果列表
</el-button> </el-button>
<el-button <el-button
type="primary" type="primary"
@@ -143,7 +168,7 @@
<el-dialog <el-dialog
v-model="problemDialogVisible" v-model="problemDialogVisible"
title="问题列表" title="JSON映射问题列表"
width="720px" width="720px"
destroy-on-close destroy-on-close
top="8vh" top="8vh"
@@ -158,7 +183,34 @@
<el-empty v-else :description="problemEmptyText" /> <el-empty v-else :description="problemEmptyText" />
</el-dialog> </el-dialog>
<el-dialog v-model="matchResultDialogVisible" title="匹配结果展示" width="640px" destroy-on-close top="12vh"> <el-dialog
v-model="icdConsistencyProblemDialogVisible"
:title="ICD_CONSISTENCY_PROBLEM_LABEL"
width="720px"
destroy-on-close
top="8vh"
class="mapping-problem-dialog"
>
<div v-if="icdConsistencyProblemList.length" class="problem-dialog-list">
<div
v-for="(problem, index) in icdConsistencyProblemList"
:key="`${index}-${problem}`"
class="problem-item"
>
<span class="problem-index">{{ index + 1 }}</span>
<span class="problem-text">{{ problem }}</span>
</div>
</div>
<el-empty v-else :description="icdConsistencyProblemEmptyText" />
</el-dialog>
<el-dialog
v-model="matchResultDialogVisible"
title="XML映射匹配结果列表"
width="640px"
destroy-on-close
top="12vh"
>
<div class="match-result-detail"> <div class="match-result-detail">
{{ methodDescribe || '当前接口返回未包含 methodDescribe' }} {{ methodDescribe || '当前接口返回未包含 methodDescribe' }}
</div> </div>
@@ -178,7 +230,7 @@
v-model="sequenceSearchKeyword" v-model="sequenceSearchKeyword"
:prefix-icon="Search" :prefix-icon="Search"
clearable clearable
placeholder="按顶层、类型、上层描述、name 或路径检索" placeholder="Search by top group, type, parent, name, or path"
/> />
<span class="dialog-search-count"> <span class="dialog-search-count">
{{ filteredSequenceRows.length }} / {{ sequenceConfigRows.length }} {{ filteredSequenceRows.length }} / {{ sequenceConfigRows.length }}
@@ -192,7 +244,7 @@
<h3 class="sequence-top-title">{{ group.topDesc || group.topKey }}</h3> <h3 class="sequence-top-title">{{ group.topDesc || group.topKey }}</h3>
<p class="sequence-top-key">{{ group.topKey }}</p> <p class="sequence-top-key">{{ group.topKey }}</p>
</div> </div>
<el-tag type="info" effect="light">{{ group.rowCount }} </el-tag> <el-tag type="info" effect="light">{{ group.rowCount }} items</el-tag>
</div> </div>
<div class="sequence-type-list"> <div class="sequence-type-list">
@@ -204,7 +256,7 @@
<div class="sequence-type-header"> <div class="sequence-type-header">
<div class="sequence-type-title">{{ typeGroup.typeName }}</div> <div class="sequence-type-title">{{ typeGroup.typeName }}</div>
<el-tag type="primary" effect="plain" size="small"> <el-tag type="primary" effect="plain" size="small">
{{ typeGroup.rows.length }} {{ typeGroup.rows.length }} items
</el-tag> </el-tag>
</div> </div>
@@ -232,9 +284,9 @@
</div> </div>
</section> </section>
</div> </div>
<el-empty v-else description="当前检索条件下没有匹配的序列。" /> <el-empty v-else description="No sequence rows match the current search." />
</template> </template>
<el-empty v-else description="当前 JSON 映射中未分析到包含 start 和 end 的序列。" /> <el-empty v-else description="No configurable sequence rows were found in the current JSON mapping." />
<template #footer> <template #footer>
<el-button @click="closeSequenceDialog">取消</el-button> <el-button @click="closeSequenceDialog">取消</el-button>
<el-button type="primary" :disabled="!sequenceConfigRows.length" @click="confirmSequenceConfig"> <el-button type="primary" :disabled="!sequenceConfigRows.length" @click="confirmSequenceConfig">
@@ -246,7 +298,17 @@
</template> </template>
<script setup lang="ts"> <script setup lang="ts">
import { CircleCheck, Connection, Document, Download, Finished, Search, Setting, Warning } from '@element-plus/icons-vue' import {
CircleCheck,
Connection,
Document,
Download,
Finished,
Search,
Setting,
Warning,
WarningFilled
} from '@element-plus/icons-vue'
import { computed, ref, watch } from 'vue' import { computed, ref, watch } from 'vue'
import JsonMappingTree from './JsonMappingTree.vue' import JsonMappingTree from './JsonMappingTree.vue'
@@ -257,6 +319,7 @@ defineOptions({
type TagType = 'success' | 'warning' | 'info' | 'primary' | 'danger' type TagType = 'success' | 'warning' | 'info' | 'primary' | 'danger'
type ResultTab = 'json' | 'xml' | 'problem' type ResultTab = 'json' | 'xml' | 'problem'
type ExportMappingType = 'json' | 'xml' type ExportMappingType = 'json' | 'xml'
type IcdConsistencyStatus = 'success' | 'failed' | ''
type JsonObject = Record<string, unknown> type JsonObject = Record<string, unknown>
type JsonPath = Array<string | number> type JsonPath = Array<string | number>
@@ -302,6 +365,8 @@ const props = withDefaults(defineProps<{
problemTabLabel: string problemTabLabel: string
problemList: string[] problemList: string[]
problemEmptyText: string problemEmptyText: string
icdConsistencyProblemList: string[]
icdConsistencyProblemEmptyText: string
methodDescribe: string methodDescribe: string
canExportJsonMapping: boolean canExportJsonMapping: boolean
canExportXmlMapping: boolean canExportXmlMapping: boolean
@@ -312,12 +377,15 @@ const props = withDefaults(defineProps<{
sequenceDialogVisible: boolean sequenceDialogVisible: boolean
showIcdCheckAction?: boolean showIcdCheckAction?: boolean
showSaveIcdCheckResult: boolean showSaveIcdCheckResult: boolean
canRunIcdConsistencyCheck: boolean
canSaveIcdCheckResult: boolean canSaveIcdCheckResult: boolean
isSavingIcdCheckResult: boolean isSavingIcdCheckResult: boolean
saveIcdCheckResultText: string saveIcdCheckResultText: string
icdConsistencyStatus?: IcdConsistencyStatus
}>(), { }>(), {
showDescription: true, showDescription: true,
showIcdCheckAction: true showIcdCheckAction: true,
icdConsistencyStatus: ''
}) })
const emit = defineEmits<{ const emit = defineEmits<{
@@ -336,10 +404,18 @@ const activeTabProxy = computed({
set: value => emit('update:activeResultTab', value) set: value => emit('update:activeResultTab', value)
}) })
const JSON_MAPPING_PROBLEM_LABEL = 'JSON\u6620\u5c04\u95ee\u9898\u5217\u8868'
const ICD_CONSISTENCY_PROBLEM_LABEL = 'ICD\u6821\u9a8c\u95ee\u9898\u5217\u8868'
const problemButtonText = computed(() => const problemButtonText = computed(() =>
props.problemList.length ? `问题列表(${props.problemList.length}` : '问题列表' props.problemList.length ? `${JSON_MAPPING_PROBLEM_LABEL} (${props.problemList.length})` : JSON_MAPPING_PROBLEM_LABEL
)
const icdConsistencyProblemButtonText = computed(() =>
props.icdConsistencyProblemList.length
? `${ICD_CONSISTENCY_PROBLEM_LABEL} (${props.icdConsistencyProblemList.length})`
: ICD_CONSISTENCY_PROBLEM_LABEL
) )
const problemDialogVisible = ref(false) const problemDialogVisible = ref(false)
const icdConsistencyProblemDialogVisible = ref(false)
const matchResultDialogVisible = ref(false) const matchResultDialogVisible = ref(false)
const sequenceConfigRows = ref<SequenceConfigRow[]>([]) const sequenceConfigRows = ref<SequenceConfigRow[]>([])
const sequenceSearchKeyword = ref('') const sequenceSearchKeyword = ref('')
@@ -399,6 +475,11 @@ const sequenceConfigGroups = computed<SequenceConfigGroup[]>(() => {
}) })
}) })
const openIcdConsistencyProblems = () => {
if (props.icdConsistencyStatus !== 'failed') return
icdConsistencyProblemDialogVisible.value = true
}
const isRecord = (value: unknown): value is JsonObject => const isRecord = (value: unknown): value is JsonObject =>
Boolean(value && typeof value === 'object' && !Array.isArray(value)) Boolean(value && typeof value === 'object' && !Array.isArray(value))
@@ -457,9 +538,9 @@ const collectSequenceRows = (
pathText: getPathText(path), pathText: getPathText(path),
topKey: getTopKey(path), topKey: getTopKey(path),
topDesc: resolveTopDesc(rootSource, path), topDesc: resolveTopDesc(rootSource, path),
parentDesc: parentDesc || '未配置上层描述', parentDesc: parentDesc || 'Unconfigured parent',
desc: toDisplayText(source.desc, '未命名序列'), desc: toDisplayText(source.desc, 'Unnamed sequence'),
name: toDisplayText(source.name, '未配置 name'), name: toDisplayText(source.name, 'Unconfigured name'),
baseflag: toDisplayText(source.baseflag, ''), baseflag: toDisplayText(source.baseflag, ''),
start: source.start === undefined || source.start === null ? '' : String(source.start), start: source.start === undefined || source.start === null ? '' : String(source.start),
end: source.end === undefined || source.end === null ? '' : String(source.end), end: source.end === undefined || source.end === null ? '' : String(source.end),
@@ -483,7 +564,7 @@ const normalizeSequenceValue = (value: string, valueType: string) => {
const numericValue = Number(value) const numericValue = Number(value)
if (!Number.isFinite(numericValue)) { if (!Number.isFinite(numericValue)) {
throw new Error('start end 需要填写有效数字') throw new Error('start and end must be valid numbers')
} }
return numericValue return numericValue
@@ -497,7 +578,7 @@ const prepareSequenceDialog = () => {
sequenceSearchKeyword.value = '' sequenceSearchKeyword.value = ''
return true return true
} catch { } catch {
ElMessage.warning('当前 JSON 映射内容无法解析,不能配置序列') ElMessage.warning('Current JSON mapping cannot be parsed for sequence configuration')
return false return false
} }
} }
@@ -576,6 +657,43 @@ const confirmSequenceConfig = () => {
margin-left: 0; margin-left: 0;
} }
.icd-consistency-status {
position: absolute;
top: 10px;
right: 10px;
z-index: 2;
display: flex;
justify-content: flex-end;
height: 22px;
}
.icd-consistency-indicator {
display: inline-flex;
align-items: center;
justify-content: center;
width: 22px;
height: 22px;
padding: 0;
border: 0;
border-radius: 50%;
background: transparent;
font-size: 20px;
cursor: default;
}
.icd-consistency-indicator.is-success {
color: #67c23a;
}
.icd-consistency-indicator.is-failed {
color: #e6a23c;
cursor: pointer;
}
.icd-consistency-indicator:disabled {
cursor: default;
}
.panel-title-tabs { .panel-title-tabs {
display: inline-flex; display: inline-flex;
align-items: center; align-items: center;
@@ -664,6 +782,7 @@ const confirmSequenceConfig = () => {
} }
.preview-tab-section { .preview-tab-section {
position: relative;
overflow: hidden; overflow: hidden;
} }