308 lines
7.8 KiB
Vue
308 lines
7.8 KiB
Vue
<template>
|
||
<view class="screen-wrap">
|
||
<!-- 顶部标题栏 -->
|
||
<view class="top-header">
|
||
<text class="title-left">{{ screenTitle }}</text>
|
||
<view class="title-right">
|
||
<text>{{ dateStr }}</text>
|
||
<text>{{ weekStr }} {{ timeStr }}</text>
|
||
</view>
|
||
</view>
|
||
<!-- 中间大字播报区 -->
|
||
<view class="big-call">
|
||
<text class="normal-text">请</text>
|
||
<text class="yellow-text">{{ currCallNo }}</text>
|
||
<text class="normal-text">到</text>
|
||
<text class="yellow-text">{{ currWinNo }}</text>
|
||
</view>
|
||
<!-- 左右双栏表格区域 -->
|
||
<view class="table-box">
|
||
<!-- 左:当前呼叫 -->
|
||
<view class="table-left">
|
||
<view class="table-head">
|
||
<text>当前呼叫</text>
|
||
<text class="tip-text">(过号需重新取号)</text>
|
||
</view>
|
||
<view class="table-list">
|
||
<view class="table-row" v-for="(item, idx) in callList" :key="idx" :class="idx % 2 === 1 ? 'row-blue' : ''">
|
||
<text>请</text>
|
||
<text class="row-no">{{ item.no }}</text>
|
||
<text>到</text>
|
||
<text class="row-no">{{ item.win }}</text>
|
||
</view>
|
||
</view>
|
||
</view>
|
||
<!-- 右:等候办理 -->
|
||
<view class="table-right">
|
||
<view class="table-head">等候办理</view>
|
||
<view class="table-list">
|
||
<view class="table-row wait-row" v-for="(row, idx) in waitList" :key="idx" :class="idx % 2 === 1 ? 'row-blue' : ''">
|
||
<text v-for="(item, i) in row" :key="i">{{ item }}</text>
|
||
</view>
|
||
</view>
|
||
</view>
|
||
</view>
|
||
</view>
|
||
</template>
|
||
|
||
<script>
|
||
import { getDisplayScreen } from '@/api/ticket.js'
|
||
|
||
export default {
|
||
name: 'CallScreen',
|
||
data() {
|
||
return {
|
||
// 大屏标题(来自接口)
|
||
screenTitle: '环江毛南族自治县政务服务中心',
|
||
// 实时日期时间
|
||
dateStr: '',
|
||
weekStr: '',
|
||
timeStr: '',
|
||
serverTimeOffset: 0, // 服务端时间与本地时间的差值(毫秒)
|
||
clockTimer: null, // 时钟刷新定时器
|
||
fetchTimer: null, // 数据轮询定时器
|
||
// 顶部大字播报
|
||
currCallNo: '',
|
||
currWinNo: '',
|
||
// 左侧当前呼叫列表
|
||
callList: [],
|
||
// 右侧等候队列(每行3个)
|
||
waitList: []
|
||
}
|
||
},
|
||
onLoad() {
|
||
// 首次加载数据(获取服务端时间后启动时钟)
|
||
this.fetchDisplayData()
|
||
// 定时轮询刷新叫号数据
|
||
this.fetchTimer = setInterval(() => {
|
||
this.fetchDisplayData()
|
||
}, 5000)
|
||
},
|
||
onUnload() {
|
||
clearInterval(this.clockTimer)
|
||
clearInterval(this.fetchTimer)
|
||
},
|
||
methods: {
|
||
// 启动时钟(首次拿到 serverTime 后调用)
|
||
startClock() {
|
||
clearInterval(this.clockTimer)
|
||
this.tick()
|
||
this.clockTimer = setInterval(() => {
|
||
this.tick()
|
||
}, 1000)
|
||
},
|
||
// 每秒走时
|
||
tick() {
|
||
// 服务端时间 + 本地已流逝的毫秒 = 当前展示时间
|
||
const now = this.serverTimeOffset ? new Date(Date.now() + this.serverTimeOffset) : new Date()
|
||
const year = now.getFullYear()
|
||
const month = this.addZero(now.getMonth() + 1)
|
||
const day = this.addZero(now.getDate())
|
||
const hour = this.addZero(now.getHours())
|
||
const minute = this.addZero(now.getMinutes())
|
||
const weekArr = ['星期日', '星期一', '星期二', '星期三', '星期四', '星期五', '星期六']
|
||
const week = weekArr[now.getDay()]
|
||
|
||
this.dateStr = `${year}年${month}月${day}日`
|
||
this.weekStr = week
|
||
this.timeStr = `${hour}:${minute}`
|
||
},
|
||
addZero(num) {
|
||
return num < 10 ? '0' + num : num
|
||
},
|
||
// 获取大屏展示数据
|
||
fetchDisplayData() {
|
||
getDisplayScreen({ calledSize: 8, waitingSize: 24 })
|
||
.then(res => {
|
||
const data = res.data
|
||
if (!data) return
|
||
|
||
// 1. 标题
|
||
if (data.title) {
|
||
this.screenTitle = data.title
|
||
}
|
||
|
||
// 2. 服务端时间(首次计算偏移量后启动时钟)
|
||
if (data.serverTime && !this.serverTimeOffset) {
|
||
const serverTs = new Date(data.serverTime.replace(/-/g, '/')).getTime()
|
||
this.serverTimeOffset = serverTs - Date.now()
|
||
this.startClock()
|
||
}
|
||
|
||
// 3. 顶部大字播报(最新呼叫)
|
||
if (data.latestCall) {
|
||
this.currCallNo = data.latestCall.queueNo || ''
|
||
this.currWinNo = data.latestCall.windowName || ''
|
||
} else {
|
||
this.currCallNo = ''
|
||
this.currWinNo = ''
|
||
}
|
||
|
||
// 4. 左侧当前呼叫列表
|
||
if (data.callingList && Array.isArray(data.callingList)) {
|
||
this.callList = data.callingList.map(item => ({
|
||
no: item.queueNo || '',
|
||
win: item.windowName || ''
|
||
}))
|
||
} else {
|
||
this.callList = []
|
||
}
|
||
|
||
// 5. 右侧等候队列(每行3个)
|
||
if (data.waitingList && Array.isArray(data.waitingList)) {
|
||
const waitNos = data.waitingList.map(item => item.queueNo || '')
|
||
const rows = []
|
||
for (let i = 0; i < waitNos.length; i += 3) {
|
||
const row = waitNos.slice(i, i + 3)
|
||
while (row.length < 3) {
|
||
row.push('')
|
||
}
|
||
rows.push(row)
|
||
}
|
||
this.waitList = rows
|
||
} else {
|
||
this.waitList = []
|
||
}
|
||
})
|
||
.catch(err => {
|
||
console.error('获取大屏数据失败:', err)
|
||
})
|
||
}
|
||
}
|
||
}
|
||
</script>
|
||
|
||
<style scoped>
|
||
* {
|
||
box-sizing: border-box;
|
||
}
|
||
|
||
/* 使用 vw/vh 自适应屏幕,设计基准 1920*1080 */
|
||
.screen-wrap {
|
||
width: 100vw;
|
||
height: 100vh;
|
||
background: #083378;
|
||
display: flex;
|
||
flex-direction: column;
|
||
overflow: hidden;
|
||
}
|
||
|
||
/*顶部蓝色栏 */
|
||
.top-header {
|
||
width: 100%;
|
||
height: 12.963vh; /* 140/1080*100 */
|
||
background: #167bff;
|
||
display: flex;
|
||
justify-content: space-between;
|
||
align-items: center;
|
||
padding: 0 2.604vw; /* 50/1920*100 */
|
||
box-sizing: border-box;
|
||
}
|
||
|
||
.title-left {
|
||
font-size: 3.229vw; /* 62/1920*100 */
|
||
color: #fff;
|
||
font-weight: bold;
|
||
}
|
||
|
||
.title-right {
|
||
display: flex;
|
||
flex-direction: column;
|
||
align-items: flex-end;
|
||
}
|
||
|
||
.title-right text {
|
||
font-size: 2.083vw; /* 40/1920*100 */
|
||
color: #fff;
|
||
line-height: 4.63vh; /* 50/1080*100 */
|
||
}
|
||
|
||
/*中间大字叫号 */
|
||
.big-call {
|
||
height: 24.074vh; /* 260/1080*100 */
|
||
display: flex;
|
||
justify-content: center;
|
||
align-items: center;
|
||
gap: 1.563vw; /* 30/1920*100 */
|
||
}
|
||
|
||
.normal-text {
|
||
font-size: 6.25vw; /* 120/1920*100 */
|
||
color: #ffffff;
|
||
font-weight: bold;
|
||
}
|
||
|
||
.yellow-text {
|
||
font-size: 6.25vw; /* 120/1920*100 */
|
||
color: #ffef33;
|
||
font-weight: bold;
|
||
}
|
||
|
||
/*左右表格容器 */
|
||
.table-box {
|
||
flex: 1;
|
||
display: flex;
|
||
gap: 1.563vw; /* 30/1920*100 */
|
||
padding: 0 1.563vw 2.778vh; /* 0 30/1920*100 30/1080*100 */
|
||
box-sizing: border-box;
|
||
}
|
||
|
||
.table-left,
|
||
.table-right {
|
||
flex: 1;
|
||
display: flex;
|
||
flex-direction: column;
|
||
border-radius: 0.833vw; /* 16/1920*100 */
|
||
}
|
||
|
||
.table-head {
|
||
width: 100%;
|
||
height: 10.185vh; /* 110/1080*100 */
|
||
background: #167bff;
|
||
display: flex;
|
||
justify-content: center;
|
||
align-items: center;
|
||
font-size: 2.708vw; /* 52/1920*100 */
|
||
color: #fff;
|
||
font-weight: bold;
|
||
}
|
||
|
||
.tip-text {
|
||
font-size: 1.875vw; /* 36/1920*100 */
|
||
margin-left: 1.042vw; /* 20/1920*100 */
|
||
opacity: 0.9;
|
||
}
|
||
|
||
.table-list {
|
||
height: 60vh;
|
||
overflow-y: auto;
|
||
}
|
||
|
||
/*表格行样式 */
|
||
.table-row {
|
||
width: 100%;
|
||
flex: 1;
|
||
display: flex;
|
||
justify-content: center;
|
||
align-items: center;
|
||
font-size: 2.813vw; /* 54/1920*100 */
|
||
background: #ffffff;
|
||
}
|
||
|
||
.wait-row {
|
||
display: grid;
|
||
grid-template-columns: 1fr 1fr 1fr;
|
||
justify-items: center;
|
||
}
|
||
|
||
.row-blue {
|
||
background: #e6f0ff;
|
||
}
|
||
|
||
.row-no {
|
||
color: #0055ff;
|
||
font-weight: bold;
|
||
margin: 0 1.042vw; /* 20/1920*100 */
|
||
}
|
||
</style>
|