9 Commits

Author SHA1 Message Date
关羽
e0a035204e Fix Bug #487: 【临床医嘱】诊疗类医嘱签发后,列表状态未实时刷新为"已签发"
诊疗类医嘱(handService)签发时仅依赖saveOrUpdate更新statusEnum,
但该方式对已有记录可能未正确将statusEnum更新为ACTIVE(2)。
修复:在handService方法末尾使用LambdaUpdateWrapper批量显式更新
所有已处理ServiceRequest的statusEnum为ACTIVE(签发)/DRAFT(保存),
与ServiceRequestServiceImpl中activeStatusEnum/updateDraftStatusBatch
等方法的实现模式保持一致。

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-10 17:20:43 +08:00
关羽
349b0453c8 Fix Bug #480: [住院护士站-医嘱执行] 非耗材类医嘱执行报"耗材库存"错误且全选逻辑联动异常
1. 修复模板结构错误:删除premature的</template>和多余的</div>标签,确保el-table正确渲染
2. 新增selectedRowIds独立维护选中行ID集合,不再依赖el-table内部selection状态,避免执行选中时联动触发全选
3. 更新所有选择事件处理器同步维护selectedRowIds
4. 补充index.vue缺失的ref/nextTick/provide导入

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-10 17:14:00 +08:00
关羽
3ddd74d679 Fix Bug #494: 住院医生工作站-检查申请:"申请单名称"字段显示为通用名称,未展示具体检查项目名称
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-10 17:06:16 +08:00
关羽
9829843b3e Fix Bug #273: 门诊医生站-》医嘱TAB页面:修改用药天数字段的值,总量字段的值未自动通过公式换算
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-10 17:05:37 +08:00
关羽
0d95cc1341 Fix Bug #389: 住院护士站-》医嘱校对:界面筛选条件失效:勾选"临时"医嘱仍显示"长期"医嘱数据
前端therapyEnum参数在type.value为undefined时会被序列化为字符串"undefined"传递给后端,
导致后端无法正确解析而跳过过滤条件。修复为条件展开语法:仅在type.value有值时才传递therapyEnum参数,
确保"全部"筛选时不传该字段以获取全部医嘱。

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-10 16:07:17 +08:00
关羽
1dfceeaf46 Fix Bug #389: 住院护士站-》医嘱校对:界面筛选条件失效:勾选"临时"医嘱仍显示"长期"医嘱数据
根因:therapyEnum 参数映射逻辑完全颠倒。
原代码:type.value === 1 ? undefined : type.value === 2 ? 1 : 2
- 选择"长期"(1)时传 undefined(不传,无过滤)
- 选择"临时"(2)时传 1(长期值)
- 选择"全部"时传 2(临时值)

修复:直接传 type.value,与后端 therapyEnum 枚举一致:
- undefined → 全部 / 1 → 长期 / 2 → 临时

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-10 16:06:21 +08:00
关羽
1b79df4f93 Fix Bug #488: 【临床医嘱】双击编辑待签发医嘱,医嘱类型回显为数字且点击确认报接口错误
- 修复 handleSaveSign 中 getBindDevice 调用时 itemNo 可能为 undefined 导致的后端报错 "Required request parameter 'itemNo' for method parameter type String is not present":增加 itemNo 空值检查,为空时 console.warn 跳过调用而非发送无效请求
- 移除模板中两处调试残留:console.log 表达式渲染到页面(类型列和频次/用法列)
- 修复签发失败处理中截断的 conso; 语法错误
2026-05-10 16:05:42 +08:00
关羽
f62a280dfc Fix Bug #390: 住院护士站-医嘱执行:通过住院号检索无法定位/筛选患者
原 handleSearch 调用 reloadAllPatients 仅尝试刷新已展开的病区节点,
对懒加载树不可靠。改为递增 treeKey 强制树组件完全重新渲染,
触发 loadNode/loadPatientList 重新从后端拉取数据并传入 searchKey 过滤。
2026-05-10 16:05:17 +08:00
关羽
e5d949a740 Fix Bug #486: [住院医生工作站-临床医嘱] 医嘱检索框不支持全局模糊搜索,未选"医嘱类型"时检索结果为空
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-10 16:02:35 +08:00
8 changed files with 104 additions and 52 deletions

