bug 445 497 565

This commit is contained in:
Ranyunqiao
2026-05-25 15:49:49 +08:00
parent 232577caaa
commit 5132de3680
13 changed files with 316 additions and 177 deletions

View File

@@ -1519,13 +1519,14 @@ function handleMedicalAdvice(row) {
}
// 🔧 每次打开临时医嘱都重新拉取最新数据,确保计费弹窗签发后数据自动更新
// 先清空旧数据
// 🔧 修复 Bug #446: 先保存旧数据再清空,避免竟态条件
const prevAdvices = [...temporaryAdvices.value]
temporaryBillingMedicines.value = []
temporaryAdvices.value = []
// 🔧 修复 Bug #446: 如果是同一 encounter 且已有提交的医嘱(有 requestId保留签名状态
const hasSubmittedAdvices = temporaryAdvices.value.length > 0 &&
temporaryAdvices.value[0]?.originalMedicine?.encounterId === row.visitId &&
temporaryAdvices.value.some(a => a.originalMedicine?.requestId);
const hasSubmittedAdvices = prevAdvices.length > 0 &&
prevAdvices[0]?.originalMedicine?.encounterId === row.visitId &&
prevAdvices.some(a => a.originalMedicine?.requestId);
temporarySigned.value = hasSubmittedAdvices; // 修复:根据已有数据状态设置,而非盲目重置
temporaryMedicalLoading.value = true // 🔧 新增:开始加载
@@ -1585,7 +1586,13 @@ function handleMedicalAdvice(row) {
adviceType: item.adviceType || contentData.adviceType || item.advice_type || null,
chargeItemId: item.chargeItemId || contentData.chargeItemId || item.charge_item_id || null,
definitionId: item.definitionId || contentData.definitionId || item.definition_id || null,
definitionDetailId: item.definitionDetailId || contentData.definitionDetailId || item.definition_detail_id || null
definitionDetailId: item.definitionDetailId || contentData.definitionDetailId || item.definition_detail_id || null,
// 🔧 修复:传递 requestId临时医嘱签署时需要用于更新已有记录而非新建
requestId: item.requestId || null,
// 🔧 保留原始 contentJson临时医嘱签发时需要展开所有字段传给后端
contentJson: item.contentJson || item.content_json || null,
// 🔧 标记是否已签发,用于临时医嘱弹窗控制按钮状态
_signed: item.statusEnum === 2
};
} catch (e) {
// 如果解析失败,使用顶层数据 - 兼容 snake_case 和 camelCase 以及后端不同字段名
@@ -1606,7 +1613,13 @@ function handleMedicalAdvice(row) {
adviceType: item.adviceType || item.advice_type || null,
chargeItemId: item.chargeItemId || item.charge_item_id || null,
definitionId: item.definitionId || item.definition_id || null,
definitionDetailId: item.definitionDetailId || item.definition_detail_id || null
definitionDetailId: item.definitionDetailId || item.definition_detail_id || null,
// 🔧 修复:传递 requestId临时医嘱签署时需要用于更新已有记录而非新建
requestId: item.requestId || null,
// 🔧 保留原始 contentJson临时医嘱签发时需要展开所有字段传给后端
contentJson: item.contentJson || item.content_json || null,
// 🔧 标记是否已签发,用于临时医嘱弹窗控制按钮状态
_signed: item.statusEnum === 2
};
}
});
@@ -1694,73 +1707,134 @@ function closeTemporaryMedical() {
// 处理临时医嘱提交
// 🔧 修复:提交成功后,更新 temporaryAdvices 中的 requestId以便下次提交时执行更新操作
function handleTemporaryMedicalSubmit(data) {
// 🔧 修复:使用用户修改后的数据,而不是重新加载数据
// 这样可以确保用户修改的内容(如剂量)在保存后仍然正确显示
if (data.temporaryAdvices && data.temporaryAdvices.length > 0) {
// 🔧 关键修复:更新 temporaryAdvices 中的数据,保留用户的修改
// 但是需要从后端返回的数据中获取 requestId以便下次提交时执行更新操作
// 如果后端返回了医嘱IDrequestId我们需要更新到 temporaryAdvices 中
temporaryAdvices.value = data.temporaryAdvices.map((advice, index) => {
const originalMedicine = advice.originalMedicine || {}
// 🔧 关键修复:从后端返回的数据中获取 requestId如果有
// 后端返回的医嘱ID可能需要通过某个字段获取这里暂时假设后端会返回
// 如果后端返回的 response 中包含医嘱ID需要更新到 originalMedicine 中
// 这里暂时保留原逻辑,等待用户提供后端返回的具体数据结构
return {
...advice,
// 确保 originalMedicine 包含所有必要字段
originalMedicine: {
...originalMedicine,
// 保留用户的修改
contentJson: originalMedicine.contentJson || JSON.stringify({
dose: advice.dosage,
methodCode: advice.usage,
rateCode: advice.frequency,
quantity: advice.quantity,
totalPrice: advice.totalPrice
// 🔧 Bug #445 修复:提交成功后重新拉取数据,确保"待生成"列表正确更新
if (data.patientInfo && data.patientInfo.visitId) {
const row = { visitId: data.patientInfo.visitId, operCode: data.patientInfo.operCode }
temporarySigned.value = true
ElMessage.success('临时医嘱已生成(已签发)')
// 重新拉取最新数据,后端已将 statusEnum 从 1(草稿) 更新为 2(已签发)
getPrescriptionList(row.visitId, 6, row.operCode).then((res) => {
if (res.code === 200 && res.data) {
const seenIds = new Set()
const filteredItems = res.data.filter(item => {
if (item.encounterId !== row.visitId) return false
const at = Number(item.adviceType ?? item.advice_type)
if (at !== 1 && at !== 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
const itemId = item.requestId || item.id
if (itemId && seenIds.has(itemId)) return false
if (itemId) seenIds.add(itemId)
return true
})
const draftItems = filteredItems.filter(item => item.statusEnum === 1)
const activeItems = filteredItems.filter(item => item.statusEnum === 2)
// 更新待生成列表:只保留未生成的项目
temporaryBillingMedicines.value = draftItems.map(item => {
try {
const jsonContent = item.contentJson || item.content_json
const contentData = jsonContent ? JSON.parse(jsonContent) : {}
return {
medicineName: contentData.adviceName || contentData.advice_name || item.adviceName || item.advice_name || item.chargeName || item.charge_name || contentData.itemName || contentData.item_name || '未知药品',
specification: contentData.volume || contentData.specification || item.volume || item.specification || '',
quantity: contentData.quantity || item.quantity || 0,
batchNumber: contentData.lotNumber || contentData.lot_number || item.lotNumber || item.lot_number || '',
unitPrice: contentData.unitPrice || contentData.unit_price || item.unitPrice || item.unit_price || 0,
subtotal: contentData.totalPrice || contentData.total_price || item.totalPrice || item.total_price ||
(contentData.unitPrice || contentData.unit_price || item.unitPrice || item.unit_price || 0) *
(contentData.quantity || item.quantity || 0),
insuranceType: (contentData.insuranceType || contentData.insurance_type) === 1 ? '医保' : (item.insuranceType === 1 || item.insurance_type === 1) ? '医保' : '自费',
adviceDefinitionId: item.adviceDefinitionId || contentData.adviceDefinitionId || item.advice_definition_id || null,
adviceTableName: item.adviceTableName || contentData.adviceTableName || item.advice_table_name || null,
adviceType: item.adviceType || contentData.adviceType || item.advice_type || null,
chargeItemId: item.chargeItemId || contentData.chargeItemId || item.charge_item_id || null,
definitionId: item.definitionId || contentData.definitionId || item.definition_id || null,
definitionDetailId: item.definitionDetailId || contentData.definitionDetailId || item.definition_detail_id || null,
// 🔧 修复:传递 requestId
requestId: item.requestId || null,
// 🔧 保留原始 contentJson临时医嘱签发时需要展开所有字段传给后端
contentJson: item.contentJson || item.content_json || null,
// 🔧 标记是否已签发,用于临时医嘱弹窗控制按钮状态
_signed: item.statusEnum === 2
}
} catch (e) {
return {
medicineName: item.adviceName || item.advice_name || item.chargeName || '',
specification: item.specification || item.volume || '',
quantity: item.quantity || item.quantity_value || item.quantityValue || 0,
batchNumber: item.lotNumber || item.lot_number || '',
unitPrice: item.unitPrice || item.unit_price || 0,
subtotal: item.totalPrice || item.total_price || 0,
insuranceType: (item.insuranceType || item.insurance_type) === 1 ? '医保' : '自费',
adviceDefinitionId: item.adviceDefinitionId || item.advice_definition_id || null,
adviceTableName: item.adviceTableName || item.advice_table_name || null,
adviceType: item.adviceType || item.advice_type || null,
chargeItemId: item.chargeItemId || item.charge_item_id || null,
definitionId: item.definitionId || item.definition_id || null,
definitionDetailId: item.definitionDetailId || item.definition_detail_id || null,
// 🔧 修复:传递 requestId
requestId: item.requestId || null,
// 🔧 保留原始 contentJson临时医嘱签发时需要展开所有字段传给后端
contentJson: item.contentJson || item.content_json || null,
// 🔧 标记是否已签发,用于临时医嘱弹窗控制按钮状态
_signed: item.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)
let usageCode = contentData.methodCode || 'iv'
return {
id: index + 1, adviceName: medicineName, dosage, unit: specUnit,
usage: usageCode, frequency: '临时',
executeTime: new Date().toLocaleString('zh-CN'),
originalMedicine: { ...item, medicineName, specification: spec, quantity: contentData.quantity || item.quantity || 1, encounterId: row.visitId }
}
} catch (e) {
return {
id: index + 1, adviceName: item.adviceName || '', dosage: 1, unit: 'ml',
usage: 'iv', frequency: '临时', executeTime: new Date().toLocaleString('zh-CN'),
originalMedicine: { ...item, medicineName: item.adviceName || '', specification: item.volume || '', quantity: item.quantity || 1, encounterId: row.visitId }
}
}
})
}
}).catch(() => {
// 拉取失败时使用本地过滤作为兜底
if (data.temporaryAdvices && data.temporaryAdvices.length > 0) {
const submittedKeys = new Set(
data.temporaryAdvices.map(a => {
const om = a.originalMedicine || {}
return `${om.medicineName || a.adviceName || ''}|||${om.specification || ''}|||${om.quantity || 0}`
}).filter(k => k !== '|||0')
)
if (submittedKeys.size > 0) {
temporaryBillingMedicines.value = (temporaryBillingMedicines.value || []).filter(m => {
return !submittedKeys.has(`${m.medicineName || ''}|||${m.specification || ''}|||${m.quantity || 0}`)
})
}
temporaryAdvices.value = data.temporaryAdvices
}
})
// 🔧 修复 Bug #445: 使用稳定的字段组合匹配已提交项目,而不是依赖可能为空的 requestId/chargeItemId
// 构建已提交项目的匹配键集合(药品名称 + 规格 + 数量)
const submittedKeys = new Set(
(data.temporaryAdvices || [])
.map(a => {
const om = a.originalMedicine || {}
const name = om.medicineName || om.adviceName || om.advice_name || a.adviceName || ''
const spec = om.specification || om.volume || ''
const qty = om.quantity || 0
return `${name}|||${spec}|||${qty}`
})
.filter(k => k !== '|||0') // 过滤掉空项
)
if (submittedKeys.size > 0) {
temporaryBillingMedicines.value = (temporaryBillingMedicines.value || []).filter(m => {
const key = `${m.medicineName || ''}|||${m.specification || ''}|||${m.quantity || 0}`
return !submittedKeys.has(key)
})
} else {
// 如果没有任何匹配键,清空待生成列表(所有项目都已提交)
temporaryBillingMedicines.value = []
}
} else {
// 如果没有传递数据,则清空
temporaryAdvices.value = []
temporaryBillingMedicines.value = []
}
// 🔧 设置签名状态,保持按钮名称一致
temporarySigned.value = true
// 显示成功提示,不关闭弹窗,让用户可以查看已签发的医嘱状态
ElMessage.success('临时医嘱已生成(已签发),可继续查看或修改')
}
function handleTemporaryMedicalCancel() {
// 🔧 修复:用户点击取消时才清空数据,因为用户可能要放弃修改
@@ -1911,7 +1985,13 @@ function handleQuoteBilling() {
orgId: contentData.orgId || item.orgId || contentData.positionId || item.positionId || userStore.orgId,
positionId: contentData.positionId || item.positionId || userStore.orgId,
definitionId: contentData.definitionId || item.definitionId,
definitionDetailId: contentData.definitionDetailId || item.definitionDetailId
definitionDetailId: contentData.definitionDetailId || item.definitionDetailId,
// 🔧 修复:传递 requestId
requestId: item.requestId || null,
// 🔧 保留原始 contentJson临时医嘱签发时需要展开所有字段传给后端
contentJson: item.contentJson || item.content_json || null,
// 🔧 标记是否已签发,用于临时医嘱弹窗控制按钮状态
_signed: item.statusEnum === 2
}
} catch (e) {
return {
@@ -1927,7 +2007,13 @@ function handleQuoteBilling() {
orgId: item.orgId || item.positionId || userStore.orgId,
positionId: item.positionId || userStore.orgId,
definitionId: item.definitionId,
definitionDetailId: item.definitionDetailId
definitionDetailId: item.definitionDetailId,
// 🔧 修复:传递 requestId
requestId: item.requestId || null,
// 🔧 保留原始 contentJson临时医嘱签发时需要展开所有字段传给后端
contentJson: item.contentJson || item.content_json || null,
// 🔧 标记是否已签发,用于临时医嘱弹窗控制按钮状态
_signed: item.statusEnum === 2
}
}
})