Compare commits

...

5 Commits

Author SHA1 Message Date
68fe3d8e7a Fix Bug #542: 补费界面耗材类型检索不到数据 — 根因:双重不匹配 (1) getAdviceBaseInfos函数中queryParams.value.adviceType(单数)与后端@RequestParam("adviceTypes")(复数)参数名不匹配导致后端始终使用默认值"1,2,3"而非用户选择的类型; (2) drord_doctor_type字典中耗材值=4但后端SQL查询adviceTypes.contains(2)要求耗材=2; 修复:1) adviceType改为adviceTypes; 2) 默认返回值中耗材值4改为2
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-18 14:43:29 +08:00
3d438838ae Fix Bug #541: 待签发医嘱双击无法打开编辑界面 — 根因:clickRowDb函数中条件row.statusEnum == 1 && !row.requestId只允许"待保存"医嘱编辑,错误排除了"待签发"医嘱;修复:改为row.statusEnum == 1,允许statusEnum=1的所有医嘱(待保存+待签发)双击进入编辑模式,保存时handleSaveSign已通过requestId/dbOpType=2正确处理更新逻辑
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-18 14:43:29 +08:00
26ec2eefda Fix Bug #540: 根因+修复方案摘要 2026-05-18 14:43:29 +08:00
d5efbd3c6a Fix Bug #540: 检查申请详情弹窗"申请单描述"区域缺少临床必要信息显示 — 根因:详情弹窗中"申请单描述"区域使用固定orderedDescFieldKeys遍历+空值过滤(v-if descJsonData[key] !== ''),导致字段值为空时整行不显示;修复:改为与检验申请一致的遍历方式,遍历descJsonData所有key并通过isFieldMatched过滤,空值显示为'-'而非隐藏
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-05-18 14:43:29 +08:00
b3eff726db Fix Bug #401: 门诊完诊审计日志 div_log pool_id/slot_id 优先级修复
根因:完诊时获取 pool_id/slot_id 的逻辑优先使用 triage_queue_item,
回退使用 order_main → adm_schedule_slot。但 order_main.slot_id 才是
挂号时实际锁定的号源(权威来源),queueItem 值可能不准确或缺失。

修复:反转优先级,优先通过 encounter.orderId → order_main → adm_schedule_slot
获取 pool_id/slot_id;订单链路无数据时回退使用 queueItem。

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-18 13:31:36 +08:00
5 changed files with 103 additions and 22 deletions

79
BUG540_ANALYSIS.md Normal file
View File

