住院护士站-》医嘱执行页面:勾选医嘱后点击“执行选中”按钮无反应,无法完成执行操作.

疾病报告管理-》报告卡管理:审核报卡界面内容与门诊医生站登记界面不一致
This commit is contained in:
2026-04-30 13:33:03 +08:00
parent b536eadd92
commit 82ef66794b
5 changed files with 328 additions and 83 deletions

View File

@@ -69,11 +69,12 @@ export function getAdjustPriceSwitchState(params) {
/**
* 批次号匹配
*/
export function lotNumberMatch(params) {
export function lotNumberMatch(params, config = {}) {
return request({
url: '/app-common/lot-number-match',
method: 'get',
params: params,
...config
});
}

View File

@@ -511,6 +511,86 @@
</template>
</el-drawer>
<InfectiousDiseaseReportDialog
ref="reportDialogRef"
:title="drawerMode === 'audit' ? '审核报卡' : '查看报卡'"
read-only
@close="handleDrawerClose"
>
<template #append>
<!-- 审核记录 -->
<div class="audit-records-section" v-if="auditRecords.length > 0">
<h3 class="section-title">审核记录</h3>
<el-timeline>
<el-timeline-item
v-for="record in auditRecords"
:key="record.auditId"
:timestamp="record.auditTime"
placement="top"
:type="getAuditType(record.auditStatusTo)"
>
<el-card>
<div class="record-content">
<div class="record-header">
<span class="auditor">{{ record.auditorName }}</span>
<el-tag size="small" :type="getAuditType(record.auditStatusTo)">
{{ getAuditTypeName(record.auditType) }}
</el-tag>
</div>
<div class="record-detail">
<span v-if="record.auditOpinion">审核意见{{ record.auditOpinion }}</span>
<span v-if="record.reasonForReturn">退回原因{{ record.reasonForReturn }}</span>
</div>
<div class="record-status">
{{ getStatusName(record.auditStatusFrom) }} {{ getStatusName(record.auditStatusTo) }}
</div>
</div>
</el-card>
</el-timeline-item>
</el-timeline>
</div>
<!-- 审核操作区域 -->
<div class="audit-action-section" v-if="drawerMode === 'audit'">
<h3 class="section-title">审核操作</h3>
<el-form :model="auditForm" label-width="100px">
<el-form-item label="审核意见" required>
<el-input
v-model="auditForm.auditOpinion"
type="textarea"
:rows="3"
placeholder="请填写审核意见"
/>
</el-form-item>
<el-form-item label="退回原因">
<el-input
v-model="auditForm.returnReason"
type="textarea"
:rows="3"
placeholder="如需退回,请填写退回原因"
maxlength="50"
show-word-limit
/>
</el-form-item>
</el-form>
</div>
</template>
<template #footer="{ close }">
<div class="drawer-footer">
<el-button @click="close">关闭</el-button>
<template v-if="drawerMode === 'audit'">
<el-button type="warning" @click="handleReturnCard" :disabled="!auditForm.returnReason">
退回修改
</el-button>
<el-button type="success" @click="handlePassCard" :disabled="!auditForm.auditOpinion">
审核通过
</el-button>
</template>
</div>
</template>
</InfectiousDiseaseReportDialog>
<!-- 批量审核弹窗 -->
<el-dialog
v-model="batchAuditDialogVisible"
@@ -568,6 +648,7 @@ import { ElMessage, ElMessageBox } from 'element-plus';
import { Clock, CircleClose, CircleCheck, Document, Search, Refresh, DocumentChecked, RefreshLeft, Download } from '@element-plus/icons-vue';
import { useDict } from '@/utils/dict';
import useUserStore from '@/store/modules/user';
import InfectiousDiseaseReportDialog from '@/views/doctorstation/components/diagnosis/infectiousDiseaseReportDialog.vue';
import {
listInfectiousCards,
getInfectiousCard,
@@ -623,6 +704,7 @@ const selectedRows = ref([]);
const drawerVisible = ref(false);
const drawerMode = ref('view'); // view | audit
const drawerLoading = ref(false);
const reportDialogRef = ref(null);
const currentCard = ref({});
const auditRecords = ref([]);
const auditForm = ref({
@@ -823,14 +905,12 @@ function handleSelectionChange(rows) {
// 审核
function handleAudit(row) {
drawerMode.value = 'audit';
drawerVisible.value = true;
loadCardDetail(row.cardNo);
}
// 查看
function handleView(row) {
drawerMode.value = 'view';
drawerVisible.value = true;
loadCardDetail(row.cardNo);
}
@@ -884,6 +964,7 @@ async function loadCardDetail(cardNo) {
};
// 如果没有单独的审核记录 API尝试从详情数据中获取
auditRecords.value = res.data.auditRecords || res.auditRecords || [];
reportDialogRef.value?.showReport(currentCard.value);
} else {
ElMessage.error('获取卡片详情失败');
}
@@ -920,7 +1001,7 @@ async function handlePassCard() {
if (res.code === 200) {
ElMessage.success('审核通过');
handleDrawerClose();
reportDialogRef.value?.close?.();
loadTableData();
loadStats();
} else {
@@ -954,7 +1035,7 @@ async function handleReturnCard() {
if (res.code === 200) {
ElMessage.success('已退回修改');
handleDrawerClose();
reportDialogRef.value?.close?.();
loadTableData();
loadStats();
} else {

View File

@@ -9,7 +9,7 @@
>
<template #header>
<el-card class="report-header" shadow="never">
<h1 class="report-title">中华人民共和国传染病报告卡</h1>
<h1 class="report-title">{{ title }}</h1>
<el-space align="center" class="card-number-row">
<span class="card-number-label">卡片编号</span>
<el-input
@@ -17,13 +17,14 @@
class="card-number-input"
placeholder="单位自编,与网络直报一致"
maxlength="12"
:disabled="readOnly"
/>
</el-space>
</el-card>
</template>
<el-card class="report-form" shadow="never">
<el-form ref="formRef" :model="form" :rules="rules" label-position="top">
<el-form ref="formRef" :model="form" :rules="rules" label-position="top" :disabled="readOnly">
<!-- 患者姓名家长姓名身份证号 -->
<el-row :gutter="16" class="form-row">
<el-col :span="8" class="form-item">
@@ -503,14 +504,17 @@
</el-col>
</el-row>
</el-form>
<slot name="append" :form="form" />
</el-card>
<template #footer>
<el-space :size="16" justify="center" class="dialog-footer-space" style="display: flex; justify-content: center; width: 100%;">
<el-button type="primary" @click="handleSubmit" :loading="submitLoading" class="blue-button">保 存</el-button>
<el-button type="info" @click="handleClose">关 闭</el-button>
<el-button type="danger" @click="handleReset"> </el-button>
</el-space>
<slot name="footer" :close="handleClose" :submit-loading="submitLoading">
<el-space :size="16" justify="center" class="dialog-footer-space" style="display: flex; justify-content: center; width: 100%;">
<el-button v-if="!readOnly" type="primary" @click="handleSubmit" :loading="submitLoading" class="blue-button">保 存</el-button>
<el-button type="info" @click="handleClose">关 闭</el-button>
<el-button v-if="!readOnly" type="danger" @click="handleReset"> </el-button>
</el-space>
</slot>
</template>
</el-dialog>
</template>
@@ -545,6 +549,14 @@ const formRef = ref(null);
const submitLoading = ref(false);
const props = defineProps({
title: {
type: String,
default: '中华人民共和国传染病报告卡',
},
readOnly: {
type: Boolean,
default: false,
},
patientInfo: {
type: Object,
default: () => ({}),
@@ -1010,6 +1022,117 @@ function parseBirthDate(birthDate) {
};
}
function normalizeDate(value) {
if (!value) return '';
return String(value).split(/[T ]/)[0];
}
function normalizeSex(value) {
if (value === '1' || value === 1 || value === '男') return '男';
if (value === '2' || value === 2 || value === '女') return '女';
return '未知';
}
function normalizeAgeUnit(value) {
const ageUnitMap = {
1: '岁',
2: '月',
3: '天',
'1': '岁',
'2': '月',
'3': '天',
'岁': '岁',
'月': '月',
'天': '天',
};
return ageUnitMap[value] || '岁';
}
function getDiseaseSelection(diseaseCode) {
const code = diseaseCode ? String(diseaseCode) : '';
return {
selectedClassA: code.startsWith('01') ? code : '',
selectedClassB: code.startsWith('02') ? code : '',
selectedClassC: code.startsWith('03') ? code : '',
};
}
function resetAddressSelector() {
provinceCode.value = '';
cityCode.value = '';
countyCode.value = '';
townCode.value = '';
cityOptions.value = [];
countyOptions.value = [];
townOptions.value = [];
}
/**
* 以只读详情方式打开报卡弹窗,供报卡管理等页面复用医生站报卡样式。
* @param {Object} reportData - 报卡详情数据
*/
function showReport(reportData = {}) {
dialogVisible.value = true;
resetAddressSelector();
initProvinceOptions();
const birthInfo = parseBirthDate(reportData.birthday || reportData.birthDate);
const diseaseCode = reportData.diseaseCode ? String(reportData.diseaseCode) : '';
const diseaseSelection = getDiseaseSelection(diseaseCode);
form.value = {
cardNo: reportData.cardNo || '',
patName: reportData.patName || reportData.patientName || '',
parentName: reportData.parentName || '',
idNo: reportData.idNo || '',
sex: normalizeSex(reportData.sex),
birthYear: birthInfo.year,
birthMonth: birthInfo.month,
birthDay: birthInfo.day,
age: reportData.age != null ? String(reportData.age) : '',
ageUnit: normalizeAgeUnit(reportData.ageUnit),
workplace: reportData.workplace || '',
phone: reportData.phone || '',
contactPhone: reportData.contactPhone || '',
addressProv: reportData.addressProv || '',
addressCity: reportData.addressCity || '',
addressCounty: reportData.addressCounty || '',
addressTown: reportData.addressTown || '',
addressVillage: reportData.addressVillage || '',
addressHouse: reportData.addressHouse || '',
patientBelong: reportData.patientBelong || 1,
occupation: reportData.occupation || '',
caseClass: reportData.caseClass != null ? String(reportData.caseClass) : '',
onsetDate: normalizeDate(reportData.onsetDate),
diagDate: normalizeDate(reportData.diagDate),
deathDate: normalizeDate(reportData.deathDate),
selectedDiseases: diseaseCode && diseaseCode !== 'OTHER' ? [diseaseCode] : [],
selectedClassA: diseaseSelection.selectedClassA,
selectedClassB: diseaseSelection.selectedClassB,
selectedClassC: diseaseSelection.selectedClassC,
otherDisease: reportData.otherDisease || (diseaseCode === 'OTHER' ? reportData.diseaseName || '' : ''),
diseaseType: reportData.diseaseType || '',
reportOrg: reportData.reportOrg || '',
reportOrgPhone: reportData.reportOrgPhone || '',
reportDoc: reportData.reportDoc || '',
reportDate: normalizeDate(reportData.reportDate || reportData.createdAt),
correctName: reportData.correctName || '',
withdrawReason: reportData.withdrawReason || '',
remark: reportData.remark || '',
encounterId: reportData.encounterId || reportData.visitId || '',
patientId: reportData.patientId || reportData.patId || '',
diagnosisId: reportData.diagnosisId || reportData.diagId || '',
};
initAddressByName(
form.value.addressProv,
form.value.addressCity,
form.value.addressCounty,
form.value.addressTown
);
}
/**
* 从身份证号自动解析出生日期和性别
* @param {string} idNo - 身份证号码15位或18位
@@ -1118,13 +1241,7 @@ function show(diagnosisData) {
dialogVisible.value = true;
// 重置地址选择器状态
provinceCode.value = '';
cityCode.value = '';
countyCode.value = '';
townCode.value = '';
cityOptions.value = [];
countyOptions.value = [];
townOptions.value = [];
resetAddressSelector();
// 初始化省级地址选项
initProvinceOptions();
@@ -1502,7 +1619,7 @@ function handleClose() {
emit('close');
}
defineExpose({ show });
defineExpose({ show, showReport, close: handleClose });
</script>
<style scoped>

View File

@@ -14,8 +14,8 @@
<el-date-picker
v-model="deadline"
type="datetime"
format="YYYY/MM/DD HH:mm:ss"
value-format="YYYY/MM/DD HH:mm:ss"
format="YYYY-MM-DD HH:mm:ss"
value-format="YYYY-MM-DD HH:mm:ss"
:clearable="false"
@change="handleGetPrescription"
/>
@@ -33,8 +33,8 @@
<el-date-picker
v-model="exeDate"
type="datetime"
format="YYYY/MM/DD HH:mm:ss"
value-format="YYYY/MM/DD HH:mm:ss"
format="YYYY-MM-DD HH:mm:ss"
value-format="YYYY-MM-DD HH:mm:ss"
:clearable="false"
@change="handleGetPrescription"
/>
@@ -254,6 +254,20 @@ function handleRadioChange() {
handleGetPrescription();
}
/** 频次时间规整为 HH:mm与 formatDateStr 得到的时分一致,避免 8:00 vs 08:00 对不上) */
function normalizeDayTimeHm(part) {
if (part == null || String(part).trim() === '') {
return '00:00';
}
const s = String(part).trim();
const [hRaw, mRaw = '0'] = s.split(':').map((x) => String(x).trim());
const hNum = Number.parseInt(hRaw || '0', 10);
const mNum = Number.parseInt(mRaw || '0', 10);
const h = Number.isNaN(hNum) ? '00' : String(hNum).padStart(2, '0');
const m = Number.isNaN(mNum) ? '00' : String(mNum).padStart(2, '0');
return `${h}:${m}`;
}
function handleGetPrescription() {
if (patientInfoList.value.length > 0) {
loading.value = true;
@@ -273,8 +287,12 @@ function handleGetPrescription() {
let rate;
let times;
if (prescription.therapyEnum == 1) {
// 长期医嘱 后台返回执行时间点字符串,直接拆分取时间点
rate = prescription.dayTimes?.split(',');
// 长期医嘱dayTimes 可能为「8:00,12:00」未补零需与后端 occurrenceTime 格式化后对齐
rate = prescription.dayTimes
?.split(',')
.map((x) => x.trim())
.filter((x) => x !== '')
.map((x) => normalizeDayTimeHm(x));
// 用截止时间和医嘱签发时间算出全部执行日期
times = getDateRange(prescription.requestTime, deadline.value);
} else {
@@ -390,6 +408,11 @@ function handleGetPrescription() {
}
});
}
// 「待执行」tab 下医嘱所有时间点都已执行/不执行时,跳过该医嘱
// (后端待执行查询未做该过滤,需要前端补充;其他 tab 后端已按记录列表过滤,不再处理)
if (props.exeStatus === 1 && (!prescription.times || prescription.times.length === 0)) {
return groups;
}
// 把相同encounterId的医嘱放在同一个数组中
const encounterId = prescription.encounterId;
if (!groups[encounterId]) {
@@ -435,8 +458,13 @@ function handleExecute() {
console.log(list, 'list');
adviceExecute({ exeDate: exeDate.value, adviceExecuteDetailList: list }).then((res) => {
if (res.code == 200) {
proxy.$modal.msgSuccess(res.msg || '医嘱执行成功');
handleGetPrescription();
lotNumberMatch({ encounterIdList: encounterIds });
lotNumberMatch({ encounterIdList: encounterIds }, { skipErrorMsg: true }).catch((error) => {
console.warn('lotNumberMatch failed after adviceExecute:', error);
});
} else {
proxy.$modal.msgError(res.msg || '医嘱执行失败');
}
});
}
@@ -456,7 +484,10 @@ function handleNoExecute() {
console.log(list, 'list');
adviceNoExecute({ adviceExecuteDetailList: list }).then((res) => {
if (res.code == 200) {
proxy.$modal.msgSuccess(res.msg || '操作成功');
handleGetPrescription();
} else {
proxy.$modal.msgError(res.msg || '操作失败');
}
});
}
@@ -477,7 +508,10 @@ function handleCancel() {
});
adviceCancel({ adviceExecuteDetailList: producerIds }).then((res) => {
if (res.code == 200) {
proxy.$modal.msgSuccess(res.msg || '取消执行成功');
handleGetPrescription();
} else {
proxy.$modal.msgError(res.msg || '取消执行失败');
}
});
}
@@ -761,4 +795,4 @@ defineExpose({
font-size: 15px;
font-weight: 500;
}
</style>
</style>