You cannot select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
723 lines
15 KiB
Vue
723 lines
15 KiB
Vue
<template>
|
|
<view class="page-container">
|
|
<div class="top-div">
|
|
<div></div>
|
|
<tn-button width="80px" height="32px" :plain="true" text-color="#0099ff" @click="backToIndex">
|
|
<uni-icons type="arrow-left" size="18" color="#0099ff" style="margin-right: 5px;"></uni-icons>返回
|
|
</tn-button>
|
|
</div>
|
|
<view class="content">
|
|
<!-- 窗口状态监控 -->
|
|
<view class="status-section">
|
|
<view class="section-card">
|
|
<view class="section-header">
|
|
<text class="section-title">窗口状态监控</text>
|
|
<view class="status-summary">
|
|
<text class="summary-item">空闲窗口: {{ freeWindowsCount }}</text>
|
|
<text class="summary-item">忙碌窗口: {{ busyWindowsCount }}</text>
|
|
</view>
|
|
</view>
|
|
|
|
<!-- 人工窗口 -->
|
|
<view class="window-group">
|
|
<view class="group-title">
|
|
<uni-icons type="staff" size="20" color="#1890ff"></uni-icons>
|
|
<text>人工服务窗口</text>
|
|
</view>
|
|
<view class="windows-grid">
|
|
<view v-for="window in manualWindows" :key="window.id" class="window-item"
|
|
:class="getWindowStatusClass(window.status)" @click="handleWindowClick(window)">
|
|
<view class="window-header">
|
|
<text class="window-number">{{ window.number }}</text>
|
|
<view class="window-status" :class="'status-' + window.status">
|
|
{{ getWindowStatusText(window.status) }}
|
|
</view>
|
|
</view>
|
|
<view class="window-info">
|
|
<text class="window-type">{{ window.type }}</text>
|
|
<text class="waiting-count">等候: {{ window.waitingCount }}人</text>
|
|
</view>
|
|
<view class="window-current">
|
|
<text class="current-task" v-if="window.currentTask">
|
|
当前: {{ window.currentTask }}
|
|
</text>
|
|
<text class="no-task" v-else>暂无业务</text>
|
|
</view>
|
|
</view>
|
|
</view>
|
|
</view>
|
|
|
|
<!-- 自助设备 -->
|
|
<view class="window-group">
|
|
<view class="group-title">
|
|
<uni-icons type="settings" size="20" color="#52c41a"></uni-icons>
|
|
<text>自助办税设备</text>
|
|
</view>
|
|
<view class="devices-grid">
|
|
<view v-for="device in selfServiceDevices" :key="device.id" class="device-item"
|
|
:class="getDeviceStatusClass(device.status)">
|
|
<view class="device-icon">
|
|
<uni-icons :type="device.icon" size="30"
|
|
:color="getDeviceIconColor(device.status)"></uni-icons>
|
|
</view>
|
|
<view class="device-info">
|
|
<text class="device-name">{{ device.name }}</text>
|
|
<text class="device-status" :class="'status-' + device.status">
|
|
{{ getDeviceStatusText(device.status) }}
|
|
</text>
|
|
<text class="device-usage" v-if="device.status === 'busy'">
|
|
使用中
|
|
</text>
|
|
<text class="device-usage" v-else>
|
|
空闲
|
|
</text>
|
|
</view>
|
|
</view>
|
|
</view>
|
|
</view>
|
|
</view>
|
|
</view>
|
|
</view>
|
|
|
|
<!-- 窗口详情弹窗 -->
|
|
<uni-popup ref="windowPopup" type="center" background-color="#fff">
|
|
<view class="popup-content" v-if="selectedWindow">
|
|
<view class="popup-header">
|
|
<text class="popup-title">窗口详情 - {{ selectedWindow.number }}</text>
|
|
<uni-icons type="close" size="20" color="#999" @click="closeWindowPopup"></uni-icons>
|
|
</view>
|
|
|
|
<view class="popup-body">
|
|
<view class="detail-grid">
|
|
<view class="detail-item">
|
|
<text class="detail-label">窗口编号:</text>
|
|
<text class="detail-value">{{ selectedWindow.number }}</text>
|
|
</view>
|
|
<view class="detail-item">
|
|
<text class="detail-label">窗口类型:</text>
|
|
<text class="detail-value">{{ selectedWindow.type }}</text>
|
|
</view>
|
|
<view class="detail-item">
|
|
<text class="detail-label">当前状态:</text>
|
|
<view class="detail-value">
|
|
<view class="status-badge" :class="'status-' + selectedWindow.status">
|
|
{{ getWindowStatusText(selectedWindow.status) }}
|
|
</view>
|
|
</view>
|
|
</view>
|
|
<view class="detail-item">
|
|
<text class="detail-label">等候人数:</text>
|
|
<text class="detail-value">{{ selectedWindow.waitingCount }}人</text>
|
|
</view>
|
|
<view class="detail-item">
|
|
<text class="detail-label">当前业务:</text>
|
|
<text class="detail-value">{{ selectedWindow.currentTask || '无' }}</text>
|
|
</view>
|
|
<view class="detail-item">
|
|
<text class="detail-label">工作人员:</text>
|
|
<text class="detail-value">{{ selectedWindow.operator || '未分配' }}</text>
|
|
</view>
|
|
</view>
|
|
|
|
<view class="waiting-list" v-if="selectedWindow.waitingCount > 0">
|
|
<view class="list-title">等候队列</view>
|
|
<view class="list-items">
|
|
<view v-for="(person, index) in getWaitingList(selectedWindow.waitingCount)" :key="index"
|
|
class="list-item">
|
|
<text class="queue-number">第{{ index + 1 }}位</text>
|
|
<text class="estimate-time">预计等待: {{ (index + 1) * 5 }}分钟</text>
|
|
</view>
|
|
</view>
|
|
</view>
|
|
</view>
|
|
|
|
<view class="popup-footer">
|
|
<tn-button type="primary" @click="handleWindowAction(selectedWindow)">
|
|
<uni-icons type="gear" size="16" color="#fff"></uni-icons>
|
|
窗口操作
|
|
</tn-button>
|
|
</view>
|
|
</view>
|
|
</uni-popup>
|
|
</view>
|
|
</template>
|
|
|
|
<script setup lang="ts">
|
|
import { ref, reactive, computed, onMounted } from 'vue'
|
|
|
|
// 弹窗引用
|
|
const windowPopup = ref()
|
|
|
|
// 人工窗口数据
|
|
const manualWindows = ref([{
|
|
id: 1,
|
|
number: 'A001',
|
|
type: '综合业务',
|
|
status: 'busy',
|
|
waitingCount: 5,
|
|
currentTask: '增值税申报',
|
|
operator: '张三'
|
|
},
|
|
{
|
|
id: 2,
|
|
number: 'A002',
|
|
type: '发票办理',
|
|
status: 'free',
|
|
waitingCount: 2,
|
|
currentTask: '',
|
|
operator: '李四'
|
|
},
|
|
{
|
|
id: 3,
|
|
number: 'A003',
|
|
type: '税务登记',
|
|
status: 'busy',
|
|
waitingCount: 3,
|
|
currentTask: '新办企业登记',
|
|
operator: '王五'
|
|
},
|
|
{
|
|
id: 4,
|
|
number: 'A004',
|
|
type: '社保缴纳',
|
|
status: 'free',
|
|
waitingCount: 0,
|
|
currentTask: '',
|
|
operator: '赵六'
|
|
},
|
|
{
|
|
id: 5,
|
|
number: 'A005',
|
|
type: '个税业务',
|
|
status: 'pause',
|
|
waitingCount: 4,
|
|
currentTask: '个税汇算清缴',
|
|
operator: '钱七'
|
|
},
|
|
{
|
|
id: 6,
|
|
number: 'A006',
|
|
type: '咨询导税',
|
|
status: 'free',
|
|
waitingCount: 1,
|
|
currentTask: '',
|
|
operator: '孙八'
|
|
}
|
|
])
|
|
|
|
// 自助设备数据
|
|
const selfServiceDevices = ref([{
|
|
id: 1,
|
|
name: '自助发票申领机',
|
|
status: 'free',
|
|
icon: 'printer'
|
|
},
|
|
{
|
|
id: 2,
|
|
name: '自助代开发票机',
|
|
status: 'busy',
|
|
icon: 'document'
|
|
},
|
|
{
|
|
id: 3,
|
|
name: '自助查询终端',
|
|
status: 'free',
|
|
icon: 'search'
|
|
},
|
|
{
|
|
id: 4,
|
|
name: '自助申报设备',
|
|
status: 'maintenance',
|
|
icon: 'cloud-upload'
|
|
},
|
|
{
|
|
id: 5,
|
|
name: '自助打印终端',
|
|
status: 'free',
|
|
icon: 'paperplane'
|
|
},
|
|
{
|
|
id: 6,
|
|
name: '自助缴税设备',
|
|
status: 'busy',
|
|
icon: 'card'
|
|
}
|
|
])
|
|
|
|
// 计算属性
|
|
const freeWindowsCount = computed(() => {
|
|
return manualWindows.value.filter(window => window.status === 'free').length
|
|
})
|
|
|
|
const busyWindowsCount = computed(() => {
|
|
return manualWindows.value.filter(window => window.status === 'busy').length
|
|
})
|
|
|
|
// 计算属性
|
|
const editing = computed(() => {
|
|
return hallParams.value.some(param => param.editing)
|
|
})
|
|
|
|
// 选中的窗口
|
|
const selectedWindow = ref<any>(null)
|
|
|
|
// 窗口状态处理
|
|
const getWindowStatusClass = (status : string) => {
|
|
const classMap : any = {
|
|
free: 'window-free',
|
|
busy: 'window-busy',
|
|
pause: 'window-pause'
|
|
}
|
|
return classMap[status] || 'window-free'
|
|
}
|
|
|
|
const getWindowStatusText = (status : string) => {
|
|
const textMap : any = {
|
|
free: '空闲',
|
|
busy: '忙碌',
|
|
pause: '暂停'
|
|
}
|
|
return textMap[status] || '未知'
|
|
}
|
|
|
|
// 设备状态处理
|
|
const getDeviceStatusClass = (status : string) => {
|
|
const classMap : any = {
|
|
free: 'device-free',
|
|
busy: 'device-busy',
|
|
maintenance: 'device-maintenance'
|
|
}
|
|
return classMap[status] || 'device-free'
|
|
}
|
|
|
|
const getDeviceStatusText = (status : string) => {
|
|
const textMap : any = {
|
|
free: '空闲',
|
|
busy: '使用中',
|
|
maintenance: '维护中'
|
|
}
|
|
return textMap[status] || '未知'
|
|
}
|
|
|
|
const getDeviceIconColor = (status : string) => {
|
|
const colorMap : any = {
|
|
free: '#52c41a',
|
|
busy: '#fa541c',
|
|
maintenance: '#faad14'
|
|
}
|
|
return colorMap[status] || '#52c41a'
|
|
}
|
|
|
|
// 窗口点击处理
|
|
const handleWindowClick = (window : any) => {
|
|
selectedWindow.value = window
|
|
windowPopup.value.open()
|
|
}
|
|
|
|
const closeWindowPopup = () => {
|
|
windowPopup.value.close()
|
|
}
|
|
|
|
// 获取等候列表(模拟数据)
|
|
const getWaitingList = (count : number) => {
|
|
return Array.from({
|
|
length: count
|
|
}, (_, index) => ({
|
|
id: index + 1,
|
|
queueNumber: index + 1
|
|
}))
|
|
}
|
|
|
|
// 窗口操作
|
|
const handleWindowAction = (window : any) => {
|
|
uni.showActionSheet({
|
|
itemList: ['暂停服务', '重置窗口', '呼叫下一位', '查看统计'],
|
|
success: (res) => {
|
|
const actions = ['pause', 'reset', 'callNext', 'viewStats']
|
|
const action = actions[res.tapIndex]
|
|
// 执行相应操作
|
|
uni.showToast({
|
|
title: `执行操作: ${action}`,
|
|
icon: 'success'
|
|
})
|
|
}
|
|
})
|
|
}
|
|
|
|
const backToIndex = () => {
|
|
uni.navigateTo({
|
|
url: '/pages/index/index'
|
|
})
|
|
}
|
|
</script>
|
|
|
|
<style lang="scss" scoped>
|
|
.page-container {
|
|
min-height: 100vh;
|
|
background-color: #f5f7fa;
|
|
}
|
|
|
|
.top-div {
|
|
display: flex;
|
|
justify-content: space-between;
|
|
background-color: #fff;
|
|
padding: 6vh 20px 10px 20px;
|
|
}
|
|
|
|
.content {
|
|
padding: 20rpx;
|
|
}
|
|
|
|
// 通用卡片样式
|
|
.section-card {
|
|
background: #fff;
|
|
border-radius: 16rpx;
|
|
padding: 30rpx;
|
|
margin-bottom: 30rpx;
|
|
box-shadow: 0 4rpx 20rpx rgba(0, 0, 0, 0.05);
|
|
}
|
|
|
|
.section-header {
|
|
display: flex;
|
|
justify-content: space-between;
|
|
align-items: center;
|
|
margin-bottom: 30rpx;
|
|
|
|
.section-title {
|
|
font-size: 32rpx;
|
|
font-weight: bold;
|
|
color: #333;
|
|
}
|
|
|
|
.status-summary {
|
|
display: flex;
|
|
gap: 20rpx;
|
|
|
|
.summary-item {
|
|
font-size: 24rpx;
|
|
color: #666;
|
|
}
|
|
}
|
|
}
|
|
|
|
// 窗口组样式
|
|
.window-group {
|
|
margin-bottom: 40rpx;
|
|
|
|
.group-title {
|
|
display: flex;
|
|
align-items: center;
|
|
gap: 10rpx;
|
|
margin-bottom: 20rpx;
|
|
font-size: 28rpx;
|
|
font-weight: bold;
|
|
color: #333;
|
|
}
|
|
}
|
|
|
|
// 人工窗口网格
|
|
.windows-grid {
|
|
display: grid;
|
|
grid-template-columns: repeat(2, 1fr);
|
|
gap: 20rpx;
|
|
|
|
.window-item {
|
|
padding: 24rpx;
|
|
border-radius: 12rpx;
|
|
border: 2rpx solid #e8e8e8;
|
|
transition: all 0.3s;
|
|
|
|
&.window-free {
|
|
border-color: #52c41a;
|
|
background: #f6ffed;
|
|
}
|
|
|
|
&.window-busy {
|
|
border-color: #fa541c;
|
|
background: #fff2e8;
|
|
}
|
|
|
|
&.window-pause {
|
|
border-color: #faad14;
|
|
background: #fffbe6;
|
|
}
|
|
|
|
&:active {
|
|
transform: scale(0.98);
|
|
}
|
|
|
|
.window-header {
|
|
display: flex;
|
|
justify-content: space-between;
|
|
align-items: center;
|
|
margin-bottom: 15rpx;
|
|
|
|
.window-number {
|
|
font-size: 28rpx;
|
|
font-weight: bold;
|
|
color: #333;
|
|
}
|
|
|
|
.window-status {
|
|
padding: 6rpx 12rpx;
|
|
border-radius: 12rpx;
|
|
font-size: 20rpx;
|
|
font-weight: 500;
|
|
|
|
&.status-free {
|
|
background: #52c41a;
|
|
color: #fff;
|
|
}
|
|
|
|
&.status-busy {
|
|
background: #fa541c;
|
|
color: #fff;
|
|
}
|
|
|
|
&.status-pause {
|
|
background: #faad14;
|
|
color: #fff;
|
|
}
|
|
}
|
|
}
|
|
|
|
.window-info {
|
|
display: flex;
|
|
justify-content: space-between;
|
|
align-items: center;
|
|
margin-bottom: 10rpx;
|
|
|
|
.window-type {
|
|
font-size: 24rpx;
|
|
color: #666;
|
|
}
|
|
|
|
.waiting-count {
|
|
font-size: 22rpx;
|
|
color: #fa541c;
|
|
font-weight: 500;
|
|
}
|
|
}
|
|
|
|
.window-current {
|
|
.current-task {
|
|
font-size: 22rpx;
|
|
color: #333;
|
|
background: #f0f0f0;
|
|
padding: 4rpx 8rpx;
|
|
border-radius: 6rpx;
|
|
}
|
|
|
|
.no-task {
|
|
font-size: 22rpx;
|
|
color: #999;
|
|
font-style: italic;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// 自助设备网格
|
|
.devices-grid {
|
|
display: grid;
|
|
grid-template-columns: repeat(3, 1fr);
|
|
gap: 20rpx;
|
|
|
|
.device-item {
|
|
padding: 24rpx;
|
|
border-radius: 12rpx;
|
|
border: 2rpx solid #e8e8e8;
|
|
text-align: center;
|
|
|
|
&.device-free {
|
|
border-color: #52c41a;
|
|
background: #f6ffed;
|
|
}
|
|
|
|
&.device-busy {
|
|
border-color: #fa541c;
|
|
background: #fff2e8;
|
|
}
|
|
|
|
&.device-maintenance {
|
|
border-color: #faad14;
|
|
background: #fffbe6;
|
|
}
|
|
|
|
.device-icon {
|
|
margin-bottom: 15rpx;
|
|
}
|
|
|
|
.device-info {
|
|
.device-name {
|
|
display: block;
|
|
font-size: 24rpx;
|
|
color: #333;
|
|
margin-bottom: 8rpx;
|
|
}
|
|
|
|
.device-status {
|
|
display: block;
|
|
font-size: 20rpx;
|
|
margin-bottom: 5rpx;
|
|
|
|
&.status-free {
|
|
color: #52c41a;
|
|
}
|
|
|
|
&.status-busy {
|
|
color: #fa541c;
|
|
}
|
|
|
|
&.status-maintenance {
|
|
color: #faad14;
|
|
}
|
|
}
|
|
|
|
.device-usage {
|
|
font-size: 18rpx;
|
|
color: #999;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// 参数表格样式
|
|
.param-name {
|
|
font-size: 26rpx;
|
|
color: #333;
|
|
font-weight: 500;
|
|
}
|
|
|
|
.param-value {
|
|
display: flex;
|
|
align-items: center;
|
|
justify-content: center;
|
|
gap: 8rpx;
|
|
|
|
.param-unit {
|
|
font-size: 22rpx;
|
|
color: #999;
|
|
}
|
|
}
|
|
|
|
.param-edit {
|
|
display: flex;
|
|
justify-content: center;
|
|
}
|
|
|
|
.param-actions {
|
|
display: flex;
|
|
justify-content: center;
|
|
|
|
.edit-actions {
|
|
display: flex;
|
|
gap: 10rpx;
|
|
}
|
|
}
|
|
|
|
// 弹窗样式
|
|
.popup-content {
|
|
width: 650rpx;
|
|
border-radius: 20rpx;
|
|
overflow: hidden;
|
|
background: #fff;
|
|
}
|
|
|
|
.popup-header {
|
|
display: flex;
|
|
justify-content: space-between;
|
|
align-items: center;
|
|
padding: 30rpx;
|
|
border-bottom: 1rpx solid #f0f0f0;
|
|
|
|
.popup-title {
|
|
font-size: 28rpx;
|
|
font-weight: bold;
|
|
color: #333;
|
|
}
|
|
}
|
|
|
|
.popup-body {
|
|
padding: 30rpx;
|
|
max-height: 60vh;
|
|
overflow-y: auto;
|
|
}
|
|
|
|
.detail-grid {
|
|
.detail-item {
|
|
display: flex;
|
|
justify-content: space-between;
|
|
align-items: center;
|
|
padding: 15rpx 0;
|
|
border-bottom: 1rpx solid #f5f5f5;
|
|
|
|
.detail-label {
|
|
font-size: 26rpx;
|
|
color: #666;
|
|
}
|
|
|
|
.detail-value {
|
|
font-size: 26rpx;
|
|
color: #333;
|
|
font-weight: 500;
|
|
}
|
|
|
|
.status-badge {
|
|
padding: 6rpx 12rpx;
|
|
border-radius: 12rpx;
|
|
font-size: 20rpx;
|
|
color: #fff;
|
|
|
|
&.status-free {
|
|
background: #52c41a;
|
|
}
|
|
|
|
&.status-busy {
|
|
background: #fa541c;
|
|
}
|
|
|
|
&.status-pause {
|
|
background: #faad14;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
.waiting-list {
|
|
margin-top: 30rpx;
|
|
|
|
.list-title {
|
|
font-size: 26rpx;
|
|
font-weight: bold;
|
|
color: #333;
|
|
margin-bottom: 15rpx;
|
|
}
|
|
|
|
.list-items {
|
|
.list-item {
|
|
display: flex;
|
|
justify-content: space-between;
|
|
align-items: center;
|
|
padding: 12rpx 0;
|
|
border-bottom: 1rpx solid #f5f5f5;
|
|
|
|
.queue-number {
|
|
font-size: 24rpx;
|
|
color: #333;
|
|
}
|
|
|
|
.estimate-time {
|
|
font-size: 22rpx;
|
|
color: #999;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
.popup-footer {
|
|
padding: 30rpx;
|
|
border-top: 1rpx solid #f0f0f0;
|
|
text-align: center;
|
|
}
|
|
</style> |