From c28b322e91ff03928c9e4aee0ea036f6cecdcc4a Mon Sep 17 00:00:00 2001 From: Ranyunqiao <2499115710@qq.com> Date: Tue, 19 May 2026 14:22:40 +0800 Subject: [PATCH] bug 443 444 445 478 494 521 --- .../DoctorStationAdviceAppServiceImpl.java | 5 - .../web/doctorstation/dto/SurgeryItemDto.java | 3 + .../DoctorStationAdviceAppMapper.xml | 4 + .../RequestFormManageAppMapper.xml | 2 - .../bargain/component/prescriptionlist.vue | 5 +- .../applicationShow/examineApplication.vue | 10 +- .../applicationForm/medicalExaminations.vue | 43 +- .../src/views/surgicalschedule/index.vue | 381 ++++++------------ .../surgicalschedule/temporaryMedical.vue | 38 +- 9 files changed, 218 insertions(+), 273 deletions(-) diff --git a/openhis-server-new/openhis-application/src/main/java/com/openhis/web/doctorstation/appservice/impl/DoctorStationAdviceAppServiceImpl.java b/openhis-server-new/openhis-application/src/main/java/com/openhis/web/doctorstation/appservice/impl/DoctorStationAdviceAppServiceImpl.java index 40bd312e..4fa5d990 100755 --- a/openhis-server-new/openhis-application/src/main/java/com/openhis/web/doctorstation/appservice/impl/DoctorStationAdviceAppServiceImpl.java +++ b/openhis-server-new/openhis-application/src/main/java/com/openhis/web/doctorstation/appservice/impl/DoctorStationAdviceAppServiceImpl.java @@ -2192,11 +2192,6 @@ public class DoctorStationAdviceAppServiceImpl implements IDoctorStationAdviceAp CommonConstants.TableName.MED_MEDICATION_REQUEST, CommonConstants.TableName.WOR_DEVICE_REQUEST, CommonConstants.TableName.WOR_SERVICE_REQUEST, practitionerId, Whether.NO.getCode(), sourceEnum, sourceBillNo); - // 手术计费场景:sourceBillNo 不为空时,过滤掉药品(1),保留耗材(2)和诊疗(3/6) - if (sourceBillNo != null && !sourceBillNo.isEmpty()) { - requestBaseInfo.removeIf(dto -> dto.getAdviceType() != null - && dto.getAdviceType() == 1); - } for (RequestBaseDto requestBaseDto : requestBaseInfo) { // 请求状态 requestBaseDto diff --git a/openhis-server-new/openhis-application/src/main/java/com/openhis/web/doctorstation/dto/SurgeryItemDto.java b/openhis-server-new/openhis-application/src/main/java/com/openhis/web/doctorstation/dto/SurgeryItemDto.java index d49f340e..9615e09c 100644 --- a/openhis-server-new/openhis-application/src/main/java/com/openhis/web/doctorstation/dto/SurgeryItemDto.java +++ b/openhis-server-new/openhis-application/src/main/java/com/openhis/web/doctorstation/dto/SurgeryItemDto.java @@ -23,6 +23,9 @@ public class SurgeryItemDto { @JsonSerialize(using = ToStringSerializer.class) private Long orgId; + /** 所属科室名称 */ + private String orgName; + /** 执行科室ID */ @JsonSerialize(using = ToStringSerializer.class) private Long positionId; diff --git a/openhis-server-new/openhis-application/src/main/resources/mapper/doctorstation/DoctorStationAdviceAppMapper.xml b/openhis-server-new/openhis-application/src/main/resources/mapper/doctorstation/DoctorStationAdviceAppMapper.xml index c3067563..15dae0b5 100755 --- a/openhis-server-new/openhis-application/src/main/resources/mapper/doctorstation/DoctorStationAdviceAppMapper.xml +++ b/openhis-server-new/openhis-application/src/main/resources/mapper/doctorstation/DoctorStationAdviceAppMapper.xml @@ -901,6 +901,7 @@ t1.ID AS advice_definition_id, t1.NAME AS advice_name, t1.org_id AS org_id, + t3.name AS org_name, t1.org_id AS position_id, t2.ID AS charge_item_definition_id, t2.price AS price, @@ -912,6 +913,9 @@ AND t2.delete_flag = '0' AND t2.status_enum = #{statusEnum} AND t2.instance_table = 'wor_activity_definition' + LEFT JOIN adm_organization t3 + ON t3.id = t1.org_id + AND t3.delete_flag = '0' WHERE t1.delete_flag = '0' AND t1.category_code = '23' diff --git a/openhis-server-new/openhis-application/src/main/resources/mapper/regdoctorstation/RequestFormManageAppMapper.xml b/openhis-server-new/openhis-application/src/main/resources/mapper/regdoctorstation/RequestFormManageAppMapper.xml index 7d13f14e..782d38c4 100755 --- a/openhis-server-new/openhis-application/src/main/resources/mapper/regdoctorstation/RequestFormManageAppMapper.xml +++ b/openhis-server-new/openhis-application/src/main/resources/mapper/regdoctorstation/RequestFormManageAppMapper.xml @@ -57,8 +57,6 @@ AND ae.delete_flag = '0' LEFT JOIN adm_patient AS ap ON ap.ID = ae.patient_id AND ap.delete_flag = '0' - LEFT JOIN wor_service_request AS wsr ON wsr.prescription_no = drf.prescription_no - AND wsr.delete_flag = '0' WHERE drf.delete_flag = '0' AND drf.encounter_id = #{encounterId} AND drf.type_code = #{typeCode} diff --git a/openhis-ui-vue3/src/views/clinicmanagement/bargain/component/prescriptionlist.vue b/openhis-ui-vue3/src/views/clinicmanagement/bargain/component/prescriptionlist.vue index 4910dd2e..96847cb5 100755 --- a/openhis-ui-vue3/src/views/clinicmanagement/bargain/component/prescriptionlist.vue +++ b/openhis-ui-vue3/src/views/clinicmanagement/bargain/component/prescriptionlist.vue @@ -1173,8 +1173,9 @@ function handleSaveSign(row, index) { cleanRow.generateSourceEnum = 6; // 手术计费 cleanRow.sourceBillNo = props.patientInfo.sourceBillNo; } - console.log('cleanRow', cleanRow) - savePrescription({ adviceSaveList: [cleanRow] }, '1').then((res) => { + // 🔧 门诊计费场景:保存为草稿,让药品出现在临时医嘱弹窗"已引用计费药品(待生成医嘱)"中 + const adviceOpType = props.patientInfo.sourceBillNo ? '0' : '1' + savePrescription({ adviceSaveList: [cleanRow] }, adviceOpType).then((res) => { if (res.code === 200) { proxy.$modal.msgSuccess('保存成功'); getListInfo(false); diff --git a/openhis-ui-vue3/src/views/inpatientDoctor/home/components/applicationShow/examineApplication.vue b/openhis-ui-vue3/src/views/inpatientDoctor/home/components/applicationShow/examineApplication.vue index c840ccf3..7975cda6 100755 --- a/openhis-ui-vue3/src/views/inpatientDoctor/home/components/applicationShow/examineApplication.vue +++ b/openhis-ui-vue3/src/views/inpatientDoctor/home/components/applicationShow/examineApplication.vue @@ -86,7 +86,7 @@ - + @@ -444,11 +444,9 @@ const buildApplicationName = (row) => { if (!details || details.length === 0) { return row.name || '-'; } - if (details.length === 1) { - return details[0].adviceName || row.name || '-'; - } - const first = details[0]; - return `${first.adviceName || ''}等${details.length}项`; + const names = details.map(d => d.adviceName).filter(Boolean); + if (names.length === 0) return row.name || '-'; + return names.join(' + '); }; /** diff --git a/openhis-ui-vue3/src/views/inpatientDoctor/home/components/order/applicationForm/medicalExaminations.vue b/openhis-ui-vue3/src/views/inpatientDoctor/home/components/order/applicationForm/medicalExaminations.vue index 11613b94..c1f071ec 100755 --- a/openhis-ui-vue3/src/views/inpatientDoctor/home/components/order/applicationForm/medicalExaminations.vue +++ b/openhis-ui-vue3/src/views/inpatientDoctor/home/components/order/applicationForm/medicalExaminations.vue @@ -428,11 +428,52 @@ const loadEditData = () => { const projectWithDepartment = (selectProjectIds) => { if (!selectProjectIds || selectProjectIds.length === 0) { form.targetDepartment = ''; + return; } + + // 获取第一个选中项目的发往科室(orgId) + // 优先使用配置的发往科室,如果没有则保留手动选择 + const selectedProject = applicationListAll.value?.find( + item => selectProjectIds.includes(item.adviceDefinitionId) + ); + + if (selectedProject && selectedProject.orgId) { + // 项目配置了发往科室,自动填充 + const orgId = selectedProject.orgId; + const orgName = selectedProject.orgName; + + // 查找树中对应的节点,获取正确的 id 类型 + const findNode = (nodes, targetId) => { + if (!nodes) return null; + for (const node of nodes) { + if (String(node.id) === String(targetId)) { + return node; + } + if (node.children && node.children.length > 0) { + const found = findNode(node.children, targetId); + if (found) return found; + } + } + return null; + }; + + const treeNode = findNode(orgOptions.value, orgId); + if (treeNode) { + // 使用树节点的原始 id 值(确保类型匹配) + form.targetDepartment = treeNode.id; + } else { + // 科室不在列表中(可能已删除),留空让用户手动选择 + form.targetDepartment = ''; + } + } + // 如果没有配置发往科室,保留手动选择(不修改 form.targetDepartment) }; watch(() => transferValue.value, (newValue) => { - projectWithDepartment(newValue); + // 使用 nextTick 确保 DOM 更新完成后再设置值 + nextTick(() => { + projectWithDepartment(newValue); + }); }); const getPriorityCode = () => { diff --git a/openhis-ui-vue3/src/views/surgicalschedule/index.vue b/openhis-ui-vue3/src/views/surgicalschedule/index.vue index 8039c920..25b3597b 100755 --- a/openhis-ui-vue3/src/views/surgicalschedule/index.vue +++ b/openhis-ui-vue3/src/views/surgicalschedule/index.vue @@ -1067,15 +1067,6 @@ const temporaryPatientInfo = ref({}) const temporaryBillingMedicines = ref([]) const temporaryAdvices = ref([]) -// 🔧 新增:监听 temporaryAdvices 的变化,用于调试 -watch(temporaryAdvices, (newVal, oldVal) => { - console.log('=== temporaryAdvices 变化 ===') - console.log('=== 新值 ===', newVal) - console.log('=== 新值[1]?.dosage ===', newVal[1]?.dosage) - console.log('=== 旧值 ===', oldVal) - console.log('=== 旧值[1]?.dosage ===', oldVal[1]?.dosage) -}, { deep: true }) - const temporaryMedicalLoading = ref(false) // 🔧 新增:临时医嘱加载状态 const temporarySigned = ref(false) // 🔧 新增:签名状态,用于保持按钮名称一致性 @@ -1499,9 +1490,6 @@ async function closeChargeDialog() { chargeSurgeryInfo.value = {} } -// 🔧 新增:标志位,用于区分是"打开"还是"刷新" -const isRefreshAction = ref(false) - // 处理医嘱按钮点击事件 function handleMedicalAdvice(row) { // 如果没有传入行数据,使用选中的行 @@ -1529,31 +1517,7 @@ function handleMedicalAdvice(row) { applyId: row.applyId // 手术申请单ID,用于过滤关联医嘱 } - // 🔧 关键修复:如果已有提交的医嘱数据,并且是同一个患者的就诊,则使用保存的数据 - // 这样可以保留 requestId,避免重复创建医嘱记录 - console.log('=== 检查是否使用已保存的医嘱数据 ===') - console.log('=== temporaryAdvices.value.length ===', temporaryAdvices.value.length) - console.log('=== temporaryAdvices.value[0]?.originalMedicine?.encounterId ===', temporaryAdvices.value[0]?.originalMedicine?.encounterId) - console.log('=== row.visitId ===', row.visitId) - console.log('=== isRefreshAction.value ===', isRefreshAction.value) - - const isSameEncounter = temporaryAdvices.value.length > 0 && - temporaryAdvices.value[0]?.originalMedicine?.encounterId === row.visitId && - !isRefreshAction.value - - console.log('=== isSameEncounter ===', isSameEncounter) - - if (isSameEncounter) { - console.log('=== 使用已保存的医嘱数据,避免重复创建 ===') - console.log('=== temporaryAdvices.value[0]?.originalMedicine?.requestId ===', temporaryAdvices.value[0]?.originalMedicine?.requestId) - // 直接打开弹窗,使用已保存的数据 - showTemporaryMedical.value = true - temporaryMedicalLoading.value = false - isRefreshAction.value = false // 重置标志位 - return - } - - // 🔧 修复:每次打开临时医嘱时都重新加载数据,避免使用缓存数据导致数据重复 + // 🔧 每次打开临时医嘱都重新拉取最新数据,确保计费弹窗签发后数据自动更新 // 先清空旧数据 temporaryBillingMedicines.value = [] temporaryAdvices.value = [] @@ -1566,46 +1530,39 @@ function handleMedicalAdvice(row) { // 调用计费接口获取数据 getPrescriptionList(row.visitId, 6, row.operCode).then((res) => { - console.log('=== 拉取计费数据返回结果 ===', res) if (res.code === 200 && res.data) { - // 🔧 修复:显示所有药品请求数据,不管有没有计费项目 - // 根据用户需求:已引用计费药品(待生成医嘱)和临时医嘱预览(已生成)显示的数据应该相同 - // 在提交医嘱之前状态应该是"待签发",提交之后变为"已签发" - // 再次打开医嘱界面的时候能看到这两个状态的药品 const seenIds = new Set(); const filteredItems = res.data.filter(item => { // 匹配 encounterId if (item.encounterId !== row.visitId) return false; - // 只保留药品类型(adviceType=1),过滤掉耗材(2)和诊疗项目(3/6) - // 🔧 修复 Bug #444: 使用 Number() 显式转换,避免字符串 "1" 被 !== 1 放行 + // 只保留药品(1)和耗材(2),屏蔽诊疗(3)和手术(6) const at = Number(item.adviceType ?? item.advice_type); - if (at !== 1) return false; + if (at !== 1 && at !== 2) return false; // 过滤掉名称为空的项目 const medicineName = item.adviceName || item.advice_name; if (!medicineName || medicineName.trim() === '') return false; - // 🔧 修复 Bug #444: 二次过滤,排除名称中包含手术/检查/诊疗关键词的非药品项目 - // 某些计费项目可能在 adm_charge_item 中被错误标注为 adviceType=1 + // 排除名称中包含手术/检查/诊疗关键词的非药品项目 const excludedKeywords = ['术', '超声', '多普勒', '检查', '检验', '彩超', 'X线', 'CT', 'MRI', '扫描', '造影']; if (excludedKeywords.some(kw => medicineName.includes(kw))) return false; - // 🔧 修复 Bug #445: 过滤掉已生成医嘱的项目(已有 requestId 的不应出现在"待生成"列表中) - if (item.requestId) return false; // 根据药品请求ID去重,避免重复显示 const itemId = item.requestId || item.id; if (itemId && seenIds.has(itemId)) return false; if (itemId) seenIds.add(itemId); return true; }) + // 按 statusEnum 区分:1=草稿(待生成),2=已签发(已生成) + const draftItems = filteredItems.filter(item => item.statusEnum === 1) + const activeItems = filteredItems.filter(item => item.statusEnum === 2) // 🔧 修复:限制返回数量,最多显示前100条,避免数据过多导致页面卡死 const maxItems = 100 - if (filteredItems.length > maxItems) { - ElMessage.warning(`待签发医嘱数量过多(${filteredItems.length}条),仅显示前${maxItems}条`) - filteredItems.length = maxItems + if (draftItems.length > maxItems) { + ElMessage.warning(`待签发医嘱数量过多(${draftItems.length}条),仅显示前${maxItems}条`) + draftItems.length = maxItems } - // 将过滤后的数据转换为临时医嘱需要的格式 - 兼容驼峰和下划线命名 - // 对于从 adm_charge_item(计费项目表)查询来的项目,特殊处理 - temporaryBillingMedicines.value = filteredItems.map(item => { + // === 待生成列表:statusEnum=1 草稿状态的项目 === + temporaryBillingMedicines.value = draftItems.map(item => { try { // 从 contentJson 或 content_json 中解析详细数据 - 兼容下划线和驼峰命名 const jsonContent = item.contentJson || item.content_json; @@ -1652,74 +1609,65 @@ function handleMedicalAdvice(row) { }; } }); - } else { - // 如果没有数据或接口调用失败,初始化空列表 - temporaryBillingMedicines.value = [] - } - - // 将计费药品转换为临时医嘱数据 - temporaryAdvices.value = temporaryBillingMedicines.value.map((medicine, index) => { - // 解析规格中的数值和单位 - const specMatch = medicine.specification ? medicine.specification.match(/(\d+)(\D+)/) : null - const specValue = specMatch ? parseInt(specMatch[1]) : 1 - const specUnit = specMatch ? specMatch[2] : 'ml' - // 计算剂量 = 规格数值 × 数量 - const dosage = specValue * (medicine.quantity || 1) + // === 已生成列表:statusEnum=2 已签发状态的项目,直接转为医嘱格式 === + temporaryAdvices.value = activeItems.map((item, index) => { + try { + const jsonContent = item.contentJson || item.content_json; + const contentData = jsonContent ? JSON.parse(jsonContent) : {}; + const medicineName = contentData.adviceName || contentData.advice_name || item.adviceName || item.advice_name || ''; + const spec = contentData.volume || contentData.specification || item.volume || item.specification || ''; + const specMatch = spec.match(/(\d+)(\D+)/) + const specValue = specMatch ? parseInt(specMatch[1]) : 1 + const specUnit = specMatch ? specMatch[2] : 'ml' + const dosage = specValue * (contentData.quantity || item.quantity || 1) - // 🔧 修复:优先从 contentJson 中读取已有的用法,如果没有则根据药品名称判断 - let usageCode = 'iv' // 默认静脉注射编码 - let usageLabel = '静脉注射' // 默认显示名称 + let usageCode = contentData.methodCode || 'iv' + let usageLabel = getUsageLabel(usageCode) + if (usageCode === 'iv') { + if (medicineName.includes('注射液')) { usageCode = 'iv'; usageLabel = '静脉注射' } + } else if (usageCode === 'po') { + if (medicineName.includes('片') || medicineName.includes('胶囊')) { usageCode = 'po'; usageLabel = '口服' } + } - // 尝试从 contentJson 中读取用法 - try { - const jsonContent = medicine.contentJson || medicine.content_json; - if (jsonContent) { - const contentData = JSON.parse(jsonContent); - if (contentData.methodCode) { - usageCode = contentData.methodCode; - usageLabel = getUsageLabel(contentData.methodCode); + return { + id: index + 1, + adviceName: medicineName, + dosage, + unit: specUnit, + usage: usageCode, + usageLabel, + frequency: '临时', + executeTime: new Date().toLocaleString('zh-CN'), + originalMedicine: { + ...item, + medicineName: medicineName, + specification: spec, + quantity: contentData.quantity || item.quantity || 1, + encounterId: row.visitId + } + } + } catch (e) { + return { + id: index + 1, + adviceName: item.adviceName || item.advice_name || '', + dosage: 1, unit: 'ml', usage: 'iv', usageLabel: '静脉注射', + frequency: '临时', + executeTime: new Date().toLocaleString('zh-CN'), + originalMedicine: { + ...item, + medicineName: item.adviceName || item.advice_name || '', + specification: item.volume || item.specification || '', + quantity: item.quantity || 1, + encounterId: row.visitId + } } } - } catch (e) { - // 解析失败,继续使用默认值 - } - - // 如果没有从 contentJson 中读取到用法,根据药品名称判断 - if (!usageCode || usageCode === 'iv') { - if (medicine.medicineName && medicine.medicineName.includes('注射液')) { - usageCode = 'iv' - usageLabel = '静脉注射' - } else if (medicine.medicineName && medicine.medicineName.includes('片')) { - usageCode = 'po' - usageLabel = '口服' - } else if (medicine.medicineName && medicine.medicineName.includes('胶囊')) { - usageCode = 'po' - usageLabel = '口服' - } - } - - return { - id: index + 1, - adviceName: medicine.medicineName || '', - dosage: dosage, - unit: specUnit, - usage: usageCode, // 🔧 修复:保存的是编码 - usageLabel: usageLabel, // 🔧 新增:保存显示名称 - frequency: '临时', - executeTime: new Date().toLocaleString('zh-CN'), - // 🔧 关键修复:确保 originalMedicine 中包含 encounterId 和匹配字段 - // medicineName/specification/quantity 用于 handleTemporaryMedicalSubmit 中的 - // 已提交项目匹配过滤(Bug #445),缺少这些字段会导致过滤失效 - originalMedicine: { - ...medicine, - medicineName: medicine.medicineName, - specification: medicine.specification, - quantity: medicine.quantity, - encounterId: row.visitId // 添加 encounterId 字段 - } - } - }) + }) + } else { + temporaryBillingMedicines.value = [] + temporaryAdvices.value = [] + } // 打开临时医嘱弹窗 showTemporaryMedical.value = true @@ -1745,11 +1693,6 @@ function closeTemporaryMedical() { // 处理临时医嘱提交 // 🔧 修复:提交成功后,更新 temporaryAdvices 中的 requestId,以便下次提交时执行更新操作 function handleTemporaryMedicalSubmit(data) { - console.log('=== handleTemporaryMedicalSubmit 被调用 ===') - console.log('=== data ===', data) - console.log('=== data.temporaryAdvices ===', data.temporaryAdvices) - console.log('=== data.temporaryAdvices[1]?.dosage ===', data.temporaryAdvices[1]?.dosage) - // 🔧 修复:使用用户修改后的数据,而不是重新加载数据 // 这样可以确保用户修改的内容(如剂量)在保存后仍然正确显示 if (data.temporaryAdvices && data.temporaryAdvices.length > 0) { @@ -1804,9 +1747,7 @@ function handleTemporaryMedicalSubmit(data) { // 如果没有任何匹配键,清空待生成列表(所有项目都已提交) temporaryBillingMedicines.value = [] } - - console.log('=== 使用用户修改后的临时医嘱数据 ===', temporaryAdvices.value) - console.log('=== temporaryAdvices.value[1]?.dosage ===', temporaryAdvices.value[1]?.dosage) + } else { // 如果没有传递数据,则清空 temporaryAdvices.value = [] @@ -1878,21 +1819,70 @@ function handleQuoteBilling() { // 🔧 修复 Bug #445: 只保留药品类型(adviceType=1),过滤掉耗材(2)和诊疗项目(3/6) // 同时过滤掉已有 requestId 的项目(已生成医嘱的不需要再次显示在"待生成"列表中) - const filteredItems = res.data.filter(item => { - // 匹配 encounterId + // 先提取已签发项目(statusEnum=2)填充已生成列表 + const activeItems = res.data.filter(item => { if (item.encounterId !== temporaryPatientInfo.value.visitId) return false; - // 只保留药品类型(adviceType=1),过滤掉耗材(2)和诊疗项目(3/6) - // 🔧 修复 Bug #444: 使用 Number() 显式转换 + snake_case 回退,避免字符串 "1" 匹配失败 const at = Number(item.adviceType ?? item.advice_type); - if (at !== 1) return false; - // 过滤掉名称为空的项目 + if (at !== 1 && at !== 2) return false; + if (item.statusEnum !== 2) return false; + const medicineName = item.adviceName || item.advice_name; + if (!medicineName || medicineName.trim() === '') return false; + const excludedKeywords = ['术', '超声', '多普勒', '检查', '检验', '彩超', 'X线', 'CT', 'MRI', '扫描', '造影']; + if (excludedKeywords.some(kw => medicineName.includes(kw))) return false; + return true; + }) + temporaryAdvices.value = activeItems.map((item, index) => { + try { + const jsonContent = item.contentJson || item.content_json; + const contentData = jsonContent ? JSON.parse(jsonContent) : {}; + const medicineName = contentData.adviceName || contentData.advice_name || item.adviceName || item.advice_name || ''; + const spec = contentData.volume || contentData.specification || item.volume || item.specification || ''; + const specMatch = spec.match(/(\d+)(\D+)/) + const specValue = specMatch ? parseInt(specMatch[1]) : 1 + const specUnit = specMatch ? specMatch[2] : 'ml' + const dosage = specValue * (contentData.quantity || item.quantity || 1) + let usageCode = contentData.methodCode || 'iv' + let usageLabel = getUsageLabel(usageCode) + if (usageCode === 'iv' && medicineName.includes('注射液')) { usageLabel = '静脉注射' } + else if (usageCode === 'po' && (medicineName.includes('片') || medicineName.includes('胶囊'))) { usageLabel = '口服' } + return { + id: index + 1, adviceName: medicineName, dosage, unit: specUnit, + usage: usageCode, usageLabel, frequency: '临时', + executeTime: new Date().toLocaleString('zh-CN'), + originalMedicine: { + ...item, + medicineName: medicineName, + specification: spec, + quantity: contentData.quantity || item.quantity || 1, + encounterId: temporaryPatientInfo.value.visitId + } + } + } catch (e) { + return { + id: index + 1, adviceName: item.adviceName || item.advice_name || '', + dosage: 1, unit: 'ml', usage: 'iv', usageLabel: '静脉注射', + frequency: '临时', executeTime: new Date().toLocaleString('zh-CN'), + originalMedicine: { + ...item, + medicineName: item.adviceName || item.advice_name || '', + specification: item.volume || item.specification || '', + quantity: item.quantity || 1, + encounterId: temporaryPatientInfo.value.visitId + } + } + } + }) + + // 再提取草稿项目(statusEnum=1)填充待生成列表 + const filteredItems = res.data.filter(item => { + if (item.encounterId !== temporaryPatientInfo.value.visitId) return false; + const at = Number(item.adviceType ?? item.advice_type); + if (at !== 1 && at !== 2) return false; + if (item.statusEnum !== 1) return false; const medicineName = item.adviceName || item.advice_name; if (!medicineName || medicineName.trim() === '') return false; - // 🔧 修复 Bug #444: 二次过滤,排除名称中包含手术/检查/诊疗关键词的非药品项目 const excludedKeywords = ['术', '超声', '多普勒', '检查', '检验', '彩超', 'X线', 'CT', 'MRI', '扫描', '造影']; if (excludedKeywords.some(kw => medicineName.includes(kw))) return false; - // 🔧 修复 Bug #445: 过滤掉已生成医嘱的项目(已有 requestId) - if (item.requestId) return false; return true; }) // 🔧 修复:限制返回数量,最多显示前100条,避免数据过多导致页面卡死 @@ -1905,7 +1895,6 @@ function handleQuoteBilling() { // 将过滤后的数据转换为临时医嘱需要的格式 temporaryBillingMedicines.value = filteredItems.map(item => { try { - // 从 contentJson 或 content_json 中解析详细数据 - 兼容下划线和驼峰命名 const jsonContent = item.contentJson || item.content_json; const contentData = jsonContent ? JSON.parse(jsonContent) : {}; return { @@ -1924,10 +1913,9 @@ function handleQuoteBilling() { definitionDetailId: contentData.definitionDetailId || item.definitionDetailId } } catch (e) { - // 如果解析失败,使用顶层数据 - 兼容 snake_case 和 camelCase return { medicineName: item.adviceName || item.advice_name || '', - specification: item.specification || item.specification || item.volume || '', + specification: item.specification || item.volume || '', quantity: item.quantity || item.quantity_value || 0, batchNumber: item.lotNumber || item.lot_number || '', unitPrice: item.unitPrice || item.unit_price || 0, @@ -1943,123 +1931,6 @@ function handleQuoteBilling() { } }) - // 将计费药品转换为临时医嘱数据 - temporaryAdvices.value = temporaryBillingMedicines.value.map((medicine, index) => { - // 解析规格中的数值和单位 - const specMatch = medicine.specification ? medicine.specification.match(/(\d+)(\D+)/) : null - const specValue = specMatch ? parseInt(specMatch[1]) : 1 - const specUnit = specMatch ? specMatch[2] : 'ml' - - // 计算剂量 = 规格数值 × 数量 - const dosage = specValue * (medicine.quantity || 1) - - // 🔧 修复:优先从 contentJson 中读取已有的用法,如果没有则根据药品名称判断 - let usageCode = 'iv' // 默认静脉注射编码 - let usageLabel = '静脉注射' // 默认显示名称 - - // 尝试从 contentJson 中读取用法 - try { - const jsonContent = medicine.contentJson || medicine.content_json; - if (jsonContent) { - const contentData = JSON.parse(jsonContent); - if (contentData.methodCode) { - usageCode = contentData.methodCode; - usageLabel = getUsageLabel(contentData.methodCode); - } - } - } catch (e) { - // 解析失败,继续使用默认值 - } - - // 如果没有从 contentJson 中读取到用法,根据药品名称判断 - if (!usageCode || usageCode === 'iv') { - if (medicine.medicineName && medicine.medicineName.includes('注射液')) { - usageCode = 'iv' - usageLabel = '静脉注射' - } else if (medicine.medicineName && medicine.medicineName.includes('片')) { - usageCode = 'po' - usageLabel = '口服' - } else if (medicine.medicineName && medicine.medicineName.includes('胶囊')) { - usageCode = 'po' - usageLabel = '口服' - } - } - - return { - id: index + 1, - adviceName: medicine.medicineName || '', - dosage: dosage, - unit: specUnit, - usage: usageCode, // 🔧 修复:保存的是编码 - usageLabel: usageLabel, // 🔧 新增:保存显示名称 - frequency: '临时', - executeTime: new Date().toLocaleString('zh-CN'), - // 🔧 关键修复:确保 originalMedicine 中包含 encounterId 和匹配字段 - // medicineName/specification/quantity 用于已提交项目匹配过滤(Bug #445) - originalMedicine: { - ...medicine, - medicineName: medicine.medicineName, - specification: medicine.specification, - quantity: medicine.quantity, - encounterId: temporaryPatientInfo.value.visitId // 添加 encounterId 字段 - } - } - }) - - // 🔧 修复 Bug #445: 过滤掉已生成医嘱的项目,避免"引用计费"后已提交项目重新出现在"待生成"列表 - // 使用清空前提取的 submittedKeys(名称|||规格|||数量复合键)进行匹配 - if (submittedKeys.size > 0) { - temporaryBillingMedicines.value = temporaryBillingMedicines.value.filter(m => { - const key = `${m.medicineName || ''}|||${m.specification || ''}|||${m.quantity ?? 0}` - return !submittedKeys.has(key) - }) - // 同步更新 temporaryAdvices,保持两份数据一致 - temporaryAdvices.value = temporaryBillingMedicines.value.map((medicine, index) => { - const specMatch = medicine.specification ? medicine.specification.match(/(\d+)(\D+)/) : null - const specValue = specMatch ? parseInt(specMatch[1]) : 1 - const specUnit = specMatch ? specMatch[2] : 'ml' - const dosage = specValue * (medicine.quantity || 1) - let usageCode = 'iv' - let usageLabel = '静脉注射' - try { - const jsonContent = medicine.contentJson || medicine.content_json; - if (jsonContent) { - const contentData = JSON.parse(jsonContent); - if (contentData.methodCode) { - usageCode = contentData.methodCode; - usageLabel = getUsageLabel(contentData.methodCode); - } - } - } catch (e) {} - if (!usageCode || usageCode === 'iv') { - if (medicine.medicineName && medicine.medicineName.includes('注射液')) { - usageCode = 'iv'; usageLabel = '静脉注射'; - } else if (medicine.medicineName && medicine.medicineName.includes('片')) { - usageCode = 'po'; usageLabel = '口服'; - } else if (medicine.medicineName && medicine.medicineName.includes('胶囊')) { - usageCode = 'po'; usageLabel = '口服'; - } - } - return { - id: index + 1, - adviceName: medicine.medicineName || '', - dosage, - unit: specUnit, - usage: usageCode, - usageLabel, - frequency: '临时', - executeTime: new Date().toLocaleString('zh-CN'), - originalMedicine: { - ...medicine, - medicineName: medicine.medicineName, - specification: medicine.specification, - quantity: medicine.quantity, - encounterId: temporaryPatientInfo.value.visitId - } - } - }) - } - temporaryMedicalLoading.value = false // 🔧 新增:加载完成 ElMessage.success('已成功引用最新计费药品信息!') } else { diff --git a/openhis-ui-vue3/src/views/surgicalschedule/temporaryMedical.vue b/openhis-ui-vue3/src/views/surgicalschedule/temporaryMedical.vue index 20fa1cb7..99c9cc0d 100755 --- a/openhis-ui-vue3/src/views/surgicalschedule/temporaryMedical.vue +++ b/openhis-ui-vue3/src/views/surgicalschedule/temporaryMedical.vue @@ -48,9 +48,12 @@
一、已引用计费药品(待生成医嘱) + + {{ billingExpanded ? '收起' : `展开全部(${(billingMedicines || []).length}条)` }} +
二、临时医嘱预览(已生成) + + {{ advicesExpanded ? '收起' : `展开全部(${(displayAdvices || []).length}条)` }} +
{ return meds.length > 0 && meds.every(m => m.requestId) }) +// 展开/收起控制 +const PAGE_SIZE = 3 +const billingExpanded = ref(false) +const advicesExpanded = ref(false) +const displayBillingMedicines = computed(() => { + const all = props.billingMedicines || [] + return billingExpanded.value ? all : all.slice(0, PAGE_SIZE) +}) +const displayAdvicesList = computed(() => { + const all = displayAdvices.value || [] + return advicesExpanded.value ? all : all.slice(0, PAGE_SIZE) +}) + // 响应式数据 - isSigned 从父组件传入的 prop 初始化 const isSigned = ref(props.isSignedProp) @@ -1045,6 +1064,21 @@ const editFormUsageLabel = computed(() => { padding-bottom: 12px; margin-bottom: 16px; border-bottom: 2px solid #e4e7ed; + display: flex; + align-items: center; + gap: 12px; +} + +.expand-btn { + font-size: 0.85rem; + color: #4a8bc9; + cursor: pointer; + font-weight: 400; + margin-left: auto; +} +.expand-btn:hover { + color: #2a6ba9; + text-decoration: underline; } .medicine-summary {