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

<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>