1 Commits

Author SHA1 Message Date
关羽
74ef47fcc0 Fix Bug #503: 【住院发退药】发药明细与发药汇总单数据触发时机不一致,存在业务脱节风险
根因分析:
- 发药明细单查询(selectEncounterInfoListPage)直接从 med_medication_dispense 表查询,
  护士执行医嘱后立即显示记录
- 发药汇总单查询从 wor_supply_request 表查询,只有护士执行"汇总发药申请"后才创建记录
- 两者数据源不同导致明细单先显示、汇总单后显示的业务脱节问题

修复方案:
1. 在明细单查询中增加 EXISTS 子查询,仅显示已通过汇总发药申请流程创建的就诊记录
   (通过 med_medication_dispense.summary_no 关联 wor_supply_request.bus_no)
2. 将已汇总状态(status_enum=8, SUMMARIZED)纳入明细单的状态过滤条件,确保汇总申请提交后
   明细单能正常显示对应的就诊记录

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-10 11:05:03 +08:00
11 changed files with 30 additions and 620 deletions

View File

@@ -206,8 +206,6 @@ public class AdviceProcessAppServiceImpl implements IAdviceProcessAppService {
e.setTherapyEnum_enumText(EnumUtils.getInfoByValue(TherapyTimeType.class, e.getTherapyEnum()));
// 请求状态
e.setRequestStatus_enumText(EnumUtils.getInfoByValue(RequestStatus.class, e.getRequestStatus()));
// 发药状态
e.setDispenseStatus_enumText(EnumUtils.getInfoByValue(DispenseStatus.class, e.getDispenseStatus()));
// 性别枚举
e.setGenderEnum_enumText(EnumUtils.getInfoByValue(AdministrativeGender.class, e.getGenderEnum()));
// 计算年龄
@@ -358,17 +356,6 @@ public class AdviceProcessAppServiceImpl implements IAdviceProcessAppService {
medRequestList.add(item);
}
}
// 校验药品医嘱是否已发药,已发药的医嘱不允许退回
if (!medRequestList.isEmpty()) {
List<Long> medReqIds = medRequestList.stream().map(PerformInfoDto::getRequestId).toList();
List<MedicationDispense> dispenseList = medicationDispenseService.list(
new LambdaQueryWrapper<MedicationDispense>()
.in(MedicationDispense::getMedReqId, medReqIds)
.eq(MedicationDispense::getStatusEnum, DispenseStatus.COMPLETED.getValue()));
if (!dispenseList.isEmpty()) {
return R.fail("该医嘱已发药,无法退回");
}
}
Long practitionerId = SecurityUtils.getLoginUser().getPractitionerId();
Date checkDate = new Date();
if (!serviceRequestList.isEmpty()) {

View File

@@ -255,8 +255,4 @@ public class InpatientAdviceDto {
/** 总价 */
private BigDecimal totalPrice;
/** 发药状态 */
private Integer dispenseStatus;
private String dispenseStatus_enumText;
}

View File

@@ -153,8 +153,7 @@
ii.balance_amount AS balance_amount,
ii.account_id AS account_id,
ii.performer_check_id,
ii.category_code,
ii.dispense_status
ii.category_code
FROM (( SELECT T1.encounter_id,
T1.tenant_id,
#{medMedicationRequest} AS advice_table,
@@ -198,8 +197,7 @@
pra."name" AS admitting_doctor_name,
personal_account.balance_amount,
personal_account.id AS account_id,
T2.category_code,
mmd.status_enum AS dispense_status
T2.category_code
FROM med_medication_request AS T1
LEFT JOIN med_medication_definition AS T2
ON T2.id = T1.medication_id
@@ -280,9 +278,6 @@
aa.balance_amount
) AS personal_account
ON personal_account.encounter_id = ae.id
LEFT JOIN med_medication_dispense mmd
ON mmd.med_req_id = T1.id
AND mmd.delete_flag = '0'
WHERE T1.delete_flag = '0'
AND T1.refund_medicine_id IS NULL
AND T1.generate_source_enum = #{doctorPrescription}
@@ -336,8 +331,7 @@
pra."name" AS admitting_doctor_name,
personal_account.balance_amount,
personal_account.id AS account_id,
T2.category_code,
NULL AS dispense_status
T2.category_code
FROM wor_service_request AS T1
LEFT JOIN wor_activity_definition AS T2
ON T2.id = T1.activity_id

View File

@@ -96,17 +96,23 @@
INNER JOIN med_medication_request AS T5
ON T4.med_req_id = T5.id
AND T5.delete_flag = '0'
WHERE <if test="statusEnum == null">
T4.status_enum IN (#{inProgress},#{completed},#{preparation},#{prepared})
WHERE EXISTS (
SELECT 1 FROM wor_supply_request wsr
WHERE wsr.type_enum = 3
AND wsr.delete_flag = '0'
AND wsr.bus_no = T4.summary_no
AND T4.summary_no IS NOT NULL
AND T4.summary_no != ''
)
AND <if test="statusEnum == null">
T4.status_enum IN (#{inProgress},#{completed},#{preparation},#{prepared},8)
</if>
<if test="statusEnum == 3">
T4.status_enum IN (#{inProgress},#{preparation},#{prepared})
T4.status_enum IN (#{inProgress},#{preparation},#{prepared},8)
</if>
<if test="statusEnum == 4">
T4.status_enum = #{completed}
T4.status_enum IN (#{completed},8)
</if>
AND T4.summary_no IS NOT NULL
AND T4.summary_no != ''
) AS ii
${ew.customSqlSegment}
GROUP BY ii.encounter_id,
@@ -265,8 +271,6 @@
AND T15.delete_flag = '0'
WHERE T1.delete_flag = '0'
-- 因发药配药合并,前台只能看到待发药,已发药状态,但是后台配药发药状态都查
AND T1.summary_no IS NOT NULL
AND T1.summary_no != ''
AND
<if test="dispenseStatus == null">
T1.status_enum IN (#{inProgress},#{completed},#{preparation},#{prepared})

View File

@@ -314,7 +314,6 @@
>
<template #title>
<span class="cat-title">{{ cat.categoryName }}</span>
<span v-if="categoryLoadingSet.has(cat.typeId)" class="loading-dot"></span>
</template>
<div
v-for="item in cat.items"
@@ -330,9 +329,6 @@
</el-checkbox>
<span class="item-price">¥{{ item.price }}/{{ item.unit || "次" }}</span>
</div>
<div v-if="categoryLoadingSet.has(cat.typeId)" class="category-loading-hint">
加载中...
</div>
</el-collapse-item>
</el-collapse>
</div>
@@ -521,7 +517,6 @@ const rules = {
const categoryList = ref([]); // 原始分类+项目数据
const dictSearchKey = ref('');
const activeNames = ref(''); // 当前展开的折叠项
const categoryLoadingSet = ref(new Set()); // Bug #500: 正在加载方法的分类集合
const allMethods = ref([]);
@@ -637,21 +632,16 @@ const availableMethods = computed(() => {
// 当可选方法列表改变时,如果当前选中的方法不在新列表中,则清空
// #428: 分类展开时联动加载检查方法
// Bug #500: 使用 categoryLoadingSet 替代 dictLoading避免切换分类时整个区域闪烁
async function handleCategoryExpand(cat) {
if (!cat || !cat.typeName) return;
// 如果已加载过或正在加载中,跳过
if ((cat.methods && cat.methods.length > 0) || categoryLoadingSet.value.has(cat.typeId)) return;
categoryLoadingSet.value.add(cat.typeId);
try {
const res = await searchCheckMethod({ checkType: cat.typeName });
let data = res?.data?.data || res?.data || res?.rows || res;
if (!Array.isArray(data) && res?.data && Array.isArray(res.data.data)) {
data = res.data.data;
}
if (Array.isArray(data) && data.length > 0) {
cat.methods = data.map(m => ({
id: m.id,
@@ -665,16 +655,14 @@ async function handleCategoryExpand(cat) {
}
} catch (err) {
console.error('加载分类检查方法失败', err);
} finally {
categoryLoadingSet.value.delete(cat.typeId);
}
}
// Bug #500: 改为非 async 函数,避免 el-collapse 的 @change 等待异步完成导致抖动
function handleCollapseChange(activeName) {
async function handleCollapseChange(activeName) {
// 当折叠面板展开时,加载对应分类的检查方法
if (activeName) {
const cat = filteredCategoryList.value.find(c => c.typeId == activeName);
if (cat && (!cat.methods || cat.methods.length === 0)) {
handleCategoryExpand(cat); // 异步加载,不 await
await handleCategoryExpand(cat);
}
}
}
@@ -1321,20 +1309,6 @@ defineExpose({ getList });
font-weight: 500;
color: #303133;
}
/* Bug #500: 分类加载中的小圆点动画 */
.loading-dot {
display: inline-block;
width: 6px;
height: 6px;
border-radius: 50%;
background: #409eff;
margin-left: 6px;
animation: dotPulse 1s ease-in-out infinite;
}
@keyframes dotPulse {
0%, 100% { opacity: 0.3; transform: scale(0.8); }
50% { opacity: 1; transform: scale(1.2); }
}
.item-row {
display: flex;
align-items: center;
@@ -1474,24 +1448,10 @@ defineExpose({ getList });
height: auto;
line-height: 1.5;
}
/* Bug #500: 折叠内容添加平滑过渡动画,避免切换时高度跳变 */
:deep(.el-collapse-item__content) {
padding-bottom: 4px;
transition: all 0.3s ease;
}
/* Bug #500: 折叠面板展开/收起动画使用 will-change 优化性能 */
:deep(.el-collapse-item__wrap) {
border: none;
will-change: height;
}
:deep(.el-collapse-item) {
transition: margin 0.2s ease;
}
/* Bug #500: 分类加载中提示样式 */
.category-loading-hint {
color: #909399;
font-size: 12px;
text-align: center;
padding: 8px 0;
}
</style>

View File

@@ -121,25 +121,3 @@ export function getTestResultPage(queryParams) {
data: queryParams,
});
}
/**
* 删除申请单(仅待签发状态可删除)
*/
export function deleteRequestForm(data) {
return request({
url: '/reg-doctorstation/request-form/delete',
method: 'post',
data: data,
});
}
/**
* 撤回申请单(已签发状态撤回至待签发)
*/
export function withdrawRequestForm(data) {
return request({
url: '/reg-doctorstation/request-form/withdraw',
method: 'post',
data: data,
});
}

View File

@@ -95,49 +95,9 @@
<span>{{ parseStatus(scope.row.status) }}</span>
</template>
</el-table-column>
<el-table-column label="操作" align="center" fixed="right" width="240">
<el-table-column label="操作" align="center" fixed="right">
<template #default="scope">
<el-button link type="primary" @click="handleViewDetail(scope.row)">详情</el-button>
<!-- 待签发修改删除 -->
<template v-if="scope.row.status === '0' || scope.row.status === 0">
<el-button link type="primary" @click="handleEdit(scope.row)">修改</el-button>
<el-popconfirm
title="确认删除该申请单?"
@confirm="handleDelete(scope.row)"
width="200"
>
<template #reference>
<el-button link type="danger">删除</el-button>
</template>
</el-popconfirm>
</template>
<!-- 已签发护士未校对撤回 -->
<template v-else-if="scope.row.status === '1' || scope.row.status === 1">
<el-popconfirm
title="确认撤回该申请单?撤回后将回滚至待签发状态"
@confirm="handleWithdraw(scope.row)"
width="260"
>
<template #reference>
<el-button link type="warning">撤回</el-button>
</template>
</el-popconfirm>
</template>
<!-- 已校对/待接收打印 -->
<template v-else-if="scope.row.status === '2' || scope.row.status === 2 || scope.row.status === '3' || scope.row.status === 3">
<el-button link type="primary" @click="handlePrint(scope.row)">打印</el-button>
</template>
<!-- 已接收/已检查打印看报告 -->
<template v-else-if="scope.row.status === '4' || scope.row.status === 4 || scope.row.status === '5' || scope.row.status === 5">
<el-button link type="primary" @click="handlePrint(scope.row)">打印</el-button>
<el-button link type="primary" @click="handleViewReport(scope.row)">看报告</el-button>
</template>
<!-- 已出报告打印看报告 -->
<template v-else-if="scope.row.status === '6' || scope.row.status === 6">
<el-button link type="primary" @click="handlePrint(scope.row)">打印</el-button>
<el-button link type="success" @click="handleViewReport(scope.row)">看报告</el-button>
</template>
<!-- 已作废仅详情 -->
</template>
</el-table-column>
</el-table>
@@ -209,60 +169,6 @@
<el-button @click="detailDialogVisible = false">关闭</el-button>
</template>
</el-dialog>
<!-- 修改申请单弹窗 -->
<el-dialog
v-model="editDialogVisible"
title="修改检查申请"
width="800px"
destroy-on-close
top="5vh"
:close-on-click-modal="false"
>
<div v-if="editDetail" class="applicationShow-container">
<el-form :model="editForm" label-width="120px">
<el-form-item label="申请单名称">
<el-input v-model="editForm.name" />
</el-form-item>
<el-form-item label="临床诊断">
<el-input v-model="editForm.clinicalDiagnosis" type="textarea" :rows="2" />
</el-form-item>
<el-form-item label="注意事项">
<el-input v-model="editForm.attention" type="textarea" :rows="2" />
</el-form-item>
<div v-if="editDetail.requestFormDetailList && editDetail.requestFormDetailList.length">
<el-divider>申请项目</el-divider>
<el-table :data="editDetail.requestFormDetailList" border size="small">
<el-table-column type="index" label="序号" width="60" align="center" />
<el-table-column prop="adviceName" label="医嘱名称" />
<el-table-column prop="quantity" label="数量" width="80" align="center" />
<el-table-column prop="unitCode_dictText" label="单位" width="100" />
<el-table-column prop="totalPrice" label="总价" width="100" align="right" />
</el-table>
</div>
</el-form>
</div>
<template #footer>
<el-button @click="editDialogVisible = false">取消</el-button>
<el-button type="primary" @click="handleSaveEdit" :loading="saveLoading">保存</el-button>
</template>
</el-dialog>
<!-- 报告查看弹窗 -->
<el-dialog
v-model="reportDialogVisible"
title="检查报告"
width="900px"
destroy-on-close
top="5vh"
:close-on-click-modal="false"
>
<div v-if="reportUrl" style="min-height: 500px;">
<iframe :src="reportUrl" style="width: 100%; height: 500px; border: none;" />
</div>
<el-empty v-else description="暂无报告数据" />
<template #footer>
<el-button @click="reportDialogVisible = false">关闭</el-button>
</template>
</el-dialog>
</div>
</template>
@@ -270,7 +176,7 @@
import {computed, getCurrentInstance, ref, watch} from 'vue';
import {Refresh, Search} from '@element-plus/icons-vue';
import {patientInfo} from '../../store/patient.js';
import {getCheck, deleteRequestForm, withdrawRequestForm, getTestResult} from './api';
import {getCheck} from './api';
import {getOrgList} from '@/views/doctorstation/components/api.js';
const { proxy } = getCurrentInstance();
@@ -282,16 +188,6 @@ const currentDetail = ref(null);
const descJsonData = ref(null);
const orgOptions = ref([]);
// 修改申请单相关
const editDialogVisible = ref(false);
const editDetail = ref(null);
const editForm = ref({});
const saveLoading = ref(false);
// 报告查看相关
const reportDialogVisible = ref(false);
const reportUrl = ref('');
// 筛选表单数据
const getDefaultDateRange = () => {
const now = new Date();
@@ -468,217 +364,6 @@ const handleViewDetail = (row) => {
detailDialogVisible.value = true;
};
/**
* 修改申请单
*/
const handleEdit = (row) => {
editDetail.value = row;
// 解析 descJson 为表单数据
const form = { name: row.name || '' };
if (row.descJson) {
try {
const desc = JSON.parse(row.descJson);
form.clinicalDiagnosis = desc.clinicalDiagnosis || '';
form.attention = desc.attention || '';
form.symptom = desc.symptom || '';
form.sign = desc.sign || '';
} catch (e) {
console.error('解析 descJson 失败:', e);
}
}
editForm.value = form;
editDialogVisible.value = true;
};
/**
* 保存修改
*/
const handleSaveEdit = async () => {
if (!editDetail.value?.requestFormId) {
proxy.$modal?.msgWarning?.('申请单ID不存在');
return;
}
saveLoading.value = true;
try {
// 复用 save-check 接口进行更新(传入 requestFormId 即为编辑场景)
const res = await proxy.$http?.post?.('/reg-doctorstation/request-form/save-check', {
requestFormId: editDetail.value.requestFormId,
encounterId: editDetail.value.encounterId,
patientId: editDetail.value.patientId,
name: editForm.value.name,
organizationId: editDetail.value.inHospitalOrgId || patientInfo.value?.inHospitalOrgId,
descJson: JSON.stringify({
clinicalDiagnosis: editForm.value.clinicalDiagnosis,
attention: editForm.value.attention,
symptom: editForm.value.symptom,
sign: editForm.value.sign,
}),
activityList: [], // 修改场景仅更新描述信息,不修改项目
});
if (res?.code === 200) {
proxy.$modal?.msgSuccess?.('修改成功');
editDialogVisible.value = false;
await fetchData();
} else {
proxy.$modal?.msgError?.(res?.msg || '修改失败');
}
} catch (e) {
console.warn('修改申请单失败:', e.message);
proxy.$modal?.msgError?.('修改失败: ' + (e.message || '网络异常'));
} finally {
saveLoading.value = false;
}
};
/**
* 删除申请单(仅待签发状态可删除)
*/
const handleDelete = async (row) => {
if (!row.requestFormId) {
proxy.$modal?.msgWarning?.('申请单ID不存在');
return;
}
try {
const res = await deleteRequestForm({ requestFormId: row.requestFormId });
if (res?.code === 200) {
proxy.$modal?.msgSuccess?.('删除成功');
await fetchData();
} else {
proxy.$modal?.msgError?.(res?.msg || '删除失败');
}
} catch (e) {
console.warn('删除申请单失败:', e.message);
proxy.$modal?.msgError?.('删除失败: ' + (e.message || '网络异常'));
}
};
/**
* 撤回申请单(已签发状态撤回至待签发)
*/
const handleWithdraw = async (row) => {
if (!row.requestFormId) {
proxy.$modal?.msgWarning?.('申请单ID不存在');
return;
}
try {
const res = await withdrawRequestForm({ requestFormId: row.requestFormId });
if (res?.code === 200) {
proxy.$modal?.msgSuccess?.('撤回成功');
await fetchData();
} else {
proxy.$modal?.msgError?.(res?.msg || '撤回失败');
}
} catch (e) {
console.warn('撤回申请单失败:', e.message);
proxy.$modal?.msgError?.('撤回失败: ' + (e.message || '网络异常'));
}
};
/**
* 打印申请单
*/
const handlePrint = (row) => {
// 打开新窗口进行打印
const printWindow = window.open('', '_blank');
if (!printWindow) {
proxy.$modal?.msgWarning?.('请允许浏览器弹出窗口以进行打印');
return;
}
const content = buildPrintContent(row);
printWindow.document.write(`
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>检查申请单打印</title>
<style>
body { font-family: "Microsoft YaHei", sans-serif; padding: 20px; font-size: 14px; }
h2 { text-align: center; margin-bottom: 20px; }
table { width: 100%; border-collapse: collapse; margin-top: 10px; }
th, td { border: 1px solid #ddd; padding: 8px; text-align: left; }
th { background: #f5f5f5; }
.info-row { margin: 8px 0; }
.label { font-weight: bold; display: inline-block; width: 100px; }
@media print { .no-print { display: none; } }
</style>
</head>
<body>
${content}
<div class="no-print" style="text-align: center; margin-top: 20px;">
<button onclick="window.print()">打印</button>
<button onclick="window.close()">关闭</button>
</div>
</body>
</html>
`);
printWindow.document.close();
};
/**
* 构建打印内容
*/
const buildPrintContent = (row) => {
let descHtml = '';
if (row.descJson) {
try {
const desc = JSON.parse(row.descJson);
descHtml = Object.entries(labelMap)
.filter(([key]) => desc[key])
.map(([key, label]) => `<div class="info-row"><span class="label">${label}:</span>${desc[key]}</div>`)
.join('');
} catch (e) {
console.error('解析 descJson 失败:', e);
}
}
let itemsHtml = '';
if (row.requestFormDetailList && row.requestFormDetailList.length) {
itemsHtml = `
<h3>申请项目</h3>
<table>
<tr><th>序号</th><th>医嘱名称</th><th>数量</th><th>单位</th><th>总价</th></tr>
${row.requestFormDetailList.map((item, i) => `
<tr>
<td>${i + 1}</td>
<td>${item.adviceName || '-'}</td>
<td>${item.quantity || '-'}</td>
<td>${item.unitCode_dictText || '-'}</td>
<td>${item.totalPrice || '-'}</td>
</tr>
`).join('')}
</table>
`;
}
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>${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>
<div class="info-row"><span class="label">申请单状态:</span>${parseStatus(row.status)}</div>
${descHtml ? `<h3>申请单描述</h3>${descHtml}` : ''}
${itemsHtml}
`;
};
/**
* 查看报告
*/
const handleViewReport = async (row) => {
reportUrl.value = '';
reportDialogVisible.value = true;
try {
const res = await getTestResult({ encounterId: row.encounterId, requestFormId: row.requestFormId });
if (res?.code === 200 && res?.data) {
reportUrl.value = res.data;
} else {
console.warn('获取检查报告失败:', res?.msg || '无数据');
}
} catch (e) {
console.warn('获取检查报告异常:', e.message);
}
};
watch(
() => patientInfo.value?.encounterId,
(val) => {

View File

@@ -195,9 +195,8 @@ const recursionFun = (targetDepartment) => {
name = obj.name;
}
const subObjArray = obj['children'];
if (!subObjArray) continue;
for (let i = 0; i < subObjArray.length; i++) {
const item = subObjArray[i];
for (let index = 0; index < subObjArray.length; index++) {
const item = subObjArray[index];
if (item.id == targetDepartment) {
name = item.name;
}

View File

@@ -40,7 +40,7 @@
</template>
</el-input>
</div>
<el-button type="primary" @click="openGroupSetDialog">划价组套</el-button>
<el-button type="primary">划价组套</el-button>
</div>
<!-- 弹窗内容 - 左右布局 -->
<div style="display: flex; gap: 20px; height: 70vh">
@@ -147,7 +147,6 @@
<el-select
v-model="scope.row.selectUnitCode"
placeholder="单位"
filterable
style="width: 100px"
@change="unitCodeChange(scope.row)"
>
@@ -172,13 +171,12 @@
v-if="scope.row.adviceType == 3"
clearable
filterable
:filter-method="(val) => filterOptions(val, scope.row, 'departmentOptions')"
v-model="scope.row.positionId"
placeholder="选择科室"
style="width: 220px"
>
<el-option
v-for="dept in getFilteredOptions(scope.row, 'departmentOptions')"
v-for="dept in departmentOptions"
:key="dept.id"
:label="dept.name"
:value="dept.id"
@@ -188,13 +186,12 @@
v-if="scope.row.adviceType == 2"
clearable
filterable
:filter-method="(val) => filterOptions(val, scope.row, 'locationOptions')"
v-model="scope.row.positionId"
placeholder="选择药房/耗材房"
style="width: 220px"
>
<el-option
v-for="dept in getFilteredOptions(scope.row, 'locationOptions')"
v-for="dept in locationOptions"
:key="dept.value"
:label="dept.label"
:value="dept.value"
@@ -250,49 +247,6 @@
</div>
</div>
</el-dialog>
<!-- 划价组套选择对话框 -->
<el-dialog v-model="groupSetDialogVisible" title="划价组套选择" width="600px" :close-on-click-modal="false" append-to-body>
<div style="margin-bottom: 15px; display: flex; align-items: center; gap: 10px">
<el-input
v-model="groupSetSearchText"
placeholder="请输入组套名称搜索"
clearable
style="width: 250px"
@keyup.enter="loadGroupSets"
>
<template #append>
<el-button icon="Search" @click="loadGroupSets" />
</template>
</el-input>
<el-radio-group v-model="groupSetRange" @change="loadGroupSets">
<el-radio-button :label="1">个人</el-radio-button>
<el-radio-button :label="2">科室</el-radio-button>
<el-radio-button :label="3">全院</el-radio-button>
</el-radio-group>
</div>
<el-table
:data="groupSetList"
v-loading="groupSetLoading"
highlight-current-row
border
stripe
@current-change="handleGroupSetSelect"
style="width: 100%"
>
<el-table-column type="index" label="序号" width="60" align="center" />
<el-table-column label="组套名称" prop="name" min-width="180" show-overflow-tooltip />
<el-table-column label="使用范围" prop="rangeCode_dictText" width="100" align="center" />
<el-table-column label="明细数量" width="100" align="center">
<template #default="scope">
{{ scope.row.detailList?.length || 0 }} 项
</template>
</el-table-column>
</el-table>
<div style="margin-top: 15px; text-align: right">
<el-button @click="groupSetDialogVisible = false">取消</el-button>
<el-button type="primary" @click="applyGroupSet" :disabled="!selectedGroupSet">应用</el-button>
</div>
</el-dialog>
</template>
<script setup>
@@ -300,7 +254,6 @@ import {computed, getCurrentInstance, onMounted, reactive, ref, watch} from 'vue
import {ElMessage} from 'element-plus';
import {formatDateStr} from '@/utils/index';
import {getAdviceBaseInfo, getDiseaseTreatmentInitLoc, getOrgList} from './api.js';
import {getOrderGroup} from '@/views/doctorstation/components/api.js';
import useUserStore from '@/store/modules/user';
const { proxy } = getCurrentInstance();
@@ -359,8 +312,6 @@ const locationOptions = ref([]);
const searchText = ref('');
const userId = ref('');
const orgId = ref('');
// 下拉框模糊搜索关键字(按行存储)
const filterKeywords = ref({});
const queryParams = ref({
pageSize: 100,
pageNum: 1,
@@ -438,14 +389,6 @@ const submitData = reactive({
});
const adviceLoading = ref(false);
// 划价组套相关
const groupSetDialogVisible = ref(false);
const groupSetList = ref([]);
const groupSetLoading = ref(false);
const groupSetSearchText = ref('');
const groupSetRange = ref(2);
const selectedGroupSet = ref(null);
// 【优化核心】计算总金额 - 使用计算属性实现实时更新
const totalAmount = computed(() => {
return feeItemsList.value.reduce((sum, item) => {
@@ -509,25 +452,6 @@ function getDiseaseInitLoc() {
locationOptions.value = response.data.locationOptions;
});
}
// 下拉框模糊搜索过滤
function filterOptions(val, row, optionsKey) {
const key = row.adviceDefinitionId + '_' + optionsKey;
filterKeywords.value[key] = val;
}
function getFilteredOptions(row, optionsKey) {
const key = row.adviceDefinitionId + '_' + optionsKey;
const keyword = filterKeywords.value[key];
const options = optionsKey === 'departmentOptions' ? departmentOptions.value : locationOptions.value;
if (!keyword || keyword.trim() === '') {
return options;
}
const lower = keyword.toLowerCase();
return options.filter(item => {
const name = (item.name || item.label || '').toLowerCase();
const id = String(item.id || item.value || '').toLowerCase();
return name.includes(lower) || id.includes(lower);
});
}
// 获取组套类型文本
function getItemType_Text(type) {
const map = { 2: '耗材', 3: '诊疗' };
@@ -603,24 +527,14 @@ function selectChange(row) {
const price = row.priceList?.[0]?.price || 0;
//获取大小单位
const uniqueUnitCodes = getUnitCodeOptions(row);
// 设置默认执行科室/位置
let defaultPositionId = undefined;
if (row.adviceType === 3 && departmentOptions.value.length > 0) {
// 诊疗:优先使用患者所在科室,否则取第一个科室
defaultPositionId = departmentOptions.value.find(d => d.id === props.patientInfo.organizationId)?.id
|| departmentOptions.value[0]?.id;
} else if (row.adviceType === 2 && locationOptions.value.length > 0) {
// 耗材:默认取第一个药房/耗材房
defaultPositionId = locationOptions.value[0]?.value;
}
//插入费用列表
feeItemsList.value.push({
...row,
uniqueUnitCodes: uniqueUnitCodes,
unitPrice: (price / (row.partPercent || 1)).toFixed(6), // 根据拆零比计算单价
quantity: 1,
positionId: defaultPositionId, // 默认执行科室/位置
selectUnitCode: String(row.minUnitCode || ''), // 默认选择小单位,确保字符串类型
// positionId: row.positionId === null || row.positionId === undefined ? orgId : row.positionId, // 默认执行科室
selectUnitCode: row.minUnitCode, // 默认选择小单位
});
}
@@ -734,94 +648,6 @@ function resetData() {
searchText.value = '';
executeTime.value = '';
}
// 划价组套相关功能
function openGroupSetDialog() {
groupSetDialogVisible.value = true;
groupSetSearchText.value = '';
selectedGroupSet.value = null;
loadGroupSets();
}
function loadGroupSets() {
groupSetLoading.value = true;
getOrderGroup({ organizationId: orgId.value, searchKey: groupSetSearchText.value })
.then((res) => {
const data = res?.data || {};
if (groupSetRange.value === 1) {
groupSetList.value = data.personalList || [];
} else if (groupSetRange.value === 2) {
groupSetList.value = data.organizationList || [];
} else {
groupSetList.value = data.hospitalList || [];
}
})
.catch(() => {
console.warn('组套列表加载失败(可能无权限)');
groupSetList.value = [];
})
.finally(() => {
groupSetLoading.value = false;
});
}
function handleGroupSetSelect(row) {
selectedGroupSet.value = row;
}
function applyGroupSet() {
if (!selectedGroupSet.value) {
ElMessage.warning('请先选择一个组套');
return;
}
const detailList = selectedGroupSet.value.detailList;
if (!detailList || detailList.length === 0) {
ElMessage.warning('该组套没有明细项');
return;
}
let successCount = 0;
detailList.forEach((item) => {
const orderDetail = item.orderDetailInfos || {};
const feeItem = {
adviceDefinitionId: item.orderDefinitionId || orderDetail.adviceDefinitionId,
adviceName: orderDetail.adviceName || item.orderDefinitionName || '未知项目',
adviceType: orderDetail.adviceType,
unitPrice: orderDetail.unitPrice,
minUnitPrice: orderDetail.minUnitPrice,
inventoryList: orderDetail.inventoryList || [],
priceList: orderDetail.priceList || [],
partPercent: orderDetail.partPercent || 1,
positionId: item.positionId || orderDetail.positionId,
defaultLotNumber: orderDetail.defaultLotNumber,
unitCode: item.unitCode || orderDetail.unitCode,
unitCode_dictText: item.unitCodeName || orderDetail.unitCode_dictText,
minUnitCode: orderDetail.minUnitCode,
doseUnitCode: orderDetail.doseUnitCode,
categoryCode: orderDetail.categoryCode,
pharmacologyCategoryCode: orderDetail.pharmacologyCategoryCode,
partAttributeEnum: orderDetail.partAttributeEnum,
chargeItemDefinitionId: orderDetail.chargeItemDefinitionId,
adviceTableName: orderDetail.adviceTableName,
methodCode: item.methodCode || orderDetail.methodCode,
rateCode: item.rateCode || orderDetail.rateCode,
dose: item.dose || orderDetail.dose,
doseQuantity: item.doseQuantity,
dispensePerDuration: item.dispensePerDuration || orderDetail.dispensePerDuration,
dosageInstruction: orderDetail.dosageInstruction,
skinTestFlag: orderDetail.skinTestFlag,
injectFlag: orderDetail.injectFlag,
quantity: item.quantity || 1,
chrgitmLv_dictText: orderDetail.chrgitmLv_dictText,
volume: orderDetail.volume,
};
selectChange(feeItem);
successCount++;
});
if (successCount > 0) {
ElMessage.success(`已添加 ${successCount} 项组套费用`);
}
groupSetDialogVisible.value = false;
}
</script>
<style scoped>

View File

@@ -67,9 +67,6 @@
</el-button>
</div>
</div>
</div>
</template>
</div>
<div
style="padding: 10px; background-color: #eef9fd; height: 100%; overflow-y: auto"
v-loading="loading"
@@ -500,15 +497,8 @@ function handleCancel() {
let list = getSelectRows();
let producerIds = [];
list.forEach((item) => {
// 从 exePerformRecordList 直接提取 procedureId确保取消执行时数据完整
const procedureIds = (item.exePerformRecordList || []).map((record) => record.procedureId);
if (procedureIds.length === 0 && (!item.procedureIds || item.procedureIds.length === 0)) {
proxy.$modal.msgError('请选择已执行的医嘱记录');
return;
}
const ids = procedureIds.length > 0 ? procedureIds : item.procedureIds;
producerIds.push(
...ids.map((value) => {
...item.procedureIds.map((value) => {
return {
procedureId: value,
therapyEnum: item.therapyEnum,
@@ -516,9 +506,6 @@ function handleCancel() {
})
);
});
if (producerIds.length === 0) {
return;
}
adviceCancel({ adviceExecuteDetailList: producerIds }).then((res) => {
if (res.code == 200) {
proxy.$modal.msgSuccess(res.msg || '取消执行成功');

View File

@@ -288,12 +288,6 @@ function handleCheck() {
function handleCancel() {
let list = getSelectRows();
if (list.length > 0) {
// 校验已发药的医嘱不允许退回
let dispensedItems = list.filter(item => item.dispenseStatus === 4);
if (dispensedItems.length > 0) {
proxy.$message.error('该医嘱已发药,无法退回');
return;
}
cancel(list).then((res) => {
if (res.code == 200) {
proxy.$modal.msgSuccess(res.msg);