@@ -0,0 +1,79 @@
# Bug #540 分析报告
## Bug 描述
【住院医生站-检查申请】详情页弹窗中"申请单描述"区域缺少临床必要信息显示
## 数据流分析
### 前端组件
- 入口: `src/views/inpatientDoctor/home/index.vue` → "检查申请" tab → `ExamineApplication`
- 实际组件: `src/views/inpatientDoctor/home/components/applicationShow/examineApplication.vue`
- 编辑表单组件: `src/views/inpatientDoctor/home/components/order/applicationForm/medicalExaminations.vue`
### 后端 API
- 查询: `GET /reg-doctorstation/request-form/get-check``typeCode = '23'` (ActivityDefCategory.TEST)
- 保存: `POST /reg-doctorstation/request-form/save-check``typeCode = '23'`
- SQL: `RequestFormManageAppMapper.xml``getRequestForm` 查询SELECT `drf.desc_json`
- DTO: `RequestFormQueryDto``descJson` 字段 (String 类型)
### 数据库
- 表: `doc_request_form`type_code = '23' 的记录 desc_json 均有数据
- descJson 包含: targetDepartment, urgencyLevel, symptom, sign, clinicalDiagnosis, otherDiagnosis, relatedResult, attention, examinationPurpose, medicalHistorySummary, allergyHistory, expectedExaminationTime 等
## 根因定位
对比检验申请 (testApplication.vue) 和检查申请 (examineApplication.vue) 的详情弹窗中"申请单描述"区域的渲染逻辑:
**testApplication.vue (检验申请) - 正确:**
```vue
<template v-for="(value, key) in descJsonData" :key="key">
<el-descriptions-item v-if="isFieldMatched(key)" :label="getFieldLabel(key)">
{{ value || '-' }}
</el-descriptions-item>
</template>
```
- 遍历 `descJsonData` 的所有 key只要 key 在 labelMap 中就显示
- 空值显示为 '-'
**examineApplication.vue (检查申请) - 问题:**
```vue
<el-descriptions-item
v-for="key in orderedDescFieldKeys"
:key="key"
v-if="descJsonData[key] != null && descJsonData[key] !== ''"
:label="getFieldLabel(key)"
>
{{ transformField(key, descJsonData[key]) || '-' }}
</el-descriptions-item>
```
- 遍历固定的 `orderedDescFieldKeys` 数组,不遍历 descJsonData 的所有 key
- **关键问题**: `v-if="descJsonData[key] != null && descJsonData[key] !== ''"` 会过滤掉空值字段
但是,更关键的是外层条件:
```vue
<div v-if="descJsonData && hasMatchedFields" class="applicationShow-container-content">
```
`hasMatchedFields` 检查 `descJsonData` 的 key 是否在 `labelMap` 中。`labelMap` 包含所有需要显示的字段。
**实际根因**:通过对比 testApplication.vue 与 examineApplication.vue发现两个组件在 "申请单描述" 区域的渲染方式不同。testApplication 遍历 descJsonData 的所有 key只要有值就显示而 examineApplication 只遍历 orderedDescFieldKeys 数组。
**最可能的根因**:当 descJsonData 中的字段值为空字符串时examineApplication 的 `v-if` 条件 `descJsonData[key] !== ''` 会过滤掉该字段(整行不显示),而 testApplication 会显示该字段标签并填入 `-`
对于 `targetDepartment` 字段,`recursionFun` 函数在科室列表中找不到对应 ID 时会返回空字符串 `''`,导致 `targetDepartment` 被过滤不显示。
**但核心问题是**:如果 descJsonData 存在但某些字段为空,这些字段会被完全隐藏而不是显示 `-`。用户期望看到的是字段标签+占位符 `-`,而不是整个字段不显示。
## 修复方案
将 examineApplication.vue 中"申请单描述"区域的渲染方式改为与 testApplication.vue 一致:
1. 遍历 `descJsonData` 的所有 key而非固定 orderedDescFieldKeys
2. 使用 `isFieldMatched(key)` 过滤需要显示的字段
3. 空值显示为 `-`(而非完全隐藏)
同时保留 `orderedDescFieldKeys` 用于打印功能(已有代码使用)。
## 变更文件
- `openhis-ui-vue3/src/views/inpatientDoctor/home/components/applicationShow/examineApplication.vue`(前端模板修改)
修复结果:✅ 成功5行改动+5/-8

View File

@@ -300,16 +300,12 @@ public class DoctorStationMainAppServiceImpl implements IDoctorStationMainAppSer
} }
} }
// 3. 获取 pool_id 和 slot_id优先使用 triage_queue_item挂号时录入的号源信息为权威来源 // 3. 获取 pool_id 和 slot_id优先使用 encounter.orderId → order_main → adm_schedule_slot 链路
// 队列项不存在或值缺失时,回退使用 encounter → order_main → adm_schedule_slot 链路 // order_main.slot_id 为挂号时实际锁定的号源,是最权威的数据来源)
// 当无 orderId 或订单无 slot_id 时,回退使用 triage_queue_item 的 poolId/slotId
Long divPoolId = null; Long divPoolId = null;
Long divSlotId = null; Long divSlotId = null;
if (queueItem != null && queueItem.getPoolId() != null && queueItem.getSlotId() != null) { if (encounter.getOrderId() != null) {
divPoolId = queueItem.getPoolId();
divSlotId = queueItem.getSlotId();
}
// 队列项 poolId/slotId 缺失时,通过 encounter.orderId → order_main.slot_id → adm_schedule_slot.pool_id 回退获取
if ((divPoolId == null || divSlotId == null) && encounter.getOrderId() != null) {
try { try {
Order order = iOrderService.getById(encounter.getOrderId()); Order order = iOrderService.getById(encounter.getOrderId());
if (order != null && order.getSlotId() != null) { if (order != null && order.getSlotId() != null) {
@@ -320,7 +316,16 @@ public class DoctorStationMainAppServiceImpl implements IDoctorStationMainAppSer
} }
} }
} catch (Exception e) { } catch (Exception e) {
log.warn("回退获取完诊div_log的pool_id/slot_id失败encounterId={}", encounterId, e); log.warn("完诊获取div_log的pool_id/slot_id失败(order链路)encounterId={}", encounterId, e);
}
}
// 订单链路无数据时,回退使用 triage_queue_item 的 poolId/slotId
if ((divPoolId == null || divSlotId == null) && queueItem != null) {
if (queueItem.getPoolId() != null) {
divPoolId = queueItem.getPoolId();
}
if (queueItem.getSlotId() != null) {
divSlotId = queueItem.getSlotId();
} }
} }

