baodeng_xcx/page_shanghu/paidui/shhxlist.vue
2025-08-19 17:02:49 +08:00

620 lines
15 KiB
Vue
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<template>
<view class="page">
<u-navbar title="叫号控制" :border-bottom="false" :background="bgc" back-icon-color="#fff" title-color='#fff'
title-size='36' height='36' id="navbar">
</u-navbar>
<view class="content">
<!-- 头部区域 -->
<view class="header">
<view class="date-section" @click="show = true">
<view class="date">{{ currentDate }}</view>
<view class="calendar-icon"><image src="https://api.ccttiot.com/smartmeter/img/static/uIKD9zzjb0jlIWmHrtpz" mode=""></image> </view>
</view>
<view class="search-section">
<input
class="search-input"
placeholder="输入关键字查询"
v-model="searchKeyword"
placeholder-class="search-placeholder"
/>
<view class="search-icon"><image src="https://api.ccttiot.com/smartmeter/img/static/urVMNY8rV4QlGT923KEo" mode=""></image> </view>
</view>
</view>
<!-- 统计信息 -->
<view class="statistics">
<view class="stat-text">排队{{ tjobj.pending == undefined ? '--' : tjobj.pending}} 叫号{{ tjobj.calling == undefined ? '--' : tjobj.calling }} 过号{{ tjobj.passed == undefined ? '--' : tjobj.passed }} 核销{{ tjobj.writtenOff == undefined ? '--' : tjobj.writtenOff }} 全部{{ tjobj. totalCount == undefined ? '--' : tjobj. totalCount }}</view>
</view>
<!-- 导航标签 -->
<view class="nav-tabs">
<view
v-for="(tab, index) in tabs"
:key="index"
class="tab-item"
:class="{ active: activeTab === tab.key }"
@click="switchTab(tab.key)"
>
<view class="tab-text">{{ tab.name }}</view>
</view>
</view>
<!-- 队列列表 -->
<view class="queue-list">
<view v-if="loading" style="color:#808080;font-size:28rpx;padding:20rpx;">加载中...</view>
<view v-else-if="!queueList.length" style="color:#808080;font-size:28rpx;padding:20rpx;">
{{ errorMessage || '暂无数据' }}
</view>
<view
v-for="(item, index) in queueList"
:key="index"
class="queue-item"
>
<view class="item-main">
<view class="item-left">
<view class="item-id">{{ item.number }}</view>
<view class="item-time">取号时间: {{ item.createTime }}</view>
</view>
<view class="item-right">
<view class="user-name">{{ item.nickName }}</view>
<view class="status-badge" :class="'status-' + normalizeStatus(item.status)">
<view class="status-text">{{ getStatusText(item.status) }}</view>
</view>
</view>
</view>
<!-- 分隔线 -->
<view class="separator-line"></view>
<view class="item-actions">
<view class="action-buttons">
<view
v-for="(action, actionIndex) in getAvailableActions(item.status)"
:key="actionIndex"
class="action-btn"
:class="action.type"
@click="handleAction(action,item)"
>
{{ action.text }}
</view>
</view>
</view>
</view>
</view>
<u-picker mode="time" v-model="show" :params="params" @confirm="btntime"></u-picker>
</view>
</view>
</template>
<script>
export default {
data() {
return {
params: {
year: true,
month: true,
day: true,
hour: false,
minute: false,
second: false
},
show: false,
bgc: {
backgroundColor: "#000",
},
currentDate: '',
searchKeyword: '',
activeTab: 'all',
tabs: [
{ key: '1', name: '待叫号' },
{ key: '2', name: '叫号中' },
{ key: '4', name: '已过号' },
{ key: '3', name: '已核销' },
{ key: 'all', name: '全部' }
],
status:'',
queueList: [],
allList: [],
stats: { inQueue: 0, calling: 0, missed: 0, verified: 0, total: 0 },
loading: false,
errorMessage: '',
tjobj:{}
}
},
onLoad(option) {
},
onShow() {
const now = new Date();
// 自动补零的格式化函数
function formatDate(date, separator = '-', needPadZero = true) {
const year = date.getFullYear();
const month = needPadZero
? String(date.getMonth() + 1).padStart(2, '0')
: date.getMonth() + 1;
const day = needPadZero
? String(date.getDate()).padStart(2, '0')
: date.getDate();
return `${year}${separator}${month}${separator}${day}`;
}
// 使用示例
const dateStr1 = formatDate(now);
this.currentDate = dateStr1
this.getlist()
this.gettongji()
},
methods: {
btntime(e){
console.log(e);
this.currentDate = e.year + '-' + e.month + '-' + e.day
this.searchKeyword = ''
this.getlist()
this.gettongji()
},
// 获取所有统计
gettongji(){
this.$u.get(`/bst/queueUser/selectCount?storeId=${this.$store.state.storeId}&timeRange=${this.currentDate + ',' + this.currentDate}`).then(res =>{
if(res.code == 200){
this.tjobj = res.data
}
})
},
// 规范状态为统一的 key
normalizeStatus(status) {
if (status === 1 || status === '1') return 'inQueue'
if (status === 2 || status === '2') return 'calling'
if (status === 3 || status === '3') return 'verified'
if (status === 4 || status === '4') return 'missed'
if (status === 5 || status === '5') return 'cancelled'
return status
},
// 计算统计
updateStats(list) {
const counters = { inQueue: 0, calling: 0, missed: 0, verified: 0, total: 0 }
(list || []).forEach(item => {
const key = this.normalizeStatus(item.status)
if (Object.prototype.hasOwnProperty.call(counters, key)) counters[key]++
counters.total++
})
this.stats = counters
},
// 本地过滤(按标签与关键字)
getFilteredList(list) {
const keyword = (this.searchKeyword || '').trim()
return (list || []).filter(item => {
const matchTab = this.activeTab === 'all' || this.normalizeStatus(item.status) === this.normalizeStatus(this.activeTab)
const text = `${item.number || ''} ${item.nickName || ''}`
const matchKeyword = !keyword || text.indexOf(keyword) !== -1
return matchTab && matchKeyword
})
},
// 获取排队信息
getlist(){
this.loading = true
this.errorMessage = ''
const storeId = this.$store && this.$store.state ? this.$store.state.storeId : ''
if(!storeId){
uni.showToast({ title:'缺少门店信息', icon:'none' })
this.allList = []
this.queueList = []
this.updateStats([])
this.loading = false
return
}
const query = [`storeId=${storeId}`]
if (this.status) query.push(`status=${this.status}`)
this.$u.get(`/bst/queueUser/list?storeId=${storeId}&status=${this.status}&pageNum=1&pageSize=999&orderByColumn=createTime&isAsc=desc&timeRange=${this.currentDate + ',' + this.currentDate}`).then(res =>{
if(res.code == 200){
this.queueList = res.rows
this.allList = res.rows
}else{
this.allList = []
this.queueList = []
this.errorMessage = (res && res.msg) ? res.msg : '加载失败'
}
}).catch((e)=>{
this.allList = []
this.queueList = []
this.errorMessage = '网络错误'
}).finally(()=>{ this.loading = false })
},
refreshList(){ this.getlist() },
// 通用队列操作:尝试多个可能的后端端点
switchTab(tabKey) {
this.activeTab = tabKey
this.status = tabKey === 'all' ? '' : tabKey
// 切换标签时重新拉取数据,避免后端过滤和本地过滤不一致
this.searchKeyword = ''
this.getlist()
},
showDatePicker() {
// 兜底处理:先不弹日期选择,避免环境不支持时报错
uni.showToast({ title: '日期筛选暂未开通', icon: 'none' })
},
loadDataByDate(date) {
// 根据选择的日期加载数据
console.log('加载日期数据:', date);
// 这里可以调用API获取指定日期的数据
// 例如this.fetchQueueData(date);
},
getStatusText(status) {
const statusTexts = {
'inQueue': '待叫号',
'calling': '叫号中',
'missed': '已过号',
'verified': '已核销',
'cancelled': '已取消'
};
const key = this.normalizeStatus(status)
return statusTexts[key] || '';
},
getAvailableActions(status) {
const actionMap = {
'inQueue': [
{ text: '叫号', action: 'call', type: 'primary' },
{ text: '过号', action: 'miss', type: 'primary' },
{ text: '取消', action: 'cancel', type: 'secondary' },
{ text: '核销', action: 'verify', type: 'primary' }
],
'calling': [
{ text: '过号', action: 'miss', type: 'primary' },
{ text: '取消', action: 'cancel', type: 'secondary' },
{ text: '核销', action: 'verify', type: 'primary' }
],
'missed': [
{ text: '恢复叫号', action: 'restore', type: 'primary' },
{ text: '取消', action: 'cancel', type: 'secondary' }
],
'verified': [
// { text: '恢复排队', action: 'restore', type: 'primary' },
// { text: '取消', action: 'cancel', type: 'secondary' }
]
};
const key = this.normalizeStatus(status)
return actionMap[key] || [];
},
// 点击操作
handleAction(action,item) {
console.log(action,item);
let that = this
uni.showModal({
title: '提示',
content: `您确定要执行${action.text}操作吗?`,
success: (res) => {
if (res.confirm) {
if(action.text == '叫号' || action.text == '恢复叫号'){
let data = {
queueUserId:item.id
}
that.$u.post(`/bst/queueUser/calling`,data).then(res =>{
if(res.code == 200){
uni.showToast({ title:'叫号成功', icon:'success',duration:3000})
that.getlist()
}else{
uni.showToast({ title:res.msg, icon:'none',duration:3000})
}
})
}else if(action.text == '过号'){
let data = [item.id]
that.$u.post(`/bst/queueUser/pass`,data).then(res =>{
if(res.code == 200){
uni.showToast({ title:'过号成功', icon:'success',duration:3000})
that.getlist()
}else{
uni.showToast({ title:res.msg, icon:'none',duration:3000})
}
})
}else if(action.text == '取消'){
let data = [item.id]
that.$u.post(`/bst/queueUser/cancelQueue`,data).then(res =>{
if(res.code == 200){
uni.showToast({ title:'取消成功', icon:'success',duration:3000})
that.getlist()
}else{
uni.showToast({ title:res.msg, icon:'none',duration:3000})
}
})
}else if(action.text == '核销'){
that.$u.post(`/app/queueUser/generateCode/${item.id}`).then(res => {
if (res.code == 200) {
let data = {
id:item.id,
verificationCode:res.data.verificationCode
}
that.$u.post(`/bst/queueUser/verification`,data).then(res =>{
if(res.code == 200){
uni.showToast({ title:'核销成功', icon:'success',duration:3000})
that.getlist()
}else{
uni.showToast({ title:res.msg, icon:'none',duration:3000})
}
})
}else{
uni.showToast({ title:res.msg, icon:'success',duration:3000})
}
})
}
}
}
})
},
},
watch:{
searchKeyword(){
this.queueList = this.getFilteredList(this.allList)
}
}
}
</script>
<style lang="scss">
page {
background: #0c0c0c;
}
.page {
min-height: 100vh;
background: #0c0c0c;
}
.content {
padding: 30rpx;
}
.header {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 40rpx;
}
.date-section {
display: flex;
align-items: center;
gap: 8rpx;
cursor: pointer;
padding: 10rpx;
border-radius: 8rpx;
transition: background-color 0.3s;
&:active {
background-color: rgba(255, 255, 255, 0.1);
}
.date {
color: #fff;
font-size: 36rpx;
font-weight: 500;
}
.calendar-icon {
image{
width: 20rpx;
height: 20rpx;
}
}
}
.search-section {
position: relative;
flex: 1;
margin-left: 40rpx;
.search-input {
width: 300rpx;
height: 80rpx;
background: #1a1a1a;
border: 1px solid #333;
border-radius: 10rpx;
padding: 0 70rpx 0 35rpx;
color: #fff;
font-size: 30rpx;
}
.search-placeholder {
color: #999;
font-size: 30rpx;
}
.search-icon {
position: absolute;
right: 64rpx;
top: 50%;
transform: translateY(-50%);
font-size: 28rpx;
color: #fff;
image{
width: 26rpx;
height: 26rpx;
}
}
}
.refresh-btn{
margin-left: 20rpx;
padding: 12rpx 20rpx;
border-radius: 10rpx;
font-size: 24rpx;
background: #FF8998;
color: #fff;
border: 1px solid #FF8998;
}
.statistics {
padding: 20rpx 0;
.stat-text {
color: #808080;
font-size: 30rpx;
line-height: 1.5;
}
}
.nav-tabs {
display: flex;
margin-bottom: 40rpx;
border-bottom: 1px solid #333;
.tab-item {
flex: 1;
text-align: center;
padding: 25rpx 0;
position: relative;
.tab-text {
color: #fff;
font-size: 30rpx;
}
&.active {
.tab-text {
color: #fff;
}
&::after {
content: '';
position: absolute;
bottom: -1px;
left: 50%;
transform: translateX(-50%);
width: 80rpx;
height: 6rpx;
background: #FF8998;
border-radius: 3rpx;
}
}
}
}
.queue-list {
.queue-item {
background: #1a1a1a;
border: 1px solid #333;
border-radius: 20rpx;
padding: 30rpx;
margin-bottom: 20rpx;
.item-main {
display: flex;
justify-content: space-between;
align-items: flex-start;
margin-bottom: 20rpx;
.item-left {
display: flex;
flex-direction: column;
gap: 12rpx;
.item-id {
color: #fff;
font-size: 64rpx;
font-weight: bold;
line-height: 1;
}
.item-time {
color: #999;
font-size: 24rpx;
}
}
.item-right {
display: flex;
flex-direction: column;
align-items: flex-end;
gap: 12rpx;
.user-name {
color: #fff;
font-size: 28rpx;
}
.status-badge {
padding: 6rpx 16rpx;
border-radius: 10rpx;
font-size: 22rpx;
&.status-inQueue {
background: #8883F0;
color: #fff;
}
&.status-calling {
background: #FF8998;
color: #fff;
}
&.status-missed {
background: #fff;
color: #000;
}
&.status-verified {
background: #FF8998;
color: #fff;
}
&.status-cancelled {
background: #666;
color: #fff;
}
}
}
}
.separator-line {
height: 1px;
background: #333;
margin: 20rpx 0;
}
.item-actions {
display: flex;
justify-content: space-between;
align-items: center;
.action-buttons {
display: flex;
gap: 15rpx;
.action-btn {
padding: 12rpx 20rpx;
border-radius: 10rpx;
font-size: 24rpx;
border: none;
min-width: 100rpx;
cursor: pointer;
text-align: center;
&.primary {
background: #FF8998;
color: #fff;
}
&.secondary {
background: transparent;
color: #FF8998;
border: 1px solid #FF8998;
}
}
}
.details-btn {
padding: 12rpx 20rpx;
border-radius: 20rpx;
font-size: 24rpx;
background: #fff;
color: #000;
border: 1px solid #FF8998;
min-width: 100rpx;
cursor: pointer;
text-align: center;
}
}
}
}
</style>