取号机、叫号机对接

This commit is contained in:
zwz 2026-06-17 16:56:03 +08:00
parent a74c48f1cf
commit b22438134c
15 changed files with 2581 additions and 1352 deletions

View File

@ -1,5 +1,7 @@
import http from '@/http/api.js' import http from '@/http/api.js'
// ==================== 自助取号机接口 ====================
/** /**
* 主题取号列表 * 主题取号列表
* @param {*} params * @param {*} params
@ -57,3 +59,180 @@ export const takeNumber = data => {
data data
}); });
}; };
/**
* 事项详情
* @param {*} params { id }
* @returns
*/
export const getItemDetail = params => {
return http.request({
url: 'api/blade-item/detail',
method: 'GET',
params
});
};
// ==================== 叫号机窗口端接口 ====================
/**
* 获取可选窗口列表
* @param {*} params { status, serviceStatus, current, size }
* @returns
*/
export const getWindowList = params => {
return http.request({
url: '/api/blade-windows/list',
method: 'GET',
params: {
status: 1,
serviceStatus: 1,
current: 1,
size: 100,
...params
}
});
};
/**
* 选择窗口
* @param {*} data { windowId }
* @returns
*/
export const selectWindow = data => {
return http.request({
url: '/api/blade-windows/select-window',
method: 'POST',
data
});
};
/**
* 窗口工作台获取当前叫号和等候人数
* @param {*} params { windowId }
* @returns
*/
export const getWindowWorkbench = params => {
return http.request({
url: '/api/blade-queuerecord/queueRecord/window-workbench',
method: 'GET',
params
});
};
/**
* 呼叫下一位
* @param {*} data { windowId }
* @returns
*/
export const callNext = data => {
return http.request({
url: '/api/blade-queuerecord/queueRecord/call-next',
method: 'POST',
data
});
};
/**
* 重呼
* @param {*} data { recordId, windowId }
* @returns
*/
export const recall = data => {
return http.request({
url: '/api/blade-queuerecord/queueRecord/recall',
method: 'POST',
data
});
};
/**
* 开始办理
* @param {*} data { recordId, windowId }
* @returns
*/
export const startHandle = data => {
return http.request({
url: '/api/blade-queuerecord/queueRecord/start-handle',
method: 'POST',
data
});
};
/**
* 完成办理
* @param {*} data { recordId, windowId }
* @returns
*/
export const finishHandle = data => {
return http.request({
url: '/api/blade-queuerecord/queueRecord/finish',
method: 'POST',
data
});
};
/**
* 过号
* @param {*} data { recordId, windowId }
* @returns
*/
export const passRecord = data => {
return http.request({
url: '/api/blade-queuerecord/queueRecord/pass',
method: 'POST',
data
});
};
/**
* 转移窗口
* @param {*} data { recordId, windowId, targetWindowId }
* @returns
*/
export const transferRecord = data => {
return http.request({
url: '/api/blade-queuerecord/queueRecord/transfer',
method: 'POST',
data
});
};
/**
* 挂起窗口
* @param {*} data { windowId, reason }
* @returns
*/
export const suspendWindow = data => {
return http.request({
url: '/api/blade-windows/suspend',
method: 'POST',
data
});
};
/**
* 恢复窗口
* @param {*} data { windowId }
* @returns
*/
export const resumeWindow = data => {
return http.request({
url: '/api/blade-windows/resume',
method: 'POST',
data
});
};
/**
* 窗口统计
* @param {*} params { windowId, date }
* @returns
*/
export const getWindowStat = params => {
return http.request({
url: '/api/blade-queuerecord/queueRecord/window-stat',
method: 'GET',
params
});
};

View File

@ -1,195 +1,196 @@
<template> <template>
<view class="container"> <view class="container">
<!-- 顶部标题栏 --> <!-- 顶部标题栏 -->
<view class="header"> <view class="header">
<text class="header-left">环江毛南族自治县政务服务中心</text> <text class="header-left">环江毛南族自治县政务服务中心</text>
<text class="header-center">自助取号机</text> <text class="header-center">自助取号机</text>
<view class="btn-group"> <view class="btn-group">
<view class="btn" v-if="backBtn" @click="back"> <view class="btn" v-if="backBtn" @click="back">
<img src="../../static/images/home/back_icon.png" alt="" /> <img src="../../static/images/home/back_icon.png" alt="" />
<text class="btn-text">返回</text> <text class="btn-text">返回</text>
</view> </view>
<view class="btn" v-if="logoutBtn" @click="logout"> <view class="btn" v-if="logoutBtn" @click="logout">
<img src="../../static/images/home/logout_icon.png" alt="" /> <img src="../../static/images/home/logout_icon.png" alt="" />
<text class="btn-text">退出</text> <text class="btn-text">退出</text>
</view> </view>
</view> </view>
</view> </view>
<!-- 主体内容区 --> <!-- 主体内容区 -->
<view class="main"> <view class="main">
<slot name="main"></slot> <slot name="main"></slot>
</view> </view>
<!-- 底部状态栏 --> <!-- 底部状态栏 -->
<view class="footer"> <view class="footer">
<text class="footer-left">本机编号: YJS0001</text> <text class="footer-left">本机编号: YJS0001</text>
<text class="footer-right">{{ currentTime }}</text> <text class="footer-right">{{ currentTime }}</text>
</view> </view>
</view> </view>
</template> </template>
<script> <script>
export default { export default {
props: { props: {
backBtn: { backBtn: {
type: Boolean, type: Boolean,
default: false default: false
}, },
logoutBtn: { logoutBtn: {
type: Boolean, type: Boolean,
default: false default: false
} }
}, },
data() { data() {
return { return {
currentTime: '' currentTime: ''
} }
}, },
mounted() { mounted() {
this.updateTime() this.updateTime()
setInterval(() => { setInterval(() => {
this.updateTime() this.updateTime()
}, 1000) }, 1000)
// APP // APP
// #ifdef APP-PLUS // #ifdef APP-PLUS
plus.screen.lockOrientation('landscape-primary') plus.screen.lockOrientation('landscape-primary')
// #endif // #endif
}, },
methods: { methods: {
updateTime() { updateTime() {
const now = new Date() const now = new Date()
const year = now.getFullYear() const year = now.getFullYear()
const month = String(now.getMonth() + 1).padStart(2, '0') const month = String(now.getMonth() + 1).padStart(2, '0')
const day = String(now.getDate()).padStart(2, '0') const day = String(now.getDate()).padStart(2, '0')
const hours = String(now.getHours()).padStart(2, '0') const hours = String(now.getHours()).padStart(2, '0')
const minutes = String(now.getMinutes()).padStart(2, '0') const minutes = String(now.getMinutes()).padStart(2, '0')
const seconds = String(now.getSeconds()).padStart(2, '0') const seconds = String(now.getSeconds()).padStart(2, '0')
this.currentTime = `${year}${month}${day}${hours}:${minutes}:${seconds}` this.currentTime = `${year}${month}${day}${hours}:${minutes}:${seconds}`
}, },
back() { back() {
this.$emit('handleBack') this.$emit('handleBack')
}, },
logout() { logout() {
uni.showModal({ uni.showModal({
title: '提示', title: '提示',
content: '确定要退出吗?', content: '确定要退出吗?',
success: (res) => { success: res => {
if (res.confirm) { if (res.confirm) {
uni.reLaunch({ //
url: '/pages/home/home' uni.removeStorageSync('idcard')
}) uni.reLaunch({
} url: '/pages/home/home'
} })
}) }
}, }
} })
} }
}
}
</script> </script>
<style scoped> <style scoped>
/* 全屏自适应容器 - 百分比布局 */ /* 全屏自适应容器 - viewport弹性布局 */
.container { .container {
width: 100%; width: 100vw;
height: 100vh; height: 100vh;
display: flex; display: flex;
flex-direction: column; flex-direction: column;
overflow: hidden; overflow: hidden;
position: fixed; position: fixed;
top: 0; top: 0;
left: 0; left: 0;
} }
/* 顶部标题栏 - 百分比高度 */ /* 顶部标题栏 */
.header { .header {
position: absolute; position: absolute;
left: 0; left: 0;
top: 0; top: 0;
width: 100%; width: 100%;
height: 22%; height: 22vh;
background: url('../../static/images/home/top_bg.png'); background: url('../../static/images/home/top_bg.png');
background-size: 100% 100%; background-size: 100% 100%;
display: flex; display: flex;
padding: 35px 4% 0; padding: 3vh 4% 0;
border-radius: 0 0 2% 2%; border-radius: 0 0 2% 2%;
} }
.header-left { .header-left {
color: #fff; color: #fff;
font-size: 36px; font-size: 2.5vw;
font-weight: 500; font-weight: 500;
} white-space: nowrap;
}
.header-center { .header-center {
position: absolute; position: absolute;
left: 50%; left: 50%;
top: 35px; top: 3.2vh;
transform: translateX(-50%); transform: translateX(-50%);
color: #fff; color: #fff;
font-size: 36px; font-size: 2.5vw;
font-weight: bold; font-weight: bold;
} white-space: nowrap;
}
.btn-group { .btn-group {
position: absolute; position: absolute;
right: 4%; right: 4%;
top: 35px; top: 3.2vh;
display: flex; display: flex;
justify-content: center; justify-content: center;
align-items: center; align-items: center;
.btn {
display: flex;
justify-content: center;
align-items: center;
margin-right: 1vw;
width: 8.5vw;
height: 5.5vh;
border-radius: 3vh;
background: #fff;
font-size: 1.6vw;
color: rgba(51, 51, 51, 1);
.btn { img {
display: flex; margin-right: 0.8vw;
justify-content: center; width: 2vw;
align-items: center; height: 2vw;
margin-right: 20px; }
width: 160px; }
height: 60px; }
border-radius: 30px;
background: #fff;
font-size: 30px;
color: rgba(51, 51, 51, 1);
img { /* 主体内容区 - 自动占满剩余空间 */
margin-right: 15px; .main {
width: 40px; flex: 1;
height: 40px; display: flex;
} align-items: center;
} justify-content: center;
} background: url('../../static/images/home/bg.png');
background-size: 100% 100%;
}
/* 主体内容区 - 自动占满剩余空间 */ /* 底部状态栏 */
.main { .footer {
flex: 1; position: absolute;
display: flex; left: 0;
align-items: center; bottom: 0;
justify-content: center; width: 100%;
background: url('../../static/images/home/bg.png'); height: 7vh;
background-size: 100% 100%; background: url('../../static/images/home/footer_bg.png');
} background-size: 100% 100%;
display: flex;
align-items: center;
justify-content: space-between;
padding: 0 3%;
border-radius: 2% 2% 0 0;
}
.footer-left,
.footer-right {
/* 底部状态栏 - 百分比高度 */ color: #fff;
.footer { font-size: 2vw;
position: absolute; }
left: 0;
bottom: 0;
width: 100%;
height: 80px;
background: url('../../static/images/home/footer_bg.png');
background-size: 100% 100%;
display: flex;
align-items: center;
justify-content: space-between;
padding: 0 3%;
border-radius: 2% 2% 0 0;
}
.footer-left,
.footer-right {
color: #fff;
font-size: 3vh;
}
</style> </style>

