|  |  | 
 |  |  | <script setup lang="ts"> | 
 |  |  |  | 
 |  |  | import {ScreenApi, statusMap, CallingVO} from "@/api/ecg/screen"; | 
 |  |  | import {ScreenApi, ScreenQueueVO} from "@/api/ecg/screen"; | 
 |  |  | import {CallApi, CallVO} from "@/api/ecg/call"; | 
 |  |  | import {DICT_TYPE} from "@/utils/dict"; | 
 |  |  |  | 
 |  |  | defineOptions({ name: 'bigscreen' }) | 
 |  |  |  | 
 |  |  | const listReady = ref<CallingVO[]>([]) // 列表的数据 | 
 |  |  | const listWaiting = ref<CallingVO[]>([]) // 列表的数据 | 
 |  |  | const listPassed = ref<CallingVO[]>([]) // | 
 |  |  | const listReady = ref<ScreenQueueVO[]>([]) // 列表的数据 | 
 |  |  | const listWaiting = ref<ScreenQueueVO[]>([]) // 列表的数据 | 
 |  |  | const listPassed = ref<ScreenQueueVO[]>([]) // | 
 |  |  |  | 
 |  |  | let curSpeakPat : CallingVO | undefined = undefined; | 
 |  |  | let curSpeakSeqNum : number = 0 | 
 |  |  | let curWorkDay : Date | undefined = undefined | 
 |  |  | let curSpeakPat : CallVO | null = null; | 
 |  |  |  | 
 |  |  | const getList = async () => { | 
 |  |  |   const data = await ScreenApi.getBigScreenData() | 
 |  |  | 
 |  |  | const startScrolling = () => { | 
 |  |  |   setInterval(() => { | 
 |  |  |     getList(); | 
 |  |  |     if ( curSpeakPat === undefined ) { | 
 |  |  |     if ( curSpeakPat === null ) { | 
 |  |  |       initiateSpeak() | 
 |  |  |     } | 
 |  |  |   }, 5000); // 每X秒滚动一次 | 
 |  |  | 
 |  |  | }) | 
 |  |  |  | 
 |  |  | const onSpeachEndEvent = async (event) => { | 
 |  |  |   console.log("Speech ended" + event); | 
 |  |  |   await ScreenApi.markCalled(curSpeakPat!.patId) | 
 |  |  |   console.log("Speech ended... " + event.currentTarget.text); | 
 |  |  |   curSpeakPat!.called = 1 | 
 |  |  |   await CallApi.updateCall(curSpeakPat!) | 
 |  |  |   initiateSpeak() | 
 |  |  | } | 
 |  |  |  | 
 |  |  | const getNextSpeak = () => { | 
 |  |  |   if (listReady.value.length === 0) { | 
 |  |  |     return undefined; | 
 |  |  |   } | 
 |  |  |  | 
 |  |  |   // TODO 处理每天 反转的情况 | 
 |  |  |   if ( curWorkDay === undefined) { | 
 |  |  |     curSpeakSeqNum = 0; | 
 |  |  |     curWorkDay = new Date() | 
 |  |  |   } else if (!isSameDate(listReady.value[0].bookDate, curWorkDay)) { | 
 |  |  |     curSpeakSeqNum = 0; | 
 |  |  |     curWorkDay.setFullYear( listReady.value[0].bookDate[0] ) | 
 |  |  |     curWorkDay.setMonth( listReady.value[0].bookDate[1] ) | 
 |  |  |     curWorkDay.setDate( listReady.value[0].bookDate[2] ) | 
 |  |  |   } | 
 |  |  |  | 
 |  |  |   return listReady.value.find( item => item.status === 30 && item.called === 0 && item.seqNum > curSpeakSeqNum) | 
 |  |  | } | 
 |  |  |  | 
 |  |  | const initiateSpeak = () => { | 
 |  |  |   const queuePat = getNextSpeak() | 
 |  |  |   if (queuePat !== undefined) { | 
 |  |  |     curSpeakPat = queuePat | 
 |  |  |     curSpeakSeqNum = curSpeakPat.seqNum | 
 |  |  |     speak(queuePat.patName + queuePat.roomName); | 
 |  |  |   } else { | 
 |  |  |     curSpeakPat = undefined | 
 |  |  | const initiateSpeak = async () => { | 
 |  |  |   curSpeakPat = await CallApi.getNextCall() | 
 |  |  |   console.info( curSpeakPat ) | 
 |  |  |   if (curSpeakPat !== null) { | 
 |  |  |     speak("请、" + curSpeakPat.patName + "到" + curSpeakPat.roomName + "就诊"); | 
 |  |  |   } | 
 |  |  | } | 
 |  |  |  | 
 |  |  | const speak = (msg) => { | 
 |  |  |   console.info("speak " + msg); | 
 |  |  |   var speech = new SpeechSynthesisUtterance() | 
 |  |  |   speech.text = msg | 
 |  |  |   speech.text = msg + "。。。" + msg + "。。。" + msg  + "。。。" | 
 |  |  |   speech.pitch = 1 // 获取并设置话语的音调(0-2 默认1,值越大越尖锐,越低越低沉) | 
 |  |  |   speech.rate = 0.9 // 获取并设置说话的速度(0.1-10 默认1,值越大语速越快,越小语速越慢) | 
 |  |  |   speech.volume = 100 // 获取并设置说话的音量 | 
 |  |  | 
 |  |  |   speechSynthesis.speak(speech) | 
 |  |  | } | 
 |  |  |  | 
 |  |  | const isSameDate = ( date1: number[], date2 : Date) => { | 
 |  |  |   if ( date1[0] === date2.getFullYear() | 
 |  |  |        && date1[1] === date2.getMonth() | 
 |  |  |        && date1[2] === date2.getDate() ) { | 
 |  |  |     return true | 
 |  |  |   } else { | 
 |  |  |     return false | 
 |  |  | const nameDesensitize = (patName) => { | 
 |  |  |   if (patName.length == 2) { | 
 |  |  |     //截取name 字符串截取第一个字符, | 
 |  |  |     return patName.substring(0, 1) + '*'; | 
 |  |  |   } else if (patName.length == 3) { | 
 |  |  |     //截取第一个和第三个字符 | 
 |  |  |     return patName.substring(0, 1) + '*' + patName.substring(2, 3); | 
 |  |  |   } else if (patName.length > 3) { | 
 |  |  |     //截取第一个和大于第4个字符 | 
 |  |  |     return ( | 
 |  |  |         patName.substring(0, 1) + '*' + '*' + patName.substring(3, patName.length) | 
 |  |  |     ); | 
 |  |  |   } | 
 |  |  | } | 
 |  |  |  | 
 |  |  | </script> | 
 |  |  |  | 
 |  |  | <template> | 
 |  |  |   <el-container> | 
 |  |  |     <el-header>Header</el-header> | 
 |  |  |   <el-container style="height: 100%;"> | 
 |  |  |     <el-header> | 
 |  |  |       <img | 
 |  |  |           height="40px" | 
 |  |  |           src="@/assets/imgs/jinhua.png" | 
 |  |  |       /> | 
 |  |  |       金华人民医院 | 
 |  |  |     </el-header> | 
 |  |  |     <el-container> | 
 |  |  |       <el-aside width="500px"> | 
 |  |  |         <el-table | 
 |  |  |             :data="listReady" | 
 |  |  |             stripe | 
 |  |  |             :show-header="false" | 
 |  |  |             style="width: 100%"> | 
 |  |  |             style="width: 100%; height: 100%; border: solid var(--el-color-primary-light-7); font-size: 18px;"> | 
 |  |  |           <el-table-column | 
 |  |  |               prop="patName" | 
 |  |  |               label="患者姓名" | 
 |  |  |               width="80"/> | 
 |  |  |           <el-table-column label="状态" align="center" prop="status" width="80"> | 
 |  |  |               width="80"> | 
 |  |  |             <template #default="scope"> | 
 |  |  |               {{ statusMap.get(scope.row.status) }} | 
 |  |  |               {{scope.row.seqNum}}   {{ nameDesensitize(scope.row.patName) }} | 
 |  |  |             </template> | 
 |  |  |           </el-table-column> | 
 |  |  |           <el-table-column label="状态" align="center" prop="status" width="80"> | 
 |  |  |             <template #default="scope"> | 
 |  |  |               <dict-tag :type="DICT_TYPE.ECG_QUEUE_STATUS" :value="scope.row.status" /> | 
 |  |  |             </template> | 
 |  |  |           </el-table-column> | 
 |  |  |           <el-table-column | 
 |  |  |               prop="roomName" | 
 |  |  |               label="诊室" | 
 |  |  |               width="120"/> | 
 |  |  |         </el-table> | 
 |  |  |       </el-aside> | 
 |  |  |       <el-container> | 
 |  |  |         <el-main> | 
 |  |  |           <el-table | 
 |  |  |             :data="listWaiting" | 
 |  |  |             stripe | 
 |  |  |             :show-header="false" | 
 |  |  |             style="width: 100%"> | 
 |  |  |             <el-table-column | 
 |  |  |               prop="patName" | 
 |  |  |               label="患者姓名" | 
 |  |  |               width="80"/> | 
 |  |  |             <el-table-column label="状态" align="center" prop="status" width="80"> | 
 |  |  |               <template #default="scope"> | 
 |  |  |                 {{ statusMap.get(scope.row.status) }} | 
 |  |  |               </template> | 
 |  |  |             </el-table-column> | 
 |  |  |           </el-table> | 
 |  |  |         <el-main style="padding: 0 0 "> | 
 |  |  |           <el-header height="30px" style="background-color: #98b8e5; line-height: 30px;">等候区</el-header> | 
 |  |  |           <span v-for="(waitingItem, index) in listWaiting" :key="index"> | 
 |  |  |             {{nameDesensitize(waitingItem.patName) + "    "}} | 
 |  |  |           </span> | 
 |  |  |         </el-main> | 
 |  |  |         <el-footer height="100px"> | 
 |  |  |           <el-table | 
 |  |  |             :data="listPassed" | 
 |  |  |             stripe | 
 |  |  |             :show-header="false" | 
 |  |  |             style="width: 100%"> | 
 |  |  |             <el-table-column | 
 |  |  |               prop="patName" | 
 |  |  |               label="患者姓名" | 
 |  |  |               width="80"/> | 
 |  |  |             <el-table-column label="状态" align="center" prop="status" width="80"> | 
 |  |  |               <template #default="scope"> | 
 |  |  |                 {{ statusMap.get(scope.row.status) }} | 
 |  |  |               </template> | 
 |  |  |             </el-table-column> | 
 |  |  |           </el-table> | 
 |  |  |         <el-footer height="100px" style="padding: 0 0"> | 
 |  |  |           <el-header height="30px" style="background-color: #98b8e5; line-height: 30px;">过号区</el-header> | 
 |  |  |           <span v-for="(passedItem, index) in listPassed" :key="index"> | 
 |  |  |             {{nameDesensitize(passedItem.patName) + " " + passedItem.roomName + "    "}} | 
 |  |  |           </span> | 
 |  |  |         </el-footer> | 
 |  |  |       </el-container> | 
 |  |  |     </el-container> | 
 |  |  |     <el-button @click="speak('请 特朗普 到二号诊室 就诊')" >叫号</el-button> | 
 |  |  |     <el-button @click="speak('欢迎使用')" >欢迎使用</el-button> | 
 |  |  |   </el-container> | 
 |  |  | </template> | 
 |  |  |  | 
 |  |  | <style scoped lang="scss"> | 
 |  |  | .el-header, .el-footer { | 
 |  |  |   background-color: #B3C0D1; | 
 |  |  | .el-header { | 
 |  |  |   background-color: var(--el-color-primary-light-7); | 
 |  |  |   color: #333; | 
 |  |  |   text-align: center; | 
 |  |  |   line-height: 40px; | 
 |  |  |  | 
 |  |  |   display: flex; | 
 |  |  |   align-items: center; | 
 |  |  |   justify-content: center; | 
 |  |  | } | 
 |  |  |  | 
 |  |  | .el-footer { | 
 |  |  |   //background-color: var(--el-color-primary-light-7); | 
 |  |  |   color: #333; | 
 |  |  |   text-align: center; | 
 |  |  |   line-height: 60px; | 
 |  |  | } | 
 |  |  |  | 
 |  |  | .el-aside { | 
 |  |  |   background-color: #D3DCE6; | 
 |  |  |   background-color: var(--el-color-primary-light-7); | 
 |  |  |   color: #333; | 
 |  |  |   text-align: center; | 
 |  |  |   line-height: 200px; | 
 |  |  | } | 
 |  |  |  | 
 |  |  | .el-main { | 
 |  |  |   background-color: #E9EEF3; | 
 |  |  |   //background-color: var(--el-color-primary-light-7); | 
 |  |  |   color: #333; | 
 |  |  |   text-align: center; | 
 |  |  |   line-height: 160px; | 
 |  |  | 
 |  |  |   margin-bottom: 40px; | 
 |  |  | } | 
 |  |  |  | 
 |  |  | .el-container:nth-child(5) .el-aside, | 
 |  |  | .el-container:nth-child(6) .el-aside { | 
 |  |  |   line-height: 260px; | 
 |  |  | } | 
 |  |  |  | 
 |  |  | .el-container:nth-child(7) .el-aside { | 
 |  |  |   line-height: 320px; | 
 |  |  | } | 
 |  |  | </style> |