| | |
| | | <template> |
| | | <div class="bedside-auxiliary-screen-header"> |
| | | <div class="header-left"> |
| | | <!-- 设备号 --> |
| | | <span class="info-text">{{ deviceNo }}</span> |
| | | <!-- 未排班 --> |
| | | <template v-if="type === 0"> |
| | | <span class="info-text">当前尚未排班</span> |
| | | </template> |
| | | <!-- 有排班 --> |
| | | <!-- 没有设备编号 --> |
| | | <span v-if="pageType === pageTypeEnum.NOT_INIT" class="info-text" |
| | | >未绑定设备</span |
| | | > |
| | | <template v-else> |
| | | <span class="info-text">{{ name }}</span> |
| | | <span class="info-text">{{ age }}岁</span> |
| | | <span class="info-text">{{ gender }}</span> |
| | | <span v-if="formTypeNoText" class="info-text">{{ |
| | | formTypeNoText |
| | | <!-- 设备号 --> |
| | | <span class="info-text">{{ |
| | | bedsideAuxiliaryScreenStore.deviceData.devicdeNo |
| | | }}</span> |
| | | <!-- 加载中 --> |
| | | <span v-if="pageType === pageTypeEnum.LOADING" class="info-text" |
| | | >页面初始化中,请耐心等待!</span |
| | | > |
| | | <!-- 未排班 --> |
| | | <span |
| | | v-else-if="pageType === pageTypeEnum.UNPLANNED_SCHEDULE" |
| | | class="info-text" |
| | | >当前尚未排班</span |
| | | > |
| | | <!-- 有排班 --> |
| | | <template v-else> |
| | | <span class="info-text">{{ patientInfo.patientName }}</span> |
| | | <span v-if="isShowBirthDate" class="info-text">{{ patientInfo.birthDate }}</span> |
| | | <span v-else class="info-text">{{ patientInfo.age }}岁</span> |
| | | <span class="info-text">{{ patientInfo.gender }}</span> |
| | | <span v-if="patientInfo.patFormNumber" class="info-text"> |
| | | {{ patientInfo.patForm }}:{{ patientInfo.patFormNumber }}</span |
| | | > |
| | | <span |
| | | v-if=" |
| | | pageType === pageTypeEnum.DURING_DIALYSIS && |
| | | patientInfo.dialysisAge |
| | | " |
| | | class="info-text" |
| | | > |
| | | 透析龄: {{ patientInfo.dialysisAge?.years }}年{{ |
| | | patientInfo.dialysisAge?.months |
| | | }}月 |
| | | </span> |
| | | </template> |
| | | </template> |
| | | </div> |
| | | <div class="header-right"> |
| | | <img :src="atRegularTimeImg" class="btn-img" alt="" /> |
| | | <span |
| | | v-if=" |
| | | bedsideAuxiliaryScreenStore.taskData && |
| | | bedsideAuxiliaryScreenStore.taskData.length > 0 |
| | | " |
| | | class="countdown" |
| | | > |
| | | {{ formattedCountdown }} |
| | | </span> |
| | | <img |
| | | :src="atRegularTimeImg" |
| | | class="btn-img" |
| | | alt="" |
| | | @click="openScheduledTaskDialog" |
| | | /> |
| | | <img |
| | | :src="setUpImg" |
| | | class="btn-img" |
| | | alt="" |
| | | @click="openSettingDeviceDialog" |
| | | /> |
| | | <img :src="userImg" class="btn-img" alt="" /> |
| | | <img v-if="userInfo?.code" :src="userInfo?.userAvatar" class="btn-img" alt="" @click="openLoginDialog" /> |
| | | <img v-else :src="userImg" class="btn-img" alt="" @click="openLoginDialog" /> |
| | | </div> |
| | | </div> |
| | | <!-- 设置设备编号组件 --> |
| | | <SettingDeviceDialog ref="settingDeviceDialogRef" /> |
| | | <!-- 定时任务组件 --> |
| | | <ScheduledTaskDialog ref="scheduledTaskDialogRef" /> |
| | | <!-- 定时任务提醒组件 --> |
| | | <TaskAlert ref="taskAlertRef" @close="taskAlaetClose" /> |
| | | <!-- 用户登录组件 --> |
| | | <LoginDialog ref="loginDialogRef" /> |
| | | </template> |
| | | |
| | | <script lang="ts" setup name="Header"> |
| | | import { ref, computed, defineAsyncComponent } from "vue"; |
| | | import { |
| | | ref, |
| | | computed, |
| | | defineAsyncComponent, |
| | | onMounted, |
| | | onUnmounted, |
| | | watch, |
| | | } from "vue"; |
| | | import dayjs from "dayjs"; |
| | | import type { Task } from "@/store/type/task.type"; |
| | | const SettingDeviceDialog = defineAsyncComponent( |
| | | () => import("./SettingDeviceDialog.vue") |
| | | ); |
| | | const ScheduledTaskDialog = defineAsyncComponent( |
| | | () => import("./ScheduledTask.vue") |
| | | ); |
| | | const TaskAlert = defineAsyncComponent(() => import("./TaskAlart.vue")); |
| | | const LoginDialog = defineAsyncComponent(() => import('./Login/index.vue')); |
| | | |
| | | import atRegularTimeImg from "../../../../img/dingshi.png"; |
| | | import setUpImg from "../../../../img/shezhi.png"; |
| | | import userImg from "../../../../img/user.png"; |
| | | |
| | | type HearderType = 0 | 1; // 0未排班 1其它 |
| | | type FormType = 0 | 1; // 0门诊 1住院 |
| | | import { useBedsideAuxiliaryScreenStore } from "@/store/bedsideAuxiliaryScreen"; |
| | | import { EPatForm, EPageType } from "@/store/type/bedsideAuxiliaryScreen.type"; |
| | | import { ElMessage } from "element-plus"; |
| | | import { maskName } from '@/utils/utils'; |
| | | |
| | | interface Props { |
| | | type: HearderType; // 类型 |
| | | deviceNo: number | string; // 设备号 |
| | | name?: string; // 姓名 |
| | | age?: number | string; // 年龄 |
| | | gender?: string; // 性别 |
| | | formType?: FormType; // 患者来源 |
| | | formNo?: number | string; // 门诊/住院号 |
| | | } |
| | | const props = defineProps<Props>(); |
| | | const bedsideAuxiliaryScreenStore = useBedsideAuxiliaryScreenStore(); |
| | | |
| | | let timer: ReturnType<typeof setInterval> | null = null; |
| | | |
| | | const pageTypeEnum = ref(EPageType); |
| | | const settingDeviceDialogRef = ref<any>(null); |
| | | const scheduledTaskDialogRef = ref<any>(null); |
| | | const taskAlertRef = ref<any>(null); |
| | | const loginDialogRef = ref<any>(null); |
| | | |
| | | const formTypeNoText = computed(() => { |
| | | if (props.formNo) { |
| | | let result = props?.formType === 1 ? "住院号" : "门诊号"; |
| | | result += props.formNo; |
| | | return result; |
| | | } |
| | | return ""; |
| | | const countdown = ref(null); // 定时任务的倒计时 |
| | | const isTaskAlartIsOpen = ref(false); // 定时任务的提醒弹框是否显示 |
| | | |
| | | const pageType = computed(() => { |
| | | return bedsideAuxiliaryScreenStore.deviceData.pageType; |
| | | }); |
| | | |
| | | |
| | | const patientInfo = computed(() => { |
| | | return { |
| | | patientName: bedsideAuxiliaryScreenStore.deviceData.customConfiguration.患者信息是否加密显示 === 1 ? maskName(bedsideAuxiliaryScreenStore.deviceData.patientName) : bedsideAuxiliaryScreenStore.deviceData.patientName, |
| | | patientPhone: bedsideAuxiliaryScreenStore.deviceData.patientPhone, |
| | | age: bedsideAuxiliaryScreenStore.deviceData.age, |
| | | gender: bedsideAuxiliaryScreenStore.deviceData.gender, |
| | | patForm: |
| | | bedsideAuxiliaryScreenStore.deviceData.patForm === |
| | | EPatForm.OUTPATIENT_SERVICE |
| | | ? "门诊号" |
| | | : "住院号", |
| | | patFormNumber: bedsideAuxiliaryScreenStore.deviceData.patFormNumber, |
| | | dialysisAge: bedsideAuxiliaryScreenStore.deviceData.underTreatment |
| | | .dialysisAge |
| | | ? convertMonths( |
| | | bedsideAuxiliaryScreenStore.deviceData.underTreatment.dialysisAge |
| | | ) |
| | | : null, |
| | | birthDate: bedsideAuxiliaryScreenStore.deviceData.患者出生日期, |
| | | }; |
| | | }); |
| | | |
| | | const isShowBirthDate = computed(() => { |
| | | return bedsideAuxiliaryScreenStore.deviceData.customConfiguration.是否显示患者出生年月日 === 1; |
| | | }); |
| | | |
| | | const formattedCountdown = computed(() => { |
| | | if (countdown.value == null || countdown.value <= 0) return "0s"; |
| | | |
| | | const minutes = Math.floor(countdown.value / 60); |
| | | const seconds = countdown.value % 60; |
| | | |
| | | if (minutes > 0) { |
| | | return `${minutes}m ${seconds}s`; |
| | | } else { |
| | | return `${seconds}s`; |
| | | } |
| | | }); |
| | | |
| | | |
| | | const userInfo = computed(() => { |
| | | if (!bedsideAuxiliaryScreenStore.userInfo) return null |
| | | return Object.assign({}, bedsideAuxiliaryScreenStore.userInfo, { |
| | | userAvatar: bedsideAuxiliaryScreenStore.userInfo?.userAvatar ? bedsideAuxiliaryScreenStore.userInfo?.userAvatar : generateCircularAvatar(bedsideAuxiliaryScreenStore.userInfo?.userName) |
| | | }); |
| | | }); |
| | | |
| | | watch( |
| | | () => bedsideAuxiliaryScreenStore.taskData?.[0]?.countdown, |
| | | (val) => { |
| | | if (typeof val === "number") { |
| | | startCountdown(val); |
| | | } else { |
| | | clearTimer(); |
| | | } |
| | | }, |
| | | { immediate: true } |
| | | ); |
| | | |
| | | watch(countdown, (newVal) => { |
| | | if (newVal <= 0 && !isTaskAlartIsOpen.value) { |
| | | isTaskAlartIsOpen.value = true; |
| | | showTaskAlart(); |
| | | } |
| | | }); |
| | | |
| | | // 清除定时器函数 |
| | | function clearTimer() { |
| | | if (timer) { |
| | | clearInterval(timer); |
| | | timer = null; |
| | | } |
| | | } |
| | | |
| | | // 启动新的倒计时 |
| | | function startCountdown(seconds: number) { |
| | | clearTimer(); |
| | | countdown.value = seconds; |
| | | |
| | | timer = setInterval(() => { |
| | | if (countdown.value > 0) { |
| | | countdown.value -= 1; |
| | | } else { |
| | | clearTimer(); |
| | | } |
| | | }, 1000); |
| | | } |
| | | |
| | | const convertMonths = (months: number): { years: number; months: number } => { |
| | | const years = Math.floor(months / 12); |
| | | const remainingMonths = months % 12; |
| | | return { years, months: remainingMonths }; |
| | | }; |
| | | |
| | | const showTaskAlart = () => { |
| | | clearTimer(); |
| | | taskAlertRef.value.openDialog( |
| | | bedsideAuxiliaryScreenStore.taskData?.[0]?.taskName |
| | | ); |
| | | }; |
| | | |
| | | const taskAlaetClose = () => { |
| | | clearTimer(); |
| | | bedsideAuxiliaryScreenStore.clearTask(); |
| | | isTaskAlartIsOpen.value = false; |
| | | }; |
| | | |
| | | const openSettingDeviceDialog = () => { |
| | | settingDeviceDialogRef.value?.openDialog(); |
| | | }; |
| | | |
| | | const openScheduledTaskDialog = () => { |
| | | if ( |
| | | !bedsideAuxiliaryScreenStore.deviceCode || |
| | | !bedsideAuxiliaryScreenStore.deviceData.deviceCode |
| | | ) |
| | | return ElMessage.warning("未初始化或正在进行初始化操作中"); |
| | | scheduledTaskDialogRef.value?.openDialog(); |
| | | }; |
| | | |
| | | const openLoginDialog = () => { |
| | | if ( |
| | | !bedsideAuxiliaryScreenStore.deviceCode || |
| | | !bedsideAuxiliaryScreenStore.deviceData.deviceCode |
| | | ) |
| | | return ElMessage.warning("未初始化或正在进行初始化操作中"); |
| | | console.log(loginDialogRef) |
| | | loginDialogRef.value.openDialog(); |
| | | }; |
| | | |
| | | function generateCircularAvatar(name: string, size = 100): string { |
| | | console.log('name: ', name) |
| | | const canvas = document.createElement('canvas'); |
| | | canvas.width = size; |
| | | canvas.height = size; |
| | | const ctx = canvas.getContext('2d')!; |
| | | |
| | | // 绘制圆形背景 |
| | | ctx.fillStyle = '#dae5ec'; |
| | | ctx.beginPath(); |
| | | ctx.arc(size / 2, size / 2, size / 2, 0, Math.PI * 2); |
| | | ctx.fill(); |
| | | |
| | | // 绘制文字(第一个字) |
| | | ctx.fillStyle = '#70a3dd'; // 字体颜色 |
| | | ctx.font = `${size * 0.5}px sans-serif`; // 字体大小为头像大小的一半 |
| | | ctx.textAlign = 'center'; |
| | | ctx.textBaseline = 'middle'; |
| | | ctx.fillText(name.charAt(0), size / 2, size / 2); |
| | | |
| | | return canvas.toDataURL('image/png'); // 返回 Base64 图片 |
| | | } |
| | | |
| | | onUnmounted(() => { |
| | | clearTimer(); |
| | | }); |
| | | </script> |
| | | |
| | | <style lang="less" scoped> |
| | |
| | | .info-text { |
| | | font-family: PingFangSC, PingFang SC; |
| | | font-weight: 600; |
| | | font-size: 11px; |
| | | font-size: 9px; |
| | | color: #ffffff; |
| | | text-align: left; |
| | | font-style: normal; |
| | | &:not(:first-child) { |
| | | margin-left: 6px; |
| | | margin-left: 4px; |
| | | } |
| | | } |
| | | } |
| | |
| | | |
| | | transition: all 0.2s; |
| | | } |
| | | .countdown { |
| | | font-family: PingFangSC, PingFang SC; |
| | | font-weight: 600; |
| | | font-size: 9px; |
| | | color: #bb3e3e; |
| | | line-height: 15rpx; |
| | | text-align: left; |
| | | font-style: normal; |
| | | } |
| | | } |
| | | } |
| | | </style> |