View File

@ -4,7 +4,14 @@
"^wf-(.*)": "@/components/wf-ui/components/wf-$1/wf-$1.vue" "^wf-(.*)": "@/components/wf-ui/components/wf-$1/wf-$1.vue"
}, },
"pages": [ //pageshttps://uniapp.dcloud.io/collocation/pages "pages": [ //pageshttps://uniapp.dcloud.io/collocation/pages
{
"path": "pages/home/chooseMachine",
"style": {
"navigationBarTitleText": "选择机器类别",
"enablePullDownRefresh": false,
"navigationStyle": "custom"
}
},
{ {
"path": "pages/home/home", "path": "pages/home/home",
"style": { "style": {
@ -54,7 +61,15 @@
} }
}, },
{ {
"path": "pages/takeTicket/index", "path": "pages/callTicket/chooseWindow",
"style": {
"navigationBarTitleText": "选择叫号机窗口",
"enablePullDownRefresh": false,
"navigationStyle": "custom"
}
},
{
"path": "pages/callTicket/callTicket",
"style": { "style": {
"navigationBarTitleText": "叫号机", "navigationBarTitleText": "叫号机",
"enablePullDownRefresh": false, "enablePullDownRefresh": false,

View File

@ -0,0 +1,783 @@
<template>
<!-- 外层容器使用 vw/vh 自适应屏幕 -->
<view class="wrap">
<!-- 顶部蓝色头部 -->
<view class="header-bar">
<view class="header-left">
<text class="win-no">窗口号{{ windowName }}</text>
<view class="user-dept">
<text>{{ handlerUserName }}</text>
<text>{{ handlerDeptName }}</text>
</view>
</view>
<view class="exit-btn" @click="handleExit">
<image class="icon-exit" src="../../static/images/home/logout_icon.png" />
<text>退出</text>
</view>
</view>
<!-- 中间内容区域 -->
<view class="content-box">
<view class="item-col">
<text class="col-title">当前叫号</text>
<text class="call-no">{{ currentCallNo }}</text>
</view>
<view class="item-col">
<text class="col-title">当前等候</text>
<text class="wait-num">{{ waitingCount }}</text>
</view>
</view>
<!-- 挂起提示条 -->
<view class="suspend-bar" v-if="serviceStatus === 2">
<text>窗口已挂起无法进行叫号操作</text>
</view>
<!-- 底部功能按钮区 -->
<view class="btn-group">
<view class="btn-item green" @click="handleCall" :class="{ disabled: callDisabled }">
<image class="btn-icon" src="../../static/images/takeTicket/hj_icon.png" mode="aspectFit" />
<text>呼叫</text>
</view>
<view class="btn-item blue" @click="handleReCall" :class="{ disabled: recallDisabled }">
<image class="btn-icon" src="../../static/images/takeTicket/ch_icon.png" mode="aspectFit" />
<text>重呼</text>
</view>
<view class="btn-item sky" @click="handleTransfer" :class="{ disabled: transferDisabled }">
<image class="btn-icon" src="../../static/images/takeTicket/zy_icon.png" mode="aspectFit" />
<text>转移</text>
</view>
<view class="btn-item orange" @click="handlePass" :class="{ disabled: passDisabled }">
<image class="btn-icon" src="../../static/images/takeTicket/gh_icon.png" mode="aspectFit" />
<text>过号</text>
</view>
<view class="btn-item red" @click="handleSuspend" v-if="serviceStatus === 1">
<image class="btn-icon" src="../../static/images/takeTicket/gq_icon.png" mode="aspectFit" />
<text>挂起</text>
</view>
<view class="btn-item green" @click="handleResume" v-if="serviceStatus === 2">
<image class="btn-icon" src="../../static/images/takeTicket/hj_icon.png" mode="aspectFit" />
<text>恢复</text>
</view>
</view>
<!-- 转移弹窗选择目标窗口 -->
<view class="transfer-mask" v-if="showTransfer" @click="showTransfer = false">
<view class="transfer-dialog" @click.stop>
<text class="dialog-title">请选择目标窗口</text>
<scroll-view class="dialog-list" scroll-y>
<view class="transfer-item" v-for="win in targetWindowList" :key="win.id" @click="confirmTransfer(win)">
<text class="transfer-name">{{ win.windowName }}</text>
</view>
<view class="dialog-empty" v-if="targetWindowList.length === 0">
<text>暂无可转移的窗口</text>
</view>
</scroll-view>
<view class="dialog-cancel" @click="showTransfer = false">
<text>取消</text>
</view>
</view>
</view>
<!-- 挂起原因弹窗 -->
<view class="transfer-mask" v-if="showSuspend" @click="showSuspend = false">
<view class="suspend-dialog" @click.stop>
<text class="dialog-title">请输入挂起原因</text>
<input class="suspend-input" v-model="suspendReason" placeholder="请输入挂起原因(选填)" />
<view class="suspend-btns">
<view class="dialog-cancel" @click="showSuspend = false">
<text>取消</text>
</view>
<view class="dialog-confirm" @click="confirmSuspend">
<text>确认挂起</text>
</view>
</view>
</view>
</view>
</view>
</template>
<script>
import { getWindowWorkbench, callNext, recall, transferRecord, passRecord, suspendWindow, resumeWindow, getWindowList } from '@/api/ticket.js'
export default {
name: 'CallWindow',
data() {
return {
//
windowId: '',
windowName: '',
serviceStatus: 1, // 1=, 2=
handlerUserName: '',
handlerDeptName: '',
handlerUserId: '',
//
currentRecord: null, //
waitingCount: 0, //
currentDeptName: '', //
//
showTransfer: false,
targetWindowList: [],
//
showSuspend: false,
suspendReason: '',
//
actionLock: false
}
},
computed: {
//
currentCallNo() {
return this.currentRecord ? this.currentRecord.queueNo : '----'
},
//
statusText() {
if (!this.currentRecord) return ''
const map = { 1: '等待办理', 2: '已叫号', 3: '已过号', 4: '已完成', 5: '已取消', 7: '办理中' }
return map[this.currentRecord.status] || ''
},
//
statusTagClass() {
if (!this.currentRecord) return ''
if (this.currentRecord.status === 2) return 'tag-called'
if (this.currentRecord.status === 7) return 'tag-handling'
return ''
},
//
suspended() {
return this.serviceStatus === 2
},
//
callDisabled() {
return this.suspended || !!this.currentRecord || this.actionLock
},
// /
recallDisabled() {
if (this.suspended || !this.currentRecord || this.actionLock) return true
return ![2, 7].includes(this.currentRecord.status)
},
//
transferDisabled() {
return this.suspended || !this.currentRecord || this.actionLock
},
// /
passDisabled() {
if (this.suspended || !this.currentRecord || this.actionLock) return true
return ![2, 7].includes(this.currentRecord.status)
}
},
onLoad() {
//
const winInfo = uni.getStorageSync('windowInfo')
if (!winInfo || !winInfo.windowId) {
uni.showToast({ title: '请先选择窗口', icon: 'none' })
setTimeout(() => {
uni.redirectTo({ url: '/pages/callTicket/chooseWindow' })
}, 1000)
return
}
this.windowId = winInfo.windowId
this.windowName = winInfo.windowName
this.serviceStatus = winInfo.serviceStatus || 1
this.handlerUserName = winInfo.handlerUserName || ''
this.handlerDeptName = winInfo.handlerDeptName || ''
this.handlerUserId = winInfo.handlerUserId || ''
//
this.fetchWorkbench()
},
methods: {
// ========== ==========
//
async fetchWorkbench() {
try {
const res = await getWindowWorkbench({ windowId: this.windowId })
if (res.success && res.data) {
const data = res.data
//
this.serviceStatus = data.serviceStatus
this.waitingCount = data.waitingCount || 0
this.currentRecord = data.currentRecord || null
//
const winInfo = uni.getStorageSync('windowInfo') || {}
winInfo.serviceStatus = data.serviceStatus
uni.setStorageSync('windowInfo', winInfo)
//
if (data.currentRecord && data.currentRecord.itemDeptName) {
this.currentDeptName = data.currentRecord.itemDeptName
}
}
} catch (e) {
console.error('获取工作台数据失败', e)
}
},
//
async refreshAfterAction(delay = 500) {
await new Promise(resolve => setTimeout(resolve, delay))
await this.fetchWorkbench()
},
// ========== ==========
// 退
handleExit() {
uni.showModal({
title: '确认退出',
content: '确定要退出当前窗口吗?退出后需重新选择窗口。',
success: res => {
if (res.confirm) {
uni.removeStorageSync('windowInfo')
uni.redirectTo({ url: '/pages/callTicket/chooseWindow' })
}
}
})
},
//
async handleCall() {
if (this.callDisabled) return
this.actionLock = true
uni.showLoading({ title: '呼叫中...', mask: true })
try {
const res = await callNext({ windowId: this.windowId })
if (res.success && res.data) {
this.currentRecord = res.data
this.waitingCount = Math.max(0, this.waitingCount - 1)
if (res.data.itemDeptName) {
this.currentDeptName = res.data.itemDeptName
}
uni.showToast({ title: `已呼叫 ${res.data.queueNo}`, icon: 'success', duration: 1500 })
await this.refreshAfterAction(1000)
}
} catch (e) {
console.error('呼叫失败', e)
} finally {
uni.hideLoading()
this.actionLock = false
}
},
//
async handleReCall() {
if (this.recallDisabled) return
if (!this.currentRecord) return
this.actionLock = true
uni.showLoading({ title: '重呼中...', mask: true })
try {
const res = await recall({
recordId: this.currentRecord.id,
windowId: this.windowId
})
if (res.success && res.data) {
this.currentRecord = res.data
uni.showToast({ title: `已重呼 ${res.data.queueNo}`, icon: 'success', duration: 1500 })
await this.refreshAfterAction()
}
} catch (e) {
console.error('重呼失败', e)
} finally {
uni.hideLoading()
this.actionLock = false
}
},
// -
async handleTransfer() {
if (this.transferDisabled) return
//
uni.showLoading({ title: '加载窗口...', mask: true })
try {
const res = await getWindowList({ status: 1, serviceStatus: 1 })
if (res.success && res.data) {
const allWindows = res.data.records || []
this.targetWindowList = allWindows.filter(w => w.id !== this.windowId)
if (this.targetWindowList.length === 0) {
uni.showToast({ title: '暂无可转移的窗口', icon: 'none' })
return
}
this.showTransfer = true
}
} catch (e) {
console.error('获取窗口列表失败', e)
uni.showToast({ title: '获取窗口列表失败', icon: 'none' })
} finally {
uni.hideLoading()
}
},
//
async confirmTransfer(win) {
if (!this.currentRecord) return
this.showTransfer = false
this.actionLock = true
uni.showLoading({ title: '转移中...', mask: true })
try {
const res = await transferRecord({
recordId: this.currentRecord.id,
windowId: this.windowId,
targetWindowId: win.id
})
if (res.success) {
uni.showToast({ title: `已转移至 ${win.windowName}`, icon: 'success', duration: 1500 })
await this.refreshAfterAction(1000)
}
} catch (e) {
console.error('转移失败', e)
} finally {
uni.hideLoading()
this.actionLock = false
}
},
//
async handlePass() {
if (this.passDisabled) return
if (!this.currentRecord) return
uni.showModal({
title: '确认过号',
content: `确定将 ${this.currentRecord.queueNo} 标记为过号吗?`,
success: async modalRes => {
if (!modalRes.confirm) return
this.actionLock = true
uni.showLoading({ title: '处理中...', mask: true })
try {
const res = await passRecord({
recordId: this.currentRecord.id,
windowId: this.windowId
})
if (res.success) {
uni.showToast({ title: '已标记过号', icon: 'success', duration: 1500 })
await this.refreshAfterAction(1000)
}
} catch (e) {
console.error('过号失败', e)
} finally {
uni.hideLoading()
this.actionLock = false
}
}
})
},
// -
handleSuspend() {
this.suspendReason = ''
this.showSuspend = true
},
//
async confirmSuspend() {
this.showSuspend = false
this.actionLock = true
uni.showLoading({ title: '挂起中...', mask: true })
try {
const res = await suspendWindow({
windowId: this.windowId,
reason: this.suspendReason || '临时离岗'
})
if (res.success && res.data) {
this.serviceStatus = res.data.serviceStatus
//
const winInfo = uni.getStorageSync('windowInfo') || {}
winInfo.serviceStatus = res.data.serviceStatus
uni.setStorageSync('windowInfo', winInfo)
uni.showToast({ title: '窗口已挂起', icon: 'success', duration: 1500 })
await this.refreshAfterAction()
}
} catch (e) {
console.error('挂起失败', e)
} finally {
uni.hideLoading()
this.actionLock = false
}
},
//
async handleResume() {
this.actionLock = true
uni.showLoading({ title: '恢复中...', mask: true })
try {
const res = await resumeWindow({ windowId: this.windowId })
if (res.success && res.data) {
this.serviceStatus = res.data.serviceStatus
//
const winInfo = uni.getStorageSync('windowInfo') || {}
winInfo.serviceStatus = res.data.serviceStatus
uni.setStorageSync('windowInfo', winInfo)
uni.showToast({ title: '窗口已恢复', icon: 'success', duration: 1500 })
await this.refreshAfterAction()
}
} catch (e) {
console.error('恢复失败', e)
} finally {
uni.hideLoading()
this.actionLock = false
}
}
}
}
</script>
<style scoped>
* {
box-sizing: border-box;
}
.wrap {
width: 100vw;
height: 100vh;
display: flex;
flex-direction: column;
overflow: hidden;
background: url('../../static/images/home/bg.png') no-repeat center center;
background-size: cover;
}
/* 顶部头部 */
.header-bar {
width: 100%;
background-color: rgba(19, 114, 255, 1);
display: flex;
justify-content: space-between;
align-items: center;
padding: 1.944vh 2.083vw 1.296vh;
box-sizing: border-box;
}
.header-left {
display: flex;
flex-direction: column;
justify-content: center;
}
.win-no {
font-size: 3.125vw;
color: #fff;
font-weight: bold;
}
.user-dept {
display: flex;
margin-top: 1.111vh;
}
.user-dept text {
font-size: 2.083vw;
color: #fff;
margin-right: 2.604vw;
}
.exit-btn {
background: #fff;
border-radius: 2.604vw;
display: flex;
align-items: center;
padding: 1.389vh 1.302vw;
cursor: pointer;
}
.icon-exit {
width: 2.604vw;
height: 2.604vw;
margin-right: 1.042vw;
}
.exit-btn text {
font-size: 2.083vw;
}
/* 中间内容 */
.content-box {
flex: 1;
display: flex;
}
.item-col {
flex: 1;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
}
.col-title {
font-size: 3.125vw;
color: rgba(51, 51, 51, 1);
/* margin-bottom: 2.778vh; */
}
.call-no {
font-size: 11.458vw;
color: rgba(19, 114, 255, 1);
font-weight: bold;
}
.call-name {
font-size: 2.5vw;
color: #333;
margin-top: 2vh;
}
.call-item {
font-size: 2vw;
color: #666;
margin-top: 1vh;
}
/* 状态标签 */
.status-tag {
margin-top: 2vh;
padding: 1vh 2vw;
border-radius: 2vw;
}
.status-tag text {
font-size: 2vw;
color: #fff;
}
.tag-called {
background-color: #1976f2;
}
.tag-handling {
background-color: #28c958;
}
.wait-num {
font-size: 11.458vw;
color: #ff5522;
font-weight: bold;
}
/* 挂起提示条 */
.suspend-bar {
width: 100%;
background-color: #ff6633;
padding: 2vh 0;
text-align: center;
}
.suspend-bar text {
font-size: 2.5vw;
color: #fff;
font-weight: bold;
}
/* 底部按钮 */
.btn-group {
display: flex;
padding: 0 2.083vw 5.556vh;
gap: 1.667vw;
}
.btn-item {
flex: 1;
height: 22.222vh;
border-radius: 1.667vw;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
cursor: pointer;
transition: opacity 0.2s ease;
}
.btn-item:active {
opacity: 0.8;
}
.btn-icon {
width: 3.646vw;
height: 3.646vw;
margin-bottom: 1.667vh;
}
.btn-item text {
font-size: 3.021vw;
color: #fff;
}
.green {
background-color: #28c958;
}
.blue {
background-color: #1976f2;
}
.sky {
background-color: #18a0f0;
}
.orange {
background-color: #ff9922;
}
.red {
background-color: #ff6633;
}
/* 按钮禁用态 */
.btn-item.disabled {
opacity: 0.4;
pointer-events: none;
}
/* ========== 弹窗通用 ========== */
.transfer-mask {
position: fixed;
top: 0;
left: 0;
right: 0;
bottom: 0;
background: rgba(0, 0, 0, 0.5);
display: flex;
align-items: center;
justify-content: center;
z-index: 999;
}
/* 转移弹窗 */
.transfer-dialog {
width: 55vw;
max-height: 70vh;
background: #fff;
border-radius: 2vw;
padding: 3vh 2vw;
display: flex;
flex-direction: column;
}
.suspend-dialog {
width: 45vw;
background: #fff;
border-radius: 2vw;
padding: 4vh 3vw;
display: flex;
flex-direction: column;
}
.dialog-title {
font-size: 3vw;
font-weight: bold;
color: #333;
text-align: center;
margin-bottom: 3vh;
flex-shrink: 0;
}
.dialog-list {
flex: 1;
max-height: 45vh;
overflow-y: auto;
margin-bottom: 2vh;
}
.transfer-item {
display: flex;
justify-content: space-between;
align-items: center;
padding: 3vh 2vw;
border-bottom: 1px solid #eee;
cursor: pointer;
}
.transfer-item:active {
background: #f0f4ff;
}
.transfer-name {
font-size: 2.4vw;
font-weight: bold;
color: #1976f2;
}
.transfer-desc {
font-size: 2vw;
color: #999;
}
.dialog-empty {
text-align: center;
padding: 6vh 0;
font-size: 2.2vw;
color: #999;
}
.dialog-cancel {
text-align: center;
padding: 2vh 0;
font-size: 2.5vw;
color: #999;
cursor: pointer;
}
/* 挂起弹窗 */
.suspend-input {
width: 100%;
height: 8vh;
border: 1px solid #ddd;
border-radius: 1vw;
padding: 0 2vw;
font-size: 2.2vw;
margin-bottom: 3vh;
}
.suspend-btns {
display: flex;
justify-content: space-between;
gap: 3vw;
}
.dialog-confirm {
flex: 1;
text-align: center;
padding: 2.5vh 0;
font-size: 2.5vw;
background: #ff6633;
color: #fff;
border-radius: 1.5vw;
cursor: pointer;
}
.dialog-confirm:active {
opacity: 0.8;
}
.dialog-cancel {
flex: 1;
text-align: center;
padding: 2.5vh 0;
font-size: 2.5vw;
background: #eee;
color: #666;
border-radius: 1.5vw;
cursor: pointer;
}
</style>

View File

@ -0,0 +1,259 @@
<template>
<view class="choose-win-wrap">
<!-- 顶部标题栏 -->
<view class="top-header">
<text class="header-title">环江毛南族自治县政务服务中心</text>
</view>
<!-- 主体内容区 -->
<view class="main-content">
<text class="main-title">请选择窗口</text>
<text class="main-subtitle">选择您当前所在的窗口工位</text>
<!-- 窗口列表卡片 -->
<view class="window-list" v-if="windowList.length > 0">
<view class="window-card" v-for="item in windowList" :key="item.id" @click="handleSelect(item)">
<view class="win-icon-text">{{ item.windowName }}</view>
<!-- <view class="win-info">
<text class="win-type">{{ item.windowType || '综合窗口' }}</text>
<text class="win-biz">{{ item.businessType || '综合业务' }}</text>
</view> -->
</view>
</view>
<!-- 空状态 -->
<view class="empty-state" v-else-if="!loading">
<text class="empty-text">暂无可选窗口</text>
<text class="empty-sub">请联系管理员检查窗口配置</text>
</view>
<!-- 加载中 -->
<view class="loading-state" v-if="loading">
<text class="loading-text">正在加载窗口列表...</text>
</view>
</view>
</view>
</template>
<script>
import { getWindowList, selectWindow } from '@/api/ticket.js'
export default {
name: 'ChooseWindow',
data() {
return {
windowList: [],
loading: false
}
},
mounted() {
this.fetchWindowList()
},
methods: {
//
async fetchWindowList() {
this.loading = true
try {
const res = await getWindowList({ status: 1, serviceStatus: 1 })
if (res.success && res.data) {
this.windowList = res.data.records || []
}
} catch (e) {
console.error('获取窗口列表失败', e)
uni.showToast({ title: '获取窗口列表失败', icon: 'none' })
} finally {
this.loading = false
}
},
//
async handleSelect(item) {
uni.showLoading({ title: '选择中...', mask: true })
try {
const res = await selectWindow({ windowId: item.id })
if (res.success && res.data) {
//
const winInfo = {
windowId: res.data.windowId,
windowName: res.data.windowName,
serviceStatus: res.data.serviceStatus,
handlerUserId: res.data.handlerUserId,
handlerUserName: res.data.handlerUserName,
handlerDeptName: res.data.handlerDeptName
}
uni.setStorageSync('windowInfo', winInfo)
uni.hideLoading()
uni.showToast({ title: `已选择 ${res.data.windowName}`, icon: 'success', duration: 1000 })
//
setTimeout(() => {
uni.redirectTo({
url: '/pages/callTicket/callTicket'
})
}, 800)
}
} catch (e) {
uni.hideLoading()
console.error('选择窗口失败', e)
uni.showToast({ title: '选择窗口失败,请重试', icon: 'none' })
}
}
}
}
</script>
<style scoped>
* {
box-sizing: border-box;
}
.choose-win-wrap {
width: 100vw;
height: 100vh;
display: flex;
flex-direction: column;
overflow: hidden;
}
/* ========== 顶部标题栏 ========== */
.top-header {
width: 100%;
background-color: rgba(19, 114, 255, 1);
display: flex;
align-items: center;
padding: 2.5vh 0;
box-sizing: border-box;
}
.header-title {
font-size: 3.125vw;
color: #ffffff;
font-weight: bold;
padding-left: 2.083vw;
/* letter-spacing: 0.3vw; */
white-space: nowrap;
}
/* ========== 主体内容区 ========== */
.main-content {
flex: 1;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
padding: 2vh 3vw;
overflow: hidden;
}
.main-title {
font-size: 3.125vw;
font-weight: bold;
color: #1a1a2e;
margin-bottom: 1vh;
flex-shrink: 0;
}
.main-subtitle {
font-size: 2vw;
color: rgba(26, 26, 46, 0.55);
margin-bottom: 2vh;
flex-shrink: 0;
}
/* ========== 窗口列表 ========== */
.window-list {
display: flex;
flex-wrap: wrap;
justify-content: center;
/* align-content: center; */
/* gap: 2vh 1.5vw; */
width: 100%;
flex: 1;
}
.window-card {
margin-right: 2vw;
width: 14vw;
height: 28vh;
background: #ffffff;
border-radius: 1.5vw;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
box-shadow: 0 0.4vw 2vw rgba(95, 136, 255, 0.12);
cursor: pointer;
transition:
transform 0.2s ease,
box-shadow 0.2s ease;
}
.window-card:active {
transform: scale(0.95);
box-shadow: 0 0.2vw 1vw rgba(95, 136, 255, 0.3);
}
.win-icon-text {
padding: 0 1vw;
font-size: 2vw;
font-weight: bold;
color: #1976f2;
text-align: center;
/* white-space: nowrap; */
}
.win-info {
display: flex;
flex-direction: column;
align-items: center;
text-align: center;
width: 100%;
overflow: hidden;
}
.win-type {
font-size: 1.4vw;
font-weight: bold;
color: #1a1a2e;
margin-bottom: 0.5vh;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
max-width: 90%;
}
.win-biz {
font-size: 1.1vw;
color: #999999;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
max-width: 90%;
}
/* ========== 空状态 ========== */
.empty-state {
display: flex;
flex-direction: column;
align-items: center;
}
.empty-text {
font-size: 3vw;
color: #999;
margin-bottom: 1vh;
}
.empty-sub {
font-size: 2vw;
color: #bbb;
}
/* ========== 加载中 ========== */
.loading-text {
font-size: 2.5vw;
color: #999;
}
</style>

View File

@ -1,241 +1,234 @@
<template> <template>
<!-- backBtn+logoutBtn 开启顶部两个按钮 --> <!-- backBtn+logoutBtn 开启顶部两个按钮 -->
<Container backBtn logoutBtn @handleBack="goBack"> <Container backBtn logoutBtn @handleBack="goBack">
<template #main> <template #main>
<view class="wrap"> <view class="wrap">
<view class="list-card"> <view class="list-card">
<view class="page-title" v-if="titleName">{{ titleName }}</view> <view class="item" v-for="(item, idx) in businessList" :key="idx">
<view class="item" v-for="(item, idx) in businessList" :key="idx"> <view class="left">
<view class="left"> <text class="dot"></text>
<text class="dot"></text> <text class="title-text">{{ item.itemName }}</text>
<text class="title-text">{{ item.itemName }}</text> </view>
</view> <view class="btn-box">
<view class="btn-box"> <view class="btn-guide" @click="openGuide(item)">办事指南</view>
<view class="btn-guide" @click="openGuide(item)">办事指南</view> <view class="btn-take" @click="toTakeNum(item)">取号</view>
<view class="btn-take" @click="toTakeNum(item)">取号</view> </view>
</view> </view>
</view> <view class="empty" v-if="isEmpty === false"> 暂无事项 </view>
<view class="empty" v-if="businessList.length === 0"> </view>
暂无事项 </view>
</view> </template>
</view> </Container>
</view>
</template>
</Container>
</template> </template>
<script> <script>
import Container from '@/components/container/container.vue' import Container from '@/components/container/container.vue'
import { import { getBusinessList, getHistoryList, takeNumber } from '@/api/ticket.js'
getBusinessList,
getHistoryList,
takeNumber,
} from '@/api/ticket.js'
export default { export default {
components: { components: {
Container Container
}, },
data() { data() {
return { return {
titleName: '', titleName: '',
source: '', source: '',
subjectId: '', subjectId: '',
deptId: '', deptId: '',
idcard: '', businessList: [],
businessList: [] isEmpty: null
} }
}, },
onLoad(options) { computed: {
this.source = options.source || '' idcard() {
this.subjectId = options.subjectId || '' return uni.getStorageSync('idcard') || ''
this.deptId = options.deptId || '' }
this.idcard = options.idcard || '' },
this.titleName = options.titleName || options.subjectName || options.deptName || '' onLoad(options) {
this.fetchList() this.source = options.source || ''
}, this.subjectId = options.subjectId || ''
methods: { this.deptId = options.deptId || ''
// this.titleName = options.titleName || options.subjectName || options.deptName || ''
goBack() { this.fetchList()
uni.navigateBack() },
}, methods: {
//
goBack() {
uni.navigateBack()
},
// source // source
async fetchList() { async fetchList() {
let res = null let res = null
try { try {
switch (this.source) { switch (this.source) {
case 'history': case 'history':
// //
if (!this.idcard) { if (!this.idcard) {
console.warn('历史取号缺少身份证号') console.warn('历史取号缺少身份证号')
return return
} }
res = await getHistoryList({ res = await getHistoryList({
applicantIdCard: this.idcard applicantIdCard: this.idcard
}) })
break break
case 'business': case 'business':
// //
res = await getBusinessList({ res = await getBusinessList({
status: 1 status: 1
}) })
break break
case 'subject': case 'subject':
default: default:
// / source // / source
const params = { status: 1 } const params = {
if (this.subjectId) { status: 1
params.subjectId = this.subjectId }
} else if (this.deptId) { if (this.subjectId) {
params.deptId = this.deptId params.subjectId = this.subjectId
} else { } else if (this.deptId) {
// params.deptId = this.deptId
} } else {
res = await getBusinessList(params) //
break }
} res = await getBusinessList(params)
if (res && res.success && res.data) { break
this.businessList = res.data.records || [] }
} if (res && res.success && res.data) {
} catch (e) { this.businessList = res.data.records || []
console.error('获取列表失败', e) this.isEmpty = this.businessList.length > 0 ? true : false
} }
}, } catch (e) {
console.error('获取列表失败', e)
}
},
// ID itemId id // ID itemId id
getItemId(item) { getItemId(item) {
return item.itemId || item.id return item.itemId || item.id
}, },
// //
async toTakeNum(row) { async toTakeNum(row) {
if (!this.idcard) { if (!this.idcard) {
uni.showToast({ uni.showToast({
title: '请先刷身份证', title: '请先刷身份证',
icon: 'none' icon: 'none'
}) })
return return
} }
try { try {
const res = await takeNumber({ const res = await takeNumber({
applicantIdCard: this.idcard, applicantIdCard: this.idcard,
itemId: this.getItemId(row) itemId: this.getItemId(row)
}) })
if (res.success) { if (res.success) {
uni.showToast({ uni.showToast({
title: '取号成功', title: '取号成功',
icon: 'none' icon: 'none'
}) })
} }
} catch (e) { } catch (e) {
console.error('取号失败', e) console.error('取号失败', e)
} }
}, },
// //
openGuide(item) { openGuide(item) {
const id = this.getItemId(item) const id = this.getItemId(item)
uni.navigateTo({ uni.navigateTo({
url: `/pages/home/guide?id=${id}&itemName=${item.itemName}` url: `/pages/home/guide?id=${id}&itemName=${item.itemName}`
}) })
} }
} }
} }
</script> </script>
<style scoped> <style scoped>
.wrap { .wrap {
width: 100%; width: 100%;
height: 100%; height: 100%;
padding: 200px 60px 150px; padding: 15vh 5vw 10vh;
display: flex; display: flex;
align-items: center; flex-direction: column;
justify-content: center; align-items: center;
box-sizing: border-box; justify-content: center;
} box-sizing: border-box;
}
.list-card { .list-card {
width: 100%; position: relative;
height: 740px; z-index: 100;
background: #fff; width: 100%;
border-radius: 60px; height: 75vh;
padding: 30px 80px; background: #fff;
display: flex; border-radius: 5vh;
flex-direction: column; padding: 3vh 5vw;
overflow-y: auto; display: flex;
} flex-direction: column;
overflow-y: auto;
}
.page-title { .item {
font-size: 36px; display: flex;
font-weight: bold; align-items: center;
color: rgba(51, 51, 51, 1); justify-content: space-between;
text-align: center; padding: 2vh 0;
padding: 20px 0 30px; border-bottom: 0.1vh solid rgba(188, 200, 209, 1);
border-bottom: 2px solid rgba(188, 200, 209, 1); }
margin-bottom: 20px;
}
.item { .item:first-child {
display: flex; padding-top: 0;
align-items: center; }
justify-content: space-between;
padding: 20px;
border-bottom: 1px solid rgba(188, 200, 209, 1);
}
.item:first-child { .item:last-child {
padding-top: 0; padding-bottom: 0;
} border-bottom: none;
}
.item:last-child { .left {
padding-bottom: 0; display: flex;
border-bottom: none; align-items: flex-start;
} width: 68%;
}
.left { .dot {
display: flex; color: #3688ff;
align-items: flex-start; font-size: 1.6vw;
width: 68%; margin-right: 1.5vw;
} }
.dot { .title-text {
color: #3688ff; font-size: 1.6vw;
font-size: 30px; color: rgba(51, 51, 51, 1);
margin-right: 20px; line-height: 1.5;
} }
.title-text { .btn-box {
font-size: 30px; display: flex;
color: rgba(51, 51, 51, 1); gap: 1.5vw;
line-height: 1.5; }
}
.btn-box { .btn-guide {
display: flex; background: #28c068;
gap: 20px; color: #fff;
} font-size: 1.6vw;
padding: 1.2vh 3vw;
border-radius: 99px;
}
.btn-guide { .btn-take {
background: #28c068; background: #367bfa;
color: #fff; color: #fff;
font-size: 30px; font-size: 1.6vw;
padding: 1.2vh 3vw; padding: 1.2vh 3vw;
border-radius: 99px; border-radius: 99px;
} }
.btn-take { .empty {
background: #367bfa; text-align: center;
color: #fff; font-size: 1.6vw;
font-size: 30px; color: #999;
padding: 1.2vh 3vw; padding: 10vh 0;
border-radius: 99px; }
}
.empty {
text-align: center;
font-size: 30px;
color: #999;
padding: 100px 0;
}
</style> </style>

View File

@ -0,0 +1,212 @@
<template>
<view class="choose-wrap">
<!-- 顶部标题栏 -->
<view class="top-header">
<text class="header-title">环江毛南族自治县政务服务中心</text>
</view>
<!-- 主体内容区 -->
<view class="main-content">
<text class="main-title">请选择设备类型</text>
<text class="main-subtitle">请根据当前设备用途选择对应的使用模式</text>
<!-- 三个选项卡片 -->
<view class="card-group">
<!-- 取号机 -->
<view class="card" @click="goToPage('/pages/home/home', '取号机')">
<view class="card-icon-box ticket-bg">
<text class="card-icon-text"></text>
</view>
<text class="card-title">取号机</text>
<text class="card-desc">自助排队取号</text>
</view>
<!-- 叫号机 -->
<view class="card" @click="goToPage('/pages/callTicket/chooseWindow', '叫号机')">
<view class="card-icon-box call-bg">
<text class="card-icon-text call-text"></text>
</view>
<text class="card-title">叫号机</text>
<text class="card-desc">窗口叫号管理</text>
</view>
<!-- 公告屏 -->
<view class="card" @click="goToPage('/pages/screen/index', '公告屏')">
<view class="card-icon-box screen-bg">
<text class="card-icon-text screen-text"></text>
</view>
<text class="card-title">公告屏</text>
<text class="card-desc">大屏信息展示</text>
</view>
</view>
</view>
</view>
</template>
<script>
export default {
name: 'ChooseMachine',
methods: {
goToPage(url, name) {
uni.showModal({
title: '确认选择',
content: `确定进入【${name}】模式吗?选择后需重新登录才能切换。`,
success: res => {
if (res.confirm) {
uni.redirectTo({
url: url,
fail() {
uni.switchTab({
url: url
})
}
})
}
}
})
}
}
}
</script>
<style scoped>
* {
box-sizing: border-box;
}
/* 全屏自适应容器 - flex弹性布局 */
.choose-wrap {
width: 100vw;
height: 100vh;
display: flex;
flex-direction: column;
overflow: hidden;
/* background: linear-gradient(180deg, #5f88ff 0%, #e8edfa 45%, #f5f7fc 100%); */
}
/* ========== 顶部标题栏 ========== */
.top-header {
width: 100%;
height: 16vh;
min-height: 100px;
display: flex;
align-items: center;
justify-content: center;
background: rgba(19, 114, 255, 0.95);
}
.header-title {
font-size: clamp(32px, 5vw, 64px);
color: #ffffff;
font-weight: bold;
letter-spacing: 0.4vw;
white-space: nowrap;
}
/* ========== 主体内容区 ========== */
.main-content {
flex: 1;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
padding: 0 4vw 4vh;
}
.main-title {
font-size: clamp(36px, 6vw, 72px);
font-weight: bold;
color: #1a1a2e;
margin-bottom: 1.5vh;
letter-spacing: 0.3vw;
}
.main-subtitle {
font-size: clamp(22px, 3vw, 36px);
color: rgba(26, 26, 46, 0.55);
margin-bottom: 7vh;
letter-spacing: 0.1vw;
}
/* ========== 卡片组 ========== */
.card-group {
display: flex;
flex-wrap: wrap;
justify-content: center;
gap: clamp(30px, 5vw, 80px);
width: 100%;
}
.card {
width: clamp(260px, 22vw, 400px);
min-width: 200px;
background: #ffffff;
border-radius: 32px;
padding: clamp(40px, 6vh, 80px) clamp(30px, 4vw, 50px) clamp(35px, 5vh, 70px);
display: flex;
flex-direction: column;
align-items: center;
box-shadow: 0 0.8vw 4vw rgba(95, 136, 255, 0.15);
cursor: pointer;
transition:
transform 0.2s ease,
box-shadow 0.2s ease;
}
.card:active {
transform: scale(0.95);
box-shadow: 0 0.4vw 2vw rgba(95, 136, 255, 0.3);
}
/* 图标圆形 */
.card-icon-box {
width: clamp(100px, 12vw, 180px);
height: clamp(100px, 12vw, 180px);
border-radius: 50%;
display: flex;
align-items: center;
justify-content: center;
margin-bottom: clamp(24px, 4vh, 48px);
}
.ticket-bg {
background: linear-gradient(135deg, #e8f0ff, #c5d8ff);
}
.call-bg {
background: linear-gradient(135deg, #e0f5e8, #b0e0c0);
}
.screen-bg {
background: linear-gradient(135deg, #fff3e8, #ffe0c0);
}
/* 图标文字 */
.card-icon-text {
font-size: clamp(40px, 5vw, 80px);
font-weight: bold;
color: #5f88ff;
}
.call-text {
color: #2e9e5f;
}
.screen-text {
color: #e89840;
}
/* 卡片文字 */
.card-title {
font-size: clamp(28px, 3.5vw, 52px);
font-weight: bold;
color: #1a1a2e;
margin-bottom: 1.5vh;
}
.card-desc {
font-size: clamp(18px, 2.2vw, 30px);
color: #999999;
letter-spacing: 0.1vw;
}
</style>

View File

@ -1,119 +1,120 @@
<template> <template>
<Container backBtn logoutBtn @handleBack="handleBack"> <Container backBtn logoutBtn @handleBack="handleBack">
<template #main> <template #main>
<view class="theme-page"> <view class="theme-page">
<view class="card"> <view class="card">
<view class="title">部门取号</view> <view class="title">部门取号</view>
<view class="btn-group"> <view class="btn-group">
<view class="btn-item" v-for="(item, index) in deptList" :key="index" <view class="btn-item" v-for="(item, index) in deptList" :key="index" @click="handleSelect(item)">
@click="handleSelect(item)"> {{ item.deptName }}
{{ item.deptName }} </view>
</view> </view>
</view> </view>
</view> </view>
</view> </template>
</template> </Container>
</Container>
</template> </template>
<script> <script>
import Container from '@/components/container/container.vue' import Container from '@/components/container/container.vue'
import { import { getList } from '@/api/system/dept.js'
getList,
} from '@/api/system/dept.js'
export default { export default {
components: { components: {
Container Container
}, },
data() { data() {
return { return {
deptList: [], deptList: []
idcard: '' }
} },
}, computed: {
onLoad(options) { idcard() {
this.idcard = options.idcard || '' return uni.getStorageSync('idcard') || ''
}, }
mounted() { },
this.fetchDeptList() mounted() {
}, this.fetchDeptList()
methods: { },
handleBack() { methods: {
uni.navigateBack() handleBack() {
}, uni.navigateBack()
},
handleSelect(item) { handleSelect(item) {
uni.navigateTo({ uni.navigateTo({
url: `/pages/home/businessList?source=dept&deptId=${item.id}&deptName=${item.deptName}&idcard=${this.idcard}` url: `/pages/home/businessList?source=dept&deptId=${item.id}&deptName=${item.deptName}`
}) })
}, },
async fetchDeptList() { async fetchDeptList() {
try { try {
const res = await getList({ parentId: '1123598813738675202' }) const res = await getList({ parentId: '1123598813738675202' })
if (res.success && res.data) { if (res.success && res.data) {
this.deptList = res.data.records || res.data || [] this.deptList = res.data.records || res.data || []
} }
} catch (e) { } catch (e) {
console.error('获取部门列表失败', e) console.error('获取部门列表失败', e)
} }
} }
} }
} }
</script> </script>
<style scoped> <style scoped>
.theme-page { .theme-page {
width: 100%; width: 100%;
height: 100%; height: 100%;
padding: 200px 60px 150px; padding: 15vh 5vw 10vh;
display: flex; display: flex;
align-items: center; align-items: center;
justify-content: center; justify-content: center;
box-sizing: border-box; box-sizing: border-box;
} }
.card { .card {
width: 100%; width: 100%;
height: 740px; height: 100%;
background: #fff; background: #fff;
border-radius: 60px; border-radius: 5vh;
box-sizing: border-box; box-sizing: border-box;
display: flex; display: flex;
flex-direction: column; flex-direction: column;
align-items: center; align-items: center;
padding: 40px; padding: 4vh 3vw;
} }
.title { .title {
font-size: 40px; font-size: 2.5vw;
font-weight: bold; font-weight: bold;
color: rgba(51, 51, 51, 1); color: rgba(51, 51, 51, 1);
margin-bottom: 60px; margin-bottom: 5vh;
} }
.btn-group { .btn-group {
width: 100%; width: 100%;
flex: 1; flex: 1;
display: flex; display: flex;
flex-wrap: wrap; flex-wrap: wrap;
align-content: flex-start; align-content: flex-start;
gap: 40px; }
}
.btn-item { .btn-item {
padding: 17px 25px; margin: 0 2vw 2vw 0;
width: 400px; padding: 1.5vh 1.5vw;
height: 140px; width: 19vw;
text-align: center; height: 11vh;
background: rgba(236, 243, 255, 1); text-align: center;
border-radius: 20px; background: rgba(236, 243, 255, 1);
display: flex; border-radius: 2vh;
align-items: center; display: flex;
justify-content: center; align-items: center;
font-size: 40px; justify-content: center;
color: rgba(51, 51, 51, 1); font-size: 1.7vw;
cursor: pointer; color: rgba(51, 51, 51, 1);
} cursor: pointer;
}
.btn-item:nth-child(4n) {
margin-right: 0;
}
</style> </style>

View File

@ -1,115 +1,154 @@
<template> <template>
<Container backBtn logoutBtn @handleBack="backPrev"> <Container backBtn logoutBtn @handleBack="backPrev">
<template #main> <template #main>
<view class="page-wrap"> <view class="page-wrap">
<view class="content-card"> <view class="content-card">
<!-- 标题+右上角按钮 --> <!-- 标题+右上角按钮 -->
<view class="top-row"> <view class="top-row">
<view class="title">{{ info.title }}</view> <view class="title">{{ info.title }}</view>
<view class="btn-take" @click="goScanCard">立即取号</view> <view class="btn-take" @click="goScanCard">立即取号</view>
</view> </view>
<!-- 部门信息 --> <!-- 部门信息 -->
<view class="dept-text">部门{{ info.dept }}</view> <view class="dept-text">部门{{ info.dept }}</view>
<!-- 办事详情正文 --> <!-- 办事详情正文 -->
<view class="desc-text">{{ info.content }}</view> <view class="desc-text">{{ info.content }}</view>
</view> </view>
</view> </view>
</template> </template>
</Container> </Container>
</template> </template>
<script> <script>
import Container from '@/components/container/container.vue' import Container from '@/components/container/container.vue'
export default { import { getItemDetail, takeNumber } from '@/api/ticket.js'
components: {
Container
},
data() {
return {
//
info: {
title: '城镇职工基本养老保险关系转移接续申请',
dept: '公积金',
content: '《住房公积金管理条例》(1999年4月3日中华人民共和国国务院令第262号发布 根据2002年3月24日《国务院关于修改<住房公积金管理条例>的决定》和2019年3月24日《国务院关于修改部分行政法规的决定》修订) 第二十四条 职工有下列情形之一的,可以提取职工住房公积金账户内的存储余额: (一)购买、建造、翻建、大修自住住房的; (二)离休、退休的; (三)完全丧失劳动能力,并与单位终止劳动关系的;(四)出境定居的; (五)偿还购房贷款本息的; (六)房租超出家庭工资收入的规定比例的。'
}
}
},
onLoad(options) {
//
if (options.businessName) {
this.info.title = decodeURIComponent(options.businessName)
}
},
methods: {
//
backPrev() {
uni.navigateBack()
},
// export default {
goScanCard() { components: {
uni.showToast({ Container
title: '取号成功', },
icon: 'none' data() {
}) return {
} itemId: '',
} info: {
} title: '',
dept: '',
content: ''
}
}
},
computed: {
idcard() {
return uni.getStorageSync('idcard') || ''
}
},
onLoad(options) {
this.itemId = options.id || ''
if (this.itemId) {
this.fetchDetail(this.itemId)
}
},
methods: {
//
backPrev() {
uni.navigateBack()
},
//
async fetchDetail(id) {
try {
const res = await getItemDetail({ id })
if (res.success && res.data) {
const d = res.data
this.info.title = d.itemName || ''
this.info.dept = d.deptName || ''
this.info.content = d.guideContent || ''
}
} catch (e) {
console.error('获取事项详情失败', e)
}
},
//
async goScanCard() {
if (!this.idcard) {
uni.showToast({
title: '请先刷身份证',
icon: 'none'
})
return
}
try {
const res = await takeNumber({
applicantIdCard: this.idcard,
itemId: this.itemId
})
if (res.success) {
uni.showToast({
title: '取号成功',
icon: 'none'
})
}
} catch (e) {
console.error('取号失败', e)
}
}
}
}
</script> </script>
<style scoped> <style scoped>
.page-wrap { .page-wrap {
width: 100%; width: 100%;
height: 100%; height: 100%;
padding: 200px 60px 150px; padding: 15vh 5vw 10vh;
display: flex; display: flex;
align-items: center; align-items: center;
justify-content: center; justify-content: center;
box-sizing: border-box; box-sizing: border-box;
} }
.content-card { .content-card {
width: 100%; width: 100%;
height: 740px; height: 100%;
background: #fff; background: #fff;
border-radius: 60px; border-radius: 5vh;
padding: 30px 80px; padding: 3vh 5vw;
display: flex; display: flex;
flex-direction: column; flex-direction: column;
overflow-y: auto; overflow-y: auto;
} }
.top-row { .top-row {
display: flex; display: flex;
justify-content: space-between; justify-content: space-between;
align-items: flex-start; align-items: flex-start;
margin-bottom: 1vh; margin-bottom: 1vh;
} }
.title { .title {
font-size: 40px; font-size: 2vw;
font-weight: bold; font-weight: bold;
color: rgba(51, 51, 51, 1); color: rgba(51, 51, 51, 1);
width: 75%; width: 75%;
} }
.btn-take { .btn-take {
background-color: #367bfa; background-color: #367bfa;
color: #fff; color: #fff;
font-size: 40px; font-size: 2vw;
padding: 2vh 5vw; padding: 1.5vh 4vw;
border-radius: 999px; border-radius: 999px;
} }
.dept-text { .dept-text {
font-size: 36px; font-size: 2vw;
color: rgba(51, 51, 51, 1); color: rgba(51, 51, 51, 1);
margin-bottom: 3vh; margin-bottom: 3vh;
} }
.desc-text { .desc-text {
font-size: 36px; font-size: 2vw;
color: rgba(51, 51, 51, 1); color: rgba(51, 51, 51, 1);
line-height: 1.8; line-height: 1.8;
} }
</style> </style>

View File

@ -1,101 +1,101 @@
<template> <template>
<view class="container"> <view class="container">
<Container> <Container>
<template #main> <template #main>
<view class="card"> <view class="card">
<!-- 身份证图标区域 --> <!-- 身份证图标区域 -->
<view class="id-card"> <view class="id-card"> </view>
</view> <!-- 提示文字 -->
<!-- 提示文字 --> <text class="tip">请刷身份证取号</text>
<text class="tip">请刷身份证取号</text> <input v-model="inputVal" placeholder="请输入内容" confirm-type="done" @confirm="goRoute" />
<input v-model="inputVal" placeholder="请输入内容" confirm-type="done" @confirm="goRoute" /> </view>
</view> </template>
</template> </Container>
</Container> </view>
</view>
</template> </template>
<script> <script>
import Container from '@/components/container/container.vue' import Container from '@/components/container/container.vue'
export default { export default {
name: 'SelfServiceMachine', name: 'SelfServiceMachine',
components: { components: {
Container Container
}, },
data() { data() {
return { return {
inputVal: '', inputVal: '',
currentTime: '' currentTime: ''
} }
}, },
mounted() { mounted() {
this.updateTime() this.updateTime()
setInterval(() => { setInterval(() => {
this.updateTime() this.updateTime()
}, 1000) }, 1000)
// APP // APP
// #ifdef APP-PLUS // #ifdef APP-PLUS
plus.screen.lockOrientation('landscape-primary') plus.screen.lockOrientation('landscape-primary')
// #endif // #endif
}, },
methods: { methods: {
updateTime() { updateTime() {
const now = new Date() const now = new Date()
const year = now.getFullYear() const year = now.getFullYear()
const month = String(now.getMonth() + 1).padStart(2, '0') const month = String(now.getMonth() + 1).padStart(2, '0')
const day = String(now.getDate()).padStart(2, '0') const day = String(now.getDate()).padStart(2, '0')
const hours = String(now.getHours()).padStart(2, '0') const hours = String(now.getHours()).padStart(2, '0')
const minutes = String(now.getMinutes()).padStart(2, '0') const minutes = String(now.getMinutes()).padStart(2, '0')
const seconds = String(now.getSeconds()).padStart(2, '0') const seconds = String(now.getSeconds()).padStart(2, '0')
this.currentTime = `${year}${month}${day}${hours}:${minutes}:${seconds}` this.currentTime = `${year}${month}${day}${hours}:${minutes}:${seconds}`
}, },
goRoute() { goRoute() {
uni.navigateTo({ //
url: `/pages/home/ticket?idcard=${this.inputVal}` uni.setStorageSync('idcard', this.inputVal)
}) uni.navigateTo({
} url: '/pages/home/ticket'
} })
} }
}
}
</script> </script>
<style scoped> <style scoped>
/* 中间卡片 - 百分比宽高 */ /* 中间卡片 - 自适应宽高 */
.card { .card {
background: #ffffff; background: #ffffff;
width: 1200px; width: 63vw;
height: 740px; height: 68vh;
border-radius: 2.5%; border-radius: 2.5%;
box-shadow: 0 1vh 3vh rgba(0, 123, 255, 0.15); box-shadow: 0 1vh 3vh rgba(0, 123, 255, 0.15);
display: flex; display: flex;
flex-direction: column; flex-direction: column;
align-items: center; align-items: center;
justify-content: center; justify-content: center;
} }
/* 身份证卡片 */ /* 身份证卡片 */
.id-card { .id-card {
width: 511px; width: 27vw;
height: 307px; height: 28vh;
background: url('../../static/images/home/idcard_bg.png'); background: url('../../static/images/home/idcard_bg.png');
background-size: 100% 100%; background-size: 100% 100%;
display: flex; display: flex;
align-items: center; align-items: center;
padding: 0 5%; padding: 0 5%;
margin-bottom: 5%; margin-bottom: 5%;
box-sizing: border-box; box-sizing: border-box;
} }
/* 提示文字 */
.tip {
font-size: 5vh;
color: #222;
font-weight: 500;
letter-spacing: 0.2vh;
}
/* 提示文字 */ ::v-deep .uni-input-wrapper {
.tip { width: 60vw;
font-size: 5vh; border: 0.2vh solid #ccc;
color: #222; }
font-weight: 500;
letter-spacing: 0.2vh;
}
::v-deep .uni-input-wrapper {
width: 800rpx;
border: 2rpx solid #ccc;
}
</style> </style>

View File

@ -1,119 +1,121 @@
<template> <template>
<Container backBtn logoutBtn @handleBack="handleBack"> <Container backBtn logoutBtn @handleBack="handleBack">
<template #main> <template #main>
<view class="theme-page"> <view class="theme-page">
<view class="card"> <view class="card">
<view class="title">主题取号</view> <view class="title">主题取号</view>
<view class="btn-group"> <view class="btn-group">
<view class="btn-item" v-for="(item, index) in themeList" :key="index" <view class="btn-item" v-for="(item, index) in themeList" :key="index" @click="handleSelect(item)">
@click="handleSelect(item)"> {{ item.subjectName }}
{{ item.subjectName }} </view>
</view> </view>
</view> </view>
</view> </view>
</view> </template>
</template> </Container>
</Container>
</template> </template>
<script> <script>
import Container from '@/components/container/container.vue' import Container from '@/components/container/container.vue'
import { import { getThemeList } from '@/api/ticket.js'
getThemeList,
} from '@/api/ticket.js'
export default { export default {
components: { components: {
Container Container
}, },
data() { data() {
return { return {
themeList: [], themeList: []
idcard: '' }
} },
}, computed: {
onLoad(options) { idcard() {
this.idcard = options.idcard || '' return uni.getStorageSync('idcard') || ''
}, }
mounted() { },
this.fetchThemeList() mounted() {
}, this.fetchThemeList()
methods: { },
handleBack() { methods: {
uni.navigateBack() handleBack() {
}, uni.navigateBack()
},
handleSelect(item) { handleSelect(item) {
uni.navigateTo({ uni.navigateTo({
url: `/pages/home/businessList?source=subject&subjectId=${item.id}&subjectName=${item.subjectName}&idcard=${this.idcard}` url: `/pages/home/businessList?source=subject&subjectId=${item.id}&subjectName=${item.subjectName}`
}) })
}, },
async fetchThemeList() { async fetchThemeList() {
try { try {
const res = await getThemeList({ status: 1 }) const res = await getThemeList({ status: 1 })
if (res.success && res.data) { if (res.success && res.data) {
this.themeList = res.data.records || [] this.themeList = res.data.records || []
} }
} catch (e) { } catch (e) {
console.error('获取主题列表失败', e) console.error('获取主题列表失败', e)
} }
} }
} }
} }
</script> </script>
<style scoped> <style scoped>
.theme-page { .theme-page {
width: 100%; width: 100%;
height: 100%; height: 100%;
padding: 200px 60px 150px; padding: 15vh 5vw 10vh;
display: flex; display: flex;
align-items: center; align-items: center;
justify-content: center; justify-content: center;
box-sizing: border-box; box-sizing: border-box;
} }
.card { .card {
width: 100%; width: 100%;
height: 740px; height: 100%;
background: #fff; background: #fff;
border-radius: 60px; border-radius: 5vh;
box-sizing: border-box; box-sizing: border-box;
display: flex; display: flex;
flex-direction: column; flex-direction: column;
align-items: center; align-items: center;
padding: 40px; padding: 4vh 3vw;
} }
.title { .title {
font-size: 40px; font-size: 2.5vw;
font-weight: bold; font-weight: bold;
color: rgba(51, 51, 51, 1); color: rgba(51, 51, 51, 1);
margin-bottom: 60px; margin-bottom: 5vh;
} }
.btn-group { .btn-group {
width: 100%; width: 100%;
flex: 1; flex: 1;
display: flex; display: flex;
flex-wrap: wrap; flex-wrap: wrap;
align-content: flex-start; align-content: flex-start;
gap: 40px; }
}
.btn-item { .btn-item {
padding: 17px 25px; margin: 0 2vw 2vw 0;
width: 400px; padding: 1.5vh 1.5vw;
height: 140px; width: 19vw;
text-align: center; height: 11vh;
background: rgba(236, 243, 255, 1); text-align: center;
border-radius: 20px; background: rgba(236, 243, 255, 1);
display: flex; border-radius: 2vh;
align-items: center; display: flex;
justify-content: center; align-items: center;
font-size: 40px; justify-content: center;
color: rgba(51, 51, 51, 1); font-size: 2vw;
cursor: pointer; color: rgba(51, 51, 51, 1);
} cursor: pointer;
}
.btn-item:nth-child(4n) {
margin-right: 0;
}
</style> </style>

View File

@ -35,12 +35,13 @@ export default {
}, },
data() { data() {
return { return {
recommendList: [], recommendList: []
idcard: ''
} }
}, },
onLoad(options) { computed: {
this.idcard = options.idcard || '' idcard() {
return uni.getStorageSync('idcard') || ''
}
}, },
mounted() { mounted() {
this.fetchHotList() this.fetchHotList()
@ -53,7 +54,7 @@ export default {
hot: 1, hot: 1,
status: 1, status: 1,
current: 1, current: 1,
size: 6, size: 12,
descs: 'create_time,id' descs: 'create_time,id'
}) })
if (res.success && res.data) { if (res.success && res.data) {
@ -74,25 +75,25 @@ export default {
// //
goTheme() { goTheme() {
uni.navigateTo({ uni.navigateTo({
url: `/pages/home/theme?idcard=${this.idcard}` url: '/pages/home/theme'
}) })
}, },
// //
goDept() { goDept() {
uni.navigateTo({ uni.navigateTo({
url: `/pages/home/dept?idcard=${this.idcard}` url: '/pages/home/dept'
}) })
}, },
// //
goHistory() { goHistory() {
uni.navigateTo({ uni.navigateTo({
url: `/pages/home/businessList?source=history&idcard=${this.idcard}&titleName=历史取号` url: '/pages/home/businessList?source=history&titleName=历史取号'
}) })
}, },
// //
goBusiness() { goBusiness() {
uni.navigateTo({ uni.navigateTo({
url: `/pages/home/businessList?source=business&titleName=业务取号` url: '/pages/home/businessList?source=business&titleName=业务取号'
}) })
} }
} }
@ -109,33 +110,42 @@ export default {
height: 100%; height: 100%;
display: flex; display: flex;
align-items: center; align-items: center;
justify-content: space-around; justify-content: space-between;
padding: 0 5%; padding: 0 4vw;
box-sizing: border-box; box-sizing: border-box;
} }
/* 左侧按钮组 */ /* 左侧四大功能按钮 - 2×2 网格 */
.btn-group { .btn-group {
/* width: 60%; */ padding-top: 2vw;
/* height: 85%; */
display: flex; display: flex;
flex-wrap: wrap; flex-wrap: wrap;
/* justify-content: space-between; */ align-content: center;
align-content: space-around; /* justify-content: center; */
/* gap: 3vh 3vw; */
} }
.btn-item { .btn-item {
width: 500px; margin: 0 2vw 2vw 0;
height: 340px; width: 28vw;
height: 32vh;
display: flex; display: flex;
flex-direction: column; flex-direction: column;
align-items: center; align-items: center;
justify-content: center; justify-content: center;
background-size: 100% 100%;
background-repeat: no-repeat;
background-position: center;
}
.btn-item:nth-child(n + 3),
.btn-item:nth-child(n + 4) {
margin-bottom: 0;
}
.btn-item:nth-child(2n) {
margin-right: 0;
} }
.btn-item.blue { .btn-item.blue {
margin-right: 60px;
margin-bottom: 60px;
background: url('../../static/images/ticket/theme_bg.png'); background: url('../../static/images/ticket/theme_bg.png');
background-size: 100% 100%; background-size: 100% 100%;
} }
@ -146,7 +156,6 @@ export default {
} }
.btn-item.orange { .btn-item.orange {
margin-right: 60px;
background: url('../../static/images/ticket/history_bg.png'); background: url('../../static/images/ticket/history_bg.png');
background-size: 100% 100%; background-size: 100% 100%;
} }
@ -158,28 +167,33 @@ export default {
/* 右侧热门推荐 */ /* 右侧热门推荐 */
.hot-recommend { .hot-recommend {
width: 55%; margin-top: 2vw;
height: 740px; width: 54vw;
height: 68vh;
background: #fff; background: #fff;
border-radius: 20px; border-radius: 1.5vw;
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.08); box-shadow: 0 0.5vh 1.5vh rgba(0, 0, 0, 0.08);
padding: 40px 40px 16px; padding: 3vh 2vw 2vh;
box-sizing: border-box; box-sizing: border-box;
display: flex;
flex-direction: column;
} }
.recommend-title { .recommend-title {
font-size: 40px; font-size: 2.4vw;
font-weight: bold; font-weight: bold;
color: rgba(51, 51, 51, 1); color: rgba(51, 51, 51, 1);
text-align: center; text-align: center;
margin-bottom: 30px; margin-bottom: 2.5vh;
flex-shrink: 0;
} }
.recommend-list { .recommend-list {
display: flex; display: flex;
flex-direction: column; flex-direction: column;
justify-content: space-around; gap: 2.5vh;
height: 85%; flex: 1;
overflow-y: auto;
} }
.recommend-item { .recommend-item {
@ -190,12 +204,13 @@ export default {
.dot { .dot {
color: #4080ff; color: #4080ff;
font-size: 30px; font-size: 1.8vw;
margin-right: 10px; margin-right: 0.8vw;
flex-shrink: 0;
} }
.text { .text {
font-size: 30px; font-size: 1.8vw;
color: rgba(51, 51, 51, 1); color: rgba(51, 51, 51, 1);
} }
</style> </style>

View File

@ -47,7 +47,7 @@
username: '', username: '',
password: '', password: '',
disabled: false, disabled: false,
redirect: '/pages/home/home' redirect: '/pages/home/chooseMachine'
} }
}, },
methods: { methods: {

View File

@ -1,284 +1,276 @@
<template> <template>
<view class="screen-wrap" :style="scaleWrap"> <view class="screen-wrap">
<!-- 顶部标题栏 --> <!-- 顶部标题栏 -->
<view class="top-header"> <view class="top-header">
<text class="title-left">环江毛南族自治县政务服务中心</text> <text class="title-left">环江毛南族自治县政务服务中心</text>
<view class="title-right"> <view class="title-right">
<text>{{dateStr}}</text> <text>{{ dateStr }}</text>
<text>{{weekStr}} {{timeStr}}</text> <text>{{ weekStr }} {{ timeStr }}</text>
</view> </view>
</view> </view>
<!-- 中间大字播报区 --> <!-- 中间大字播报区 -->
<view class="big-call"> <view class="big-call">
<text class="normal-text"></text> <text class="normal-text"></text>
<text class="yellow-text">{{currCallNo}}</text> <text class="yellow-text">{{ currCallNo }}</text>
<text class="normal-text"></text> <text class="normal-text"></text>
<text class="yellow-text">{{currWinNo}}号窗口</text> <text class="yellow-text">{{ currWinNo }}号窗口</text>
</view> </view>
<!-- 左右双栏表格区域 --> <!-- 左右双栏表格区域 -->
<view class="table-box"> <view class="table-box">
<!-- 当前呼叫 --> <!-- 当前呼叫 -->
<view class="table-left"> <view class="table-left">
<view class="table-head"> <view class="table-head">
<text>当前呼叫</text> <text>当前呼叫</text>
<text class="tip-text">(过号需重新取号)</text> <text class="tip-text">(过号需重新取号)</text>
</view> </view>
<view class="table-row" v-for="(item,idx) in callList" :key="idx" :class="idx%2===1?'row-blue':''"> <view class="table-list">
<text></text> <view class="table-row" v-for="(item, idx) in callList" :key="idx" :class="idx % 2 === 1 ? 'row-blue' : ''">
<text class="row-no">{{item.no}}</text> <text></text>
<text></text> <text class="row-no">{{ item.no }}</text>
<text class="row-no">{{item.win}}号窗口</text> <text></text>
</view> <text class="row-no">{{ item.win }}号窗口</text>
</view> </view>
<!-- 等候办理 --> </view>
<view class="table-right"> </view>
<view class="table-head">等候办理</view> <!-- 等候办理 -->
<view class="table-row wait-row" v-for="(row,idx) in waitList" :key="idx" <view class="table-right">
:class="idx%2===1?'row-blue':''"> <view class="table-head">等候办理</view>
<text v-for="(item,i) in row" :key="i">{{item}}</text> <view class="table-list">
</view> <view class="table-row wait-row" v-for="(row, idx) in waitList" :key="idx" :class="idx % 2 === 1 ? 'row-blue' : ''">
</view> <text v-for="(item, i) in row" :key="i">{{ item }}</text>
</view> </view>
</view> </view>
</view>
</view>
</view>
</template> </template>
<script> <script>
export default { export default {
name: "CallScreen", name: 'CallScreen',
data() { data() {
return { return {
designW: 1920, //
designH: 1080, dateStr: '',
scaleWrap: { weekStr: '',
width: "1920px", timeStr: '',
height: "1080px", timer: null, //
transformOrigin: "left top", //
transform: "scale(1)" currCallNo: 'A010',
}, currWinNo: '2',
// //
dateStr: "", callList: [
weekStr: "", {
timeStr: "", no: 'A0009',
timer: null, // win: '1'
// },
currCallNo: "A010", {
currWinNo: "2", no: 'A0009',
// win: '1'
callList: [{ },
no: "A0009", {
win: "1" no: 'A0009',
}, win: '1'
{ },
no: "A0009", {
win: "1" no: 'A0009',
}, win: '1'
{ },
no: "A0009", {
win: "1" no: 'A0009',
}, win: '1'
{ },
no: "A0009", {
win: "1" no: 'A0009',
}, win: '1'
{ },
no: "A0009", {
win: "1" no: 'A0009',
}, win: '1'
{ },
no: "A0009", {
win: "1" no: 'A0009',
}, win: '1'
{ }
no: "A0009", ],
win: "1" //
}, waitList: [
{ ['A0001', 'A0002', 'A0003'],
no: "A0009", ['A0004', 'A0005', 'A0006'],
win: "1" ['A0001', 'A0001', 'A0001'],
}, ['A0001', 'A0001', 'A0001'],
], ['A0001', 'A0001', 'A0001'],
// ['A0001', 'A0001', 'A0001'],
waitList: [ ['A0001', 'A0001', 'A0001'],
["A0001", "A0002", "A0003"], ['A0001', 'A0001', 'A0001']
["A0004", "A0005", "A0006"], ]
["A0001", "A0001", "A0001"], }
["A0001", "A0001", "A0001"], },
["A0001", "A0001", "A0001"], onLoad() {
["A0001", "A0001", "A0001"], this.getNowTime() //
["A0001", "A0001", "A0001"], //
["A0001", "A0001", "A0001"], this.timer = setInterval(() => {
] this.getNowTime()
} }, 1000)
}, },
onLoad() { onUnload() {
this.setScale() //
this.getNowTime() // clearInterval(this.timer)
// },
this.timer = setInterval(() => { methods: {
this.getNowTime() //
}, 1000) getNowTime() {
window.addEventListener("resize", this.setScale) const now = new Date()
}, const year = now.getFullYear()
onUnload() { const month = this.addZero(now.getMonth() + 1)
window.removeEventListener("resize", this.setScale) const day = this.addZero(now.getDate())
// const hour = this.addZero(now.getHours())
clearInterval(this.timer) const minute = this.addZero(now.getMinutes())
}, // const second = this.addZero(now.getSeconds())
methods: { //
// const weekArr = ['星期日', '星期一', '星期二', '星期三', '星期四', '星期五', '星期六']
setScale() { const week = weekArr[now.getDay()]
const w = document.documentElement.clientWidth
const h = document.documentElement.clientHeight
const sX = w / this.designW
const sY = h / this.designH
const scale = Math.min(sX, sY)
this.scaleWrap.transform = `scale(${scale})`
},
//
getNowTime() {
const now = 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 second = this.addZero(now.getSeconds())
//
const weekArr = ["星期日", "星期一", "星期二", "星期三", "星期四", "星期五", "星期六"]
const week = weekArr[now.getDay()]
this.dateStr = `${year}${month}${day}` this.dateStr = `${year}${month}${day}`
this.weekStr = week this.weekStr = week
this.timeStr = `${hour}:${minute}` this.timeStr = `${hour}:${minute}`
}, },
// 0 // 0
addZero(num) { addZero(num) {
return num < 10 ? '0' + num : num return num < 10 ? '0' + num : num
} }
} }
} }
</script> </script>
<style scoped> <style scoped>
/*基准1920*1080大屏 */ * {
.screen-wrap { box-sizing: border-box;
width: 1920px; }
height: 1080px;
background: #083378;
display: flex;
flex-direction: column;
overflow: hidden;
}
/*顶部蓝色栏 */ /* 使用 vw/vh 自适应屏幕,设计基准 1920*1080 */
.top-header { .screen-wrap {
width: 100%; width: 100vw;
height: 140px; height: 100vh;
background: #167bff; background: #083378;
display: flex; display: flex;
justify-content: space-between; flex-direction: column;
align-items: center; overflow: hidden;
padding: 0 50px; }
box-sizing: border-box;
}
.title-left { /*顶部蓝色栏 */
font-size: 62px; .top-header {
color: #fff; width: 100%;
font-weight: bold; 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-right { .title-left {
display: flex; font-size: 3.229vw; /* 62/1920*100 */
flex-direction: column; color: #fff;
align-items: flex-end; font-weight: bold;
} }
.title-right text { .title-right {
font-size: 40px; display: flex;
color: #fff; flex-direction: column;
line-height: 50px; align-items: flex-end;
} }
/*中间大字叫号 */ .title-right text {
.big-call { font-size: 2.083vw; /* 40/1920*100 */
height: 260px; color: #fff;
display: flex; line-height: 4.63vh; /* 50/1080*100 */
justify-content: center; }
align-items: center;
gap: 30px;
}
.normal-text { /*中间大字叫号 */
font-size: 120px; .big-call {
color: #ffffff; height: 24.074vh; /* 260/1080*100 */
font-weight: bold; display: flex;
} justify-content: center;
align-items: center;
gap: 1.563vw; /* 30/1920*100 */
}
.yellow-text { .normal-text {
font-size: 120px; font-size: 6.25vw; /* 120/1920*100 */
color: #ffef33; color: #ffffff;
font-weight: bold; font-weight: bold;
} }
/*左右表格容器 */ .yellow-text {
.table-box { font-size: 6.25vw; /* 120/1920*100 */
flex: 1; color: #ffef33;
display: flex; font-weight: bold;
gap: 30px; }
padding: 0 30px 30px;
box-sizing: border-box;
}
.table-left, /*左右表格容器 */
.table-right { .table-box {
flex: 1; flex: 1;
display: flex; display: flex;
flex-direction: column; gap: 1.563vw; /* 30/1920*100 */
border-radius: 16px; padding: 0 1.563vw 2.778vh; /* 0 30/1920*100 30/1080*100 */
overflow: hidden; box-sizing: border-box;
} }
.table-head { .table-left,
width: 100%; .table-right {
height: 110px; flex: 1;
background: #167bff; display: flex;
display: flex; flex-direction: column;
justify-content: center; border-radius: 0.833vw; /* 16/1920*100 */
align-items: center; }
font-size: 52px;
color: #fff;
font-weight: bold;
}
.tip-text { .table-head {
font-size: 36px; width: 100%;
margin-left: 20px; height: 10.185vh; /* 110/1080*100 */
opacity: 0.9; background: #167bff;
} display: flex;
justify-content: center;
align-items: center;
font-size: 2.708vw; /* 52/1920*100 */
color: #fff;
font-weight: bold;
}
/*表格行样式 */ .tip-text {
.table-row { font-size: 1.875vw; /* 36/1920*100 */
width: 100%; margin-left: 1.042vw; /* 20/1920*100 */
flex: 1; opacity: 0.9;
display: flex; }
justify-content: center;
align-items: center;
font-size: 54px;
background: #ffffff;
}
.wait-row { .table-list {
justify-content: space-around; height: 60vh;
} overflow-y: auto;
}
.row-blue { /*表格行样式 */
background: #e6f0ff; .table-row {
} width: 100%;
flex: 1;
display: flex;
justify-content: center;
align-items: center;
font-size: 2.813vw; /* 54/1920*100 */
background: #ffffff;
}
.row-no { .wait-row {
color: #0055ff; justify-content: space-around;
font-weight: bold; }
margin: 0 20px;
} .row-blue {
background: #e6f0ff;
}
.row-no {
color: #0055ff;
font-weight: bold;
margin: 0 1.042vw; /* 20/1920*100 */
}
</style> </style>

View File

@ -1,262 +0,0 @@
<template>
<!-- 外层容器固定设计稿1920*1080大屏自动等比缩放 -->
<view class="wrap" :style="scaleStyle">
<!-- 顶部蓝色头部 -->
<view class="header-bar">
<view class="header-left">
<text class="win-no">窗口号001</text>
<view class="user-dept">
<text>王专员</text>
<text>人社局</text>
</view>
</view>
<view class="exit-btn" @click="handleExit">
<!-- 退出电源图标 预留image -->
<img class="icon-exit" src="../../static/images/home/logout_icon.png" />
<text>退出</text>
</view>
</view>
<!-- 中间内容区域 -->
<view class="content-box">
<view class="item-col">
<text class="col-title">当前叫号</text>
<text class="call-no">A0001</text>
</view>
<view class="item-col">
<text class="col-title">当前等候</text>
<text class="wait-num">10</text>
</view>
</view>
<!-- 底部功能按钮区 -->
<view class="btn-group">
<view class="btn-item green" @click="handleCall">
<img class="btn-icon" src="../../static/images/takeTicket/hj_icon.png" mode="aspectFit"></img>
<text>呼叫</text>
</view>
<view class="btn-item blue" @click="handleReCall">
<img class="btn-icon" src="../../static/images/takeTicket/ch_icon.png" mode="aspectFit"></img>
<text>重呼</text>
</view>
<view class="btn-item sky" @click="handleTransfer">
<img class="btn-icon" src="../../static/images/takeTicket/zy_icon.png" mode="aspectFit"></img>
<text>转移</text>
</view>
<view class="btn-item orange" @click="handleOver">
<img class="btn-icon" src="../../static/images/takeTicket/gh_icon.png" mode="aspectFit"></img>
<text>过号</text>
</view>
<view class="btn-item red" @click="handleHang">
<img class="btn-icon" src="../../static/images/takeTicket/gq_icon.png" mode="aspectFit"></img>
<text>挂起</text>
</view>
</view>
</view>
</template>
<script>
export default {
name: 'CallWindow',
data() {
return {
// 稿 1920*1080
designW: 1920,
designH: 1080,
scaleStyle: {
width: '1920px',
height: '1080px',
transformOrigin: 'left top',
transform: 'scale(1)'
}
}
},
onMounted() {
//
this.calcScale()
window.addEventListener('resize', this.calcScale)
},
onUnload() {
window.removeEventListener('resize', this.calcScale)
},
methods: {
//
calcScale() {
const screenW = document.documentElement.clientWidth
const screenH = document.documentElement.clientHeight
const scaleX = screenW / this.designW
const scaleY = screenH / this.designH
const scale = Math.min(scaleX, scaleY)
this.scaleStyle.transform = `scale(${scale})`
},
//
handleExit() {
uni.navigateBack()
},
handleCall() {
console.log('呼叫')
},
handleReCall() {
console.log('重呼')
},
handleTransfer() {
console.log('转移')
},
handleOver() {
console.log('过号')
},
handleHang() {
console.log('挂起')
}
}
}
</script>
<style scoped>
* {
box-sizing: border-box;
}
.wrap {
width: 100%;
height: 100%;
display: flex;
flex-direction: column;
overflow: hidden;
background: url('../../static/images/home/bg.png');
background-size: 100% 100%;
}
/* 顶部头部 */
.header-bar {
width: 100%;
/* height: 180px; */
background-color: rgba(19, 114, 255, 1);
display: flex;
justify-content: space-between;
align-items: center;
padding: 21px 40px 14px;
box-sizing: border-box;
}
.header-left {
display: flex;
flex-direction: column;
justify-content: center;
}
.win-no {
font-size: 60px;
color: #fff;
font-weight: bold;
}
.user-dept {
display: flex;
margin-top: 12px;
}
.user-dept text {
font-size: 40px;
color: #fff;
margin-right: 50px;
}
.exit-btn {
background: #fff;
border-radius: 50px;
display: flex;
align-items: center;
padding: 15px 25px;
}
.icon-exit {
width: 50px;
height: 50px;
margin-right: 20px;
}
.exit-btn text {
font-size: 40px;
}
/* 中间内容 */
.content-box {
flex: 1;
display: flex;
}
.item-col {
flex: 1;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
}
.col-title {
font-size: 60px;
color: rgba(51, 51, 51, 1);
margin-bottom: 30px;
}
.call-no {
font-size: 220px;
color: rgba(19, 114, 255, 1);
font-weight: bold;
}
.wait-num {
font-size: 220px;
color: #ff5522;
font-weight: bold;
}
/* 底部按钮 */
.btn-group {
display: flex;
padding: 0 40px 60px;
gap: 32px;
}
.btn-item {
flex: 1;
height: 240px;
border-radius: 32px;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
}
.btn-icon {
width: 70px;
height: 70px;
margin-bottom: 18px;
}
.btn-item text {
font-size: 58px;
color: #fff;
}
.green {
background-color: #28c958;
}
.blue {
background-color: #1976f2;
}
.sky {
background-color: #18a0f0;
}
.orange {
background-color: #ff9922;
}
.red {
background-color: #ff6633;
}
</style>