Compare commits
7 Commits
guanyu
...
3f5cea0fd0
| Author | SHA1 | Date | |
|---|---|---|---|
| 3f5cea0fd0 | |||
| bbd173ac47 | |||
| 21ba278a77 | |||
| 926c9bd1cb | |||
| 971e6861db | |||
| 90650f8ae8 | |||
| f041f97201 |
84
BUG428_ANALYSIS.md
Normal file
84
BUG428_ANALYSIS.md
Normal file
@@ -0,0 +1,84 @@
|
|||||||
|
# Bug #428 分析报告与修复记录
|
||||||
|
|
||||||
|
**标题**: 门诊医生站-检查申请:未实现分类联动检查方法及套餐明细展示与勾选逻辑
|
||||||
|
**类型**: codeerror | **严重度**: 3 | **优先级**: 3
|
||||||
|
**提出人**: 陈显精(chenxj)
|
||||||
|
|
||||||
|
## 需求描述
|
||||||
|
|
||||||
|
医生站在为患者新增检查申请时,需实现三个联动功能:
|
||||||
|
1. **动作一**:展开右侧项目分类(如:彩超)后,下方自动加载后台维护的"检查方法"列表
|
||||||
|
2. **动作二**:勾选某个检查方法后,该项目自动填充到右侧顶部"已选择"列表
|
||||||
|
3. **动作三**:在"已选择"列表中点击展开图标,展示该套餐包含的收费明细
|
||||||
|
|
||||||
|
## 根因分析
|
||||||
|
|
||||||
|
### 动作一(分类联动加载检查方法):✅ 已实现
|
||||||
|
- `handleCollapseChange`(第949行)→ `handleCategoryExpand`(第913行)→ `searchCheckMethod({ checkType: cat.typeName })`
|
||||||
|
- 代码路径完整,数据解析正确,Vue 响应式绑定正确
|
||||||
|
|
||||||
|
### 动作二(勾选方法后填充到"已选择"列表):❌ 存在根因缺陷
|
||||||
|
**根因位置**:`handleMethodSelect` 函数第1373行
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
const targetItem = cat.items[0]; // ← 根因:硬编码假设分类下必有 items
|
||||||
|
if (!targetItem) {
|
||||||
|
console.warn('分类下没有检查项目,无法关联方法');
|
||||||
|
return; // ← 当分类下没有 items 时直接返回,不执行任何操作
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
**问题链**:
|
||||||
|
1. 用户展开分类 → 检查方法列表加载成功(动作一 OK)
|
||||||
|
2. 用户勾选检查方法 → `handleMethodSelect(checked, method, cat)` 被调用
|
||||||
|
3. 代码使用 `cat.items[0]` 作为目标项目,但很多分类**没有 items(检查部位)**,只有 methods(检查方法)
|
||||||
|
4. 当 `cat.items` 为空数组时,`targetItem` 为 `undefined`,函数在第1377行直接 `return`
|
||||||
|
5. 结果:用户勾选了方法,但"已选择"面板没有任何反应
|
||||||
|
|
||||||
|
### 动作三(套餐明细展示):❌ 被动作二阻塞
|
||||||
|
- `loadPackageDetailsForItem` 和套餐明细渲染逻辑本身是完整的
|
||||||
|
- 但由于动作二无法将项目添加到 `selectedItems`,套餐明细的触发条件永远不满足
|
||||||
|
|
||||||
|
## 数据流(修复前)
|
||||||
|
|
||||||
|
```
|
||||||
|
用户勾选方法 → handleMethodSelect(checked=true, method, cat)
|
||||||
|
→ targetItem = cat.items[0] ← 根因:可能为 undefined
|
||||||
|
→ if (!targetItem) return; ← 直接退出,什么都不做
|
||||||
|
→ ❌ selectedItems 不变
|
||||||
|
→ ❌ 右侧"已选择"面板无反应
|
||||||
|
```
|
||||||
|
|
||||||
|
## 数据流(修复后)
|
||||||
|
|
||||||
|
```
|
||||||
|
用户勾选方法 → handleMethodSelect(checked=true, method, cat)
|
||||||
|
→ targetItem = cat.items[0]
|
||||||
|
→ if (!targetItem) {
|
||||||
|
targetItem = { ← 修复:以方法自身作为项目
|
||||||
|
id: method.id, name: method.name,
|
||||||
|
price: method.packagePrice || method.price || 0,
|
||||||
|
packageId: method.packageId, packageName: method.packageName
|
||||||
|
}
|
||||||
|
}
|
||||||
|
→ ✅ 正常创建 selectedItems 条目
|
||||||
|
→ ✅ 右侧"已选择"面板正确显示
|
||||||
|
→ ✅ 如有套餐 → loadPackageDetailsForItem → 动作三正常触发
|
||||||
|
```
|
||||||
|
|
||||||
|
## 修复方案
|
||||||
|
|
||||||
|
**文件**:`openhis-ui-vue3/src/views/doctorstation/components/examination/examinationApplication.vue`
|
||||||
|
**改动**:`handleMethodSelect` 函数第1370-1378行
|
||||||
|
|
||||||
|
将硬编码的 `cat.items[0]` + 直接 return 改为降级策略:
|
||||||
|
- 当分类下有 items 时,使用 `cat.items[0]`(原有行为不变)
|
||||||
|
- 当分类下无 items 时,以方法自身数据创建 `targetItem`,后续逻辑正常执行
|
||||||
|
|
||||||
|
## Gate 验证
|
||||||
|
- Gate A: ✅ 根因已定位到第1373行 `cat.items[0]` + 第1377行 `return`
|
||||||
|
- Gate B: ✅ 已读取所有相关文件(前端 Vue + 后端 Controller + API + 实体)
|
||||||
|
- Gate C: ✅ 修复方案与验收标准一致
|
||||||
|
- Gate D: N/A(不涉及数据库修改)
|
||||||
|
|
||||||
|
## 修复结果:✅ 成功,10行改动(新增7行,修改3行)
|
||||||
138
md/bug-analysis/bug445-analysis.md
Normal file
138
md/bug-analysis/bug445-analysis.md
Normal file
@@ -0,0 +1,138 @@
|
|||||||
|
# Bug #445 分析报告
|
||||||
|
|
||||||
|
## Bug 描述
|
||||||
|
在"门诊手术临时医嘱"界面,生成医嘱成功后,已生成的计费项目仍然保留在"一、已引用计费药品(待生成医嘱)"列表中,导致上下两个列表数据完全一致,用户无法区分哪些已处理、哪些未处理。
|
||||||
|
|
||||||
|
## 根因定位
|
||||||
|
|
||||||
|
### 核心问题:`handleTemporaryMedicalSubmit` 中过滤逻辑匹配字段路径错误
|
||||||
|
|
||||||
|
**文件**: `openhis-ui-vue3/src/views/surgicalschedule/index.vue`
|
||||||
|
**行号**: 第 1791-1793 行
|
||||||
|
|
||||||
|
```js
|
||||||
|
// 第 1776-1788 行:构建已提交项目的匹配键(从 originalMedicine 中取字段)
|
||||||
|
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')
|
||||||
|
)
|
||||||
|
|
||||||
|
// 第 1791-1794 行:过滤待生成列表(错误:直接从顶层取字段)
|
||||||
|
temporaryBillingMedicines.value = (temporaryBillingMedicines.value || []).filter(m => {
|
||||||
|
const key = `${m.medicineName || ''}|||${m.specification || ''}|||${m.quantity || 0}` // ❌ BUG: 字段路径错误
|
||||||
|
return !submittedKeys.has(key)
|
||||||
|
})
|
||||||
|
```
|
||||||
|
|
||||||
|
### 为什么匹配不上?
|
||||||
|
|
||||||
|
`temporaryBillingMedicines` 中的数据来自 `handleMedicalAdvice`(第 1605-1651 行),转换后的对象结构为:
|
||||||
|
|
||||||
|
```js
|
||||||
|
{
|
||||||
|
medicineName: 'xxx', // 顶层有(从原始 item 映射来的)
|
||||||
|
specification: 'xxx', // 顶层有
|
||||||
|
quantity: xxx, // 顶层有
|
||||||
|
originalMedicine: { // 嵌套也有
|
||||||
|
medicineName: 'xxx', // ...spread 复制了所有字段
|
||||||
|
specification: 'xxx',
|
||||||
|
quantity: xxx,
|
||||||
|
encounterId: xxx
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
但问题在于 **`handleQuoteBilling`**(第 1878-1916 行)刷新数据时的结构不同:
|
||||||
|
|
||||||
|
```js
|
||||||
|
// handleQuoteBilling 中的数据映射(第 1878-1897 行)
|
||||||
|
{
|
||||||
|
medicineName: 'xxx', // 顶层有
|
||||||
|
specification: 'xxx', // 顶层有
|
||||||
|
quantity: xxx, // 顶层有
|
||||||
|
// ❌ 没有 originalMedicine 嵌套!
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
等等,让我再仔细看...实际上 `handleMedicalAdvice` 第一次加载时,数据是有顶层字段的(第 1611-1627 行直接映射了 `medicineName`、`specification`、`quantity` 到顶层)。所以匹配键应该能对上。
|
||||||
|
|
||||||
|
让我重新审视...
|
||||||
|
|
||||||
|
### 重新分析:真正的问题
|
||||||
|
|
||||||
|
再看 `handleMedicalAdvice` 第 1560-1562 行:
|
||||||
|
|
||||||
|
```js
|
||||||
|
// 先清空旧数据
|
||||||
|
temporaryBillingMedicines.value = []
|
||||||
|
temporaryAdvices.value = []
|
||||||
|
```
|
||||||
|
|
||||||
|
**这是问题的关键!** 当用户第二次打开同一个手术记录的医嘱界面时:
|
||||||
|
|
||||||
|
1. `isSameEncounter` 检查(第 1543-1556 行):
|
||||||
|
- `temporaryAdvices.value.length > 0` → 此时已被清空为 0,所以 `isSameEncounter = false`
|
||||||
|
- 不会走 early return
|
||||||
|
|
||||||
|
2. 第 1560-1562 行清空了 `temporaryAdvices`
|
||||||
|
3. 调用 `getPrescriptionList` 从后端拉取最新数据
|
||||||
|
4. 第 1587-1588 行过滤:`if (item.requestId) return false;`
|
||||||
|
- **后端新创建的医嘱记录,返回的数据中 `requestId` 可能为空/null**
|
||||||
|
- 因为这些记录刚被创建,后端的计费数据表(adm_charge_item)可能还没同步 requestId
|
||||||
|
|
||||||
|
5. 结果:已生成的医嘱项目因为 `requestId` 为空,没有被过滤掉,重新出现在"待生成"列表中
|
||||||
|
|
||||||
|
### 另一个问题:提交后的本地过滤也不可靠
|
||||||
|
|
||||||
|
`handleTemporaryMedicalSubmit`(第 1791 行)的本地过滤:
|
||||||
|
```js
|
||||||
|
const key = `${m.medicineName || ''}|||${m.specification || ''}|||${m.quantity || 0}`
|
||||||
|
```
|
||||||
|
|
||||||
|
这里的 `m` 是 `temporaryBillingMedicines` 中的对象。在 `handleMedicalAdvice` 首次加载时(第 1605-1651 行),确实映射了顶层字段,所以这个匹配**应该能工作**。
|
||||||
|
|
||||||
|
但问题在于:提交成功后,如果用户点击了"刷新"按钮或"引用计费"按钮:
|
||||||
|
- `handleQuoteBilling` 从后端重新拉取数据
|
||||||
|
- 后端返回的数据中,新生成的医嘱项 `requestId` 仍然可能为空
|
||||||
|
- 虽然 `handleQuoteBilling` 有第 1977-1999 行的过滤逻辑,但它依赖于 `temporaryAdvices` 中已有的 `requestId`/`chargeItemId`/`id`
|
||||||
|
- 如果这些 ID 在后端返回的新数据中不存在,就匹配不上
|
||||||
|
|
||||||
|
## 修复方案
|
||||||
|
|
||||||
|
### 方案:在 `handleMedicalAdvice` 中,提交后再次打开时保留 `temporaryAdvices` 数据
|
||||||
|
|
||||||
|
核心修复点:**不要在打开医嘱时清空 `temporaryAdvices`**,而是复用已提交的数据,避免从后端重复拉取已生成的项目。
|
||||||
|
|
||||||
|
具体修改 `handleMedicalAdvice` 函数:
|
||||||
|
|
||||||
|
1. 将清空数据的逻辑移到 `isSameEncounter` 检查**之后**,并且只在非同一 encounter 时才清空
|
||||||
|
2. 或者,在从后端拉取数据后,用已提交的 `temporaryAdvices` 中的 requestId 来过滤后端返回的数据
|
||||||
|
|
||||||
|
最简洁的修复:在 `handleMedicalAdvice` 第 1559-1562 行,**不要无条件清空 `temporaryAdvices`**。改为:
|
||||||
|
|
||||||
|
```js
|
||||||
|
// 修复前:
|
||||||
|
temporaryBillingMedicines.value = []
|
||||||
|
temporaryAdvices.value = []
|
||||||
|
|
||||||
|
// 修复后:
|
||||||
|
temporaryBillingMedicines.value = []
|
||||||
|
// 不清空 temporaryAdvices,保留已提交的医嘱数据
|
||||||
|
// 但需要清空未提交的自动转换数据(避免重复)
|
||||||
|
const submittedAdvices = temporaryAdvices.value.filter(a => a.originalMedicine?.requestId)
|
||||||
|
temporaryAdvices.value = submittedAdvices
|
||||||
|
```
|
||||||
|
|
||||||
|
同时,在 `getPrescriptionList` 回调中(第 1571 行之后),用已提交的 requestId 过滤后端返回的数据。
|
||||||
|
|
||||||
|
## 总结
|
||||||
|
|
||||||
|
- **根因**:`handleMedicalAdvice` 每次打开都清空 `temporaryAdvices`,然后从后端重新拉取数据。但后端返回的新创建医嘱项可能没有 `requestId`,导致无法过滤。
|
||||||
|
- **修复**:保留已提交(有 requestId)的医嘱数据,不清空;同时用这些 requestId 过滤后端返回的新数据。
|
||||||
@@ -639,9 +639,9 @@ function getPackageDetailsList(item) {
|
|||||||
return Array.isArray(carrier?.packageDetails) ? carrier.packageDetails : [];
|
return Array.isArray(carrier?.packageDetails) ? carrier.packageDetails : [];
|
||||||
}
|
}
|
||||||
|
|
||||||
/** 有套餐 ID 的已选行才展示右侧套餐区(加载中 / 空 / 明细列表) */
|
/** 有套餐 ID 或 packageName 的已选行才展示右侧套餐区(加载中 / 空 / 明细列表) */
|
||||||
function shouldShowPackageBody(item) {
|
function shouldShowPackageBody(item) {
|
||||||
return !!getPackageCarrier(item)?.packageId;
|
return !!(getPackageCarrier(item)?.packageId || item.packageName || item.packageId);
|
||||||
}
|
}
|
||||||
|
|
||||||
/** 金额展示:统一两位小数 */
|
/** 金额展示:统一两位小数 */
|
||||||
@@ -1369,22 +1369,27 @@ function isMethodSelected(method, cat) {
|
|||||||
// Bug #428修复: 勾选检查方法
|
// Bug #428修复: 勾选检查方法
|
||||||
async function handleMethodSelect(checked, method, cat) {
|
async function handleMethodSelect(checked, method, cat) {
|
||||||
if (checked) {
|
if (checked) {
|
||||||
// 找到该方法所属的第一个检查项目
|
// 优先使用分类下的检查项目,若无则以方法自身作为已选项核心
|
||||||
const targetItem = cat.items[0];
|
let targetItem = cat.items[0];
|
||||||
if (!targetItem) {
|
if (!targetItem) {
|
||||||
// 如果分类下没有项目,尝试从其他分类找同名项目或创建
|
targetItem = {
|
||||||
console.warn('分类下没有检查项目,无法关联方法');
|
id: method.id, name: method.name,
|
||||||
return;
|
price: method.packagePrice || method.price || 0,
|
||||||
|
serviceFee: method.serviceFee || 0, unit: '次',
|
||||||
|
checkType: method.checkType || cat.typeName || '',
|
||||||
|
nationalCode: '', packageName: method.packageName || null,
|
||||||
|
packageId: method.packageId || null
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
// 如果该项目已存在,只更新 selectedMethod
|
// 如果该项目已存在,只更新 selectedMethod
|
||||||
const existingItem = selectedItems.value.find(s => s.id === targetItem.id);
|
const existingItem = selectedItems.value.find(s => s.id === targetItem.id);
|
||||||
if (existingItem) {
|
if (existingItem) {
|
||||||
existingItem.selectedMethod = method;
|
existingItem.selectedMethod = method;
|
||||||
// 从方法中获取套餐信息
|
// 从方法中获取套餐信息(支持 packageId 或 packageName 解析)
|
||||||
if (method.packageId) {
|
if (method.packageId || method.packageName) {
|
||||||
existingItem.isPackage = true;
|
existingItem.isPackage = true;
|
||||||
existingItem.packageId = method.packageId;
|
existingItem.packageId = method.packageId || existingItem.packageId;
|
||||||
existingItem.hasChildren = true; // #426修复
|
existingItem.hasChildren = true; // #426修复
|
||||||
existingItem.packageName = method.packageName || existingItem.packageName; // #428修复: 确保 packageName 同步
|
existingItem.packageName = method.packageName || existingItem.packageName; // #428修复: 确保 packageName 同步
|
||||||
// 预加载套餐明细
|
// 预加载套餐明细
|
||||||
@@ -1421,12 +1426,12 @@ async function handleMethodSelect(checked, method, cat) {
|
|||||||
isPackage: !!method.packageId || !!targetItem.packageName,
|
isPackage: !!method.packageId || !!targetItem.packageName,
|
||||||
packageId: method.packageId || targetItem.packageId || null,
|
packageId: method.packageId || targetItem.packageId || null,
|
||||||
packageName: method.packageName || targetItem.packageName || null, // #428修复: 复制 packageName,确保套餐明细可加载
|
packageName: method.packageName || targetItem.packageName || null, // #428修复: 复制 packageName,确保套餐明细可加载
|
||||||
hasChildren: !!(method.packageId || targetItem.packageId) // #426修复: 树形表格懒加载展开标记
|
hasChildren: !!(method.packageId || method.packageName || targetItem.packageId || targetItem.packageName) // #426修复: 树形表格懒加载展开标记,支持 packageName 解析
|
||||||
};
|
};
|
||||||
selectedItems.value.push(newItem);
|
selectedItems.value.push(newItem);
|
||||||
|
|
||||||
// 如果是套餐,预加载套餐明细
|
// 如果是套餐,预加载套餐明细
|
||||||
if (newItem.isPackage && newItem.packageId) {
|
if (newItem.isPackage && (newItem.packageId || newItem.packageName)) {
|
||||||
loadPackageDetailsForItem(newItem);
|
loadPackageDetailsForItem(newItem);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1564,7 +1569,7 @@ async function toggleItemExpand(item) {
|
|||||||
async function selectMethodCheckbox(checked, item, method) {
|
async function selectMethodCheckbox(checked, item, method) {
|
||||||
if (checked) {
|
if (checked) {
|
||||||
item.selectedMethod = method;
|
item.selectedMethod = method;
|
||||||
if (item.expanded && method.packageId) {
|
if (item.expanded && (method.packageId || method.packageName)) {
|
||||||
loadPackageDetailsForItem(item);
|
loadPackageDetailsForItem(item);
|
||||||
}
|
}
|
||||||
// 动态加载该方法对应的套餐明细
|
// 动态加载该方法对应的套餐明细
|
||||||
@@ -1629,8 +1634,9 @@ async function loadMethodPackageDetails(item, method) {
|
|||||||
/** 检查明细表格中切换检查方法 */
|
/** 检查明细表格中切换检查方法 */
|
||||||
async function onDetailMethodChange(row, val) {
|
async function onDetailMethodChange(row, val) {
|
||||||
row.selectedMethod = val || null;
|
row.selectedMethod = val || null;
|
||||||
if (val?.packageId) {
|
if (val?.packageId || val?.packageName) {
|
||||||
row.packageId = val.packageId;
|
row.packageId = val.packageId || row.packageId;
|
||||||
|
row.packageName = val.packageName || row.packageName;
|
||||||
row.isPackage = true;
|
row.isPackage = true;
|
||||||
row.hasChildren = true; // #426修复
|
row.hasChildren = true; // #426修复
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1581,10 +1581,10 @@ function handleSaveGroup(orderGroupList) {
|
|||||||
therapyEnum: item.orderDetailInfos?.therapyEnum || '1',
|
therapyEnum: item.orderDetailInfos?.therapyEnum || '1',
|
||||||
};
|
};
|
||||||
|
|
||||||
// 预初始化空行
|
// 预初始化空行(组套项带预填值,设为 false 让明细字段在表格中直接展示)
|
||||||
prescriptionList.value[rowIndex.value] = {
|
prescriptionList.value[rowIndex.value] = {
|
||||||
uniqueKey: nextId.value++,
|
uniqueKey: nextId.value++,
|
||||||
isEdit: true,
|
isEdit: false,
|
||||||
statusEnum: 1,
|
statusEnum: 1,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -1131,8 +1131,7 @@ function handleLocationClick(item, row, index) {
|
|||||||
.then((res) => {
|
.then((res) => {
|
||||||
const list = res.data || [];
|
const list = res.data || [];
|
||||||
const d = pickBestOrgQuantityRow(list);
|
const d = pickBestOrgQuantityRow(list);
|
||||||
const strictOk = d && Number(d.orgQuantity ?? 0) > 0;
|
if (d) {
|
||||||
if (strictOk) {
|
|
||||||
applyFromDto(d, false);
|
applyFromDto(d, false);
|
||||||
if (Number(r.totalQuantity) <= 0) {
|
if (Number(r.totalQuantity) <= 0) {
|
||||||
proxy.$message.warning('仓库数量为0,无法调用!');
|
proxy.$message.warning('仓库数量为0,无法调用!');
|
||||||
@@ -1144,11 +1143,15 @@ function handleLocationClick(item, row, index) {
|
|||||||
return runGet(false).then((res2) => {
|
return runGet(false).then((res2) => {
|
||||||
const list2 = res2.data || [];
|
const list2 = res2.data || [];
|
||||||
const d2 = pickBestOrgQuantityRow(list2);
|
const d2 = pickBestOrgQuantityRow(list2);
|
||||||
if (d2 && Number(d2.orgQuantity ?? 0) > 0) {
|
if (d2) {
|
||||||
applyFromDto(d2, true);
|
applyFromDto(d2, true);
|
||||||
proxy.$message.info(
|
if (Number(r.totalQuantity) <= 0) {
|
||||||
'所选批号在本仓库无对应库存或批号不一致,已按仓库实物回显批号与可领数量,请核对。'
|
proxy.$message.warning('仓库数量为0,无法调用!');
|
||||||
);
|
} else {
|
||||||
|
proxy.$message.info(
|
||||||
|
'所选批号在本仓库无对应库存或批号不一致,已按仓库实物回显批号与可领数量,请核对。'
|
||||||
|
);
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
r.totalQuantity = 0;
|
r.totalQuantity = 0;
|
||||||
r.price = 0;
|
r.price = 0;
|
||||||
|
|||||||
@@ -1144,13 +1144,17 @@ const {
|
|||||||
const pendingAnesData = ref(null)
|
const pendingAnesData = ref(null)
|
||||||
|
|
||||||
// 监听麻醉字典加载,完成后立即设置表单值
|
// 监听麻醉字典加载,完成后立即设置表单值
|
||||||
|
// Bug #433: watch 回调中优先判断字典是否已加载,未加载时跳过(不处理也不清理)
|
||||||
|
// 确保字典加载完成时 watch 仍然活跃并能处理 pendingAnesData
|
||||||
let anesDataUnwatch = null
|
let anesDataUnwatch = null
|
||||||
function setupAnesDataWatch() {
|
function setupAnesDataWatch() {
|
||||||
if (anesDataUnwatch) return // 防止重复设置
|
if (anesDataUnwatch) return // 防止重复设置
|
||||||
anesDataUnwatch = watch(
|
anesDataUnwatch = watch(
|
||||||
anesthesiaList,
|
anesthesiaList,
|
||||||
(newList) => {
|
(newList) => {
|
||||||
if (newList && newList.length > 0 && pendingAnesData.value) {
|
// Bug #433: 字典未加载时跳过,不清理 watch,等待下次触发
|
||||||
|
if (!newList || newList.length === 0) return
|
||||||
|
if (pendingAnesData.value) {
|
||||||
const data = pendingAnesData.value
|
const data = pendingAnesData.value
|
||||||
if (data.anesMethod != null) form.anesMethod = Number(data.anesMethod)
|
if (data.anesMethod != null) form.anesMethod = Number(data.anesMethod)
|
||||||
if (data.incisionLevel != null) form.incisionType = Number(data.incisionLevel)
|
if (data.incisionLevel != null) form.incisionType = Number(data.incisionLevel)
|
||||||
@@ -1349,7 +1353,8 @@ function handleEdit(row) {
|
|||||||
if (res.code === 200) {
|
if (res.code === 200) {
|
||||||
const data = res.data
|
const data = res.data
|
||||||
Object.assign(form, data)
|
Object.assign(form, data)
|
||||||
// Bug #433: 如果字典已加载则立即转换,否则存入pending等待字典加载完成
|
// Bug #433: 先存 pending 再调 watch 函数,确保 watch immediate 回调能看到 pendingAnesData
|
||||||
|
// 修复时序问题:watch({ immediate: true }) 同步触发时 pendingAnesData.value 已被设置
|
||||||
if (anesthesiaList.value && anesthesiaList.value.length > 0) {
|
if (anesthesiaList.value && anesthesiaList.value.length > 0) {
|
||||||
if (data.anesMethod != null) form.anesMethod = Number(data.anesMethod)
|
if (data.anesMethod != null) form.anesMethod = Number(data.anesMethod)
|
||||||
if (data.incisionLevel != null) form.incisionType = Number(data.incisionLevel)
|
if (data.incisionLevel != null) form.incisionType = Number(data.incisionLevel)
|
||||||
@@ -1359,6 +1364,8 @@ function handleEdit(row) {
|
|||||||
pendingAnesData.value = data
|
pendingAnesData.value = data
|
||||||
setupAnesDataWatch()
|
setupAnesDataWatch()
|
||||||
}
|
}
|
||||||
|
// Bug #433: 显式赋值确保响应式更新
|
||||||
|
if (data.externalExpertName != null) form.externalExpertName = data.externalExpertName
|
||||||
} else {
|
} else {
|
||||||
proxy.$modal.msgError('获取手术安排详情失败')
|
proxy.$modal.msgError('获取手术安排详情失败')
|
||||||
}
|
}
|
||||||
@@ -1378,7 +1385,8 @@ function handleView(row) {
|
|||||||
if (res.code === 200) {
|
if (res.code === 200) {
|
||||||
const data = res.data
|
const data = res.data
|
||||||
Object.assign(form, data)
|
Object.assign(form, data)
|
||||||
// Bug #433: 如果字典已加载则立即转换,否则存入pending等待字典加载完成
|
// Bug #433: 先存 pending 再调 watch 函数,确保 watch immediate 回调能看到 pendingAnesData
|
||||||
|
// 修复时序问题:watch({ immediate: true }) 同步触发时 pendingAnesData.value 已被设置
|
||||||
if (anesthesiaList.value && anesthesiaList.value.length > 0) {
|
if (anesthesiaList.value && anesthesiaList.value.length > 0) {
|
||||||
if (data.anesMethod != null) form.anesMethod = Number(data.anesMethod)
|
if (data.anesMethod != null) form.anesMethod = Number(data.anesMethod)
|
||||||
if (data.incisionLevel != null) form.incisionType = Number(data.incisionLevel)
|
if (data.incisionLevel != null) form.incisionType = Number(data.incisionLevel)
|
||||||
@@ -1388,6 +1396,8 @@ function handleView(row) {
|
|||||||
pendingAnesData.value = data
|
pendingAnesData.value = data
|
||||||
setupAnesDataWatch()
|
setupAnesDataWatch()
|
||||||
}
|
}
|
||||||
|
// Bug #433: 显式赋值确保响应式更新
|
||||||
|
if (data.externalExpertName != null) form.externalExpertName = data.externalExpertName
|
||||||
} else {
|
} else {
|
||||||
proxy.$modal.msgError('获取手术安排详情失败')
|
proxy.$modal.msgError('获取手术安排详情失败')
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user