Files
his/openhis-ui-vue3/src/views/doctorstation/index.vue
chenqi 75737cf95c feat(doctorstation): 添加取消接诊功能
- 在医生工作站界面添加取消接诊按钮
- 实现取消接诊的前端处理逻辑和确认对话框
- 添加计算属性控制取消接诊按钮的禁用状态
- 完善后端取消接诊服务的安全性检查和异常处理
- 优化取消接诊时的业务数据验证流程
- 添加详细的错误提示和用户反馈机制
2026-03-11 16:21:49 +08:00

1169 lines
38 KiB
Vue
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<template>
<div style="display: flex; justify-content: space-between; height: 90vh">
<div style="width: 15%; height: 100%; border: 1px solid #eee; border-right: 0">
<div
style="padding: 10px; border: 1px solid #eee; height: 50px; border-right: 0; display: flex; align-items: center; justify-content: space-between">
<div style="display: flex; align-items: center;">
<span style="margin-right: 20px; font-weight: 600;">现诊患者</span>
<el-button type="primary" size="small" @click.stop="handleOpenCallDialog" title="点击打开叫号界面">
<i class="el-icon-bell"></i> 呼叫
</el-button>
</div>
<el-badge :value="waitCount > 0 ? waitCount : ''" :max="10" style="color: #409eff; cursor: pointer;">
<span @click="openDrawer" style="font-weight: 600;"> 患者队列 </span>
</el-badge>
</div>
<div style="width: 100%; padding: 10px">
<el-input v-model="queryParams.searchKey" placeholder="请输入患者名" clearable
style="width: 100%; margin-bottom: 10px" @keyup.enter="getPatientList">
<template #append>
<el-button icon="Search" @click="getPatientList" />
</template>
</el-input>
<el-date-picker v-model="registerTime" @change="handleTimeChange" type="date"
style="width: 100%; margin-bottom: 10px" :clearable="false" placeholder="挂号时间" format="YYYY-MM-DD"
value-format="YYYY-MM-DD" />
<el-scrollbar height="700px">
<div v-for="(item, index) in patientList" :class="item.active ? 'patient-card actived' : 'patient-card'"
:key="item.id" @click="handleCardClick(item, index)">
<div class="main-info-container">
<!-- <el-avatar
:size="30"
src="\src\assets\images\man.png"
fit="scale-down"
/> -->
<div class="name-container">
<!-- 患者姓名 -->
<div class="name" style="max-width: 90px">
<el-text tclass="name" width="auto">{{ item.patientName || '未知' }}</el-text>
</div>
</div>
<div class="name-container">
<!-- 患者性别/年龄 -->
<div class="age">
<el-text tclass="name" width="auto">
{{ item.genderEnum_enumText }}/{{ item.age }}/{{ item.typeCode_dictText }}
</el-text>
</div>
</div>
</div>
<div class="doctor-parent-line" />
<div class="personal-info-container">
<div class="name-container">
<div class="name">
<el-text tclass="name" width="auto">挂号时间</el-text>
<el-text tclass="name" width="auto">
{{ item.registerTime ? formatDate(item.registerTime) : '-' }}
</el-text>
</div>
</div>
</div>
<div class="personal-info-container">
<div class="name-container">
<div class="name">
<el-text tclass="name" width="auto">病历号</el-text>
<el-text tclass="name" width="auto">
{{ item.busNo ? item.busNo : '-' }}
</el-text>
</div>
</div>
</div>
</div>
</el-scrollbar>
</div>
</div>
<div class="disabled-wrapper" style="width: 85%; border: 1px solid #eee; position: relative">
<div style="padding: 10px; border: 1px solid #eee; min-height: 50px; border-left: 0; display: flex; align-items: center; justify-content: space-between; flex-wrap: wrap; gap: 10px;">
<div style="display: flex; align-items: flex-start; gap: 20px; flex: 1; flex-wrap: wrap;">
<div style="display: flex; align-items: flex-start;">
<span style="color: #606266; font-size: 14px; margin-right: 8px; flex-shrink: 0; line-height: 1.5;">患者信息:</span>
<span style="font-size: 14px; line-height: 1.5; word-break: break-all;">
{{
Object.keys(patientInfo).length !== 0
? patientInfo.patientName + ' / ' + patientInfo.age + ' / ' + patientInfo.genderEnum_enumText + ' / ' + (patientInfo?.contractName ? patientInfo.contractName : '') + '/' + patientInfo.phone + '/' + patientInfo.busNo
: '-'
}}
</span>
</div>
<div style="display: flex; align-items: center; flex-shrink: 0; line-height: 1.5;">
<span style="color: #606266; font-size: 14px; margin-right: 8px;">挂号时间:</span>
<span style="font-size: 14px;">{{ Object.keys(patientInfo).length !== 0 ? formatDate(patientInfo.registerTime) : '-' }}</span>
</div>
<div style="display: flex; align-items: center; flex-shrink: 0; line-height: 1.5;">
<span style="color: #606266; font-size: 14px; margin-right: 8px;">医生:</span>
<span style="font-size: 14px;">{{ userStore.nickName }}</span>
</div>
</div>
<div style="display: flex; align-items: center; gap: 8px; flex-shrink: 0;">
<el-radio-group v-model="firstEnum" style="margin-right: 5px;">
<el-radio :label="1">初诊</el-radio>
<el-radio :label="2">复诊</el-radio>
</el-radio-group>
<el-button type="primary" plain @click.stop="handleFinish(patientInfo.encounterId)" size="small">完诊</el-button>
<el-button type="primary" plain @click.stop="handleLeave(patientInfo.encounterId)" size="small">暂离</el-button>
<el-button type="warning" plain :disabled="isCancelButtonDisabled" @click.stop="handleCancel(patientInfo.encounterId)" size="small">取消接诊</el-button>
<el-button type="primary" plain :disabled="isRefundButtonDisabled" @click.stop="handleRefund(patientInfo.encounterId)" size="small">退费</el-button>
<el-button type="primary" plain class="top-layer-btn" @click.stop="getEnPrescription(patientInfo.encounterId)" size="small">处方单</el-button>
<el-button type="primary" plain class="top-layer-btn" :disabled="isHospitalizationButtonDisabled" @click.stop="handleHospitalizationClick()" size="small">办理住院</el-button>
</div>
</div>
<div style="padding: 10px; position: relative">
<el-tabs type="card" style="width: 100%; height: 100%" v-loading="loading" v-model="activeTab"
@tab-change="handleClick(activeTab)">
<el-tab-pane label="门诊病历" name="hospitalizationEmr">
<hospitalizationEmr :patientInfo="patientInfo" :activeTab="activeTab" @emrSaved="handleEmrSaved" />
</el-tab-pane>
<el-tab-pane label="待写病历" name="pendingEmr">
<PendingEmr @writeEmr="handleWriteEmr" @viewPatient="handleViewPatient" />
</el-tab-pane>
<!-- <el-tab-pane label="病历" name="emr">
<Emr
:patientInfo="patientInfo"
ref="emrRef"
@save="
(value) => {
saveStatus = value;
}
"
/>
</el-tab-pane> -->
<el-tab-pane label="诊断" name="diagnosis">
<Diagnosis :patientInfo="patientInfo" ref="diagnosisRef" @diagnosisSave="
(value) => {
saveStatus = value;
}
" />
</el-tab-pane>
<el-tab-pane label="医嘱" name="prescription">
<prescriptionlist :patientInfo="patientInfo" ref="prescriptionRef" :activeTab="activeTab"
:outpatientEmrSaved="outpatientEmrSaved" />
</el-tab-pane>
<el-tab-pane label="中医" name="tcm">
<tcmAdvice :patientInfo="patientInfo" ref="tcmRef" />
</el-tab-pane>
<el-tab-pane label="检验" name="inspection">
<inspectionApplication :patientInfo="patientInfo" :activeTab="activeTab" ref="inspectionRef" />
</el-tab-pane>
<el-tab-pane label="检查" name="examination">
<examinationApplication :patientInfo="patientInfo" :activeTab="activeTab" ref="examinationRef"
@saved="() => prescriptionRef?.getListInfo()" />
</el-tab-pane>
<el-tab-pane label="手术申请" name="surgery">
<surgeryApplication :patientInfo="patientInfo" :activeTab="activeTab" ref="surgeryRef" />
</el-tab-pane>
<el-tab-pane label="电子处方" name="eprescription">
<eprescriptionlist :patientInfo="patientInfo" ref="eprescriptionRef" />
</el-tab-pane>
<el-tab-pane label="报告查询" name="reportQuery">
<ReportQuery :patientInfo="patientInfo" ref="reportQueryRef" />
</el-tab-pane>
<el-tab-pane label="会诊" name="consultation">
<Consultation :patientInfo="patientInfo" :activeTab="activeTab" ref="consultationRef" />
</el-tab-pane>
<el-tab-pane label="传染病报卡" name="infectiousReport">
<InfectiousReport :patientInfo="patientInfo" :activeTab="activeTab" ref="infectiousReportRef" @saved="handleInfectiousReportSaved" />
</el-tab-pane>
</el-tabs>
<div class="overlay" :class="{ 'overlay-disabled': disabled }" v-if="disabled"></div>
</div>
</div>
<el-drawer v-model="drawer" title="患者队列" direction="ltr" @open="handleOpen">
<PatientList ref="patientDrawerRef" @toCurrent="handleReceive" />
</el-drawer>
<RefundListDialog :open="openRefundListDialog" :encounterId="currentEncounterId"
@close="openRefundListDialog = false" @refresh="() => prescriptionRef.getListInfo()" />
<HospitalizationDialog :open="openDialog" :patientInfo="patientInfo" :encounterId="currentEncounterId"
:mainDiagnosis="mainDiagnosis" @close="openDialog = false" />
<PrescriptionInfo :open="openPrescriptionDialog" :precriptionInfo="prescriptionInfo"
@close="openPrescriptionDialog = false" />
<!-- 新增叫号弹窗组件 -->
<DoctorCallDialog v-model:dialogVisible="dialogVisible" :current-patient="currentCallPatient"
:current-patient-list="currentWaitPatientList" :room-no="roomNo"
:department="userStore.orgName || patientInfo.organizationName || '心内科'" @callNext="callNext" @reCall="reCall"
@finish="finishCall" @skip="skip" @requeue="requeue" @markSeen="markSeen" @callThis="callThis" />
</div>
</template>
<script setup>
import hospitalizationEmr from './components/hospitalizationEmr/index.vue';
import PendingEmr from './components/pendingEmr/index.vue';
import {
completeEncounter,
getEncounterDiagnosis,
getEnPrescriptionInfo,
getList,
isHospitalization,
leaveEncounter,
cancelEncounter,
} from './components/api.js';
import prescriptionlist from './components/prescription/prescriptionlist.vue';
import RefundListDialog from './components/prescription/refundListDialog.vue';
import ReportQuery from './components/reportQuery.vue';
import Consultation from './components/consultation.vue';
import PatientList from './components/patientList.vue';
import Diagnosis from './components/diagnosis/diagnosis.vue';
import PrescriptionInfo from './components/prescription/prescriptionInfo.vue';
import eprescriptionlist from './components/eprescriptionlist.vue';
import HospitalizationDialog from './components/hospitalizationDialog.vue';
import tcmAdvice from './components/tcm/tcmAdvice.vue';
import inspectionApplication from './components/inspection/inspectionApplication.vue';
import examinationApplication from './components/examination/examinationApplication.vue';
import surgeryApplication from './components/surgery/surgeryApplication.vue';
import DoctorCallDialog from './components/callQueue/DoctorCallDialog.vue';
import InfectiousReport from './components/infectiousReport/index.vue';
import { formatDate, formatDateStr } from '@/utils/index';
import useUserStore from '@/store/modules/user';
import { nextTick } from 'vue';
import { updatePatientInfo } from './components/store/patient.js';
import { ElMessage, ElMessageBox } from 'element-plus';
import { useRoute } from 'vue-router';
// // 监听路由离开事件
// onBeforeRouteLeave((to, from, next) => {
// // 弹出确认框
// const confirmLeave = window.confirm('确定要离开吗?未保存的数据可能丢失!');
// if (confirmLeave) {
// next(); // 允许离开
// } else {
// next(false); // 取消离开
// }
// });
defineOptions({
name: 'PatientParentCard',
});
const route = useRoute();
// 监听路由参数变化
watch(
() => route.query.tab,
(newTab) => {
if (newTab === 'pendingEmr') {
console.log('Route tab changed to pendingEmr');
activeTab.value = 'pendingEmr';
}
},
{ immediate: true }
);
const userStore = useUserStore();
const bedfont = 'bed-font';
const queryParams = ref({
pageNo: 1,
pageSize: 300,
registerTimeSTime: formatDateStr(new Date(), 'YYYY-MM-DD') + ' 00:00:00',
registerTimeETime: formatDateStr(new Date(), 'YYYY-MM-DD') + ' 23:59:59',
});
const drawer = ref(false);
const openRefundListDialog = ref(false);
const openDialog = ref(false);
const openPrescriptionDialog = ref(false);
const dialogVisible = ref(false); // 叫号弹窗显示/隐藏
const currentCallPatient = ref({}); // 传给弹窗的【当前就诊患者】
const currentWaitPatientList = ref([]); // 传给弹窗的【候诊患者列表】
const roomNo = ref('4号'); // 诊室号
const saveStatus = ref(false);
const outpatientEmrSaved = ref(false); // 门诊病历保存状态
const currentEncounterId = ref('');
const emits = defineEmits(['click']);
// const activeTab = ref('emr');
const activeTab = ref('hospitalizationEmr');
const mainDiagnosis = ref(null);
const patientList = ref([]);
const patientInfo = ref({});
const visitTypeDisabled = ref(false);
// 计算属性:确定办理住院按钮是否应被禁用
const isHospitalizationButtonDisabled = computed(() => {
return !patientInfo.value ||
typeof patientInfo.value !== 'object' ||
!patientInfo.value.encounterId ||
patientInfo.value.encounterId === '' ||
patientInfo.value.encounterId === null ||
patientInfo.value.encounterId === undefined;
});
// 计算属性:确定取消接诊按钮是否应被禁用
const isCancelButtonDisabled = computed(() => {
return !patientInfo.value ||
typeof patientInfo.value !== 'object' ||
!patientInfo.value.encounterId ||
patientInfo.value.encounterId === '' ||
patientInfo.value.encounterId === null ||
patientInfo.value.encounterId === undefined;
});
// 计算属性:确定退费按钮是否应被禁用(与住院按钮使用相同的逻辑)
const isRefundButtonDisabled = computed(() => {
return !patientInfo.value ||
typeof patientInfo.value !== 'object' ||
!patientInfo.value.encounterId ||
patientInfo.value.encounterId === '' ||
patientInfo.value.encounterId === null ||
patientInfo.value.encounterId === undefined;
});
const prescriptionInfo = ref([]);
const registerTime = ref(formatDate(new Date()));
const patientDrawerRef = ref();
const prescriptionRef = ref();
const tcmRef = ref();
const inspectionRef = ref();
const examinationRef = ref();
const surgeryRef = ref();
const emrRef = ref();
const diagnosisRef = ref();
const consultationRef = ref();
const infectiousReportRef = ref();
const waitCount = ref(0);
const loading = ref(false);
const { proxy } = getCurrentInstance();
const visitType = ref('');
const firstVisitDate = ref('');
const firstEnum = ref(1); // 初复诊标识1=初诊2=复诊
const disabled = computed(() => {
// 只有在有患者信息但某些条件不满足时才启用覆盖层
// 当前逻辑保持不变,但我们将在按钮级别处理禁用状态
return Object.keys(patientInfo.value).length === 0;
});
const shortcuts = [
{
text: '今天',
value: new Date(),
},
{
text: '昨天',
value: () => {
const date = new Date();
date.setDate(date.getDate() - 1);
return date;
},
},
{
text: '三天内',
value: () => {
const date = new Date();
date.setDate(date.getDate() - 3);
return date;
},
},
{
text: '一周内',
value: () => {
const date = new Date();
date.setDate(date.getDate() - 7);
return date;
},
},
];
const eprescriptionRef = ref();
onMounted(() => {
getWaitPatient();
getWaitPatientList();
getPatientList();
// 检查路由参数,如果指定了待写病历,则默认选中该选项卡
console.log('Route query:', route.query); // 调试信息
if (route.query.tab === 'pendingEmr') {
console.log('Switching to pendingEmr tab'); // 调试信息
activeTab.value = 'pendingEmr';
}
// 确保DOM更新后激活正确的选项卡
nextTick(() => {
if (route.query.tab === 'pendingEmr') {
// 强制触发选项卡切换
handleClick('pendingEmr');
}
});
});
// 获取现诊患者列表
function getPatientList() {
queryParams.value.statusEnum = 2;
getList(queryParams.value).then((res) => {
// console.log('API返回的完整数据:', res); // 添加这行来查看完整返回
// console.log('API返回的数据记录:', res.data.records); // 查看具体数据记录
patientList.value = res.data.records.map((item) => {
// console.log('处理的单个患者数据:', item); // 查看处理的单个患者数据
return {
...item,
active: currentEncounterId.value ? item.encounterId == currentEncounterId.value : false,
};
});
});
}
function setVisitType(type) {
visitType.value = type;
}
function checkPatientHistory(patient) {
// 重置状态
visitTypeDisabled.value = false;
firstVisitDate.value = '';
// 如果患者没有身份证号,无法判断是否为初诊
if (!patient.idCard) {
// 默认设置为初诊
visitType.value = 'FIRST';
return;
}
// 查询患者历史就诊记录
const params = {
patientId: patient.patientId,
idCard: patient.idCard
};
getEmrHistoryList(params).then(res => {
if (res.code === 200) {
const records = res.data?.records || [];
// 过滤掉当前正在进行的就诊记录排除相同encounterId的记录
const historyRecords = records.filter(record => record.encounterId !== patient.encounterId);
// 如果有历史记录(排除当前就诊),则为复诊
if (historyRecords.length > 0) {
visitType.value = 'FOLLOW_UP';
// 计算最早一次病历创建时间作为初诊日期
const earliest = historyRecords.reduce((min, cur) => {
const ct = new Date(cur.createTime).getTime();
return ct < min ? ct : min;
}, new Date(historyRecords[0].createTime).getTime());
// 使用统一格式化
firstVisitDate.value = formatDate(earliest);
} else {
// 如果没有历史记录,则为初诊
visitType.value = 'FIRST';
firstVisitDate.value = '';
}
} else {
// 请求失败,默认设置为初诊
visitType.value = 'FIRST';
firstVisitDate.value = '';
}
}).catch(() => {
// 异常情况,默认设置为初诊
visitType.value = 'FIRST';
firstVisitDate.value = '';
});
}
function getWaitPatient() {
queryParams.value.registerTimeSTime = formatDateStr(new Date(), 'YYYY-MM-DD') + ' 00:00:00';
queryParams.value.registerTimeETime = formatDateStr(new Date(), 'YYYY-MM-DD') + ' 23:59:59';
queryParams.value.statusEnum = 1;
getList(queryParams.value).then((res) => {
waitCount.value = res.data.total;
});
}
// 新增:获取候诊患者完整列表
function getWaitPatientList() {
queryParams.value.registerTimeSTime = formatDateStr(new Date(), 'YYYY-MM-DD') + ' 00:00:00';
queryParams.value.registerTimeETime = formatDateStr(new Date(), 'YYYY-MM-DD') + ' 23:59:59';
queryParams.value.statusEnum = 1; // 筛选【待诊】患者
getList(queryParams.value).then((res) => {
currentWaitPatientList.value = res.data.records;
waitCount.value = res.data.total;
});
}
// 新增:打开叫号弹窗方法
function handleOpenCallDialog() {
// 刷新患者列表和候诊列表,确保数据是最新的
getPatientList();
getWaitPatientList();
// 检查当前选中的患者是否已经完诊,如果已经完诊,就不设置为当前呼叫患者
if (patientInfo.value && patientInfo.value.statusEnum !== 3) { // 3代表已完诊
currentCallPatient.value = patientInfo.value;
} else {
// 如果当前患者已经完诊,就清空当前呼叫患者
currentCallPatient.value = {};
}
dialogVisible.value = true;
}
function handleClick(tab) {
switch (tab) {
case 'emr':
break;
case 'diagnosis':
diagnosisRef.value.getDetail(patientInfo.value.encounterId);
break;
case 'prescription':
prescriptionRef.value.getDiagnosisInfo();
break;
case 'tcm':
tcmRef.value.getDiagnosisInfo();
break;
case 'inspection':
// 确保检验组件获取最新的患者信息
if (patientInfo.value && patientInfo.value.encounterId) {
inspectionRef.value.getList();
}
break;
case 'examination':
if (patientInfo.value && patientInfo.value.encounterId) {
examinationRef.value.getList();
}
break;
case 'surgery':
surgeryRef.value.getList();
break;
case 'eprescription':
eprescriptionRef.value.getList();
break;
case 'consultation':
consultationRef.value.fetchConsultationList();
break;
}
// if (tab != 'emr') {
// if (!saveStatus.value) {
// emrRef.value.addEmr();
// }
// }
// if (tab != 'diagnosis') {
// if (!saveStatus.value) {
// let valid= diagnosisRef.value.handleSaveDiagnosis();
// console.log(valid);
// }
// }
}
// 查看本次就诊处方单从医嘱Tab页获取已开立的处方单信息
function getEnPrescription(encounterId) {
getEnPrescriptionInfo({ encounterId: encounterId }).then((res) => {
// console.log('处方单 res', res);
let dataArr = res.data.records || [];
if (dataArr.length <= 0) {
ElMessage({
type: 'error',
message: '暂无处方单',
});
return;
}
prescriptionInfo.value = res.data.records;
openPrescriptionDialog.value = true;
});
}
function handleRefund(encounterId) {
// 校验是否选择了患者
if (!encounterId) {
ElMessage.warning('请先选择患者后再进行退费操作');
return;
}
currentEncounterId.value = encounterId;
openRefundListDialog.value = true;
}
function handleOpen() {
patientDrawerRef.value.refreshList();
}
function handleCardClick(item, index) {
console.log('handleCardClick 被调用');
console.log('点击的患者项目:', item);
console.log('患者项目中的encounterId:', item.encounterId);
// 设置当前就诊ID确保住院功能能获取到正确的ID
currentEncounterId.value = item.encounterId;
console.log('currentEncounterId.value 设置为:', currentEncounterId.value);
loading.value = true;
patientList.value.forEach((patient) => {
patient.active = patient.encounterId === item.encounterId;
});
patientInfo.value = item;
console.log('patientInfo.value 设置为:', patientInfo.value);
console.log('patientInfo.value.encounterId:', patientInfo.value?.encounterId);
// 根据患者信息设置初复诊标识
const backendValue = item.firstEnum ?? item.first_enum;
if (backendValue !== undefined && backendValue !== null) {
firstEnum.value = Number(backendValue); // 确保是数字类型
} else {
firstEnum.value = 1;
}
// 确保患者信息包含必要的字段
if (!patientInfo.value.encounterId) {
console.error('患者信息缺少encounterId字段:', patientInfo.value);
// 可能需要从其他字段获取encounterId
if (item.id) {
patientInfo.value.encounterId = item.id;
console.log('使用item.id作为encounterId:', item.id);
}
}
// 添加安全检查确保item不为undefined或null
if (!item) {
console.error('handleCardClick: 传入的item为null或undefined');
return;
}
// 记录patientInfo内容到控制台
setTimeout(() => {
const patientInfoStr = JSON.stringify(patientInfo.value, null, 2);
console.log('当前patientInfo内容:', patientInfo.value);
console.log('patientInfo.encounterId值:', patientInfo.value?.encounterId);
}, 100);
// 将患者信息保存到store中供hospitalizationEmr组件使用
updatePatientInfo(item);
activeTab.value = 'hospitalizationEmr';
nextTick(() => {
prescriptionRef.value.getListInfo();
tcmRef.value.getListInfo();
inspectionRef.value.getList();
if(examinationRef.value) examinationRef.value.getList();
surgeryRef.value.getList();
diagnosisRef.value.getList();
eprescriptionRef.value.getList();
consultationRef.value.fetchConsultationList();
// emrRef.value.getDetail(item.encounterId);
setTimeout(() => {
loading.value = false;
}, 200);
});
}
function handleLeave(encounterId) {
leaveEncounter(encounterId).then((res) => {
if (res.code == 200) {
proxy.$modal.msgSuccess('操作成功');
patientInfo.value = {};
getPatientList();
}
});
}
function handleCancel(encounterId) {
if (!encounterId) {
ElMessage.warning('请先选择患者后再进行取消接诊操作');
return;
}
ElMessageBox.confirm('确定要取消接诊该患者吗?取消后患者将回到待诊状态。', '提示', {
confirmButtonText: '确定',
cancelButtonText: '取消',
type: 'warning'
}).then(() => {
cancelEncounter(encounterId).then((res) => {
if (res.code == 200) {
proxy.$modal.msgSuccess('取消接诊成功');
patientInfo.value = {};
getPatientList();
getWaitPatient();
} else {
proxy.$modal.msgError(res.msg || '取消接诊失败');
}
}).catch((error) => {
console.error('取消接诊失败:', error);
proxy.$modal.msgError('取消接诊失败');
});
}).catch(() => {
// 用户取消操作,不做处理
});
}
async function handleFinish(encounterId) {
// 完诊前验证诊断信息
try {
const diagnosisRes = await getEncounterDiagnosis(encounterId);
if (diagnosisRes.code === 200) {
const diagnosisList = diagnosisRes.data || [];
// 检查是否有诊断
if (diagnosisList.length === 0) {
proxy.$modal.msgWarning('请先添加诊断信息');
return;
}
// 检查诊断类型是否为空
const emptyMedType = diagnosisList.find(d => !d.medTypeCode);
if (emptyMedType) {
proxy.$modal.msgWarning('请选择诊断类型');
return;
}
// 检查是否有主诊断
if (!diagnosisList.some(d => d.maindiseFlag === 1)) {
proxy.$modal.msgWarning('请至少设置一条主诊断');
return;
}
}
} catch (error) {
console.error('获取诊断信息失败:', error);
proxy.$modal.msgWarning('获取诊断信息失败,请重试');
return;
}
completeEncounter({ encounterId, firstEnum: firstEnum.value }).then((res) => {
if (res.code == 200) {
proxy.$modal.msgSuccess('操作成功');
patientInfo.value = {};
visitType.value = ''; // 重置初复诊标识
visitTypeDisabled.value = false; // 重置禁用状态
firstEnum.value = 1; // 重置为初诊
getPatientList();
}
});
}
// 监听初复诊标识变化
watch(firstEnum, (newValue) => {
// 这里可以添加更新后端的逻辑,如果需要实时同步到后端
// 例如updateEncounterFirstEnum(patientInfo.value.encounterId, newValue)
});
function handleTimeChange(value) {
queryParams.value.registerTimeSTime = value + ' 00:00:00';
queryParams.value.registerTimeETime = value + ' 23:59:59';
getPatientList();
}
// 处理办理住院点击事件
function handleHospitalizationClick() {
console.log('handleHospitalizationClick 被调用');
console.log('当前patientInfo:', patientInfo.value);
console.log('当前patientInfo.encounterId:', patientInfo.value?.encounterId);
if (!patientInfo.value || !patientInfo.value.encounterId) {
console.log('患者信息不完整,无法办理住院');
ElMessage.warning('请先选择患者');
} else {
console.log('调用onHospitalization函数');
onHospitalization();
}
}
// 接诊回调
function handleReceive(row) {
handleCardClick(row);
currentEncounterId.value = row.encounterId;
drawer.value = false;
getPatientList();
getWaitPatient();
}
// 处理门诊病历保存成功事件
function handleEmrSaved(isSaved) {
outpatientEmrSaved.value = isSaved;
}
// 处理传染病报卡保存成功事件
function handleInfectiousReportSaved() {
// 可以在这里添加刷新列表或其他逻辑
proxy.$modal.msgSuccess('传染病报告卡保存成功');
}
// 处理写病历事件
function handleWriteEmr(row) {
console.log('处理写病历:', row);
// 这里可以触发切换到病历页面并加载患者信息
// 可以根据需要实现具体逻辑
}
// 处理查看患者事件
function handleViewPatient(row) {
console.log('处理查看患者:', row);
// 这里可以触发查看患者详细信息的逻辑
// 可以根据需要实现具体逻辑
}
function openDrawer() {
drawer.value = true;
}
// 判断是否已经入院登记
const onHospitalization = async () => {
console.log('onHospitalization 被调用');
console.log('patientInfo.value:', patientInfo.value);
console.log('patientInfo.value.encounterId:', patientInfo.value?.encounterId);
// 检查是否有有效的就诊ID
if (!patientInfo.value?.encounterId) {
console.log('缺少有效的就诊ID无法办理住院');
ElMessage({
type: 'error',
message: '患者就诊信息不完整,无法办理住院!',
});
return;
}
try {
console.log('开始获取诊断信息encounterId:', patientInfo.value.encounterId);
const diagnosisRes = await getEncounterDiagnosis(patientInfo.value.encounterId);
console.log('诊断信息获取结果:', diagnosisRes);
// 检查API调用是否成功
if (diagnosisRes.code !== 200) {
console.log('获取诊断信息失败:', diagnosisRes.msg);
ElMessage({
type: 'error',
message: diagnosisRes.msg || '获取诊断信息失败,无法办理住院!',
});
return;
}
const hasDiagnosis = diagnosisRes.data?.length > 0;
console.log('是否有诊断信息:', hasDiagnosis);
if (!hasDiagnosis) {
console.log('该患者暂无诊断信息');
const confirmResult = await ElMessageBox.confirm(
'该患者暂无诊断信息,是否前往添加诊断?',
'提示',
{
confirmButtonText: '前往添加',
cancelButtonText: '暂不办理',
type: 'warning',
}
).catch(() => {
// 用户取消操作
console.log('用户取消前往添加诊断');
return false;
});
if (confirmResult === true) {
// 自动切换到诊断标签页以便用户添加诊断
activeTab.value = 'diagnosis';
}
return;
}
const mainDiag = diagnosisRes.data.find((item) => item.maindiseFlag === 1);
console.log('主诊断信息:', mainDiag);
if (!mainDiag) {
console.log('该患者暂无主诊断信息');
const confirmResult = await ElMessageBox.confirm(
'该患者暂无主诊断信息,是否前往设置主诊断?',
'提示',
{
confirmButtonText: '前往设置',
cancelButtonText: '暂不办理',
type: 'warning',
}
).catch(() => {
// 用户取消操作
console.log('用户取消前往设置主诊断');
return false;
});
if (confirmResult === true) {
// 自动切换到诊断标签页以便用户设置主诊断
activeTab.value = 'diagnosis';
}
return;
}
mainDiagnosis.value = mainDiag;
console.log('主诊断设置成功:', mainDiagnosis.value);
console.log('检查住院状态encounterId:', patientInfo.value.encounterId);
const res = await isHospitalization({
encounterId: patientInfo.value.encounterId,
});
console.log('住院状态检查结果:', res);
// 检查API调用是否成功
if (res.code !== 200) {
console.log('检查住院状态失败:', res.msg);
ElMessage({
type: 'error',
message: res.msg || '检查住院状态失败!',
});
return;
}
console.log('住院状态检查API响应:', res);
console.log('res.data值:', res.data);
console.log('res.data类型:', typeof res.data);
if (!res.data) {
console.log('患者未办理过住院,打开住院登记对话框');
console.log('当前openDialog值:', openDialog.value);
openDialog.value = true;
console.log('设置openDialog为true后值为:', openDialog.value);
} else {
console.log('患者已办理过住院');
ElMessage({
type: 'error',
message: '该患者,已办理入院,不允许重复办理',
});
}
} catch (error) {
console.error('办理住院检查过程中发生错误:', error);
// 显示详细的错误信息
const errorMessage = error.message || error.msg || '办理住院过程中发生错误,请稍后重试!';
ElMessage({
type: 'error',
message: errorMessage,
duration: 5000 // 增加显示时长以便用户阅读
});
}
};
// ========== 叫号弹窗所有回调事件 ==========
const callNext = () => {
// 直接刷新患者列表和候诊列表API调用已经在子组件中完成
getPatientList();
getWaitPatientList();
// 子组件已处理提示,这里无需重复提示
};
const reCall = () => {
// 选呼功能已实现,无需提示
};
const finishCall = () => {
dialogVisible.value = false;
// 刷新患者列表和候诊列表
getPatientList();
getWaitPatientList();
// 清空当前呼叫患者
currentCallPatient.value = {};
// 子组件已处理提示,这里无需重复提示
};
const skip = () => {
// 直接刷新患者列表和候诊列表API调用已经在子组件中完成
getPatientList();
getWaitPatientList();
// 子组件已处理提示,这里无需重复提示
};
const requeue = () => {
// 1. 重新获取候诊患者列表
getWaitPatientList();
// 2. 刷新现诊患者列表
getPatientList();
// 3. 刷新候诊人数统计
getWaitPatient();
// 4. 清空当前呼叫患者
currentCallPatient.value = {};
// 子组件已处理提示,这里无需重复提示
};
const markSeen = async () => {
// 直接刷新患者列表和候诊列表API调用已经在子组件中完成
getPatientList();
getWaitPatientList();
// 清空当前呼叫患者
currentCallPatient.value = {};
};
const callThis = (row) => {
handleCardClick(row);
currentCallPatient.value = row;
dialogVisible.value = false;
// 刷新患者列表和候诊列表
getPatientList();
getWaitPatientList();
};
</script>
<style lang="scss" scoped>
// 患者信息
.patient-info-descriptions {
:deep(.el-descriptions__label) {
font-size: 16px !important;
}
:deep(.el-descriptions__content) {
font-size: 16px !important;
}
}
.patient-card {
width: 100%;
overflow: hidden;
background-color: #fff;
border: 1px solid;
border-color: #eee;
border-radius: 4px;
box-shadow: 0 2px 2px 0 rgba(57.55, 69.04, 86.28, 20%);
margin-bottom: 10px;
cursor: pointer;
&.actived {
background-color: rgb(7, 155, 140, 5%);
border-color: var(--el-color-primary);
}
.cross-dept {
height: 24px;
padding: 0 16px;
color: #fff;
font-size: 14px;
line-height: 24px;
background-color: #256d95;
}
.main-info-container {
display: flex;
align-items: center;
justify-content: space-between;
height: 32px;
margin: 7px 0;
padding: 0 16px;
.bed-container {
display: flex;
flex: 1;
align-items: center;
min-width: 0;
.bed {
flex-grow: 0;
flex-shrink: 1;
min-width: 0;
:deep(.bed-font) {
color: #333;
font-weight: 600;
font-size: 16px;
}
}
.bed_new {
flex-shrink: 0;
width: 10px;
height: 10px;
margin-left: 4px;
background: #29af6f;
border-radius: 50%;
}
}
.indepatient-code-container {
display: flex;
flex-shrink: 0;
align-items: center;
padding-left: 6px;
color: #666;
font-size: 14px;
.sign {
width: 24px;
height: 24px;
color: white;
line-height: 24px;
text-align: center;
border-radius: 50%;
user-select: none;
}
}
}
.doctor-parent-line {
margin: 0 16px;
border-bottom: 1px dashed #ddd;
}
.personal-info-container {
display: flex;
align-items: center;
justify-content: space-between;
margin: 8px 0;
padding: 0 16px;
.name-container {
display: flex;
align-items: center;
height: 18px;
.name {
color: #333;
font-size: 14px;
}
.age {
margin-left: 10px;
color: #666;
font-size: 14px;
}
}
.change-department {
width: 58px;
height: 24px;
color: #5585e3;
font-size: 14px;
line-height: 24px;
text-align: center;
background: #e6edfb;
border-radius: 4px;
}
}
.dept {
margin-bottom: 4px;
padding: 0 16px;
display: flex;
justify-content: space-between;
align-items: center;
.doctor {
display: flex;
align-items: center;
height: 32px;
line-height: 32px;
.doctor_name {
display: flex;
align-items: center;
margin-left: 4px;
color: #333;
}
}
.deptNurseName {
display: flex;
align-items: center;
height: 32px;
color: #256d95;
line-height: 32px;
}
}
}
:deep(.el-tabs__header) {
padding: 0;
position: relative;
margin: 0 0 5px !important;
}
:deep(.el-drawer__header) {
margin-bottom: 15px !important;
}
:deep(.el-drawer__body) {
padding: 10px !important;
}
.el-badge {
--el-badge-padding: 6px;
}
.disabled-wrapper .overlay {
position: absolute;
top: 50px;
left: 0;
width: 100%;
height: calc(100% - 50px);
z-index: 998;
/* 降低z-index避免覆盖按钮 */
/* 确保覆盖在内容上方,但不覆盖顶部按钮区域 */
cursor: not-allowed;
background-color: rgba(255, 255, 255, 0.01);
pointer-events: none;
/* 默认不捕获鼠标事件,只在真正需要禁用时才启用 */
}
.disabled-wrapper .overlay.overlay-disabled {
pointer-events: auto;
/* 当需要真正禁用时启用指针事件 */
}
/* 顶层按钮样式,确保按钮始终在最上层 */
.top-layer-btn {
position: relative !important;
z-index: 1000 !important;
}
</style>