View File

@@ -511,6 +511,9 @@ public class AdviceManageAppServiceImpl implements IAdviceManageAppService {
// 签发操作
boolean is_sign = AdviceOpType.SIGN_ADVICE.getCode().equals(adviceOpType);
// 收集已处理的requestId用于批量更新状态
List<Long> processedRequestIds = new ArrayList<>();
// 声明长期医嘱诊疗请求
ServiceRequest longServiceRequest;
// 新增 + 修改 (长期医嘱)
@@ -555,6 +558,9 @@ public class AdviceManageAppServiceImpl implements IAdviceManageAppService {
}
}
iServiceRequestService.saveOrUpdate(longServiceRequest);
if (longServiceRequest.getId() != null) {
processedRequestIds.add(longServiceRequest.getId());
}
}
// 声明临时医嘱诊疗请求
@@ -603,6 +609,9 @@ public class AdviceManageAppServiceImpl implements IAdviceManageAppService {
}
}
iServiceRequestService.saveOrUpdate(tempServiceRequest);
if (tempServiceRequest.getId() != null) {
processedRequestIds.add(tempServiceRequest.getId());
}
// 保存时,保存诊疗费用项
if (is_save) {
@@ -654,6 +663,14 @@ public class AdviceManageAppServiceImpl implements IAdviceManageAppService {
}
}
// 批量更新诊疗医嘱状态(使用 update 确保状态字段必定更新)
if (!processedRequestIds.isEmpty()) {
iServiceRequestService.update(null,
new LambdaUpdateWrapper<ServiceRequest>()
.set(ServiceRequest::getStatusEnum,
is_save ? RequestStatus.DRAFT.getValue() : RequestStatus.ACTIVE.getValue())
.in(ServiceRequest::getId, processedRequestIds));
}
}
/**

View File

@@ -313,6 +313,7 @@
data-prop="dispensePerDuration">
<el-input-number v-model="scope.row.dispensePerDuration" style="width: 80px" :min="1"
controls-position="right" :controls="false" :ref="(el) => (inputRefs.dispensePerDuration = el)"
@change="calculateTotalAmount(scope.row, scope.$index)"
@keyup.enter.prevent="
handleEnter('dispensePerDuration', scope.row, scope.$index)
">

View File

@@ -86,7 +86,11 @@
</template>
<el-table-column type="index" label="序号" width="60" align="center" />
<el-table-column prop="patientName" label="患者姓名" width="120" />
<el-table-column prop="name" label="申请单名称" width="140" />
<el-table-column label="申请单名称" width="140">
<template #default="scope">
<span>{{ getItemName(scope.row) }}</span>
</template>
</el-table-column>
<el-table-column prop="createTime" label="创建时间" width="160" />
<el-table-column prop="prescriptionNo" label="处方号" width="140" />
<el-table-column prop="requesterId_dictText" label="申请者" width="120" />
@@ -159,7 +163,7 @@
currentDetail.patientName || '-'
}}</el-descriptions-item>
<el-descriptions-item label="申请单名称">{{
currentDetail.name || '-'
getItemName(currentDetail)
}}</el-descriptions-item>
<el-descriptions-item label="申请单状态">{{
parseStatus(currentDetail.status)
@@ -399,6 +403,28 @@ const parseStatus = (status) => {
return statusMap[String(status)] || '-';
};
/**
* 获取申请单展示名称:优先使用具体医嘱名称列表
*/
const getItemName = (row) => {
const items = row?.requestFormDetailList;
if (items && items.length > 0) {
return items.map(item => item.adviceName).filter(Boolean).join('、') || row.name || '-';
}
return row.name || '-';
};
/**
* 构建打印用项目名称字符串(同步函数,用于模板字符串)
*/
const buildItemNames = (row) => {
const items = row?.requestFormDetailList;
if (items && items.length > 0) {
return items.map(item => item.adviceName).filter(Boolean).join('、') || row.name || '';
}
return row.name || '';
};
const labelMap = {
categoryType: '项目类别',
targetDepartment: '发往科室',
@@ -651,7 +677,7 @@ const buildPrintContent = (row) => {
return `
<h2>检查申请单</h2>
<div class="info-row"><span class="label">患者姓名:</span>${row.patientName || '-'}</div>
<div class="info-row"><span class="label">申请单名称:</span>${row.name || '-'}</div>
<div class="info-row"><span class="label">申请单名称:</span>${buildItemNames(row) || '-'}</div>
<div class="info-row"><span class="label">创建时间:</span>${row.createTime || '-'}</div>
<div class="info-row"><span class="label">处方号:</span>${row.prescriptionNo || '-'}</div>
<div class="info-row"><span class="label">申请者:</span>${row.requesterId_dictText || '-'}</div>

View File

@@ -122,7 +122,6 @@
<el-table-column label="类型" align="center" prop="" width="120">
<template #default="scope">
{{ console.log(scope.row, 1111) }}
<el-radio-group
v-model="scope.row.therapyEnum"
size="small"
@@ -270,7 +269,6 @@
</el-table-column>
<el-table-column label="频次/用法" align="center" prop="" width="180">
<template #default="scope">
{{ console.log(scope.row) }}
<span v-if="!scope.row.isEdit && scope.row.adviceType == 1" style="text-align: right">
{{
[
@@ -879,14 +877,12 @@ function handleDiagnosisChange(item) {
function handleFocus(row, index) {
rowIndex.value = index;
row.showPopover = true;
// el-popover 懒渲染,需要等两帧组件才会挂载
const adviceType = row.adviceType !== undefined ? row.adviceType : adviceQueryParams.value.adviceType;
// 用 adviceType + categoryCode 组合查找匹配的选项
const selectValue = (adviceType == 1 && row.categoryCode) ? '1-' + row.categoryCode : adviceType;
const selectedItem = adviceTypeList.value.find(item => item.value === selectValue) || adviceTypeList.value.find(item => item.adviceType === adviceType);
// 诊疗(3)和手术(6)没有categoryCode传空字符串给refresh由子组件转为undefined不发送
// 药品(1)才有categoryCode如'1'=中成药,'2'=西药,'4'=中草药)
const categoryCode = selectedItem ? selectedItem.categoryCode : (adviceQueryParams.value.categoryCode || '');
// 修复Bug #486当行没有显式选择医嘱类型时不传categoryCode让搜索在全药库中进行
const categoryCode = selectedItem ? selectedItem.categoryCode : (row.adviceType !== undefined ? (adviceQueryParams.value.categoryCode || '') : '');
const searchKey = row.adviceName || '';
nextTick(() => {
@@ -923,8 +919,8 @@ function handleChange(value) {
// 用 adviceType + categoryCode 组合查找匹配的选项
const selectValue = (adviceType == 1 && row?.categoryCode) ? '1-' + row.categoryCode : adviceType;
const selectedItem = adviceTypeList.value.find(item => item.value === selectValue) || adviceTypeList.value.find(item => item.adviceType === adviceType);
// 诊疗/手术的categoryCode为空字符串由子组件转为undefined不发送
const categoryCode = selectedItem ? selectedItem.categoryCode : (adviceQueryParams.value.categoryCode || '');
// 修复Bug #486当行没有显式选择医嘱类型时不传categoryCode让搜索在全药库中进行
const categoryCode = selectedItem ? selectedItem.categoryCode : (row?.adviceType !== undefined ? (adviceQueryParams.value.categoryCode || '') : '');
tableRef.refresh(adviceType, categoryCode, value);
}
}
@@ -1214,12 +1210,8 @@ function handleSave() {
getListInfo(false);
bindMethod.value = {};
nextId.value == 1;
// 处方保存成功后,自动将医嘱信息同步至患者处置记录
// handleEmrTreatment();
} else {
proxy.$modal.msgError(res.message);
conso;
isSaving.value = false;
}
})
@@ -1319,22 +1311,26 @@ function handleCancelEdit(row, index) {
function handleSaveSign(row, index) {
if (row.adviceType != 2) {
let itemNo = row.adviceType == 1 ? row.methodCode : row.adviceDefinitionId;
getBindDevice({ typeCode: row.adviceType, itemNo: itemNo }).then((res) => {
if (res.data.length == 0) {
return;
}
let openBindDialog = localStorage.getItem('doctor' + userStore.id);
if (!JSON.parse(openBindDialog)) {
proxy.$refs['orderBindInfoRef'].open(res.data);
} else {
// 如果弹窗不提示带出的项目,自动带出
// 如果有未签发的项目,并且当前的项目没有带出过绑定项目,则自动带出
if (!bindMethod.value[itemNo]) {
handleOrderBindInfo(res.data);
bindMethod.value[itemNo] = true;
if (!itemNo) {
console.warn('绑定设备检查跳过itemNo为空adviceType=' + row.adviceType + ', adviceName=' + row.adviceName + '');
} else {
getBindDevice({ typeCode: row.adviceType, itemNo: itemNo }).then((res) => {
if (res.data.length == 0) {
return;
}
}
});
let openBindDialog = localStorage.getItem('doctor' + userStore.id);
if (!JSON.parse(openBindDialog)) {
proxy.$refs['orderBindInfoRef'].open(res.data);
} else {
// 如果弹窗不提示带出的项目,自动带出
// 如果有未签发的项目,并且当前的项目没有带出过绑定项目,则自动带出
if (!bindMethod.value[itemNo]) {
handleOrderBindInfo(res.data);
bindMethod.value[itemNo] = true;
}
}
});
}
}
// 更新UI状态

View File

@@ -273,8 +273,9 @@ function handleSearch() {
// 清除缓存(搜索时需要重新加载)
patientDataCache.value.clear();
// 重新加载所有已展开病区患者列表
reloadAllPatients();
// 通过递增 key 强制重新渲染树组件,触发重新加载所有病区患者列表
// 此时 searchKey 已有值,loadPatientList 会将 searchKey 传给后端进行过滤
treeKey.value += 1;
}
// 暴露方法供外部调用

View File

@@ -68,9 +68,7 @@
</div>
</div>
</div>
</template>
</div>
<div
<div
style="padding: 10px; background-color: #eef9fd; height: 100%; overflow-y: auto"
v-loading="loading"
>
@@ -232,7 +230,7 @@ import {adviceCancel, adviceExecute, adviceNoExecute, getPrescriptionList} from
import {patientInfoList} from '../../components/store/patient.js';
import {lotNumberMatch} from '@/api/public';
import {formatDateStr} from '@/utils/index';
import {getCurrentInstance, nextTick, ref} from 'vue';
import {getCurrentInstance, nextTick, ref, provide} from 'vue';
const activeNames = ref([]);
const prescriptionList = ref([]);
@@ -242,6 +240,8 @@ const therapyEnum = ref(undefined);
const { proxy } = getCurrentInstance();
const loading = ref(false);
const chooseAll = ref(false);
// 独立维护选中行ID集合避免el-table内部selection状态异常导致联动全选
const selectedRowIds = ref(new Set());
const props = defineProps({
exeStatus: {
type: Number,
@@ -442,6 +442,7 @@ function handleGetPrescription() {
chooseAll.value = false;
} else {
prescriptionList.value = [];
selectedRowIds.value.clear();
// proxy.$message.warning('请选择患者');
}
}
@@ -530,10 +531,14 @@ function handleCancel() {
}
function getSelectRows() {
// 获取选中的医嘱信息
let list = [];
prescriptionList.value.forEach((item, index) => {
list = [...list, ...proxy.$refs['tableRef' + index][0].getSelectionRows()];
// 优先从独立维护的selectedRowIds集合中获取选中行避免el-table内部selection状态异常
const list = [];
prescriptionList.value.forEach((item) => {
item.forEach((row) => {
if (selectedRowIds.value.has(row.requestId)) {
list.push(row);
}
});
});
return list;
}
@@ -609,12 +614,14 @@ function handelSwicthChange(value) {
if (value) {
// 全选选中所有行并联动checkbox
item.forEach((row) => {
selectedRowIds.value.add(row.requestId);
tableRef[0].toggleRowSelection(row, true);
selectAllCheckboxesInRow(row);
});
} else {
// 取消全选取消选中所有行并联动checkbox
item.forEach((row) => {
selectedRowIds.value.delete(row.requestId);
tableRef[0].toggleRowSelection(row, false);
unselectAllCheckboxesInRow(row);
});
@@ -625,11 +632,14 @@ function handelSwicthChange(value) {
// 默认选中全部行
function defaultSelectAllRows() {
// 清空并重建选中集合
selectedRowIds.value.clear();
prescriptionList.value.forEach((item, index) => {
const tableRef = proxy.$refs['tableRef' + index];
if (tableRef && tableRef[0]) {
// 选中该表格的所有行
item.forEach((row) => {
selectedRowIds.value.add(row.requestId);
tableRef[0].toggleRowSelection(row, true);
// 同时选中该行内部的所有checkbox
selectAllCheckboxesInRow(row);
@@ -709,13 +719,14 @@ function checkAndToggleRowSelection(row) {
const tableRef = proxy.$refs['tableRef' + tableIndex];
if (tableRef && tableRef[0]) {
const isAllSelected = isAllCheckboxesSelected(row);
const selectedRows = tableRef[0].getSelectionRows();
const isCurrentlySelected = selectedRows.some((r) => r.requestId === row.requestId);
const isCurrentlySelected = selectedRowIds.value.has(row.requestId);
// 根据checkbox状态更新表格行选中状态
if (isAllSelected && !isCurrentlySelected) {
selectedRowIds.value.add(row.requestId);
tableRef[0].toggleRowSelection(row, true);
} else if (!isAllSelected && isCurrentlySelected) {
selectedRowIds.value.delete(row.requestId);
tableRef[0].toggleRowSelection(row, false);
}
}
@@ -728,9 +739,11 @@ function handleRowSelect(selection, row, tableIndex) {
const isSelected = selection.some((item) => item.requestId === row.requestId);
if (isSelected) {
selectedRowIds.value.add(row.requestId);
// 选中行时选中该行内部的所有checkbox
selectAllCheckboxesInRow(row);
} else {
selectedRowIds.value.delete(row.requestId);
// 取消选中行时取消选中该行内部的所有checkbox
unselectAllCheckboxesInRow(row);
}
@@ -747,11 +760,13 @@ function handleSelectAll(selection, tableIndex) {
if (selection.length > 0) {
// 全选时选中所有行内部的所有checkbox
tableData.forEach((row) => {
selectedRowIds.value.add(row.requestId);
selectAllCheckboxesInRow(row);
});
} else {
// 取消全选时取消选中所有行内部的所有checkbox
tableData.forEach((row) => {
selectedRowIds.value.delete(row.requestId);
unselectAllCheckboxesInRow(row);
});
}
@@ -763,16 +778,12 @@ function handleSelectAll(selection, tableIndex) {
// 更新全选开关状态
function updateChooseAllStatus() {
let allSelected = true;
prescriptionList.value.forEach((item, index) => {
const tableRef = proxy.$refs['tableRef' + index];
if (tableRef && tableRef[0]) {
const selectedRows = tableRef[0].getSelectionRows();
if (selectedRows.length !== item.length) {
prescriptionList.value.forEach((item) => {
item.forEach((row) => {
if (!selectedRowIds.value.has(row.requestId)) {
allSelected = false;
}
} else {
allSelected = false;
}
});
});
chooseAll.value = allSelected;
}

View File

@@ -51,7 +51,7 @@
</template>
<script setup>
import {getCurrentInstance} from 'vue';
import {getCurrentInstance, ref, nextTick, provide} from 'vue';
import PatientList from '../components/patientList.vue';
import PrescriptionList from './components/prescriptionList.vue';
import { RequestStatus } from '@/utils/medicalConstants';

View File

@@ -181,7 +181,7 @@ function handleGetPrescription() {
getPrescriptionList({
encounterIds: encounterIds,
requestStatus: props.requestStatus,
therapyEnum: type.value === 1 ? undefined : type.value === 2 ? 1 : 2, // 1=全部(不传), 2=长期(1), 3=临时(2)
...(type.value !== undefined ? { therapyEnum: type.value } : {}),
pageSize: 10000,
pageNo: 1,
}).then((res) => {