<script setup lang="ts">
|
import {ScreenApi, ScreenQueueVO} from "@/api/ecg/screen";
|
import {CallApi, CallVO} from "@/api/ecg/call";
|
import {queueStatusConvert} from "@/utils/statusFormatter";
|
import {useCheckTypeStore} from "@/store/modules/checkType";
|
import {getCheckTypeSeqPrefix} from "../../../utils/checkTypeFormatter";
|
import { computed } from 'vue'
|
defineOptions({ name: 'bigscreen' })
|
|
const runningIndicator = ref<number>(0)
|
const checkTypeStore = useCheckTypeStore()
|
const displayColInfo = ref<Map<number, String[]>>(new Map())
|
const mapColumnVsPatients = ref<Map<number, ScreenQueueVO[]>>(new Map());
|
let curSpeakPat : CallVO | null = null;
|
|
const getDisplayColInfo = async () => {
|
const data = await ScreenApi.getDisplayColInfo()
|
displayColInfo.value = data
|
}
|
|
const getList = async () => {
|
const data = await ScreenApi.getBigScreenData()
|
mapColumnVsPatients.value = data;
|
}
|
|
const startScrolling = () => {
|
setInterval(() => {
|
runningIndicator.value++;
|
getList();
|
if ( curSpeakPat == null ) {
|
initiateSpeak()
|
}
|
}, 5000);
|
}
|
|
onMounted( () => {
|
getDisplayColInfo()
|
startScrolling()
|
})
|
|
const onSpeachEndEvent = async (event) => {
|
console.log("Speech ended... " + event.currentTarget.text);
|
curSpeakPat!.called = 1
|
await CallApi.updateCall(curSpeakPat!)
|
initiateSpeak()
|
}
|
|
const initiateSpeak = async () => {
|
curSpeakPat = await CallApi.getNextCall()
|
if (curSpeakPat != null && curSpeakPat.called === 0) {
|
speak("请、" + curSpeakPat.patName + "到" + curSpeakPat.roomName + "就诊");
|
}
|
}
|
const splitPatientColumns = computed(() => {
|
const result = new Map<number, ScreenQueueVO[][]>();
|
|
for (const [colId, patients] of mapColumnVsPatients.value.entries()) {
|
if (colId === 0 && patients.length > 10) {
|
// 第一列且数据超过10条,分成两列
|
const firstCol = patients.slice(0, 10);
|
const secondCol = patients.slice(10);
|
result.set(colId, [firstCol, secondCol]);
|
} else {
|
// 其他列保持原样
|
result.set(colId, [patients]);
|
}
|
}
|
|
return result;
|
});
|
const speak = (msg) => {
|
console.info("speak " + msg);
|
var speech = new SpeechSynthesisUtterance()
|
speech.text = msg + "。。。" + msg + "。。。" + msg + "。。。"
|
speech.pitch = 1
|
speech.rate = 0.9
|
speech.volume = 100
|
speech.lang = 'zh-CN'
|
speech.onend = onSpeachEndEvent
|
speechSynthesis.speak(speech)
|
}
|
|
const nameDesensitize = (patName) => {
|
if (patName.length == 2) {
|
return patName.substring(0, 1) + '*';
|
} else if (patName.length == 3) {
|
return patName.substring(0, 1) + '*' + patName.substring(2, 3);
|
} else if (patName.length > 3) {
|
return patName.substring(0, 1) + '*' + '*' + patName.substring(3, patName.length);
|
}
|
}
|
|
const getItemCssClass = (item: ScreenQueueVO) => {
|
if (item.status === 5 || item.status === 7) {
|
return "warning-row";
|
}
|
return "";
|
}
|
</script>
|
|
<template>
|
<div class="bigscreen-container">
|
<!-- 顶部医院标题 -->
|
<div class="hospital-header">
|
<img height="40px" src="@/assets/imgs/jinhua.png"/>
|
<span>金华人民医院</span>
|
<div class="running-indicator" :style="{ backgroundColor: runningIndicator%2 === 0 ? '#67c23a' : '#e6a23c' }"></div>
|
</div>
|
|
<!-- 列标题区域 -->
|
<div class="column-header">
|
<template v-for="([disColId, disNameList], index) of Object.entries(displayColInfo)" :key="disColId">
|
<div
|
class="column-title"
|
:class="{ 'double-column': index === 0 }"
|
>
|
<div v-for="(dispName, dispName_index) in disNameList" :key="dispName_index">
|
{{dispName}}
|
</div>
|
</div>
|
</template>
|
</div>
|
|
<!-- 患者列表区域 -->
|
<div class="patient-list-container">
|
<template v-for="([disColId2, columnPatientList], index) of Object.entries(mapColumnVsPatients)" :key="disColId2">
|
<!-- 第一列且数据超过10条时,分成两列 -->
|
<template v-if="index === 0 && columnPatientList.length > 13">
|
<!-- 第一列(前10条) -->
|
<div class="patient-column">
|
<div
|
v-for="(item, itemIndex) in columnPatientList.slice(0, 13)"
|
:key="itemIndex"
|
:class="getItemCssClass(item)"
|
class="patient-item"
|
>
|
<span class="patient-number">{{getCheckTypeSeqPrefix(item.bookCheckType)}}{{item.bookSeqNum}}</span>
|
<span class="patient-name">{{ nameDesensitize(item.patName) }}</span>
|
<span class="patient-status">{{queueStatusConvert(item.status)}}</span>
|
<span class="patient-room">{{item.roomName}} {{item.bedNo}}</span>
|
</div>
|
</div>
|
|
<!-- 第二列(剩余数据) -->
|
<div class="patient-column continuation-column">
|
<div
|
v-for="(item, itemIndex) in columnPatientList.slice(13)"
|
:key="itemIndex + 10"
|
:class="getItemCssClass(item)"
|
class="patient-item"
|
>
|
<span class="patient-number">{{getCheckTypeSeqPrefix(item.bookCheckType)}}{{item.bookSeqNum}}</span>
|
<span class="patient-name">{{ nameDesensitize(item.patName) }}</span>
|
<span class="patient-status">{{queueStatusConvert(item.status)}}</span>
|
<span class="patient-room">{{item.roomName}} {{item.bedNo}}</span>
|
</div>
|
</div>
|
</template>
|
|
<!-- 其他情况正常显示 -->
|
<template v-else>
|
<div
|
class="patient-column"
|
:class="{ 'double-column': index === 0 }"
|
>
|
<div
|
v-for="(item, itemIndex) in columnPatientList"
|
:key="itemIndex"
|
:class="getItemCssClass(item)"
|
class="patient-item"
|
>
|
<span class="patient-number">{{getCheckTypeSeqPrefix(item.bookCheckType)}}{{item.bookSeqNum}}</span>
|
<span class="patient-name">{{ nameDesensitize(item.patName) }}</span>
|
<span class="patient-status">{{queueStatusConvert(item.status)}}</span>
|
<span class="patient-room">{{item.roomName}} {{item.bedNo}}</span>
|
</div>
|
</div>
|
</template>
|
</template>
|
</div>
|
</div>
|
</template>
|
|
<style scoped lang="scss">
|
/* 保持原有的基础样式不变 */
|
.bigscreen-container {
|
height: 100vh;
|
display: flex;
|
flex-direction: column;
|
background: linear-gradient(135deg, #e6f0f8, #d9e4f0);
|
color: #4a5568;
|
padding: 8px;
|
}
|
|
.hospital-header {
|
display: flex;
|
justify-content: center;
|
align-items: center;
|
background: rgba(255, 255, 255, 0.9);
|
border-radius: 12px;
|
padding: 12px;
|
margin-bottom: 8px;
|
font-size: 22px;
|
font-weight: bold;
|
color: #4a7dff;
|
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
|
|
img {
|
margin-right: 8px;
|
}
|
}
|
|
.running-indicator {
|
width: 12px;
|
height: 12px;
|
border-radius: 50%;
|
margin-left: 12px;
|
transition: background-color 0.3s;
|
}
|
|
/* 修改列标题区域 */
|
.column-header {
|
display: flex;
|
justify-content: space-around;
|
align-items: flex-start;
|
background: rgba(255, 255, 255, 0.9);
|
border-radius: 12px;
|
padding: 12px 0;
|
margin-bottom: 8px;
|
font-size: 20px;
|
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
|
}
|
|
.column-title {
|
flex: 1;
|
min-width: 300px;
|
text-align: center;
|
padding: 0 12px;
|
border-left: 1px solid rgba(91, 140, 255, 0.2);
|
|
&:first-child {
|
border-left: none;
|
}
|
|
&.double-column {
|
flex: 2; /* 占据两倍宽度 */
|
min-width: 600px;
|
}
|
}
|
|
/* 修改患者列表区域 */
|
.patient-list-container {
|
display: flex;
|
justify-content: space-around;
|
align-items: flex-start;
|
flex-grow: 1;
|
background: rgba(255, 255, 255, 0.9);
|
border-radius: 12px;
|
padding: 12px 0;
|
margin-bottom: 8px;
|
font-size: 18px;
|
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
|
overflow-y: auto;
|
}
|
|
.patient-column {
|
flex: 1;
|
min-width: 300px;
|
padding: 0 12px;
|
border-left: 1px solid rgba(91, 140, 255, 0.2);
|
|
&:first-child {
|
border-left: none;
|
}
|
|
&.double-column {
|
flex: 2; /* 占据两倍宽度 */
|
min-width: 600px;
|
display: flex;
|
flex-wrap: wrap;
|
|
.patient-item {
|
width: 48%; /* 两列布局,留一点间距 */
|
margin-right: 2%;
|
|
&:nth-child(2n) {
|
margin-right: 0;
|
}
|
}
|
}
|
|
|
}
|
|
/* 保持原有的患者项样式 */
|
.patient-item {
|
padding: 10px 8px;
|
margin-bottom: 8px;
|
border-radius: 8px;
|
transition: all 0.3s;
|
|
&:hover {
|
background: rgba(91, 140, 255, 0.1);
|
}
|
|
span {
|
display: inline-block;
|
margin-right: 10px;
|
}
|
}
|
|
.patient-number {
|
width: 80px;
|
color: #5b8cff;
|
font-weight: bold;
|
}
|
|
.patient-name {
|
width: 100px;
|
}
|
|
.patient-status {
|
width: 100px;
|
color: #67c23a;
|
}
|
|
.patient-room {
|
color: #666;
|
}
|
|
.warning-row {
|
background-color: rgba(230, 162, 60, 0.1);
|
color: #e6a23c;
|
}
|
|
|
.footer-controls {
|
display: flex;
|
justify-content: center;
|
align-items: center;
|
background: rgba(255, 255, 255, 0.9);
|
border-radius: 12px;
|
padding: 12px;
|
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
|
}
|
|
.welcome-btn {
|
background: rgba(91, 140, 255, 0.1);
|
color: #4a7dff;
|
border: none;
|
border-radius: 8px;
|
padding: 10px 20px;
|
font-size: 16px;
|
|
&:hover {
|
background: rgba(91, 140, 255, 0.2);
|
}
|
|
&:active {
|
transform: scale(0.98);
|
}
|
}
|
|
/* 滚动条样式 */
|
.patient-list-container::-webkit-scrollbar {
|
width: 6px;
|
}
|
|
.patient-list-container::-webkit-scrollbar-track {
|
background: rgba(0, 0, 0, 0.05);
|
border-radius: 4px;
|
}
|
|
.patient-list-container::-webkit-scrollbar-thumb {
|
background: rgba(91, 140, 255, 0.4);
|
border-radius: 4px;
|
}
|
|
.patient-list-container::-webkit-scrollbar-thumb:hover {
|
background: rgba(91, 140, 255, 0.6);
|
}
|
</style>
|