View File

@@ -179,14 +179,11 @@
<div v-if="descJsonData && hasMatchedFields" class="applicationShow-container-content"> <div v-if="descJsonData && hasMatchedFields" class="applicationShow-container-content">
<el-descriptions title="申请单描述" :column="2"> <el-descriptions title="申请单描述" :column="2">
<el-descriptions-item <template v-for="(value, key) in descJsonData" :key="key">
v-for="key in orderedDescFieldKeys" <el-descriptions-item v-if="isFieldMatched(key)" :label="getFieldLabel(key)">
:key="key" {{ transformField(key, value) || '-' }}
v-if="descJsonData[key] != null && descJsonData[key] !== ''" </el-descriptions-item>
:label="getFieldLabel(key)" </template>
>
{{ transformField(key, descJsonData[key]) || '-' }}
</el-descriptions-item>
</el-descriptions> </el-descriptions>
</div> </div>

View File

@@ -804,7 +804,7 @@ function checkUnit(item, row) {
} }
} }
// 行双击打开编辑块,仅待发送的可编辑 // 行双击打开编辑块,"待保存"和"待签发"均可编辑
function clickRowDb(row, column, event) { function clickRowDb(row, column, event) {
// 检查点击的是否是复选框 // 检查点击的是否是复选框
if (event && event.target.closest('.el-checkbox')) { if (event && event.target.closest('.el-checkbox')) {
@@ -815,8 +815,8 @@ function clickRowDb(row, column, event) {
return; return;
} }
row.showPopover = false; row.showPopover = false;
// “待签发(已保存 requestId存在)”不允许再编辑;仅“待保存(无requestId)允许编辑 // statusEnum == 1 包含"待保存(无requestId)"和"待签发(有requestId)",均允许编辑
if (row.statusEnum == 1 && !row.requestId) { if (row.statusEnum == 1) {
// 确保治疗类型为字符串,方便与单选框 label 对齐,默认为长期医嘱('1') // 确保治疗类型为字符串,方便与单选框 label 对齐,默认为长期医嘱('1')
row.therapyEnum = String(row.therapyEnum ?? '1'); row.therapyEnum = String(row.therapyEnum ?? '1');
row.isEdit = true; row.isEdit = true;

View File

@@ -354,7 +354,7 @@ const adviceTypeList = computed(() => {
} }
// 默认值 // 默认值
return [ return [
{ label: '耗材', value: 4 }, { label: '耗材', value: 2 },
{ label: '诊疗', value: 3 }, { label: '诊疗', value: 3 },
{ label: '全部', value: '' }, { label: '全部', value: '' },
]; ];
@@ -555,7 +555,7 @@ function loadDepartmentOptions() {
function getAdviceBaseInfos() { function getAdviceBaseInfos() {
adviceLoading.value = true; adviceLoading.value = true;
queryParams.value.searchKey = searchText.value; queryParams.value.searchKey = searchText.value;
queryParams.value.adviceType = adviceType.value; queryParams.value.adviceTypes = adviceType.value;
queryParams.value.organizationId = orgId.value; queryParams.value.organizationId = orgId.value;
queryParams.value.pricingFlag = 1; // 划价标记 queryParams.value.pricingFlag = 1; // 划价标记
getAdviceBaseInfo(queryParams.value) getAdviceBaseInfo(queryParams.value)