diff --git a/frontend/src/api/tools/parsePqdif/index.ts b/frontend/src/api/tools/parsePqdif/index.ts index 51cf55a..41fa154 100644 --- a/frontend/src/api/tools/parsePqdif/index.ts +++ b/frontend/src/api/tools/parsePqdif/index.ts @@ -1,6 +1,21 @@ import http from '@/api' import type { ParsePqdif } from './interface' +const PQDIF_PATH_BASE_URL = '/api/parse-pqdif/pqdif-paths' + +const buildPqdifPathFormData = (request: ParsePqdif.PqdifPathSaveParams, pqdifFile?: File | null) => { + const formData = new FormData() + + if (pqdifFile) { + formData.append('pqdifFile', pqdifFile) + } + + // 关键业务节点:记录上传接口固定读取 request 这个 JSON part,字段名不能调整。 + formData.append('request', new Blob([JSON.stringify(request)], { type: 'application/json' })) + + return formData +} + export const parsePqdifApi = (pqdifFile: File) => { const formData = new FormData() @@ -11,3 +26,47 @@ export const parsePqdifApi = (pqdifFile: File) => { headers: { 'Content-Type': 'multipart/form-data' } }) } + +export const listPqdifPathsApi = (params: ParsePqdif.PqdifPathListParams = {}) => { + const request = { ...params } + delete request.pageNum + delete request.pageSize + + return http.post(`${PQDIF_PATH_BASE_URL}/list`, request) +} + +export const addPqdifPathApi = (params: ParsePqdif.PqdifPathSaveParams, pqdifFile?: File | null) => { + if (pqdifFile) { + return http.post(`${PQDIF_PATH_BASE_URL}/add`, buildPqdifPathFormData(params, pqdifFile), { + headers: { 'Content-Type': 'multipart/form-data' } + }) + } + + return http.post(`${PQDIF_PATH_BASE_URL}/add`, params) +} + +export const updatePqdifPathApi = (params: ParsePqdif.PqdifPathSaveParams, pqdifFile?: File | null) => { + if (pqdifFile) { + return http.post(`${PQDIF_PATH_BASE_URL}/update`, buildPqdifPathFormData(params, pqdifFile), { + headers: { 'Content-Type': 'multipart/form-data' } + }) + } + + return http.post(`${PQDIF_PATH_BASE_URL}/update`, params) +} + +export const deletePqdifPathsApi = (ids: string[]) => { + return http.post(`${PQDIF_PATH_BASE_URL}/delete`, ids) +} + +export const getPqdifParseMsgApi = (id: string) => { + return http.post(`${PQDIF_PATH_BASE_URL}/${id}/parse-msg`) +} + +export const getPqdifParseDetailApi = (id: string) => { + return http.post(`${PQDIF_PATH_BASE_URL}/${id}/parse-detail`) +} + +export const savePqdifParseResultApi = (id: string, params: ParsePqdif.SaveParseResultParams) => { + return http.post(`${PQDIF_PATH_BASE_URL}/${id}/parse-result`, params) +} diff --git a/frontend/src/api/tools/parsePqdif/interface/index.ts b/frontend/src/api/tools/parsePqdif/interface/index.ts index 24c21aa..e8d086b 100644 --- a/frontend/src/api/tools/parsePqdif/interface/index.ts +++ b/frontend/src/api/tools/parsePqdif/interface/index.ts @@ -1,6 +1,7 @@ export namespace ParsePqdif { export type ParseStatus = 'SUCCESS' | 'FAILED' | (string & {}) export type SeriesDataStatus = 'DATA_SUCCESS' | 'DATA_FAILED' | (string & {}) + export type ParseResultValue = 0 | 1 export interface RecordItem { recordIndex?: number @@ -53,4 +54,48 @@ export namespace ParsePqdif { records?: RecordItem[] observations?: ObservationItem[] } + + export interface PqdifPathListParams { + keyword?: string + result?: ParseResultValue | '' + pageNum?: number + pageSize?: number + } + + export interface PqdifPathRecord { + id?: string + name?: string + filePath?: string + recordCount?: number + observationCount?: number + sampleValueCount?: number + state?: number + result?: ParseResultValue + msg?: Record | null + createBy?: string + createTime?: string + updateBy?: string + updateTime?: string + } + + export interface PqdifPathSaveParams { + id?: string + name: string + } + + export interface PqdifParseDetail { + id?: string + name?: string + filePath?: string + } + + export type PqdifParseMsg = Record | null + + export interface SaveParseResultParams { + recordCount?: number + observationCount?: number + sampleValueCount?: number + result: ParseResultValue + msg?: Record | null + } } diff --git a/frontend/src/views/tools/mmsMapping/components/IcdPathCheckDialog.vue b/frontend/src/views/tools/mmsMapping/components/IcdPathCheckDialog.vue index e2c62db..6017ba3 100644 --- a/frontend/src/views/tools/mmsMapping/components/IcdPathCheckDialog.vue +++ b/frontend/src/views/tools/mmsMapping/components/IcdPathCheckDialog.vue @@ -86,7 +86,7 @@ :should-open-icd-consistency-problems="shouldOpenIcdConsistencyProblems" :show-description="false" @export-mapping="handleExportMappingEvent" - @generate-xml-mapping="handleGenerateXmlMapping" + @generate-xml-mapping="handleGenerateXmlMappingEvent" @icd-check="handleIcdConsistencyCheck" @confirm-icd-consistency-problems="handleConfirmIcdConsistencyProblemsEvent" @remove-icd-consistency-problem="handleRemoveIcdConsistencyProblemEvent" @@ -224,6 +224,13 @@ const handleExportMappingEvent = (...args: unknown[]) => { handleExportMapping(type) } +const handleGenerateXmlMappingEvent = (...args: unknown[]) => { + const [dataType] = args + + if (typeof dataType !== 'number') return + handleGenerateXmlMapping(dataType) +} + const handleConfirmIcdConsistencyProblemsEvent = () => { handleConfirmIcdConsistencyProblems() } diff --git a/frontend/src/views/tools/mmsMapping/contracts/check-xml-data-type-contract.mjs b/frontend/src/views/tools/mmsMapping/contracts/check-xml-data-type-contract.mjs new file mode 100644 index 0000000..f1a5c0a --- /dev/null +++ b/frontend/src/views/tools/mmsMapping/contracts/check-xml-data-type-contract.mjs @@ -0,0 +1,59 @@ +/* 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 files = { + resultPanel: path.resolve(rootDir, 'views/tools/mmsMapping/components/MappingResultPanel.vue'), + checkDialog: path.resolve(rootDir, 'views/tools/mmsMapping/components/IcdPathCheckDialog.vue'), + flow: path.resolve(rootDir, 'views/tools/mmsMapping/utils/useMmsMappingFlow.ts') +} + +const read = file => fs.readFileSync(file, 'utf8') +const resultPanelSource = read(files.resultPanel) +const checkDialogSource = read(files.checkDialog) +const flowSource = read(files.flow) + +const checks = [ + ['XML mapping opens data type dialog before generation', () => /openXmlDataTypeDialog/.test(resultPanelSource)], + ['XML data type defaults to provincial platform version', () => /xmlDataType\s*=\s*ref\(2\)/.test(resultPanelSource)], + [ + 'XML data type dialog exposes MMS and provincial platform options', + () => + /label="1"[\s\S]*MMS版本/.test(resultPanelSource) && + /label="2"[\s\S]*省级平台版本/.test(resultPanelSource) + ], + [ + 'XML generation event carries selected data type', + () => + /event:\s*'generate-xml-mapping',\s*value:\s*number/.test(resultPanelSource) && + /emit\('generate-xml-mapping',\s*xmlDataType\.value\)/.test(resultPanelSource) + ], + [ + 'ICD path check dialog forwards XML data type to shared flow', + () => + /@generate-xml-mapping="handleGenerateXmlMappingEvent"/.test(checkDialogSource) && + /const\s+handleGenerateXmlMappingEvent\s*=\s*\(\.\.\.args:\s*unknown\[\]\)/.test(checkDialogSource) && + /handleGenerateXmlMapping\(dataType\)/.test(checkDialogSource) + ], + [ + 'XML flow defaults and sends configType as backend data type', + () => + /const\s+handleGenerateXmlMapping\s*=\s*async\s*\(dataType\s*=\s*2\)/.test(flowSource) && + /mappingJson,\s*configType:\s*dataType/.test(flowSource) + ] +] + +const failures = checks.filter(([, check]) => !check()).map(([name]) => name) + +if (failures.length) { + console.error('mmsMapping XML data type contract failed:') + for (const failure of failures) { + console.error(`- ${failure}`) + } + process.exit(1) +} + +console.log('mmsMapping XML data type contract passed') diff --git a/frontend/src/views/tools/mmsMapping/utils/useMmsMappingFlow.ts b/frontend/src/views/tools/mmsMapping/utils/useMmsMappingFlow.ts index f4ed934..26417f0 100644 --- a/frontend/src/views/tools/mmsMapping/utils/useMmsMappingFlow.ts +++ b/frontend/src/views/tools/mmsMapping/utils/useMmsMappingFlow.ts @@ -354,7 +354,7 @@ export const useMmsMappingFlow = (options: UseMmsMappingFlowOptions = {}) => { return 'json' } - const handleGenerateXmlMapping = async () => { + const handleGenerateXmlMapping = async (dataType = 2) => { const mappingJson = responsePayload.value?.mappingJson?.trim() if (!mappingJson) { @@ -370,7 +370,8 @@ export const useMmsMappingFlow = (options: UseMmsMappingFlowOptions = {}) => { // 关键业务节点:XML 映射依赖本次接口返回的完整 mappingJson,避免使用旧结果生成不一致的 XML 文件。 const response = await getXmlFromJsonApi({ request: { - mappingJson + mappingJson, + configType: dataType } }) const payload = unwrapApiPayload(response) diff --git a/frontend/src/views/tools/mmsmapping/components/MappingResultPanel.vue b/frontend/src/views/tools/mmsmapping/components/MappingResultPanel.vue index 2a6bc84..1135eb9 100644 --- a/frontend/src/views/tools/mmsmapping/components/MappingResultPanel.vue +++ b/frontend/src/views/tools/mmsmapping/components/MappingResultPanel.vue @@ -21,7 +21,7 @@ :icon="Connection" :loading="isGeneratingXml" :disabled="!canGenerateXmlMapping" - @click="emit('generate-xml-mapping')" + @click="openXmlDataTypeDialog" > XML映射 @@ -239,6 +239,23 @@ + + + MMS版本 + 省级平台版本 + + + + props.icdConsistencyProblemLis const problemDialogVisible = ref(false) const icdConsistencyProblemDialogVisible = ref(false) const matchResultDialogVisible = ref(false) +const xmlDataTypeDialogVisible = ref(false) +const xmlDataType = ref(2) const sequenceConfigRows = ref([]) const sequenceSearchKeyword = ref('') const sequenceDialogVisible = computed({ @@ -634,6 +653,16 @@ const closeSequenceDialog = () => { sequenceDialogVisible.value = false } +const openXmlDataTypeDialog = () => { + xmlDataType.value = 2 + xmlDataTypeDialogVisible.value = true +} + +const confirmXmlDataType = () => { + xmlDataTypeDialogVisible.value = false + emit('generate-xml-mapping', xmlDataType.value) +} + watch( () => props.sequenceDialogVisible, visible => { @@ -1001,6 +1030,12 @@ const confirmSequenceConfig = () => { overflow-wrap: anywhere; } +.xml-data-type-options { + display: flex; + flex-direction: column; + gap: 12px; +} + .dialog-search-bar { display: flex; align-items: center; diff --git a/frontend/src/views/tools/parsePqdif/components/PqdifPathDetailDialog.vue b/frontend/src/views/tools/parsePqdif/components/PqdifPathDetailDialog.vue new file mode 100644 index 0000000..72ff14f --- /dev/null +++ b/frontend/src/views/tools/parsePqdif/components/PqdifPathDetailDialog.vue @@ -0,0 +1,127 @@ + + + + + diff --git a/frontend/src/views/tools/parsePqdif/components/PqdifPathFormDialog.vue b/frontend/src/views/tools/parsePqdif/components/PqdifPathFormDialog.vue new file mode 100644 index 0000000..823f4ec --- /dev/null +++ b/frontend/src/views/tools/parsePqdif/components/PqdifPathFormDialog.vue @@ -0,0 +1,161 @@ + + + + + diff --git a/frontend/src/views/tools/parsePqdif/components/PqdifRequestPanel.vue b/frontend/src/views/tools/parsePqdif/components/PqdifRequestPanel.vue deleted file mode 100644 index 984e02d..0000000 --- a/frontend/src/views/tools/parsePqdif/components/PqdifRequestPanel.vue +++ /dev/null @@ -1,217 +0,0 @@ - - - - - diff --git a/frontend/src/views/tools/parsePqdif/components/PqdifResultPanel.vue b/frontend/src/views/tools/parsePqdif/components/PqdifResultPanel.vue index 36b6783..b2d4223 100644 --- a/frontend/src/views/tools/parsePqdif/components/PqdifResultPanel.vue +++ b/frontend/src/views/tools/parsePqdif/components/PqdifResultPanel.vue @@ -1,5 +1,5 @@