@@ -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 ( filtered Items. length > maxItems ) {
ElMessage . warning ( ` 待签发医嘱数量过多( ${ filtered Items. length } 条),仅显示前 ${ maxItems } 条 ` )
filtered Items. length = maxItems
if ( draft Items. length > maxItems ) {
ElMessage . warning ( ` 待签发医嘱数量过多( ${ draft Items. length } 条),仅显示前 ${ maxItems } 条 ` )
draft Items. 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 = specV alue * ( medicine . quantity || 1 )
// === 已生成列表: statusEnum=2 已签发状态的项目,直接转为医嘱格式 ===
temporaryAdvices . v alue = 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 . method Code) {
usageCode = contentData . methodCode ;
usageLabel = getUsageLabel ( contentData . methodCode ) ;
return {
id : index + 1 ,
adviceName : medicineName ,
dosage ,
unit : specUnit ,
usage : usage Code ,
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 {