Compare commits

...

5 Commits

Author SHA1 Message Date
eeb5af8fc1 Fix Bug #539: 根因+修复方案摘要 2026-05-18 12:18:15 +08:00
56cd024949 Fix Bug #529: [住院医生工作站-检验申请] 点击修改打开编辑弹窗后原已选中的项目未回显
根因:时序竞态——editData watch (immediate: true) 在 applicationListAll 加载完成前触发,
匹配不到数据导致 transferValue 被置空。新增 watch 监听 applicationListAll 加载完成后重新回显。

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-18 12:18:15 +08:00
b37033d87e fix bug525:[手术管理-门诊手术安排-计费] 已勾选“待签发”项目且未收费,点击“删除”提示“只能删除待签发且未收费的项目” 2026-05-18 12:18:15 +08:00
9e07546027 Fix Bug #530: 根因+修复方案摘要 2026-05-18 12:18:15 +08:00
wangjian963
cb146ade45 修复门诊手术安排模块计费弹窗中对诊疗数据进行签发成功后回显失败的问题。 2026-05-18 12:18:15 +08:00
5 changed files with 184 additions and 59 deletions

32
BUG_522_ANALYSIS.md Normal file
View File

@@ -0,0 +1,32 @@
# Bug #522 分析报告
## Bug 描述
[住院护士站-三测单] 体征录入点击保存后缺乏执行反馈且窗口异常自动关闭
## 涉及文件
- 前端: `openhis-ui-vue3/src/views/inpatientNurse/tprChart/components/addTprDialog.vue`
- API: `openhis-ui-vue3/src/views/inpatientNurse/tprChart/components/api.js`
- 父组件: `openhis-ui-vue3/src/views/inpatientNurse/tprChart/index.vue`
## 根因分析
### 问题1弹窗异常自动关闭 — 根因
`addTprDialog.vue` 模板中,保存按钮使用了 `:disabled="buttonDisabled"`第50行和第108行**`buttonDisabled` 变量在整个 script setup 中从未声明**。
在 Vue 3 `<script setup>` + Composition API 中,模板引用的变量必须在 script 中声明。未声明的变量会触发 `ReferenceError`,导致组件渲染失败或运行时异常。这个错误会破坏组件的响应式系统,使得 `dialogVisible` 的响应式绑定失效,从而导致弹窗在保存操作后异常关闭。
### 问题2缺乏保存成功反馈 — 连带结果
虽然 `confirmCharge()` 函数在第1087行已有 `proxy.$modal.msgSuccess('保存成功')` 的调用,但由于 `buttonDisabled` 未声明引发的异常导致代码执行路径被破坏success 回调中的提示逻辑可能未能正常执行。
## 修复方案
1. **在 `addTprDialog.vue` 的 script setup 中新增 `buttonDisabled` ref 声明**,初始值为 `false`
2. **在保存操作中添加 loading 状态**点击保存后将按钮禁用API 返回后恢复,防止重复提交的同时也保证了响应式状态的一致性
## 验收标准
- [ ] 点击保存后弹窗保持开启状态
- [ ] 保存成功后弹出"保存成功"提示
- [ ] 左侧体征历史记录列表自动刷新
- [ ] 录入区域表单被清空,方便继续录入下一条

26
BUG_539_ANALYSIS.md Normal file
View File

