From 6d23d36a9cc662e496a036403d597787186f154e Mon Sep 17 00:00:00 2001 From: duzhongxu <15039018447@163.com> Date: Fri, 27 Mar 2026 16:39:41 +0800 Subject: [PATCH] =?UTF-8?q?211=20=E6=A3=80=E9=AA=8C=E9=A1=B9=E7=9B=AE?= =?UTF-8?q?=E8=AE=BE=E7=BD=AE-=E3=80=8B=E5=A5=97=E9=A4=90=E7=AE=A1?= =?UTF-8?q?=E7=90=86=EF=BC=9A=E7=82=B9=E5=87=BB=E3=80=90=E6=96=B0=E5=A2=9E?= =?UTF-8?q?=E3=80=91=E8=B7=B3=E8=BD=AC=E8=87=B3=E5=A5=97=E9=A4=90=E8=AE=BE?= =?UTF-8?q?=E7=BD=AE=E7=95=8C=E9=9D=A2=E7=B3=BB=E7=BB=9F=E6=9C=AA=E8=BF=9B?= =?UTF-8?q?=E8=A1=8C=E5=88=9D=E5=A7=8B=E5=8C=96=E6=96=B0=E5=A2=9E=E6=A8=A1?= =?UTF-8?q?=E5=BC=8F=E7=95=8C=E9=9D=A2=E6=95=B0=E6=8D=AE=20212=20=E6=A3=80?= =?UTF-8?q?=E9=AA=8C=E9=A1=B9=E7=9B=AE=E8=AE=BE=E7=BD=AE-=E3=80=8B?= =?UTF-8?q?=E5=A5=97=E9=A4=90=E7=AE=A1=E7=90=86=EF=BC=9A=E7=82=B9=E5=87=BB?= =?UTF-8?q?=E8=A1=8C=E3=80=90=E7=BC=96=E8=BE=91=E3=80=91=E8=B7=B3=E8=BD=AC?= =?UTF-8?q?=E8=87=B3=E5=A5=97=E9=A4=90=E8=AE=BE=E7=BD=AE=E7=BC=96=E8=BE=91?= =?UTF-8?q?=E6=A8=A1=E5=BC=8F=E8=AF=A5=E8=A1=8C=E7=9A=84=E5=A5=97=E9=A4=90?= =?UTF-8?q?=E6=95=B0=E6=8D=AE=E6=9C=AA=E6=AD=A3=E7=A1=AE=E5=BC=95=E5=85=A5?= =?UTF-8?q?=20213=20=E6=A3=80=E9=AA=8C=E9=A1=B9=E7=9B=AE=E8=AE=BE=E7=BD=AE?= =?UTF-8?q?-=E3=80=8B=E5=A5=97=E9=A4=90=E7=AE=A1=E7=90=86=EF=BC=9A?= =?UTF-8?q?=E7=82=B9=E5=87=BB=E8=A1=8C=E3=80=90=E6=9F=A5=E7=9C=8B=E3=80=91?= =?UTF-8?q?=E8=B7=B3=E8=BD=AC=E8=87=B3=E5=A5=97=E9=A4=90=E8=AE=BE=E7=BD=AE?= =?UTF-8?q?=E7=95=8C=E9=9D=A2=E5=A5=97=E9=A4=90=E5=86=85=E5=AE=B9=E6=98=BE?= =?UTF-8?q?=E7=A4=BA=E9=94=99=E8=AF=AF=E5=B9=B6=E4=B8=94=E6=9C=AA=E8=BF=9B?= =?UTF-8?q?=E5=85=A5=E5=8F=AA=E8=AF=BB=E6=A8=A1=E5=BC=8F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Inspection/PackageManagement.vue | 53 +++---- .../views/maintainSystem/Inspection/index.vue | 131 +++++++++++------- 2 files changed, 99 insertions(+), 85 deletions(-) diff --git a/openhis-ui-vue3/src/views/maintainSystem/Inspection/PackageManagement.vue b/openhis-ui-vue3/src/views/maintainSystem/Inspection/PackageManagement.vue index 17a28741..4f2df7bc 100644 --- a/openhis-ui-vue3/src/views/maintainSystem/Inspection/PackageManagement.vue +++ b/openhis-ui-vue3/src/views/maintainSystem/Inspection/PackageManagement.vue @@ -194,10 +194,13 @@ import {ElMessage} from 'element-plus'; import {getLocationTree} from '@/views/charge/outpatientregistration/components/outpatientregistration'; import {listInspectionPackage, delInspectionPackage} from '@/api/system/inspectionPackage'; import { getTenantPage } from '@/api/system/tenant'; +import useTagsViewStore from '@/store/modules/tagsView'; // 创建路由实例 const router = useRouter(); +const tagsViewStore = useTagsViewStore(); + // 侧边栏状态 const sidebarActive = ref(false); @@ -207,7 +210,6 @@ const departments = ref([]); // 获取科室数据 - 与门诊挂号页面保持一致 function getDepartmentList() { - console.log('调用getLocationTree API...'); getLocationTree().then((response) => { @@ -430,13 +432,17 @@ const fetchTenantList = async () => { // 初始化数据 // 整合后的 onMounted 钩子 onMounted(async () => { - // 1. 加载科室数据 + // 1. 加载科室数据(不需要等待) getDepartmentList(); - // 2. 加载机构列表 (包含默认选中逻辑) - await fetchTenantList(); + // 2. 并行发起:机构列表 + 表格数据,减少串行等待 + const tenantPromise = fetchTenantList(); - // 3. 等待 DOM 更新 + // 先用默认参数(不带 tenantId)立刻加载表格,让用户尽快看到数据 + loadData(); + + // 3. 等待机构列表加载完成 + await tenantPromise; await nextTick(); // 4. 防御性检查 @@ -444,9 +450,10 @@ onMounted(async () => { selectedTenantId.value = tenantOptions.value[0].value; } - // 5. 加载表格数据 - loadData(); - + // 5. 如果选中了机构,用 tenantId 重新过滤一次 + if (selectedTenantId.value) { + loadData(); + } }); // 过滤后的数据 - 现在直接从API获取,这里保留前端过滤作为补充 @@ -583,51 +590,27 @@ const pageButtons = computed(() => { // 处理新增 function handleAdd() { - router.push({ - path: '/maintainSystem/Inspection', - query: { - tab: '2', - mode: 'add' - } - }); + window.location.href = '/maintainSystem/Inspection?tab=2&mode=add'; } // 处理编辑 function handleEdit(item) { - // 跳转到套餐设置主界面,并传递套餐ID用于加载数据 - // 后端接口使用 basicInformationId 作为路径参数 const packageId = item.basicInformationId || item.id; if (!packageId) { ElMessage.error('无法获取套餐ID,请刷新页面后重试'); return; } - router.push({ - path: '/maintainSystem/Inspection', - query: { - tab: '2', - packageId: packageId, - mode: 'edit' - } - }); + window.location.href = `/maintainSystem/Inspection?tab=2&mode=edit&packageId=${packageId}`; } // 处理查看 function handleView(item) { - // 跳转到套餐设置主界面,并传递套餐ID用于加载数据 - // 后端接口使用 basicInformationId 作为路径参数 const packageId = item.basicInformationId || item.id; if (!packageId) { ElMessage.error('无法获取套餐ID,请刷新页面后重试'); return; } - router.push({ - path: '/maintainSystem/Inspection', - query: { - tab: '2', - packageId: packageId, - mode: 'view' - } - }); + window.location.href = `/maintainSystem/Inspection?tab=2&mode=view&packageId=${packageId}`; } // 处理删除 diff --git a/openhis-ui-vue3/src/views/maintainSystem/Inspection/index.vue b/openhis-ui-vue3/src/views/maintainSystem/Inspection/index.vue index f1c310ef..255934a3 100644 --- a/openhis-ui-vue3/src/views/maintainSystem/Inspection/index.vue +++ b/openhis-ui-vue3/src/views/maintainSystem/Inspection/index.vue @@ -813,7 +813,8 @@ import { updateInspectionPackage, getInspectionPackage, listInspectionPackageDetails, - saveInspectionPackageDetails + saveInspectionPackageDetails, + batchSaveInspectionPackageDetails } from '@/api/system/inspectionPackage'; import { getTenantPage } from '@/api/system/tenant'; import {deptTreeSelect} from '@/api/system/user'; @@ -1470,6 +1471,8 @@ const packageItems = ref([]); const packageMode = ref('add'); // 当前编辑的套餐ID(用于编辑和查看模式) const currentPackageId = ref(null); +// 正在从后端加载套餐数据的标志,加载期间跳过 calculateAmounts 防止覆盖后端金额 +const isLoadingPackage = ref(false); // 是否为查看模式(只读) const isViewMode = computed(() => packageMode.value === 'view'); @@ -2364,6 +2367,10 @@ const savePackageData = async (basicInfo, detailData) => { if (isEditMode) { // 编辑模式:调用更新API basicResponse = await updateInspectionPackage(basicInfo); + if (basicResponse.code !== 200) { + loading.close(); + throw new Error(basicResponse.msg || '更新基本信息失败'); + } packageId = currentPackageId.value; } else { // 新增模式:调用新增API @@ -2390,29 +2397,41 @@ const savePackageData = async (basicInfo, detailData) => { // 验证套餐ID是否存在 if (!packageId) { loading.close(); - console.error('无法从响应中获取套餐ID,完整响应:', basicResponse); throw new Error('保存成功但未返回套餐ID。请检查后端接口是否正确返回了packageId或id字段'); } } - // 2. 分别保存每个明细数据到明细表,并将后端返回的 id 回填到 packageItems - for (let i = 0; i < detailData.length; i++) { - const detailItem = { - ...detailData[i], - packageId: packageId + // 2. 保存明细数据:编辑模式用批量接口(先删旧后插新),新增模式逐条新增 + if (isEditMode) { + // 编辑模式:调用批量保存接口,后端会先删除旧明细再插入新明细 + const batchPayload = { + basicInformationId: packageId, + details: detailData.map(item => ({ ...item, packageId: packageId })) }; - - const detailResponse = await saveInspectionPackageDetails(detailItem); - - if (detailResponse.code !== 200) { + const batchResponse = await batchSaveInspectionPackageDetails(batchPayload); + if (batchResponse.code !== 200) { loading.close(); - throw new Error(`保存第 ${i + 1} 个明细项失败: ${detailResponse.msg || '未知错误'}`); + throw new Error(batchResponse.msg || '保存明细数据失败'); } + } else { + // 新增模式:逐条保存并回填后端生成的明细 id + for (let i = 0; i < detailData.length; i++) { + const detailItem = { + ...detailData[i], + packageId: packageId + }; - // 回填后端生成的明细 id,防止取消编辑时被误判为新增行 - // 后端返回字段名是 detailId(见 InspectionPackageDetail.java) - if (detailResponse.data && (detailResponse.data.detailId || detailResponse.data.id)) { - packageItems.value[i].id = detailResponse.data.detailId || detailResponse.data.id; + const detailResponse = await saveInspectionPackageDetails(detailItem); + + if (detailResponse.code !== 200) { + loading.close(); + throw new Error(`保存第 ${i + 1} 个明细项失败: ${detailResponse.msg || '未知错误'}`); + } + + // 回填后端生成的明细 id,防止取消编辑时被误判为新增行 + if (detailResponse.data && (detailResponse.data.detailId || detailResponse.data.id)) { + packageItems.value[i].id = detailResponse.data.detailId || detailResponse.data.id; + } } } @@ -2421,8 +2440,14 @@ const savePackageData = async (basicInfo, detailData) => { ElMessage.success('保存成功'); - // 保存成功后重置表单 - doResetForm(); + if (isEditMode) { + // 编辑模式:重新加载最新数据,保持在编辑页面 + loadInspectionPackage(String(packageId)); + } else { + // 新增模式:保存成功后重置表单 + doResetForm(); + } + } catch (error) { // 确保在错误时也关闭loading @@ -2495,7 +2520,7 @@ const doResetForm = () => { // 清空明细数据 packageItems.value = []; - // 重置模式为新增 + // 重置为新增模式(仅在外部未指定 mode 时,由调用方负责设置 packageMode) packageMode.value = 'add'; currentPackageId.value = null; @@ -2527,6 +2552,9 @@ const loadInspectionPackage = async (packageId) => { const basicData = basicResponse.data; const detailData = detailResponse.data || []; + // 【关键】暂停 packageItems 的 watch,防止赋值时触发 calculateAmounts 覆盖后端返回的金额 + isLoadingPackage.value = true; + // 填充基本信息 packageLevel.value = basicData.packageLevel; packageName.value = basicData.packageName; @@ -2536,8 +2564,8 @@ const loadInspectionPackage = async (packageId) => { showPackageName.value = basicData.showPackageName !== false; generateServiceFee.value = basicData.generateServiceFee !== false; enablePackagePrice.value = basicData.enablePackagePrice !== false; - packageAmount.value = basicData.packageAmount || 0.00; - serviceFee.value = basicData.serviceFee || 0.00; + packageAmount.value = parseFloat(basicData.packageAmount || 0); + serviceFee.value = parseFloat(basicData.serviceFee || 0); selectedLisGroup.value = basicData.lisGroup || ''; bloodVolume.value = basicData.bloodVolume || ''; remarks.value = basicData.remarks || ''; @@ -2559,11 +2587,15 @@ const loadInspectionPackage = async (packageId) => { origin: item.origin || '' })); + // 恢复监听 + isLoadingPackage.value = false; + loading.close(); ElMessage.success('套餐数据加载成功'); } catch (error) { - console.error('加载套餐数据失败:', error); + isLoadingPackage.value = false; + ElMessage.error(error.message || '加载套餐数据失败'); } }; @@ -2597,47 +2629,43 @@ watch(activity_category_code, (newVal) => { }, { immediate: true }); /** - * 关键修复: - * 从“套餐管理”跳转到这里时,通常只是改变 query(packageId/mode/tab),组件不会重新挂载, - * 所以仅靠 onMounted 读取一次 query 会导致“查看/修改”出现空白,刷新才正常。 - * 这里监听 query 变化,自动切换 tab 并加载套餐数据。 + * 统一监听 query 变化,处理 tab 切换和套餐数据加载。 + * 使用 watch(route) 深度监听,确保在 keep-alive 场景下同路由 query 变化也能响应。 */ -watch( - () => route.query.tab, - (tab) => { - if (tab === '0' || tab === '1' || tab === '2') { - activeNav.value = parseInt(String(tab)); - } - }, - { immediate: true } -); - const applyRouteForPackage = async (query) => { + const tab = query?.tab; const packageId = query?.packageId; const mode = query?.mode || 'add'; - - // 设置当前模式和套餐ID - packageMode.value = mode; - currentPackageId.value = packageId ? String(packageId) : null; - - // 如果是新增模式,重置表单 + + // 先切换 tab + if (tab === '0' || tab === '1' || tab === '2') { + activeNav.value = parseInt(tab); + }; + + // 只有 tab=2(套餐设置)才处理套餐数据 + if (activeNav.value !== 2) return; + + // 新增模式:重置表单,然后明确设置 mode if (mode === 'add' || !packageId) { - activeNav.value = 2; - doResetForm(); + doResetForm(); // 内部会设 packageMode='add',符合预期 return; } - - activeNav.value = 2; + + // 编辑/查看模式:先设置 mode 和 ID,再加载数据 + packageMode.value = mode; + currentPackageId.value = String(packageId); + await nextTick(); loadInspectionPackage(String(packageId)); }; +// 监听整个 route.query,确保任何 query 变化(tab/packageId/mode)都能触发 watch( - () => route.query.packageId, - async () => { - await applyRouteForPackage(route.query); + () => route.query, + (newQuery) => { + applyRouteForPackage(newQuery); }, - { immediate: true, flush: 'post' } + { immediate: true, deep: true } ); // 兜底:如果该页面被 keep-alive 缓存,从别的页面返回时不会触发 onMounted @@ -2652,11 +2680,14 @@ onBeforeRouteUpdate((to) => { // 监听生成服务费选项变更 watch(generateServiceFee, (newVal) => { + if (isLoadingPackage.value) return; calculateAmounts(); }); // 监听套餐项目变化 watch(packageItems, (newVal) => { + // 加载期间跳过计算,防止覆盖后端返回的金额 + if (isLoadingPackage.value) return; calculateAmounts(); }, { deep: true }); // 样本类型数据