6.0 KiB
Bug #439 分析报告
Bug描述
领用出库:选择领用药品后"总库存数量"列数据未显示
数据流分析
- 用户点击"添加行" → 新增一行,totalQuantity 初始化为空字符串 ''
- 用户在"项目"列通过 PopoverList 选择药品 → 触发
selectRow(rowValue, index) selectRow设置药品基本信息,然后调用handleLocationClick(1, rowValue, index)handleLocationClick调用getCount({ itemId, orgLocationId })获取库存getCount返回 LocationInventoryDto[] 列表,前端通过pickBestOrgQuantityRow选最大值applyFromDto设置r.totalQuantity = d.orgQuantity || 0
根因定位
在 selectRow 函数中(第1022-1049行),选择药品后:
form.purchaseinventoryList[index].unitList = rowValue.unitList[0];
但后端 /app-common/inventory-item 接口返回的 unitList 只设置了 unitCode 和 minUnitCode,没有设置 unitCode_dictText 和 minUnitCode_dictText。
在 handleLocationClick → applyFromDto 中(第1099-1121行):
r.unitCode = r.unitList.minUnitCode;
r.unitCode_dictText = r.unitList.minUnitCode_dictText; // ← undefined!
if (r.unitCode == r.unitList.minUnitCode) { // ← 这个条件始终为 true
r.price = d.price / r.partPercent || '';
r.price = r.price.toFixed(4);
}
关键问题:r.unitCode 刚被设为 r.unitList.minUnitCode,然后条件 r.unitCode == r.unitList.minUnitCode 始终为 true,
导致即使价格很小(如 0.05/1=0.05),也会进入这个分支。
但这不是总库存数量未显示的根本原因。
真正根因:handleLocationClick 函数在调用 getCount 获取库存数据后,applyFromDto 中 r.totalQuantity = d.orgQuantity || 0 的赋值逻辑依赖 d.orgQuantity > 0 的前置判断。
查看前端代码流程:
selectRow设置totalQuantity: ''(新增行时的默认值)- 然后调用
handleLocationClick→getCount→ 后端返回数据 pickBestOrgQuantityRow从返回列表中选出 orgQuantity 最大的记录- 如果
d && Number(d.orgQuantity ?? 0) > 0→ 调用applyFromDto→ 设置r.totalQuantity = d.orgQuantity || 0 - 如果条件不满足(所有记录 orgQuantity 都为 0 或返回空列表)→
applyFromDto不被调用 →r.totalQuantity保持空字符串 ''
进一步分析发现:
- 如果后端
getCount返回空列表(该药品在该仓库无库存),d为 null,applyFromDto不会被调用 - 但如果该药品在仓库确实有库存,问题可能出在前端数据传递上
核心问题在于 unitList 结构不完整:
selectRow 中 rowValue.unitList 来自药品列表查询结果,其 unitList 由后端 CommonServiceImpl.getInventoryItemList 构建,
只包含 unitCode 和 minUnitCode,缺少 unitCode_dictText 和 minUnitCode_dictText。
在 handleLocationClick 的 applyFromDto 中,r.unitCode 和 r.unitCode_dictText 的赋值依赖于 unitList 中的字段。
如果 r.unitList 是从 rowValue.unitList[0] 赋值而来(在 selectRow 中),那它应该至少有 unitCode 和 minUnitCode。
但是! 编辑模式(getTransferProductDetails)中,unitList 的构建方式不同:
form.purchaseinventoryList[index].unitList = e.unitList[0]; // 编辑详情时
新增模式(selectRow)中:
form.purchaseinventoryList[index].unitList = rowValue.unitList[0];
两种方式获取的 unitList 结构可能不同。
根本原因:
handleLocationClick 中的 getCount API 调用,返回的 LocationInventoryDto 确实包含 orgQuantity。
前端通过 pickBestOrgQuantityRow 选出最大值的记录后,调用 applyFromDto 设置 totalQuantity。
如果药品在仓库有库存但 totalQuantity 仍为空白,说明 applyFromDto 中的 d.orgQuantity 可能为 null/undefined。
经检查 selectInventoryItemInfo SQL:
SUM(CASE WHEN T1.location_id = #{orgLocationId} THEN T1.quantity ELSE 0 END) AS org_quantity
当 objLocationId 为 null/空时,WHERE 子句为:
AND T1.location_id = #{orgLocationId}
这意味着查询结果中的所有记录都来自 orgLocationId 对应的仓库。
此时 org_quantity 应该等于 SUM(T1.quantity)。
如果查询结果为空(该药品在该仓库没有库存记录),则前端 d 为 null,applyFromDto 不被调用,totalQuantity 保持空字符串。
但 Bug 的期望是"应实时检索并填充总库存数量"——如果仓库确实没有该药品的库存,那显示空白是合理的。 但如果仓库有库存却未显示,说明前端传递的参数(orgLocationId 或 itemId)有问题。
最终根因:前端 handleLocationClick 函数中,orgLocationId 的取值可能为空字符串,
导致后端查询时使用空字符串作为 location_id 条件,查不到任何记录。
let orgLocationId = r.sourceLocationId || receiptHeaderForm.headerLocationId || '';
虽然 Bug 步骤中说先选了"西药库",但如果 receiptHeaderForm.headerLocationId 在 selectRow 时已正确设置,
r.sourceLocationId 也应该被设置(在 selectRow 第1037行):
form.purchaseinventoryList[index].sourceLocationId =
receiptHeaderForm.headerLocationId || form.purchaseinventoryList[index].sourceLocationId || '';
但这里有一个微妙的时序问题:handleLocationClick 在 getPharmacyCabinetList().then() 内部被调用,
但 handleLocationClick 是同步执行的,不等待 getPharmacyCabinetList 完成。
这本身不影响 orgLocationId 的取值,因为 orgLocationId 不依赖 getPharmacyCabinetList。
修复方案
- 确保
applyFromDto即使在orgQuantity为 0 时也能被调用,正确显示"0"而不是空白 - 确保
unitList包含必要的字典文本字段
影响范围
- 前端文件:openhis-ui-vue3/src/views/medicationmanagement/requisitionManagement/requisitionManagement/index.vue
- 涉及函数:
selectRow、handleLocationClick