| <script setup> | 
| import { ref, onMounted, nextTick } from 'vue' | 
| import dayjs from 'dayjs' | 
| import UQRCode from 'uqrcodejs' | 
| import { getLargeScreenData } from '@/utils/request' | 
| import duration from 'dayjs/plugin/duration' | 
| dayjs.extend(duration) | 
| import VScaleScreen from 'v-scale-screen' | 
|   | 
| const qrcode = ref('') | 
| const contentList = ref([]) | 
| const contentTempList = ref([]) | 
| const page = ref(1) | 
| const timer = ref() | 
|   | 
| const callList = ref([]) | 
| const activeCall = ref({}) | 
| const pageCall = ref(0) | 
| const timerCall = ref() | 
|   | 
| const initData = () => { | 
|   getLargeScreenData().then(res => { | 
|     if (res.data && res.data.allList) { | 
|       contentList.value = res.data.allList.map((item, index) => { | 
|         item.index = index + 1 | 
|         if (item.optTime) { | 
|           item.optTimeTemp = dayjs(item.optTime).format('HH:mm') | 
|           if(item.status == 4){ | 
|             if(item.optTime >= new Date().getTime()){ | 
|               item.optTimeTemp = dayjs.duration(item.optTime - new Date().getTime()).format('HH:mm') | 
|             }else{ | 
|               item.optTimeTemp = '-' | 
|             } | 
|           } | 
|           // item.optTimeTemp = dayjs.duration(Math.abs(new Date().getTime() - item.optTime)).format('HH:mm') | 
|         } | 
|         return item | 
|       }) | 
|       clearInterval(timer.value) | 
|       setInitContent() | 
|     } | 
|     if (res.data && res.data.waitWorkList) { | 
|       callList.value = res.data.waitWorkList | 
|     } | 
|     qrcode.value = res.data.qrCode | 
|     initQrcode() | 
|     clearInterval(timerCall.value) | 
|     setInitCall() | 
|   }) | 
| } | 
|   | 
| const frequencyFn = (length) => { | 
|   if (length <= 16) { // 2 | 
|     return 30 * 1000 | 
|   } else if (length <= 24) { // 3 | 
|     return 20 * 1000 | 
|   } else if (length <= 32) {// 4 | 
|     return 15 * 1000 | 
|   } else if (length <= 48) {// 6 | 
|     return 10 * 1000 | 
|   } else if (length <= 60) {// 10 | 
|     return 6 * 1000 | 
|   } else { | 
|     return 4 * 1000 | 
|   } | 
| } | 
| const frequencyCallFn = (length) => { | 
|   if (length <= 10) { // 2 | 
|     return 6 * 1000 | 
|   } else if (length <= 20) { // 3 | 
|     return 3 * 1000 | 
|   } else { | 
|     return 2 * 1000 | 
|   } | 
| } | 
| const setInitContent = () => { | 
|   if (contentList.value && contentList.value.length > 0) { | 
|     contentTempList.value = contentList.value.slice(0, 8) | 
|     if (page.value * 8 >= contentList.value.length) { | 
|       page.value = 1 | 
|     } else { | 
|       page.value++ | 
|     } | 
|     timer.value = setInterval(() => { | 
|       if (page.value * 8 >= contentList.value.length) { | 
|         page.value = 1 | 
|       } else { | 
|         page.value++ | 
|       } | 
|       contentTempList.value = contentList.value.slice((page.value - 1) * 8, page.value * 8) | 
|     }, frequencyFn(contentList.value.length)) | 
|   } | 
| } | 
| const setInitCall = () => { | 
|   if (callList.value && callList.value.length > 0) { | 
|     activeCall.value = callList.value[0] | 
|     timerCall.value = setInterval(() => { | 
|       if (pageCall.value >= callList.value.length - 1) { | 
|         pageCall.value = 0 | 
|       } else { | 
|         pageCall.value++ | 
|       } | 
|       activeCall.value = callList.value[pageCall.value] | 
|     }, frequencyCallFn(callList.value.length)) | 
|   } | 
| } | 
|   | 
| onMounted(() => { | 
|   initData() | 
|   setInterval(() => { | 
|     initData() | 
|   }, 60 * 1000) | 
| }) | 
|   | 
| const initQrcode = () => { | 
|   var qr = new UQRCode() | 
|   // 设置二维码内容 | 
|   qr.data = qrcode.value | 
|   qr.size = 268 | 
|   // 调用制作二维码方法 | 
|   qr.make() | 
|   var canvas = document.getElementById("qrcode") | 
|   var canvasContext = canvas.getContext("2d") | 
|   qr.canvasContext = canvasContext | 
|   qr.drawCanvas() | 
| } | 
|   | 
| const statusMap = { | 
|   0: '待确认', | 
|   1: '待签到', | 
|   2: '等待叫号', | 
|   3: '入园等待', | 
|   4: '已叫号', // 已叫号 月台等待 | 
|   5: '作业中', | 
|   6: '作业完成', | 
|   7: '转移中', | 
|   8: '异常挂起', | 
|   9: '已授权离园', | 
|   10: '已离园', | 
|   11: '已过号', | 
|   12: '已取消', | 
| } | 
| const weekMap = ['周日', '周一', '周二', '周三', '周四', '周五', '周六'] | 
| const newWeek = ref(weekMap[new Date().getDay()]) | 
| const newDate = ref(dayjs().format('YYYY-MM-DD')) | 
| const newTime = ref(dayjs().format('HH:mm')) | 
| setInterval(() => { | 
|   newWeek.value = weekMap[new Date().getDay()] | 
|   newDate.value = dayjs().format('YYYY-MM-DD') | 
|   newTime.value = dayjs().format('HH:mm') | 
| }, 1000) | 
|   | 
| const isFullscreen = ref(false) | 
| const handleFull = () => { | 
|   if (!isFullscreen.value) { | 
|     handleFullScreen() | 
|   } else { | 
|     cancelFullscreen() | 
|   } | 
| } | 
| const handler = () => { | 
|   isFullscreen.value = document.fullscreenElement !== null | 
|   if (!isFullscreen) { | 
|     // 退出全屏时候解除监听,不然每次监听都会添加一次绑定 | 
|     document.removeEventListener("fullscreenchange", handler) | 
|   } | 
| } | 
| document.addEventListener("fullscreenchange", handler) | 
| const handleFullScreen = () => { | 
|   let elem = document.documentElement | 
|   // 尝试启用全屏模式 | 
|   if (elem.requestFullscreen) { | 
|     elem.requestFullscreen() | 
|   } else if (elem.mozRequestFullScreen) { // 兼容 Firefox | 
|     elem.mozRequestFullScreen() | 
|   } else if (elem.webkitRequestFullscreen) { // 兼容 Chrome, Safari 和 Opera | 
|     elem.webkitRequestFullscreen() | 
|   } else if (elem.msRequestFullscreen) { // 兼容 IE/Edge | 
|     elem.msRequestFullscreen() | 
|   } | 
| } | 
| function cancelFullscreen() { | 
|   if (document.exitFullscreen) { | 
|     document.exitFullscreen() | 
|   } else if (document.msExitFullscreen) { | 
|     document.msExitFullscreen() | 
|   } else if (document.mozCancelFullScreen) { | 
|     document.mozCancelFullScreen() | 
|   } else if (document.webkitExitFullscreen) { | 
|     document.webkitExitFullscreen() | 
|   } | 
| } | 
|   | 
| </script> | 
| <template> | 
|   <v-scale-screen width="1920" height="1080"> | 
|     <div class="main_app"> | 
|       <div class="main_header"> | 
|         <img class="bg" src="@/assets/images/call/title@2x.png" alt=""> | 
|         <div class="title">安泰智慧物流园区-车辆叫号大屏</div> | 
|         <div class="time_wrap" @click="handleFull"> | 
|           <div class="left"> | 
|             <div class="week">{{ newWeek }}</div> | 
|             <div class="date">{{ newDate }}</div> | 
|           </div> | 
|           <div class="time">{{ newTime }}</div> | 
|         </div> | 
|       </div> | 
|       <div class="main_content"> | 
|         <div class="list"> | 
|           <div class="line header"> | 
|             <div class="item no">序号</div> | 
|             <div class="item">车牌号</div> | 
|             <div class="item status">状态</div> | 
|             <div class="item">停靠月台</div> | 
|             <div class="item">时间</div> | 
|           </div> | 
|           <div class="line" v-for="(item, index) in contentTempList" :key="index"> | 
|             <div class="item no">{{ item.index }}</div> | 
|             <div class="item">{{ item.carCodeFront }}</div> | 
|             <div class="item"> | 
|               <div :class="{ | 
|                 call_ed: item.status == 4, | 
|                 underway: item.status == 5, | 
|                 padding: item.status == 2 || item.status == 3, | 
|               }" class="status">{{ statusMap[item.status] }}</div> | 
|             </div> | 
|             <div class="item">{{ item.platformName || '-' }}</div> | 
|             <div class="item"> | 
|               <div v-if="item.optTimeTemp" class="time_place"> | 
|                 {{ item.status == 4 ? '剩余等待时间' : '预计完成时间' }} | 
|               </div> | 
|               <div>{{ item.optTimeTemp || '-' }}</div> | 
|             </div> | 
|           </div> | 
|         </div> | 
|         <div class="current"> | 
|           <div class="tip_wrap"> | 
|             <img src="@/assets/images/call/ic_left@2x.png" class="icon icon_l" alt=""> | 
|             <img src="@/assets/images/call/ic_right@2x.png" class="icon icon_r" alt=""> | 
|             <div v-if="callList.length > 0" class="current_plat"> | 
|               <div class="id_card">{{ activeCall.carCodeFront }}</div> | 
|               <div class="no">({{ activeCall.signNum }})</div> | 
|               <div class="place"> | 
|                 <span>前往</span> | 
|                 <span class="plat"> {{ activeCall.platformName }} </span> | 
|                 <span>作业</span> | 
|               </div> | 
|             </div> | 
|             <div v-else class="empty"> | 
|               <div class="title">温馨提示</div> | 
|               <div class="line">1、请司机根据叫号屏提示入园</div> | 
|               <div class="line">2、未叫号车辆请耐心等待</div> | 
|               <div class="line">3、请有序排队入园</div> | 
|               <div class="line">4、装卸货结束禁止在园区逗留</div> | 
|               <div class="line">5、请遵守园区安全规章制度</div> | 
|             </div> | 
|           </div> | 
|           <div class="qrcode_wrap"> | 
|             <div class="qrcode"> | 
|               <canvas id="qrcode" width="268" height="268"></canvas> | 
|             </div> | 
|             <div class="title">请扫码签到</div> | 
|           </div> | 
|         </div> | 
|       </div> | 
|       <div class="main_footer"> | 
|         <img src="@/assets/images/call/ic_news@2x.png" class="icon" alt=""> | 
|         <div class="title">请根据队列提示信息,依次等候进场</div> | 
|         <div class="icon"></div> | 
|       </div> | 
|     </div> | 
|   </v-scale-screen> | 
|   | 
| </template> | 
|   | 
| <style lang="scss" scoped> | 
| div { | 
|   box-sizing: border-box; | 
| } | 
|   | 
| .main_app { | 
|   color: #fff; | 
|   width: 1920px; | 
|   height: 1080px; | 
|   overflow: hidden; | 
|   background-color: #092030; | 
|   position: relative; | 
|   display: flex; | 
|   flex-direction: column; | 
|   justify-content: space-between; | 
|   | 
|   .main_header { | 
|     width: 100%; | 
|     flex: 10.3; | 
|     position: relative; | 
|     display: flex; | 
|     align-items: center; | 
|     justify-content: space-between; | 
|     padding: 0 50px; | 
|     z-index: 11; | 
|   | 
|     .title { | 
|       font-weight: 800; | 
|       font-size: 48px; | 
|     } | 
|   | 
|     .time_wrap { | 
|       display: flex; | 
|       align-items: center; | 
|   | 
|       .date { | 
|         font-weight: 500; | 
|         font-size: 20px; | 
|         text-align: right; | 
|       } | 
|   | 
|       .week { | 
|         font-weight: 500; | 
|         font-size: 20px; | 
|         text-align: right; | 
|       } | 
|   | 
|       .time { | 
|         font-weight: 500; | 
|         font-size: 50px; | 
|         margin-left: 15px; | 
|       } | 
|     } | 
|   | 
|     .bg { | 
|       position: absolute; | 
|       top: 0; | 
|       left: 0; | 
|       width: 100%; | 
|       height: 100%; | 
|       object-fit: cover; | 
|       z-index: -1; | 
|     } | 
|   } | 
|   | 
|   .main_content { | 
|     flex: 82.2; | 
|     display: flex; | 
|     padding: 30px 20px; | 
|     position: relative; | 
|   | 
|     .list { | 
|       flex: 5; | 
|       margin-right: 20px; | 
|       display: flex; | 
|       flex-direction: column; | 
|   | 
|       .line { | 
|         display: flex; | 
|         align-items: center; | 
|         height: 11.1%; | 
|   | 
|         &:nth-of-type(2n) { | 
|           background: rgba(255, 255, 255, 0); | 
|         } | 
|   | 
|         &:nth-of-type(2n + 1) { | 
|           background: rgba(0, 237, 255, 0.13); | 
|         } | 
|   | 
|         .item { | 
|           display: flex; | 
|           justify-content: center; | 
|           align-items: center; | 
|           flex-direction: column; | 
|           font-weight: 500; | 
|           font-size: 30px; | 
|           flex: 10; | 
|           height: 100%; | 
|   | 
|           .time_place { | 
|             font-weight: 500; | 
|             font-size: 22px; | 
|           } | 
|   | 
|           .status { | 
|             /* padding: 0 28px; */ | 
|             width: 170px; | 
|             height: 72%; | 
|             border-radius: 8px; | 
|             display: flex; | 
|             justify-content: center; | 
|             align-items: center; | 
|           } | 
|   | 
|           .underway { | 
|             background-color: #51b2ce; | 
|           } | 
|   | 
|           .call_ed { | 
|             background-color: #f2a43a; | 
|           } | 
|   | 
|           .padding { | 
|             background-color: #1c485a; | 
|           } | 
|         } | 
|   | 
|         .no { | 
|           flex: 5; | 
|         } | 
|   | 
|       } | 
|   | 
|       .header { | 
|         .item { | 
|           font-size: bold; | 
|           font-size: 36px; | 
|         } | 
|   | 
|         background: linear-gradient(180deg, #00B5D1 0%, #003C57 100%) !important; | 
|       } | 
|     } | 
|   | 
|     .current { | 
|       /* height: calc( 100vh - 192px ); */ | 
|       flex: 2; | 
|       display: flex; | 
|       flex-direction: column; | 
|   | 
|       .tip_wrap { | 
|         margin-bottom: 20px; | 
|         width: 100%; | 
|         position: relative; | 
|         /* flex-shrink: 0; */ | 
|         flex: 1; | 
|   | 
|         .current_plat { | 
|           display: flex; | 
|           flex-direction: column; | 
|           align-items: center; | 
|           justify-content: center; | 
|           padding: 30px 40px; | 
|           color: #FFA000; | 
|           height: 100%; | 
|   | 
|           .id_card { | 
|             font-weight: 500; | 
|             font-size: 68px; | 
|           } | 
|   | 
|           .no { | 
|             margin: 8px 0; | 
|             font-weight: 500; | 
|             font-size: 52px; | 
|           } | 
|   | 
|           .place { | 
|             font-weight: 500; | 
|             font-size: 36px; | 
|           } | 
|   | 
|           .plat { | 
|             color: #fff; | 
|             margin: 0 6px; | 
|           } | 
|         } | 
|   | 
|         .empty { | 
|           display: flex; | 
|           flex-direction: column; | 
|           justify-content: center; | 
|           font-weight: 500; | 
|           font-size: 26px; | 
|           height: 100%; | 
|   | 
|           .title { | 
|             text-align: center; | 
|             font-weight: bold; | 
|             font-size: 32px; | 
|             margin-bottom: 16px; | 
|           } | 
|   | 
|           .line { | 
|             margin-bottom: 8px; | 
|             padding-left: 32px; | 
|           } | 
|         } | 
|   | 
|         .icon { | 
|           position: absolute; | 
|           width: 52px; | 
|           height: 52px; | 
|         } | 
|   | 
|         .icon_l { | 
|           left: 0; | 
|           top: 0; | 
|         } | 
|   | 
|         .icon_r { | 
|           right: 0; | 
|           bottom: 0; | 
|         } | 
|       } | 
|   | 
|       .qrcode_wrap { | 
|         width: 100%; | 
|         flex: 1; | 
|         /* flex-shrink: 0; */ | 
|         background-color: #51b2ce; | 
|         display: flex; | 
|         flex-direction: column; | 
|         align-items: center; | 
|         justify-content: center; | 
|   | 
|         .qrcode { | 
|           padding: 6px 6px 4px; | 
|           background-color: #fff; | 
|         } | 
|   | 
|         .title { | 
|           margin-top: 16px; | 
|           font-weight: bold; | 
|           font-size: 28px; | 
|           color: #111111; | 
|         } | 
|       } | 
|     } | 
|   } | 
|   | 
|   .main_footer { | 
|     background-color: #1D8D9E; | 
|     flex: 7.4; | 
|     display: flex; | 
|     align-items: center; | 
|     padding: 0 50px; | 
|   | 
|     .icon { | 
|       width: 52px; | 
|       height: 52px; | 
|     } | 
|   | 
|     .title { | 
|       flex: 1; | 
|       text-align: center; | 
|       font-weight: 500; | 
|       font-size: 37px; | 
|     } | 
|   } | 
|   | 
| } | 
| </style> |