Compare commits

..

3 Commits

Author SHA1 Message Date
关羽
71f1198379 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:19 +08:00
关羽
7cac4701b9 Fix Bug #489: 【医嘱闭环】医生站签发单条长期药品医嘱,护士校对界面生成重复(两条)待校对记录
根因:selectInpatientAdvicePage 查询中,med_medication (T3) 和 med_medication_dispense (mmd) 的 LEFT JOIN 在一对多关系下产生笛卡尔积,导致单条药品医嘱返回多行重复记录。

修复方案:
1. 将 T3.total_volume 改为关联子查询,避免同一药品定义多条库存记录导致行倍增
2. 将 mmd.dispense_status 改为关联子查询,避免同一医嘱请求多条发药记录导致行倍增
3. 移除对应的 LEFT JOIN

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-10 16:04:14 +08:00
赵云
905a609852 Fix Bug #498: 【住院医生工作站-检查申请】检查申请列表操作项过于单一,缺失修改/作废/打印/看报告等核心临床操作
- 根据申请单状态动态展示操作按钮(详情/修改/删除/撤回/打印/看报告)
- 待签发状态:显示修改、删除按钮
- 已签发状态:显示撤回按钮
- 已校对/待接收状态:显示打印按钮
- 已接收/已检查/已出报告状态:显示打印、看报告按钮
- 新增修改申请单弹窗和报告查看弹窗
- 新增删除、撤回申请单 API 调用

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-10 16:04:14 +08:00
4 changed files with 360 additions and 31 deletions

View File

@@ -169,7 +169,7 @@
T1.performer_check_id,
T2."name" AS advice_name,
T2.id AS item_id,
T3.total_volume AS volume,
(SELECT mm.total_volume FROM med_medication mm WHERE mm.medication_def_id = T2.id AND mm.delete_flag = '0' LIMIT 1) AS volume,
T1.lot_number AS lot_number,
T1.quantity AS quantity,
T1.unit_code AS unit_code,
@@ -199,14 +199,11 @@
personal_account.balance_amount,
personal_account.id AS account_id,
T2.category_code,
mmd.status_enum AS dispense_status
(SELECT mmd.status_enum FROM med_medication_dispense mmd WHERE mmd.med_req_id = T1.id AND mmd.delete_flag = '0' LIMIT 1) AS dispense_status
FROM med_medication_request AS T1
LEFT JOIN med_medication_definition AS T2
ON T2.id = T1.medication_id
AND T2.delete_flag = '0'
LEFT JOIN med_medication AS T3
ON T3.medication_def_id = T2.ID
AND T3.delete_flag = '0'
LEFT JOIN adm_location AS al1
ON al1.id = T1.perform_location
AND al1.delete_flag = '0'
@@ -280,9 +277,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}

View File

@@ -121,3 +121,25 @@ 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,9 +95,49 @@
<span>{{ parseStatus(scope.row.status) }}</span>
</template>
</el-table-column>
<el-table-column label="操作" align="center" fixed="right">
<el-table-column label="操作" align="center" fixed="right" width="240">
<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>
@@ -169,6 +209,60 @@
<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>
@@ -176,7 +270,7 @@
import {computed, getCurrentInstance, ref, watch} from 'vue';
import {Refresh, Search} from '@element-plus/icons-vue';
import {patientInfo} from '../../store/patient.js';
import {getCheck} from './api';
import {getCheck, deleteRequestForm, withdrawRequestForm, getTestResult} from './api';
import {getOrgList} from '@/views/doctorstation/components/api.js';
const { proxy } = getCurrentInstance();
@@ -188,6 +282,16 @@ 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();
@@ -364,6 +468,217 @@ 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

@@ -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">
{{
[
@@ -1214,12 +1212,8 @@ function handleSave() {
getListInfo(false);
bindMethod.value = {};
nextId.value == 1;
// 处方保存成功后,自动将医嘱信息同步至患者处置记录
// handleEmrTreatment();
} else {
proxy.$modal.msgError(res.message);
conso;
isSaving.value = false;
}
})
@@ -1319,22 +1313,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状态