@@ -0,0 +1,26 @@
# Bug #539 分析报告
## Bug 描述
住院护士站点击后只有一个标签可见,缺少入出转管理、护理记录等功能模块。
## 根因分析
### 数据库菜单结构
`hisdev.sys_menu`住院护士站menu_id=295是一个目录类型M路径为 `statistics`**没有设置 component 和 redirect**。
其下有 15 个子菜单(门户、入出转管理、护理记录、三测单等),都正确分配给了护士角色。
### 前端路由问题
1. `inpatientNurseStation/index.vue` 包含 10 个 `el-tab-pane`(入出转管理、住院病历、医嘱校对、医嘱执行、护理记录、体温单、住院记账、汇总领药、科室耗材盘点、滚费配置),**但这个页面不在数据库菜单中!**
2. 菜单 296门户的 component 指向 `inpatientNurse/home/index`——这是一个**患者列表页面**,没有任何标签页。
3. 当护士登录后,点击"住院护士站"(目录类型 M由于没有 redirect系统默认打开第一个子菜单"门户",显示的是患者列表而非带标签的护士站主页。
4. `constants/navigation.js` 中的路径(如 `/inHospital/statistics/inOut`)与实际前端路由(`/inpatientNurse/inOut`)不匹配。
### 结论
根本原因:`inpatientNurseStation/index.vue`(带 10 个功能标签的页面)未被纳入数据库菜单体系。护士实际打开的是 `home/index.vue`(患者列表),该页面没有标签页,导致"功能没有就一个标签"。
## 修复方案
将菜单 296门户的 component 从 `inpatientNurse/home/index` 改为 `inpatientNurse/inpatientNurseStation/index`,使护士站入口默认加载带所有标签的主页面。同时修正 navigation.js 中的路径。

View File

@@ -873,6 +873,32 @@ function ensureOrgTreeLoaded() {
});
}
/** 待签发且未收费chargeStatus=5 为已收费) */
function isPendingUnsignedAndUnpaid(item) {
return item.statusEnum == 1 && item.chargeStatus != 5
}
/**
* 门诊划价仅允许操作本人开立bizRequestFlag==1 或空)。
* 手术计费:列表接口需按库中 generate_source_enum 查询(当前多为 1故子组件仍传 generateSourceEnum=1
* 通过 patientInfo.generateSourceEnum===6手术计费在 chargePatientInfo 中已写入)识别场景,删除时不卡 bizRequestFlag。
*/
function isSurgeryChargeBillingContext() {
const fromPatient = props.patientInfo?.generateSourceEnum
return fromPatient != null && Number(fromPatient) === 6
}
function isBizRequestAllowedForDelete(item) {
if (isSurgeryChargeBillingContext()) {
return true
}
const src = props.generateSourceEnum != null ? Number(props.generateSourceEnum) : NaN
if (src === 6) {
return true
}
return Number(item.bizRequestFlag) === 1 || !item.bizRequestFlag
}
function handleDelete() {
// 🔧 修复:使用 groupIndexList 而不是 check 属性
// 因为 watch 监听器会在数据更新时重置 check 为 false
@@ -881,80 +907,93 @@ function handleDelete() {
return;
}
let deleteList = groupIndexList.value.map((index) => {
const item = prescriptionList.value[index];
// 只删除待签发且未收费的项目
if (item.statusEnum != 1 || item.chargeStatus == 5) {
return null;
}
// 🔧 Bug #442: 非本人创建的医嘱不允许删除(与签发/签退逻辑保持一致)
if (Number(item.bizRequestFlag) !== 1 && item.bizRequestFlag) {
return null;
}
// 🔧 Bug #442: 已保存的行必须有有效的 requestId否则跳过避免后端删除不存在的记录
if (item.requestId == null || item.requestId === undefined || item.requestId === '') {
return null;
}
return {
requestId: item.requestId,
dbOpType: '3',
adviceType: item.adviceType,
};
}).filter(item => item !== null); // 过滤掉已签发、已收费、非本人创建或无 requestId 的项目
if (deleteList.length == 0) {
proxy.$modal.msgWarning('只能删除待签发且未收费的项目');
return;
const canDeleteRow = (item) =>
isPendingUnsignedAndUnpaid(item) && isBizRequestAllowedForDelete(item)
const anySelectedDeletable = groupIndexList.value.some((index) =>
canDeleteRow(prescriptionList.value[index])
)
if (!anySelectedDeletable) {
proxy.$modal.msgWarning(
'只能删除「待签发」且「未收费」的项目;门诊划价还需为本人开立。已签发、已收费或非本人开立项不可删。'
)
return
}
let deleteList = groupIndexList.value
.map((index) => {
const item = prescriptionList.value[index]
if (!canDeleteRow(item)) {
return null
}
if (item.requestId == null || item.requestId === undefined || item.requestId === '') {
return null
}
return {
requestId: item.requestId,
dbOpType: '3',
adviceType: item.adviceType,
}
})
.filter((item) => item !== null)
// 删除逻辑:按索引从大到小排序,避免删除后索引变化
const sortedIndexes = groupIndexList.value.sort((a, b) => b - a);
let hasSavedItem = false;
const sortedIndexes = [...groupIndexList.value].sort((a, b) => b - a)
let hasSavedItem = false
for (const index of sortedIndexes) {
const item = prescriptionList.value[index];
if (item.statusEnum != 1) {
continue; // 跳过已签发的项目
const item = prescriptionList.value[index]
if (!canDeleteRow(item)) {
continue
}
if (!item.requestId) {
// 新增的行(未保存到数据库),直接删除
prescriptionList.value.splice(index, 1);
prescriptionList.value.splice(index, 1)
} else {
hasSavedItem = true;
hasSavedItem = true
}
}
if (hasSavedItem) {
// 🔧 Bug #454: 删除前弹出确认提示,告知用户将级联删除关联检验申请单
const hasLabItem = deleteList.some(item => item.adviceType === 3);
const confirmMsg = hasLabItem
? '删除此医嘱将同时删除关联的检验申请单,是否确认删除?'
: '确认删除选中的医嘱项目吗?';
proxy.$modal.confirm(confirmMsg).then(() => {
savePrescription({ adviceSaveList: deleteList }).then((res) => {
if (res.code == 200) {
proxy.$modal.msgSuccess('操作成功');
getListInfo(false);
expandOrder.value = [];
groupIndexList.value = [];
groupList.value = [];
isAdding.value = false;
adviceQueryParams.value.adviceType = undefined;
}
});
}).catch(() => {
// 用户取消删除
});
} else {
// 只有新增行,已经在前端删除完成
proxy.$modal.msgSuccess('操作成功');
const cleanupAfterDelete = () => {
expandOrder.value = [];
groupIndexList.value = [];
groupList.value = [];
isAdding.value = false;
adviceQueryParams.value.adviceType = undefined;
};
if (hasSavedItem) {
if (deleteList.length === 0) {
proxy.$modal.msgWarning('没有可提交删除的已保存医嘱,请刷新后重试');
getListInfo(false);
cleanupAfterDelete();
return;
}
// Bug #454: 删除前确认;检验医嘱提示级联删除申请单
const hasLabItem = deleteList.some((item) => item.adviceType === 3);
const confirmMsg = hasLabItem
? '删除此医嘱将同时删除关联的检验申请单,是否确认删除?'
: '确认删除选中的医嘱项目吗?';
proxy.$modal
.confirm(confirmMsg)
.then(() => {
savePrescription({ adviceSaveList: deleteList }).then((res) => {
if (res.code == 200) {
proxy.$modal.msgSuccess('操作成功');
getListInfo(false);
cleanupAfterDelete();
}
});
})
.catch(() => {
// 用户取消删除
});
} else {
proxy.$modal.msgSuccess('操作成功');
cleanupAfterDelete();
}
}
@@ -1057,6 +1096,9 @@ function handleSave() {
adviceTableName: item.adviceTableName,
adviceDefinitionId: item.adviceDefinitionId,
chargeItemId: item.chargeItemId,
// 🔧 Bug Fix: 签发时显式设置手术计费关键字段,避免后端 prescription_no / generateSourceEnum 回退为默认值导致查询无法匹配
generateSourceEnum: props.generateSourceEnum ?? parsedContent.generateSourceEnum,
sourceBillNo: props.sourceBillNo ?? parsedContent.sourceBillNo,
};
});
// 确保 organizationId 不为 undefined手术计费场景下可能缺失 orgId

View File

@@ -380,6 +380,28 @@ watch(
},
{ immediate: true }
);
// 编辑模式下applicationListAll 加载完成后重新回显已选项目
watch(
() => applicationListAll.value,
() => {
if (!props.editData?.requestFormId) return;
if (!props.editData.requestFormDetailList?.length) return;
if (!applicationListAll.value.length) return;
const selectedIds = [];
props.editData.requestFormDetailList.forEach((detail) => {
const matched = applicationListAll.value.find(
(item) => item.adviceName === detail.adviceName
);
if (matched) {
selectedIds.push(matched.adviceDefinitionId);
}
});
transferValue.value = selectedIds;
}
);
const submit = () => {
if (transferValue.value.length == 0) {
return proxy.$message.error('请选择申请单');

View File

@@ -777,6 +777,9 @@ const InputOptions = ref([
// 日期范围 - 体征信息搜索时间
const receptionTime = ref(null);
// 按钮禁用状态(保存按钮依赖)
const buttonDisabled = ref(false);
// 表单数据 - 体征录入
const formData = ref({
recordingDate: '',