90,分诊排队管理-》医生叫号界面
This commit is contained in:
@@ -90,4 +90,10 @@ public interface IDoctorStationMainAppService {
|
|||||||
*/
|
*/
|
||||||
List<ReceptionStatisticsDto> getReceptionStatistics(String startTime,String endTime,Long practitionerId);
|
List<ReceptionStatisticsDto> getReceptionStatistics(String startTime,String endTime,Long practitionerId);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 过号重排
|
||||||
|
* @param encounterId 就诊ID
|
||||||
|
* @return 操作结果
|
||||||
|
*/
|
||||||
|
R<?> rearrangeMissedEncounter(Long encounterId);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -26,9 +26,11 @@ import org.springframework.jdbc.core.JdbcTemplate;
|
|||||||
import org.springframework.stereotype.Service;
|
import org.springframework.stereotype.Service;
|
||||||
|
|
||||||
import lombok.extern.slf4j.Slf4j;
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
import org.springframework.transaction.annotation.Transactional;
|
||||||
|
|
||||||
import javax.annotation.Resource;
|
import javax.annotation.Resource;
|
||||||
import javax.servlet.http.HttpServletRequest;
|
import javax.servlet.http.HttpServletRequest;
|
||||||
|
import java.time.LocalDateTime;
|
||||||
import java.util.*;
|
import java.util.*;
|
||||||
import java.util.stream.Collectors;
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
@@ -323,4 +325,45 @@ public class DoctorStationMainAppServiceImpl implements IDoctorStationMainAppSer
|
|||||||
practitionerId);
|
practitionerId);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 过号重排核心实现
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
@Transactional(rollbackFor = Exception.class) // 事务保证原子性
|
||||||
|
public R<?> rearrangeMissedEncounter(Long encounterId) {
|
||||||
|
// 1. 校验就诊记录是否存在
|
||||||
|
Encounter encounter = encounterMapper.selectById(encounterId);
|
||||||
|
if (encounter == null) {
|
||||||
|
return R.fail("就诊记录不存在");
|
||||||
|
}
|
||||||
|
|
||||||
|
// 2. 校验状态:仅「在诊(IN_PROGRESS=2)」可重排
|
||||||
|
if (!EncounterStatus.IN_PROGRESS.getValue().equals(encounter.getStatusEnum())) {
|
||||||
|
return R.fail("仅「在诊」状态的患者可执行过号重排");
|
||||||
|
}
|
||||||
|
|
||||||
|
// 3. 核心更新:改回待诊+更新missed_time
|
||||||
|
Date now = new Date();
|
||||||
|
Long practitionerId = SecurityUtils.getLoginUser().getPractitionerId();
|
||||||
|
int updateCount = encounterMapper.update(null,
|
||||||
|
new LambdaUpdateWrapper<Encounter>()
|
||||||
|
.eq(Encounter::getId, encounterId)
|
||||||
|
.set(Encounter::getStatusEnum, EncounterStatus.PLANNED.getValue()) // 改回1-待诊
|
||||||
|
.set(Encounter::getMissedTime, now) // 新增:设置过号时间为当前时间
|
||||||
|
.set(Encounter::getUpdateBy, practitionerId.toString()) // 操作医生ID
|
||||||
|
.eq(Encounter::getStatusEnum, EncounterStatus.IN_PROGRESS.getValue())); // 防并发
|
||||||
|
|
||||||
|
if (updateCount == 0) {
|
||||||
|
return R.fail("过号重排失败:状态更新异常");
|
||||||
|
}
|
||||||
|
|
||||||
|
// 4. 同步更新接诊参与记录
|
||||||
|
iEncounterParticipantService.update(new LambdaUpdateWrapper<EncounterParticipant>()
|
||||||
|
.eq(EncounterParticipant::getEncounterId, encounterId)
|
||||||
|
.eq(EncounterParticipant::getTypeCode, ParticipantType.ADMITTER.getCode())
|
||||||
|
.set(EncounterParticipant::getStatusEnum, EncounterActivityStatus.COMPLETED.getValue()));
|
||||||
|
|
||||||
|
return R.ok("过号重排成功");
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -133,6 +133,29 @@ public class DoctorStationMainController {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 过号重排
|
||||||
|
*
|
||||||
|
* @param encounterId 就诊id
|
||||||
|
* @return 结果
|
||||||
|
*/
|
||||||
|
@GetMapping(value = "/rearrange-missed-encounter")
|
||||||
|
public R<?> rearrangeMissedEncounter(@RequestParam(value = "encounterId", required = false) String encounterId) {
|
||||||
|
// 1. 空值校验(和现有接口保持一致)
|
||||||
|
if (encounterId == null || "undefined".equals(encounterId) || "null".equals(encounterId)) {
|
||||||
|
return R.fail("就诊ID不能为空");
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
// 2. 字符串转Long(和现有接口保持一致)
|
||||||
|
Long id = Long.parseLong(encounterId);
|
||||||
|
// 3. 调用AppService的过号重排方法
|
||||||
|
return iDoctorStationMainAppService.rearrangeMissedEncounter(id);
|
||||||
|
} catch (NumberFormatException e) {
|
||||||
|
// 4. 格式错误处理(和现有接口保持一致)
|
||||||
|
return R.fail("就诊ID格式错误");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 查询处方号列表信息
|
* 查询处方号列表信息
|
||||||
*
|
*
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
package com.openhis.web.doctorstation.dto;
|
package com.openhis.web.doctorstation.dto;
|
||||||
|
|
||||||
|
import com.baomidou.mybatisplus.annotation.TableField;
|
||||||
import com.fasterxml.jackson.databind.annotation.JsonSerialize;
|
import com.fasterxml.jackson.databind.annotation.JsonSerialize;
|
||||||
import com.fasterxml.jackson.databind.ser.std.ToStringSerializer;
|
import com.fasterxml.jackson.databind.ser.std.ToStringSerializer;
|
||||||
import com.openhis.common.annotation.Dict;
|
import com.openhis.common.annotation.Dict;
|
||||||
@@ -127,4 +128,9 @@ public class PatientInfoDto {
|
|||||||
* 就诊卡号
|
* 就诊卡号
|
||||||
*/
|
*/
|
||||||
private String identifierNo;
|
private String identifierNo;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 过号时间
|
||||||
|
*/
|
||||||
|
private Date missedTime;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -24,7 +24,8 @@
|
|||||||
T10.practitioner_user_id,
|
T10.practitioner_user_id,
|
||||||
T10.jz_practitioner_user_id,
|
T10.jz_practitioner_user_id,
|
||||||
T10.bus_no,
|
T10.bus_no,
|
||||||
T10.identifier_no
|
T10.identifier_no,
|
||||||
|
T10.missed_time
|
||||||
from
|
from
|
||||||
(
|
(
|
||||||
SELECT T1.tenant_id AS tenant_id,
|
SELECT T1.tenant_id AS tenant_id,
|
||||||
@@ -50,7 +51,8 @@
|
|||||||
T1.reception_time AS reception_time,
|
T1.reception_time AS reception_time,
|
||||||
T1.organization_id AS org_id,
|
T1.organization_id AS org_id,
|
||||||
T8.bus_no AS bus_no,
|
T8.bus_no AS bus_no,
|
||||||
T9.identifier_no AS identifier_no
|
T9.identifier_no AS identifier_no,
|
||||||
|
T1.missed_time AS missed_time
|
||||||
FROM adm_encounter AS T1
|
FROM adm_encounter AS T1
|
||||||
LEFT JOIN adm_organization AS T2 ON T1.organization_id = T2.ID AND T2.delete_flag = '0'
|
LEFT JOIN adm_organization AS T2 ON T1.organization_id = T2.ID AND T2.delete_flag = '0'
|
||||||
LEFT JOIN adm_healthcare_service AS T3 ON T1.service_type_id = T3.ID AND T3.delete_flag = '0'
|
LEFT JOIN adm_healthcare_service AS T3 ON T1.service_type_id = T3.ID AND T3.delete_flag = '0'
|
||||||
@@ -70,18 +72,18 @@
|
|||||||
LEFT JOIN fin_contract AS T7 ON T6.contract_no = T7.bus_no AND T7.delete_flag = '0'
|
LEFT JOIN fin_contract AS T7 ON T6.contract_no = T7.bus_no AND T7.delete_flag = '0'
|
||||||
LEFT JOIN adm_patient AS T8 ON T1.patient_id = T8.ID AND T8.delete_flag = '0'
|
LEFT JOIN adm_patient AS T8 ON T1.patient_id = T8.ID AND T8.delete_flag = '0'
|
||||||
LEFT JOIN (
|
LEFT JOIN (
|
||||||
SELECT patient_id,
|
SELECT patient_id,
|
||||||
identifier_no
|
identifier_no
|
||||||
FROM (
|
FROM (
|
||||||
SELECT patient_id,
|
SELECT patient_id,
|
||||||
identifier_no,
|
identifier_no,
|
||||||
ROW_NUMBER() OVER (PARTITION BY patient_id ORDER BY create_time ASC) AS rn
|
ROW_NUMBER() OVER (PARTITION BY patient_id ORDER BY create_time ASC) AS rn
|
||||||
FROM adm_patient_identifier
|
FROM adm_patient_identifier
|
||||||
WHERE delete_flag = '0'
|
WHERE delete_flag = '0'
|
||||||
AND identifier_no IS NOT NULL
|
AND identifier_no IS NOT NULL
|
||||||
AND identifier_no != ''
|
AND identifier_no != ''
|
||||||
) t
|
) t
|
||||||
WHERE rn = 1
|
WHERE rn = 1
|
||||||
) AS T9 ON T8.id = T9.patient_id
|
) AS T9 ON T8.id = T9.patient_id
|
||||||
WHERE
|
WHERE
|
||||||
T1.delete_flag = '0'
|
T1.delete_flag = '0'
|
||||||
|
|||||||
@@ -1,8 +1,6 @@
|
|||||||
package com.openhis.administration.domain;
|
package com.openhis.administration.domain;
|
||||||
|
|
||||||
import com.baomidou.mybatisplus.annotation.IdType;
|
import com.baomidou.mybatisplus.annotation.*;
|
||||||
import com.baomidou.mybatisplus.annotation.TableId;
|
|
||||||
import com.baomidou.mybatisplus.annotation.TableName;
|
|
||||||
import com.core.common.core.domain.HisBaseEntity;
|
import com.core.common.core.domain.HisBaseEntity;
|
||||||
import com.fasterxml.jackson.databind.annotation.JsonSerialize;
|
import com.fasterxml.jackson.databind.annotation.JsonSerialize;
|
||||||
import com.fasterxml.jackson.databind.ser.std.ToStringSerializer;
|
import com.fasterxml.jackson.databind.ser.std.ToStringSerializer;
|
||||||
@@ -145,4 +143,9 @@ public class Encounter extends HisBaseEntity {
|
|||||||
*/
|
*/
|
||||||
private Long registrarId;
|
private Long registrarId;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 过号时间
|
||||||
|
*/
|
||||||
|
@TableField("missed_time")
|
||||||
|
private Date missedTime;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -54,6 +54,17 @@ export function leaveEncounter(encounterId) {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 重新排序未到诊患者
|
||||||
|
*/
|
||||||
|
export const rearrangeMissedNumber = (encounterId) => {
|
||||||
|
return request({
|
||||||
|
url: '/doctor-station/main/rearrange-missed-encounter', // 对应Controller的路径
|
||||||
|
method: 'get', // 注意:现有接口都是GET,这里和后端保持一致
|
||||||
|
params: { encounterId } // GET请求用params传参
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 完诊
|
* 完诊
|
||||||
*/
|
*/
|
||||||
|
|||||||
@@ -0,0 +1,675 @@
|
|||||||
|
<template>
|
||||||
|
<el-dialog :model-value="dialogVisible" @update:model-value="val => emit('update:dialogVisible', val)" title=""
|
||||||
|
width="90%" :close-on-click-modal="false" custom-class="call-dialog-custom">
|
||||||
|
<!-- 顶部标题栏 -->
|
||||||
|
<div class="dialog-header">
|
||||||
|
<div class="header-title">医生叫号界面 - {{ department }}</div>
|
||||||
|
<div class="header-time">{{ formatDate(new Date(), 'YYYY-MM-DD HH:mm:ss') }}</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- 当前就诊患者区域 -->
|
||||||
|
<div class="current-patient">
|
||||||
|
<div class="current-label">当前就诊患者</div>
|
||||||
|
<div class="current-info">
|
||||||
|
<div>
|
||||||
|
<span>患者姓名:</span>
|
||||||
|
<span class="info-value">{{ getPatientNo(currentCallPatient) }} {{ currentCallPatient.patientName || '暂无'
|
||||||
|
}}</span>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<span>诊室:</span>
|
||||||
|
<span class="info-value">{{ roomNo || '4号' }}</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- 叫号按钮区 -->
|
||||||
|
<div class="call-buttons">
|
||||||
|
<el-button class="btn-next" @click="callNextPatient">下一患者</el-button>
|
||||||
|
<el-button class="btn-recall" @click="showRecallDialog">选呼</el-button>
|
||||||
|
<el-button class="btn-finish" @click="finishCallPatient">完成</el-button>
|
||||||
|
<el-button class="btn-skip" @click="skipPatient">跳过</el-button>
|
||||||
|
<el-button class="btn-requeue" @click="requeuePatient">过号重排</el-button>
|
||||||
|
<el-button class="btn-seen" @click="markSeenPatient">已就诊</el-button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- 选呼对话框 -->
|
||||||
|
<el-dialog v-model="recallDialogVisible" title="选择患者呼叫" width="600px" :close-on-click-modal="false">
|
||||||
|
<div style="padding: 20px 0;">
|
||||||
|
<el-select v-model="selectedPatientEncounterId" filterable remote reserve-keyword placeholder="搜索患者姓名、身份证号或就诊ID"
|
||||||
|
:remote-method="remoteSearchPatient" :loading="patientSearchLoading" clearable
|
||||||
|
style="width: 100%; margin-bottom: 20px;">
|
||||||
|
<el-option v-for="patient in patientSearchOptions" :key="patient.encounterId"
|
||||||
|
:label="`${patient.patientName} (${patient.idCard || '无身份证号'})`" :value="patient.encounterId">
|
||||||
|
<div style="display: flex; justify-content: space-between;">
|
||||||
|
<span>{{ patient.patientName }}</span>
|
||||||
|
<span style="color: #8492a6; font-size: 14px;">{{ patient.idCard || '无身份证号' }}</span>
|
||||||
|
</div>
|
||||||
|
</el-option>
|
||||||
|
</el-select>
|
||||||
|
<div style="text-align: right;">
|
||||||
|
<el-button @click="recallDialogVisible = false">取消</el-button>
|
||||||
|
<el-button type="primary" @click="confirmRecallPatient"
|
||||||
|
:disabled="!selectedPatientEncounterId">确定呼叫</el-button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</el-dialog>
|
||||||
|
|
||||||
|
<!-- 候诊患者列表标题 -->
|
||||||
|
<div class="wait-list-title">候诊患者列表</div>
|
||||||
|
|
||||||
|
<!-- 候诊患者表格 -->
|
||||||
|
<el-table :data="sortedWaitPatientList" border style="width: 100%" header-cell-class-name="table-header"
|
||||||
|
:row-class-name="() => 'table-row'">
|
||||||
|
<el-table-column label="序号" type="index" width="60" align="center" />
|
||||||
|
<el-table-column prop="patientName" label="患者" width="180">
|
||||||
|
<template #default="scope">{{ scope.row.patientName }}</template>
|
||||||
|
</el-table-column>
|
||||||
|
<el-table-column prop="typeCode_dictText" label="号别" width="120" />
|
||||||
|
<el-table-column prop="organizationName" label="诊室" width="120" />
|
||||||
|
<el-table-column label="医生" width="120">
|
||||||
|
<template #default="scope">{{ scope.row.jz_practitioner_name || '心内科医生' }}</template>
|
||||||
|
</el-table-column>
|
||||||
|
<el-table-column label="状态" width="120">
|
||||||
|
<template #default>等待中</template>
|
||||||
|
</el-table-column>
|
||||||
|
<el-table-column label="操作" width="100" align="center">
|
||||||
|
<template #default="scope">
|
||||||
|
<el-button size="small" class="btn-receive" @click="callThisPatient(scope.row)">
|
||||||
|
接诊
|
||||||
|
</el-button>
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
</el-table>
|
||||||
|
|
||||||
|
<!-- 底部信息栏 -->
|
||||||
|
<div class="footer-info">
|
||||||
|
<div>当前时间:{{ formatDate(new Date(), 'YYYY-MM-DD HH:mm:ss') }}</div>
|
||||||
|
<div>当前号:{{ getPatientNo(currentCallPatient) || '暂无' }}</div>
|
||||||
|
<div>等待人数:{{ currentWaitPatientList.length }}</div>
|
||||||
|
</div>
|
||||||
|
</el-dialog>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup>
|
||||||
|
// ✅ 核心修复:h 从 vue 导入,而非 element-plus
|
||||||
|
import { ref, watch, onMounted, h, computed } from 'vue';
|
||||||
|
import { ElMessage, ElMessageBox } from 'element-plus';
|
||||||
|
import { receiveEncounter, completeEncounter, leaveEncounter, rearrangeMissedNumber } from '../api.js';
|
||||||
|
|
||||||
|
// ===== 1. Props/Emits 定义 =====
|
||||||
|
const props = defineProps({
|
||||||
|
dialogVisible: Boolean,
|
||||||
|
currentPatient: { type: Object, default: () => ({}) },
|
||||||
|
currentPatientList: { type: Array, default: () => [] }, // 候诊列表
|
||||||
|
roomNo: { type: String, default: '4号' },
|
||||||
|
department: { type: String, default: '心内科' } // 科室名称,默认为心内科
|
||||||
|
});
|
||||||
|
|
||||||
|
const emit = defineEmits(['update:dialogVisible', 'callNext', 'reCall', 'finish', 'skip', 'requeue', 'markSeen', 'callThis']);
|
||||||
|
|
||||||
|
// ===== 2. 响应式变量 =====
|
||||||
|
const currentWaitPatientList = ref([]); // 候诊患者列表
|
||||||
|
const currentCallPatient = ref({}); // 当前就诊患者
|
||||||
|
const selectedId = ref(''); // 选呼患者ID
|
||||||
|
|
||||||
|
// 选呼功能相关变量
|
||||||
|
const selectedPatientEncounterId = ref('');
|
||||||
|
const patientSearchOptions = ref([]);
|
||||||
|
const patientSearchLoading = ref(false);
|
||||||
|
const patientSearchQuery = ref('');
|
||||||
|
const recallDialogVisible = ref(false); // 选呼对话框显示/隐藏
|
||||||
|
|
||||||
|
// ===== 3. 计算属性 =====
|
||||||
|
// 按创建时间(create_time)和过号时间(missed_time)排序后的候诊列表
|
||||||
|
const sortedWaitPatientList = computed(() => {
|
||||||
|
// 空列表直接返回
|
||||||
|
if (!currentWaitPatientList.value.length) return [];
|
||||||
|
|
||||||
|
// 调试:打印当前时间和排序前的患者列表
|
||||||
|
const now = new Date();
|
||||||
|
console.log('当前时间:', now);
|
||||||
|
console.log('排序前的患者列表:');
|
||||||
|
currentWaitPatientList.value.forEach(item => {
|
||||||
|
console.log(`患者 ${item.patientName}:`, {
|
||||||
|
encounterId: item.encounterId,
|
||||||
|
missed_time: item.missed_time,
|
||||||
|
missedTime: item.missedTime,
|
||||||
|
create_time: item.create_time,
|
||||||
|
createTime: item.createTime,
|
||||||
|
statusEnum: item.statusEnum,
|
||||||
|
registerTime: item.registerTime,
|
||||||
|
parsed_missed_time: item.missed_time ? new Date(item.missed_time) : null,
|
||||||
|
parsed_create_time: item.create_time ? new Date(item.create_time) : null,
|
||||||
|
parsed_registerTime: item.registerTime ? new Date(item.registerTime) : null
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
// 判断患者是否为过号患者
|
||||||
|
const isMissedPatient = (patient) => {
|
||||||
|
// 检查missed_time或missedTime是否存在且不为null
|
||||||
|
return patient.missed_time != null || patient.missedTime != null;
|
||||||
|
};
|
||||||
|
|
||||||
|
// 获取患者有效时间的辅助函数
|
||||||
|
const getPatientTime = (patient) => {
|
||||||
|
// 调试:打印患者的完整时间信息
|
||||||
|
console.log(`患者 ${patient.patientName} 的时间信息:`, {
|
||||||
|
missed_time: patient.missed_time,
|
||||||
|
missedTime: patient.missedTime,
|
||||||
|
registerTime: patient.registerTime,
|
||||||
|
create_time: patient.create_time,
|
||||||
|
createTime: patient.createTime
|
||||||
|
});
|
||||||
|
|
||||||
|
// 1. 优先使用missed_time(过号时间),同时处理null和undefined
|
||||||
|
if (patient.missed_time != null) {
|
||||||
|
console.log(`患者 ${patient.patientName} 使用missed_time: ${patient.missed_time}`);
|
||||||
|
return patient.missed_time;
|
||||||
|
}
|
||||||
|
if (patient.missedTime != null) {
|
||||||
|
console.log(`患者 ${patient.patientName} 使用missedTime: ${patient.missedTime}`);
|
||||||
|
return patient.missedTime;
|
||||||
|
}
|
||||||
|
// 2. 其次使用registerTime(挂号时间)
|
||||||
|
if (patient.registerTime != null) {
|
||||||
|
console.log(`患者 ${patient.patientName} 使用registerTime: ${patient.registerTime}`);
|
||||||
|
return patient.registerTime;
|
||||||
|
}
|
||||||
|
// 3. 最后使用create_time或createTime(创建时间)
|
||||||
|
if (patient.create_time != null) {
|
||||||
|
console.log(`患者 ${patient.patientName} 使用create_time: ${patient.create_time}`);
|
||||||
|
return patient.create_time;
|
||||||
|
}
|
||||||
|
if (patient.createTime != null) {
|
||||||
|
console.log(`患者 ${patient.patientName} 使用createTime: ${patient.createTime}`);
|
||||||
|
return patient.createTime;
|
||||||
|
}
|
||||||
|
// 4. 兜底:使用当前时间减去1000年,确保排到最前面
|
||||||
|
console.warn(`患者 ${patient.patientName} 没有有效时间字段,使用默认时间`);
|
||||||
|
return new Date(0); // 1970-01-01
|
||||||
|
};
|
||||||
|
|
||||||
|
// 核心改进:明确区分过号患者和未过号患者
|
||||||
|
// 1. 未过号患者排在过号患者前面
|
||||||
|
// 2. 未过号患者按照registerTime升序排列
|
||||||
|
// 3. 过号患者按照missed_time升序排列
|
||||||
|
const sortedList = [...currentWaitPatientList.value].sort((a, b) => {
|
||||||
|
const aIsMissed = isMissedPatient(a);
|
||||||
|
const bIsMissed = isMissedPatient(b);
|
||||||
|
|
||||||
|
// 调试:打印排序比较
|
||||||
|
console.log(`排序比较: ${a.patientName}(过号:${aIsMissed}) vs ${b.patientName}(过号:${bIsMissed})`);
|
||||||
|
|
||||||
|
// 1. 未过号患者排在过号患者前面
|
||||||
|
if (aIsMissed !== bIsMissed) {
|
||||||
|
const result = aIsMissed ? 1 : -1;
|
||||||
|
console.log(`比较结果: ${result} (${aIsMissed ? 'a是过号患者,排后面' : 'b是过号患者,排后面'})`);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 2. 获取a和b的有效时间
|
||||||
|
const aTime = getPatientTime(a);
|
||||||
|
const bTime = getPatientTime(b);
|
||||||
|
|
||||||
|
// 解析时间,确保正确处理UTC时间
|
||||||
|
const timeA = new Date(aTime);
|
||||||
|
const timeB = new Date(bTime);
|
||||||
|
|
||||||
|
// 确保时间有效
|
||||||
|
if (isNaN(timeA.getTime())) {
|
||||||
|
console.log(`患者 ${a.patientName} 时间无效,排后面`);
|
||||||
|
return 1; // 无效时间排后面
|
||||||
|
}
|
||||||
|
if (isNaN(timeB.getTime())) {
|
||||||
|
console.log(`患者 ${b.patientName} 时间无效,排后面`);
|
||||||
|
return -1; // 有效时间排前面
|
||||||
|
}
|
||||||
|
|
||||||
|
// 3. 同类型患者按照时间升序排列
|
||||||
|
const result = timeA - timeB;
|
||||||
|
console.log(`比较结果: ${result} (时间比较: ${timeA} vs ${timeB})`);
|
||||||
|
return result;
|
||||||
|
});
|
||||||
|
|
||||||
|
// 调试:打印排序后的患者列表
|
||||||
|
console.log('排序后的患者列表:');
|
||||||
|
sortedList.forEach((item, index) => {
|
||||||
|
const effectiveTime = getPatientTime(item);
|
||||||
|
const isMissed = isMissedPatient(item);
|
||||||
|
console.log(`${index + 1}. ${item.patientName}:`, {
|
||||||
|
is_missed: isMissed,
|
||||||
|
missed_time: item.missed_time,
|
||||||
|
registerTime: item.registerTime,
|
||||||
|
create_time: item.create_time || item.createTime,
|
||||||
|
effective_time: new Date(effectiveTime)
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
return sortedList;
|
||||||
|
});
|
||||||
|
|
||||||
|
// ===== 4. 临时 formatDate 函数(避免外部依赖报错)=====
|
||||||
|
const formatDate = (date, format = 'YYYY-MM-DD') => {
|
||||||
|
const d = new Date(date);
|
||||||
|
const year = d.getFullYear();
|
||||||
|
const month = String(d.getMonth() + 1).padStart(2, '0');
|
||||||
|
const day = String(d.getDate()).padStart(2, '0');
|
||||||
|
const hour = String(d.getHours()).padStart(2, '0');
|
||||||
|
const minute = String(d.getMinutes()).padStart(2, '0');
|
||||||
|
const second = String(d.getSeconds()).padStart(2, '0');
|
||||||
|
if (format === 'YYYY-MM-DD HH:mm:ss') {
|
||||||
|
return `${year}-${month}-${day} ${hour}:${minute}:${second}`;
|
||||||
|
}
|
||||||
|
return `${year}-${month}-${day}`;
|
||||||
|
};
|
||||||
|
|
||||||
|
// ===== 4. 监听 Props 同步数据 =====
|
||||||
|
watch(() => props.currentPatientList, (newVal) => {
|
||||||
|
currentWaitPatientList.value = newVal || [];
|
||||||
|
}, { immediate: true });
|
||||||
|
|
||||||
|
watch(() => props.currentPatient, (newVal) => {
|
||||||
|
currentCallPatient.value = newVal || {};
|
||||||
|
}, { immediate: true });
|
||||||
|
|
||||||
|
// ===== 5. 排队号计算 =====
|
||||||
|
const getPatientNo = (patient) => {
|
||||||
|
if (!patient || !patient.encounterId || sortedWaitPatientList.value.length === 0) return '';
|
||||||
|
const index = sortedWaitPatientList.value.findIndex(item => item.encounterId === patient.encounterId);
|
||||||
|
return index > -1 ? `${index + 1}号` : '';
|
||||||
|
};
|
||||||
|
|
||||||
|
// ===== 6. 按钮核心逻辑 =====
|
||||||
|
// 6.1 下一患者
|
||||||
|
const callNextPatient = async () => {
|
||||||
|
if (sortedWaitPatientList.value.length === 0) {
|
||||||
|
ElMessage.warning('暂无候诊患者');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
// 获取第一个患者
|
||||||
|
const nextPatient = sortedWaitPatientList.value[0];
|
||||||
|
try {
|
||||||
|
// 调用接诊API
|
||||||
|
await receiveEncounter(nextPatient.encounterId);
|
||||||
|
// 更新当前呼叫患者
|
||||||
|
currentCallPatient.value = nextPatient;
|
||||||
|
// 通知父组件
|
||||||
|
emit('callNext');
|
||||||
|
ElMessage.success(`已呼叫下一位患者:${nextPatient.patientName}`);
|
||||||
|
} catch (error) {
|
||||||
|
console.error('呼叫下一位患者失败:', error);
|
||||||
|
ElMessage.error(`呼叫下一位患者失败:${error.message || '系统错误'}`);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// 6.2 显示选呼对话框
|
||||||
|
const showRecallDialog = () => {
|
||||||
|
// 清空之前的选择
|
||||||
|
selectedPatientEncounterId.value = '';
|
||||||
|
patientSearchOptions.value = [];
|
||||||
|
// 显示对话框
|
||||||
|
recallDialogVisible.value = true;
|
||||||
|
};
|
||||||
|
|
||||||
|
// 6.3 远程搜索患者
|
||||||
|
const remoteSearchPatient = (query) => {
|
||||||
|
if (query === '') {
|
||||||
|
patientSearchOptions.value = [];
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
patientSearchLoading.value = true;
|
||||||
|
|
||||||
|
// 模拟远程搜索,实际从候诊列表中查找
|
||||||
|
setTimeout(() => {
|
||||||
|
// 搜索匹配的患者 - 支持模糊搜索
|
||||||
|
patientSearchOptions.value = sortedWaitPatientList.value.filter(patient => {
|
||||||
|
// 患者姓名模糊匹配
|
||||||
|
const nameMatch = patient.patientName && patient.patientName.toLowerCase().includes(query.toLowerCase());
|
||||||
|
// 身份证号模糊匹配
|
||||||
|
const idCardMatch = patient.idCard && patient.idCard.includes(query);
|
||||||
|
// 就诊ID模糊匹配
|
||||||
|
const encounterIdMatch = patient.encounterId && patient.encounterId.toString().includes(query);
|
||||||
|
// 至少满足一个条件
|
||||||
|
return nameMatch || idCardMatch || encounterIdMatch;
|
||||||
|
});
|
||||||
|
|
||||||
|
patientSearchLoading.value = false;
|
||||||
|
}, 200);
|
||||||
|
};
|
||||||
|
|
||||||
|
// 6.4 确认呼叫患者
|
||||||
|
const confirmRecallPatient = async () => {
|
||||||
|
if (!selectedPatientEncounterId.value) return;
|
||||||
|
|
||||||
|
try {
|
||||||
|
// 查找选中的患者
|
||||||
|
const selectedPatient = sortedWaitPatientList.value.find(patient => patient.encounterId === selectedPatientEncounterId.value);
|
||||||
|
if (!selectedPatient) {
|
||||||
|
ElMessage.warning('未找到选中的患者');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 调用接诊API
|
||||||
|
await receiveEncounter(selectedPatient.encounterId);
|
||||||
|
// 更新当前呼叫患者
|
||||||
|
currentCallPatient.value = selectedPatient;
|
||||||
|
// 通知父组件
|
||||||
|
emit('reCall');
|
||||||
|
// 关闭对话框
|
||||||
|
recallDialogVisible.value = false;
|
||||||
|
// 清空选择
|
||||||
|
selectedPatientEncounterId.value = '';
|
||||||
|
ElMessage.success(`已呼叫患者:${selectedPatient.patientName}`);
|
||||||
|
} catch (error) {
|
||||||
|
console.error('选呼患者失败:', error);
|
||||||
|
ElMessage.error(`选呼患者失败:${error.message || '系统错误'}`);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// 6.5 完成
|
||||||
|
const finishCallPatient = async () => {
|
||||||
|
if (!currentCallPatient.value.encounterId) {
|
||||||
|
ElMessage.warning('当前没有就诊患者');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
await completeEncounter(currentCallPatient.value.encounterId);
|
||||||
|
emit('finish');
|
||||||
|
emit('update:dialogVisible', false);
|
||||||
|
ElMessage.success('患者已完诊');
|
||||||
|
} catch (error) {
|
||||||
|
console.error('完诊失败:', error);
|
||||||
|
ElMessage.error(`完诊失败:${error.message || '系统错误'}`);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// 6.6 跳过
|
||||||
|
const skipPatient = async () => {
|
||||||
|
if (sortedWaitPatientList.value.length === 0) {
|
||||||
|
ElMessage.warning('暂无候诊患者');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
// 获取第一个患者
|
||||||
|
const nextPatient = sortedWaitPatientList.value[0];
|
||||||
|
try {
|
||||||
|
// 调用接诊API
|
||||||
|
await receiveEncounter(nextPatient.encounterId);
|
||||||
|
// 更新当前呼叫患者
|
||||||
|
currentCallPatient.value = nextPatient;
|
||||||
|
// 通知父组件
|
||||||
|
emit('skip');
|
||||||
|
ElMessage.success(`已跳过当前患者,呼叫下一位患者:${nextPatient.patientName}`);
|
||||||
|
} catch (error) {
|
||||||
|
console.error('跳过患者失败:', error);
|
||||||
|
ElMessage.error(`跳过患者失败:${error.message || '系统错误'}`);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// 6.7 过号重排
|
||||||
|
const requeuePatient = async () => {
|
||||||
|
// 1. 校验:当前必须有就诊患者才能重排
|
||||||
|
if (!currentCallPatient.value || !currentCallPatient.value.encounterId) {
|
||||||
|
ElMessage.warning('当前没有就诊患者,无法过号重排');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
// 2. 显示确认对话框
|
||||||
|
await ElMessageBox.confirm(
|
||||||
|
`确定将患者 ${currentCallPatient.value.patientName} 进行过号重排吗?`,
|
||||||
|
'过号重排确认',
|
||||||
|
{
|
||||||
|
confirmButtonText: '确定',
|
||||||
|
cancelButtonText: '取消',
|
||||||
|
type: 'warning',
|
||||||
|
draggable: true
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
// 3. 调用过号重排接口
|
||||||
|
console.log('开始调用过号重排接口,患者ID:', currentCallPatient.value.encounterId);
|
||||||
|
const res = await rearrangeMissedNumber(currentCallPatient.value.encounterId);
|
||||||
|
|
||||||
|
// 4. 处理后端返回结果
|
||||||
|
console.log('过号重排接口返回结果:', res);
|
||||||
|
if (res.code === 200) {
|
||||||
|
// 5. 直接通知父组件刷新候诊列表,依赖父组件重新获取数据
|
||||||
|
emit('requeue');
|
||||||
|
|
||||||
|
// 6. 提示用户+清空当前就诊患者
|
||||||
|
ElMessage.success(res.msg || '过号重排成功,患者已排至队尾');
|
||||||
|
currentCallPatient.value = {};
|
||||||
|
} else {
|
||||||
|
// 7. 处理后端返回的错误信息
|
||||||
|
console.error('过号重排失败,后端返回错误:', res);
|
||||||
|
ElMessage.error(res.msg || '过号重排失败');
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
// 处理取消操作
|
||||||
|
if (error !== 'cancel') {
|
||||||
|
console.error('过号重排失败,捕获异常:', error);
|
||||||
|
ElMessage.error(`过号重排失败:${error.message || '系统错误'}`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// 6.8 标记已就诊
|
||||||
|
const markSeenPatient = async () => {
|
||||||
|
if (!currentCallPatient.value.encounterId) {
|
||||||
|
ElMessage.warning('当前没有就诊患者');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
await leaveEncounter(currentCallPatient.value.encounterId);
|
||||||
|
emit('markSeen');
|
||||||
|
emit('update:dialogVisible', false);
|
||||||
|
ElMessage.success('患者已暂离');
|
||||||
|
} catch (error) {
|
||||||
|
console.error('暂离失败:', error);
|
||||||
|
ElMessage.error(`暂离失败:${error.message || '系统错误'}`);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// 6.9 接诊指定患者
|
||||||
|
const callThisPatient = async (row) => {
|
||||||
|
try {
|
||||||
|
// 调用接诊API
|
||||||
|
await receiveEncounter(row.encounterId);
|
||||||
|
// 更新当前呼叫患者
|
||||||
|
currentCallPatient.value = row;
|
||||||
|
// 通知父组件
|
||||||
|
emit('callThis', row);
|
||||||
|
ElMessage.success(`已接诊患者:${row.patientName}`);
|
||||||
|
} catch (error) {
|
||||||
|
console.error('接诊患者失败:', error);
|
||||||
|
ElMessage.error(`接诊患者失败:${error.message || '系统错误'}`);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
.call-dialog-custom {
|
||||||
|
--el-dialog-body-padding: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.dialog-header {
|
||||||
|
background-color: #0b8074;
|
||||||
|
color: #fff;
|
||||||
|
padding: 12px 20px;
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
align-items: center;
|
||||||
|
border-top-left-radius: 8px;
|
||||||
|
border-top-right-radius: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.header-title {
|
||||||
|
font-size: 24px;
|
||||||
|
font-weight: bold;
|
||||||
|
color: #fff;
|
||||||
|
}
|
||||||
|
|
||||||
|
.header-time {
|
||||||
|
font-size: 16px;
|
||||||
|
font-weight: normal;
|
||||||
|
background-color: rgba(255, 255, 255, 0.2);
|
||||||
|
padding: 8px 16px;
|
||||||
|
border-radius: 20px;
|
||||||
|
color: #fff;
|
||||||
|
}
|
||||||
|
|
||||||
|
.current-patient {
|
||||||
|
background-color: #f8fafc;
|
||||||
|
padding: 20px 20px;
|
||||||
|
margin-bottom: 20px;
|
||||||
|
border-radius: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.current-label {
|
||||||
|
font-size: 20px;
|
||||||
|
font-weight: 700;
|
||||||
|
color: #1f7a7a;
|
||||||
|
margin-bottom: 12px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.current-info {
|
||||||
|
display: flex;
|
||||||
|
gap: 60px;
|
||||||
|
font-size: 18px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.info-value {
|
||||||
|
font-weight: 800 !important;
|
||||||
|
color: #1f7a7a !important;
|
||||||
|
font-size: 26px !important;
|
||||||
|
background-color: #e6f7ff;
|
||||||
|
padding: 8px 16px;
|
||||||
|
border-radius: 8px;
|
||||||
|
margin-left: 8px;
|
||||||
|
display: inline-block;
|
||||||
|
min-width: 200px;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.call-buttons {
|
||||||
|
padding: 0 20px 20px;
|
||||||
|
display: flex;
|
||||||
|
gap: 15px;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
}
|
||||||
|
|
||||||
|
.btn-next {
|
||||||
|
background-color: #10b981 !important;
|
||||||
|
border-color: #10b981 !important;
|
||||||
|
color: #fff !important;
|
||||||
|
font-size: 16px !important;
|
||||||
|
padding: 14px 24px !important;
|
||||||
|
font-weight: 600;
|
||||||
|
border-radius: 6px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.btn-recall {
|
||||||
|
background-color: #f59e0b !important;
|
||||||
|
border-color: #f59e0b !important;
|
||||||
|
color: #fff !important;
|
||||||
|
font-size: 16px !important;
|
||||||
|
padding: 14px 24px !important;
|
||||||
|
font-weight: 600;
|
||||||
|
border-radius: 6px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.btn-finish {
|
||||||
|
background-color: #3b82f6 !important;
|
||||||
|
border-color: #3b82f6 !important;
|
||||||
|
color: #fff !important;
|
||||||
|
font-size: 16px !important;
|
||||||
|
padding: 14px 24px !important;
|
||||||
|
font-weight: 600;
|
||||||
|
border-radius: 6px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.btn-skip {
|
||||||
|
background-color: #f5c518 !important;
|
||||||
|
border-color: #f5c518 !important;
|
||||||
|
color: #fff !important;
|
||||||
|
font-size: 16px !important;
|
||||||
|
padding: 14px 24px !important;
|
||||||
|
font-weight: 600;
|
||||||
|
border-radius: 6px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.btn-requeue {
|
||||||
|
background-color: #165dff !important;
|
||||||
|
border-color: #165dff !important;
|
||||||
|
color: #fff !important;
|
||||||
|
font-size: 16px !important;
|
||||||
|
padding: 14px 24px !important;
|
||||||
|
font-weight: 600;
|
||||||
|
border-radius: 6px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.btn-seen {
|
||||||
|
background-color: #86909c !important;
|
||||||
|
border-color: #86909c !important;
|
||||||
|
color: #fff !important;
|
||||||
|
font-size: 16px !important;
|
||||||
|
padding: 14px 24px !important;
|
||||||
|
font-weight: 600;
|
||||||
|
border-radius: 6px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.btn-receive {
|
||||||
|
background-color: #10b981 !important;
|
||||||
|
border-color: #10b981 !important;
|
||||||
|
color: #fff !important;
|
||||||
|
font-size: 14px !important;
|
||||||
|
padding: 8px 12px !important;
|
||||||
|
border-radius: 4px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.wait-list-title {
|
||||||
|
padding: 0 20px 12px;
|
||||||
|
font-size: 20px;
|
||||||
|
font-weight: 700;
|
||||||
|
color: #1f7a7a;
|
||||||
|
}
|
||||||
|
|
||||||
|
:deep(.table-header) {
|
||||||
|
background-color: #1f7a7a !important;
|
||||||
|
color: #fff !important;
|
||||||
|
font-weight: 700 !important;
|
||||||
|
font-size: 18px !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
:deep(.table-row) {
|
||||||
|
font-size: 18px !important;
|
||||||
|
font-weight: 500;
|
||||||
|
}
|
||||||
|
|
||||||
|
:deep(.el-table__cell) {
|
||||||
|
padding: 16px 0 !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
:deep(.el-table--enable-row-hover .el-table__body tr:hover>td) {
|
||||||
|
background-color: #f0f9ff !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.footer-info {
|
||||||
|
background-color: #1f7a7a;
|
||||||
|
color: #fff;
|
||||||
|
padding: 14px 20px;
|
||||||
|
margin-top: 20px;
|
||||||
|
font-size: 18px;
|
||||||
|
font-weight: 500;
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
gap: 10px;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
@@ -1,11 +1,16 @@
|
|||||||
<template>
|
<template>
|
||||||
<div style="display: flex; justify-content: space-between; height: 90vh">
|
<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="width: 15%; height: 100%; border: 1px solid #eee; border-right: 0">
|
||||||
<div style="padding: 10px; border: 1px solid #eee; height: 50px; border-right: 0">
|
<div
|
||||||
<span>现诊患者</span>
|
style="padding: 10px; border: 1px solid #eee; height: 50px; border-right: 0; display: flex; align-items: center; justify-content: space-between">
|
||||||
<el-badge :value="waitCount > 0 ? waitCount : ''" :max="10"
|
<div style="display: flex; align-items: center;">
|
||||||
style="float: right; color: #409eff; cursor: pointer; margin-right: 10px">
|
<span style="margin-right: 20px; font-weight: 600;">现诊患者</span>
|
||||||
<span @click="openDrawer"> 患者队列 </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>
|
</el-badge>
|
||||||
</div>
|
</div>
|
||||||
<div style="width: 100%; padding: 10px">
|
<div style="width: 100%; padding: 10px">
|
||||||
@@ -15,16 +20,9 @@
|
|||||||
<el-button icon="Search" @click="getPatientList" />
|
<el-button icon="Search" @click="getPatientList" />
|
||||||
</template>
|
</template>
|
||||||
</el-input>
|
</el-input>
|
||||||
<el-date-picker
|
<el-date-picker v-model="registerTime" @change="handleTimeChange" type="date"
|
||||||
v-model="registerTime"
|
style="width: 100%; margin-bottom: 10px" :clearable="false" placeholder="挂号时间" format="YYYY-MM-DD"
|
||||||
@change="handleTimeChange"
|
value-format="YYYY-MM-DD" />
|
||||||
type="date"
|
|
||||||
style="width: 100%; margin-bottom: 10px"
|
|
||||||
:clearable="false"
|
|
||||||
placeholder="挂号时间"
|
|
||||||
format="YYYY-MM-DD"
|
|
||||||
value-format="YYYY-MM-DD"
|
|
||||||
/>
|
|
||||||
<el-scrollbar height="700px">
|
<el-scrollbar height="700px">
|
||||||
<div v-for="(item, index) in patientList" :class="item.active ? 'patient-card actived' : 'patient-card'"
|
<div v-for="(item, index) in patientList" :class="item.active ? 'patient-card actived' : 'patient-card'"
|
||||||
:key="item.id" @click="handleCardClick(item, index)">
|
:key="item.id" @click="handleCardClick(item, index)">
|
||||||
@@ -86,11 +84,11 @@
|
|||||||
' / ' +
|
' / ' +
|
||||||
patientInfo.genderEnum_enumText +
|
patientInfo.genderEnum_enumText +
|
||||||
' / ' +
|
' / ' +
|
||||||
(patientInfo?.contractName ? patientInfo.contractName : '') +
|
(patientInfo?.contractName ? patientInfo.contractName : '') +
|
||||||
'/' +
|
'/' +
|
||||||
patientInfo.phone +
|
patientInfo.phone +
|
||||||
'/' +
|
'/' +
|
||||||
patientInfo.busNo
|
patientInfo.busNo
|
||||||
: '-'
|
: '-'
|
||||||
}}
|
}}
|
||||||
</el-descriptions-item>
|
</el-descriptions-item>
|
||||||
@@ -110,27 +108,22 @@
|
|||||||
<el-button type="primary" plain @click.stop="handleRefund(patientInfo.encounterId)">
|
<el-button type="primary" plain @click.stop="handleRefund(patientInfo.encounterId)">
|
||||||
退费
|
退费
|
||||||
</el-button>
|
</el-button>
|
||||||
<el-button type="primary" plain class="top-layer-btn" @click.stop="getEnPrescription(patientInfo.encounterId)">
|
<el-button type="primary" plain class="top-layer-btn"
|
||||||
|
@click.stop="getEnPrescription(patientInfo.encounterId)">
|
||||||
处方单
|
处方单
|
||||||
</el-button>
|
</el-button>
|
||||||
<el-button type="primary" plain class="top-layer-btn" :disabled="isHospitalizationButtonDisabled" @click.stop="handleHospitalizationClick()" @mouseenter="console.log('办理住院按钮状态:', { patientInfo: patientInfo?.value, hasEncounterId: patientInfo?.value?.encounterId, isDisabled: isHospitalizationButtonDisabled })"> 办理住院 </el-button>
|
<el-button type="primary" plain class="top-layer-btn" :disabled="isHospitalizationButtonDisabled"
|
||||||
|
@click.stop="handleHospitalizationClick()"
|
||||||
|
@mouseenter="console.log('办理住院按钮状态:', { patientInfo: patientInfo?.value, hasEncounterId: patientInfo?.value?.encounterId, isDisabled: isHospitalizationButtonDisabled })">
|
||||||
|
办理住院 </el-button>
|
||||||
</el-descriptions-item>
|
</el-descriptions-item>
|
||||||
</el-descriptions>
|
</el-descriptions>
|
||||||
</div>
|
</div>
|
||||||
<div style="padding: 10px; position: relative">
|
<div style="padding: 10px; position: relative">
|
||||||
<el-tabs
|
<el-tabs type="card" style="width: 100%; height: 100%" v-loading="loading" v-model="activeTab"
|
||||||
type="card"
|
@tab-change="handleClick(activeTab)">
|
||||||
style="width: 100%; height: 100%"
|
|
||||||
v-loading="loading"
|
|
||||||
v-model="activeTab"
|
|
||||||
@tab-change="handleClick(activeTab)"
|
|
||||||
>
|
|
||||||
<el-tab-pane label="门诊病历" name="hospitalizationEmr">
|
<el-tab-pane label="门诊病历" name="hospitalizationEmr">
|
||||||
<hospitalizationEmr
|
<hospitalizationEmr :patientInfo="patientInfo" :activeTab="activeTab" @emrSaved="handleEmrSaved" />
|
||||||
:patientInfo="patientInfo"
|
|
||||||
:activeTab="activeTab"
|
|
||||||
@emrSaved="handleEmrSaved"
|
|
||||||
/>
|
|
||||||
</el-tab-pane>
|
</el-tab-pane>
|
||||||
<!-- <el-tab-pane label="病历" name="emr">
|
<!-- <el-tab-pane label="病历" name="emr">
|
||||||
<Emr
|
<Emr
|
||||||
@@ -151,12 +144,8 @@
|
|||||||
" />
|
" />
|
||||||
</el-tab-pane>
|
</el-tab-pane>
|
||||||
<el-tab-pane label="医嘱" name="prescription">
|
<el-tab-pane label="医嘱" name="prescription">
|
||||||
<prescriptionlist
|
<prescriptionlist :patientInfo="patientInfo" ref="prescriptionRef" :activeTab="activeTab"
|
||||||
:patientInfo="patientInfo"
|
:outpatientEmrSaved="outpatientEmrSaved" />
|
||||||
ref="prescriptionRef"
|
|
||||||
:activeTab="activeTab"
|
|
||||||
:outpatientEmrSaved="outpatientEmrSaved"
|
|
||||||
/>
|
|
||||||
</el-tab-pane>
|
</el-tab-pane>
|
||||||
<el-tab-pane label="中医" name="tcm">
|
<el-tab-pane label="中医" name="tcm">
|
||||||
<tcmAdvice :patientInfo="patientInfo" ref="tcmRef" />
|
<tcmAdvice :patientInfo="patientInfo" ref="tcmRef" />
|
||||||
@@ -180,24 +169,17 @@
|
|||||||
<el-drawer v-model="drawer" title="患者队列" direction="ltr" @open="handleOpen">
|
<el-drawer v-model="drawer" title="患者队列" direction="ltr" @open="handleOpen">
|
||||||
<PatientList ref="patientDrawerRef" @toCurrent="handleReceive" />
|
<PatientList ref="patientDrawerRef" @toCurrent="handleReceive" />
|
||||||
</el-drawer>
|
</el-drawer>
|
||||||
<RefundListDialog
|
<RefundListDialog :open="openRefundListDialog" :encounterId="currentEncounterId"
|
||||||
:open="openRefundListDialog"
|
@close="openRefundListDialog = false" @refresh="() => prescriptionRef.getListInfo()" />
|
||||||
:encounterId="currentEncounterId"
|
<HospitalizationDialog :open="openDialog" :patientInfo="patientInfo" :encounterId="currentEncounterId"
|
||||||
@close="openRefundListDialog = false"
|
:mainDiagnosis="mainDiagnosis" @close="openDialog = false" />
|
||||||
@refresh="() => prescriptionRef.getListInfo()"
|
<PrescriptionInfo :open="openPrescriptionDialog" :precriptionInfo="prescriptionInfo"
|
||||||
/>
|
@close="openPrescriptionDialog = false" />
|
||||||
<HospitalizationDialog
|
<!-- 新增叫号弹窗组件 -->
|
||||||
:open="openDialog"
|
<DoctorCallDialog v-model:dialogVisible="dialogVisible" :current-patient="currentCallPatient"
|
||||||
:patientInfo="patientInfo"
|
:current-patient-list="currentWaitPatientList" :room-no="roomNo"
|
||||||
:encounterId="currentEncounterId"
|
:department="userStore.orgName || patientInfo.organizationName || '心内科'" @callNext="callNext" @reCall="reCall"
|
||||||
:mainDiagnosis="mainDiagnosis"
|
@finish="finishCall" @skip="skip" @requeue="requeue" @markSeen="markSeen" @callThis="callThis" />
|
||||||
@close="openDialog = false"
|
|
||||||
/>
|
|
||||||
<PrescriptionInfo
|
|
||||||
:open="openPrescriptionDialog"
|
|
||||||
:precriptionInfo="prescriptionInfo"
|
|
||||||
@close="openPrescriptionDialog = false"
|
|
||||||
/>
|
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
<script setup>
|
<script setup>
|
||||||
@@ -221,11 +203,12 @@ import HospitalizationDialog from './components/hospitalizationDialog.vue';
|
|||||||
import tcmAdvice from './components/tcm/tcmAdvice.vue';
|
import tcmAdvice from './components/tcm/tcmAdvice.vue';
|
||||||
import inspectionApplication from './components/inspection/inspectionApplication.vue';
|
import inspectionApplication from './components/inspection/inspectionApplication.vue';
|
||||||
import surgeryApplication from './components/surgery/surgeryApplication.vue';
|
import surgeryApplication from './components/surgery/surgeryApplication.vue';
|
||||||
import {formatDate, formatDateStr} from '@/utils/index';
|
import DoctorCallDialog from './components/callQueue/DoctorCallDialog.vue';
|
||||||
|
import { formatDate, formatDateStr } from '@/utils/index';
|
||||||
import useUserStore from '@/store/modules/user';
|
import useUserStore from '@/store/modules/user';
|
||||||
import {nextTick} from 'vue';
|
import { nextTick } from 'vue';
|
||||||
import {updatePatientInfo} from './components/store/patient.js';
|
import { updatePatientInfo } from './components/store/patient.js';
|
||||||
import {ElMessage, ElMessageBox} from 'element-plus';
|
import { ElMessage, ElMessageBox } from 'element-plus';
|
||||||
|
|
||||||
// // 监听路由离开事件
|
// // 监听路由离开事件
|
||||||
// onBeforeRouteLeave((to, from, next) => {
|
// onBeforeRouteLeave((to, from, next) => {
|
||||||
@@ -253,6 +236,10 @@ const drawer = ref(false);
|
|||||||
const openRefundListDialog = ref(false);
|
const openRefundListDialog = ref(false);
|
||||||
const openDialog = ref(false);
|
const openDialog = ref(false);
|
||||||
const openPrescriptionDialog = 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 saveStatus = ref(false);
|
||||||
const outpatientEmrSaved = ref(false); // 门诊病历保存状态
|
const outpatientEmrSaved = ref(false); // 门诊病历保存状态
|
||||||
const currentEncounterId = ref('');
|
const currentEncounterId = ref('');
|
||||||
@@ -267,11 +254,11 @@ const visitTypeDisabled = ref(false);
|
|||||||
// 计算属性:确定办理住院按钮是否应被禁用
|
// 计算属性:确定办理住院按钮是否应被禁用
|
||||||
const isHospitalizationButtonDisabled = computed(() => {
|
const isHospitalizationButtonDisabled = computed(() => {
|
||||||
return !patientInfo.value ||
|
return !patientInfo.value ||
|
||||||
typeof patientInfo.value !== 'object' ||
|
typeof patientInfo.value !== 'object' ||
|
||||||
!patientInfo.value.encounterId ||
|
!patientInfo.value.encounterId ||
|
||||||
patientInfo.value.encounterId === '' ||
|
patientInfo.value.encounterId === '' ||
|
||||||
patientInfo.value.encounterId === null ||
|
patientInfo.value.encounterId === null ||
|
||||||
patientInfo.value.encounterId === undefined;
|
patientInfo.value.encounterId === undefined;
|
||||||
});
|
});
|
||||||
|
|
||||||
const prescriptionInfo = ref([]);
|
const prescriptionInfo = ref([]);
|
||||||
@@ -327,8 +314,9 @@ const shortcuts = [
|
|||||||
const eprescriptionRef = ref();
|
const eprescriptionRef = ref();
|
||||||
onMounted(() => {
|
onMounted(() => {
|
||||||
getWaitPatient();
|
getWaitPatient();
|
||||||
|
getWaitPatientList();
|
||||||
|
getPatientList();
|
||||||
});
|
});
|
||||||
getPatientList();
|
|
||||||
// 获取现诊患者列表
|
// 获取现诊患者列表
|
||||||
function getPatientList() {
|
function getPatientList() {
|
||||||
queryParams.value.statusEnum = 2;
|
queryParams.value.statusEnum = 2;
|
||||||
@@ -405,6 +393,33 @@ function getWaitPatient() {
|
|||||||
waitCount.value = res.data.total;
|
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) {
|
function handleClick(tab) {
|
||||||
switch (tab) {
|
switch (tab) {
|
||||||
@@ -456,10 +471,10 @@ function getEnPrescription(encounterId) {
|
|||||||
type: 'error',
|
type: 'error',
|
||||||
message: '暂无处方单',
|
message: '暂无处方单',
|
||||||
});
|
});
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
prescriptionInfo.value = res.data.records;
|
prescriptionInfo.value = res.data.records;
|
||||||
openPrescriptionDialog.value = true;
|
openPrescriptionDialog.value = true;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -716,6 +731,58 @@ const onHospitalization = async () => {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// ========== 叫号弹窗所有回调事件 ==========
|
||||||
|
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>
|
</script>
|
||||||
|
|
||||||
<style lang="scss" scoped>
|
<style lang="scss" scoped>
|
||||||
@@ -724,6 +791,7 @@ const onHospitalization = async () => {
|
|||||||
:deep(.el-descriptions__label) {
|
:deep(.el-descriptions__label) {
|
||||||
font-size: 16px !important;
|
font-size: 16px !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
:deep(.el-descriptions__content) {
|
:deep(.el-descriptions__content) {
|
||||||
font-size: 16px !important;
|
font-size: 16px !important;
|
||||||
}
|
}
|
||||||
@@ -906,15 +974,18 @@ const onHospitalization = async () => {
|
|||||||
left: 0;
|
left: 0;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
height: calc(100% - 50px);
|
height: calc(100% - 50px);
|
||||||
z-index: 998; /* 降低z-index,避免覆盖按钮 */
|
z-index: 998;
|
||||||
|
/* 降低z-index,避免覆盖按钮 */
|
||||||
/* 确保覆盖在内容上方,但不覆盖顶部按钮区域 */
|
/* 确保覆盖在内容上方,但不覆盖顶部按钮区域 */
|
||||||
cursor: not-allowed;
|
cursor: not-allowed;
|
||||||
background-color: rgba(255, 255, 255, 0.01);
|
background-color: rgba(255, 255, 255, 0.01);
|
||||||
pointer-events: none; /* 默认不捕获鼠标事件,只在真正需要禁用时才启用 */
|
pointer-events: none;
|
||||||
|
/* 默认不捕获鼠标事件,只在真正需要禁用时才启用 */
|
||||||
}
|
}
|
||||||
|
|
||||||
.disabled-wrapper .overlay.overlay-disabled {
|
.disabled-wrapper .overlay.overlay-disabled {
|
||||||
pointer-events: auto; /* 当需要真正禁用时启用指针事件 */
|
pointer-events: auto;
|
||||||
|
/* 当需要真正禁用时启用指针事件 */
|
||||||
}
|
}
|
||||||
|
|
||||||
/* 顶层按钮样式,确保按钮始终在最上层 */
|
/* 顶层按钮样式,确保按钮始终在最上层 */
|
||||||
|
|||||||
Reference in New Issue
Block a user