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.

490 lines
10 KiB
Vue

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

<!-- pages/queue/business-select.vue -->
<template>
<view class="business-container">
<!-- 头部信息 -->
<!-- <view class="user-info">
<text class="info-title">您好{{ userName }}</text>
<text class="info-subtitle">请选择需要办理的业务类型</text> -->
<view class="top-div">
<view></view>
<tn-button width="80px" height="32px" :plain="true" text-color="#0099ff" @click="goBack">
<uni-icons type="arrow-left" size="18" color="#0099ff" style="margin-right: 5px;"></uni-icons>
</tn-button>
</view>
<!-- </view> -->
<!-- 业务列表 -->
<scroll-view class="business-list" scroll-y>
<view class="business-grid">
<view v-for="(item, index) in businessList" :key="index" class="business-item"
:class="{ 'selected': selectedBusiness === item.value }" @click="selectBusiness(item)">
<view class="business-icon">{{ item.icon }}</view>
<view class="business-content">
<text class="business-name">{{ item.name }}</text>
<text class="business-desc">{{ item.description }}</text>
</view>
<view class="business-check" v-if="selectedBusiness === item.value">
<text>✓</text>
</view>
</view>
</view>
</scroll-view>
<!-- 操作按钮 -->
<view class="action-buttons">
<button class="btn-back" @click="goBack">返回修改</button>
<button class="btn-confirm" :disabled="!selectedBusiness" @click="handleConfirm">
确认取号
</button>
</view>
<!-- 取号结果弹窗 -->
<uni-popup ref="resultPopup" type="center" :is-mask-click="false">
<view class="result-popup">
<view class="result-header">
<view class="result-close" @click="closeResultPopup">×</view>
<text class="result-icon">✅</text>
<text class="result-title">取号成功</text>
</view>
<view class="result-content">
<view class="ticket-info">
<text class="ticket-label">您的排队号码</text>
<text class="ticket-number">{{ ticketInfo.ticketNumber }}</text>
</view>
<view class="ticket-details">
<view class="detail-item">
<text class="detail-label">业务类型</text>
<text class="detail-value">{{ ticketInfo.businessName }}</text>
</view>
<view class="detail-item">
<text class="detail-label">取号时间</text>
<text class="detail-value">{{ ticketInfo.tktDate + ' ' + ticketInfo.tktTime }}</text>
</view>
<view class="detail-item">
<text class="detail-label">前方等候</text>
<text class="detail-value">{{ ticketInfo.waitingCount }}位</text>
</view>
<!-- <view class="detail-item">
<text class="detail-label">预计等候</text>
<text class="detail-value">{{ ticketInfo.waitingTime }}分钟</text>
</view> -->
<!-- <view class="detail-item">
<text class="detail-label">办理窗口:</text>
<text class="detail-value">{{ ticketInfo.serviceWindow }}</text>
</view> -->
</view>
</view>
<view class="result-actions">
<!-- <button class="btn-print" @click="handlePrint">打印小票</button>
<button class="btn-notify" @click="handleNotify"></button> -->
</view>
</view>
</uni-popup>
</view>
</template>
<script setup>
import { ref } from 'vue';
import { onLoad, onReady } from '@dcloudio/uni-app'
import { getBizList } from '@/api/index.js'
import { takeTicket } from '@/api/ticket.js'
const userName = ref('');
const selectedBusiness = ref('');
const businessList = ref([]);
const ticketInfo = ref({});
const resultPopup = ref(null);
// 通过接口获取业务列表
const loadBusinessList = async () => {
try {
const res = await getBizList()
console.log(res)
// 兼容后端多种字段命名,并为前端展示结构做一次映射
const list = Array.isArray(res) ? res : []
businessList.value = list.map((item, index) => ({
icon: item.icon || '🧾',
name: item.name || item.bizName || '',
value: item.value || item.uid || item.id || String(index),
prefix: item.prefix || item.remark || ''
}))
} catch (error) {
console.error('获取业务列表失败:', error)
uni.showToast({
title: '获取业务列表失败',
icon: 'none'
})
// 如果接口失败,保留空列表或按需补充本地兜底数据
businessList.value = []
}
}
// 获取页面参数
onLoad((options) => {
userName.value = decodeURIComponent(options.name || '');
loadBusinessList()
});
// 选择业务
const selectBusiness = (item) => {
selectedBusiness.value = item.value;
};
// 返回修改
const goBack = () => {
uni.navigateBack();
};
// 确认取号
const handleConfirm = async () => {
try {
// 调用取号接口
const result = await takeTicket({
bizUid: selectedBusiness.value,
idCard: '412827199805026017',
rankUserName:'尹朋虎',
rankUserPhone:'15382312786'
});
// 根据 tktId 是否存在且非空判断是否取号成功
if (result && result.tktId && String(result.tktId).trim()) {
const currentBiz = businessList.value.find(b => b.value === selectedBusiness.value)
ticketInfo.value = {
ticketNumber: result.tktId,
businessName: result.bizName || currentBiz?.name || '',
waitingTime: result.estimatedWaitMinutes ?? result.waitingCount ?? 0,
serviceWindow: result.windowNames || '请等候叫号',
waitingCount: result.waitingCount,
tktDate:result.tktDate,
tktTime:result.tktTime
};
// 显示取号结果
resultPopup.value.open();
} else {
uni.showToast({
title: '取号失败',
icon: 'none'
});
}
console.log(result)
} catch (error) {
uni.showToast({
title: error.message || '业务获取失败',
icon: 'none'
});
}
};
// 打印小票
const handlePrint = async () => {
uni.showLoading({
title: '正在取号...'
});
try {
const result = await takeTicket({
businessType: selectedBusiness.value
});
// request 封装已处理业务码,能进入这里通常即成功
if (result) {
uni.showToast({
title: '取号成功',
icon: 'success'
});
}
} catch (error) {
uni.showToast({
title: error.message || '取号失败',
icon: 'none'
});
} finally {
uni.hideLoading();
}
};
// 推送消息
const handleNotify = async () => {
uni.showLoading({
title: '正在推送...'
});
try {
const result = await sendNotification(ticketInfo.value);
if (result.code === 200) {
uni.showToast({
title: '消息已推送到手机',
icon: 'success'
});
}
} catch (error) {
uni.showToast({
title: error.message || '推送失败',
icon: 'none'
});
} finally {
uni.hideLoading();
}
};
// 关闭结果弹窗
const closeResultPopup = () => {
resultPopup.value?.close();
};
</script>
<style scoped>
.business-container {
padding: 40rpx 32rpx;
min-height: 100vh;
background: linear-gradient(135deg, #f5f7fa 0%, #c3cfe2 100%);
}
.user-info {
text-align: center;
margin-bottom: 40rpx;
display: flex;
}
.top-div {
display: flex;
justify-content: space-between;
background-color: #fff;
padding: 6vh 20px 10px 20px;
position: fixed;
top: 0;
left: 0;
right: 0;
z-index: 100;
box-sizing: border-box;
}
.info-title {
font-size: 36rpx;
font-weight: bold;
color: #1a1a1a;
display: block;
margin-bottom: 12rpx;
}
.info-subtitle {
font-size: 26rpx;
color: #666;
display: block;
}
.business-list {
height: 80vh;
margin-bottom: 40rpx;
}
.business-grid {
display: grid;
grid-template-columns: repeat(2, minmax(0, 1fr));
gap: 20rpx;
}
.business-item {
background: white;
border-radius: 16rpx;
padding: 32rpx;
margin-bottom: 20rpx;
display: flex;
align-items: center;
border: 2rpx solid #e0e0e0;
transition: all 0.3s ease;
}
.business-item.selected {
border-color: #007AFF;
background: #f0f7ff;
box-shadow: 0 4rpx 20rpx rgba(0, 122, 255, 0.15);
}
.business-icon {
font-size: 48rpx;
margin-right: 24rpx;
}
.business-content {
flex: 1;
}
.business-name {
font-size: 30rpx;
font-weight: 600;
color: #333;
display: block;
margin-bottom: 8rpx;
}
.business-desc {
font-size: 24rpx;
color: #666;
display: block;
}
.business-check {
width: 40rpx;
height: 40rpx;
background: #007AFF;
border-radius: 50%;
display: flex;
align-items: center;
justify-content: center;
color: white;
font-size: 24rpx;
}
.action-buttons {
display: flex;
gap: 20rpx;
}
.action-buttons button {
flex: 1;
height: 88rpx;
border-radius: 16rpx;
font-size: 30rpx;
font-weight: 500;
border: none;
}
.btn-back {
background: #f0f0f0;
color: #666;
}
.btn-confirm {
background: linear-gradient(135deg, #007AFF 0%, #0056CC 100%);
color: white;
}
.btn-confirm:disabled {
background: #cccccc;
opacity: 0.6;
}
/* 取号结果弹窗 */
.result-popup {
width: 650rpx;
max-width: 90vw;
background: white;
border-radius: 24rpx;
overflow: hidden;
}
.result-header {
position: relative;
padding: 40rpx 40rpx 20rpx;
text-align: center;
background: linear-gradient(135deg, #4CAF50 0%, #2E7D32 100%);
color: white;
}
.result-close {
position: absolute;
right: 24rpx;
top: 20rpx;
width: 48rpx;
height: 48rpx;
border-radius: 50%;
line-height: 48rpx;
text-align: center;
font-size: 40rpx;
color: #fff;
background: rgba(255, 255, 255, 0.2);
}
.result-icon {
font-size: 80rpx;
display: block;
margin-bottom: 16rpx;
}
.result-title {
font-size: 36rpx;
font-weight: bold;
display: block;
}
.result-content {
padding: 40rpx;
}
.ticket-info {
text-align: center;
margin-bottom: 40rpx;
padding-bottom: 40rpx;
border-bottom: 2rpx dashed #e0e0e0;
}
.ticket-label {
font-size: 28rpx;
color: #666;
display: block;
margin-bottom: 16rpx;
}
.ticket-number {
font-size: 72rpx;
font-weight: bold;
color: #007AFF;
display: block;
letter-spacing: 4rpx;
}
.ticket-details {
display: flex;
flex-direction: column;
gap: 20rpx;
}
.detail-item {
display: flex;
justify-content: space-between;
align-items: center;
}
.detail-label {
font-size: 28rpx;
color: #666;
}
.detail-value {
font-size: 28rpx;
font-weight: 600;
color: #333;
}
.result-actions {
display: flex;
gap: 20rpx;
padding: 0 40rpx 40rpx;
}
.result-actions button {
flex: 1;
height: 80rpx;
border-radius: 12rpx;
font-size: 28rpx;
font-weight: 500;
border: none;
}
.btn-print {
background: #4CAF50;
color: white;
}
.btn-notify {
background: #2196F3;
color: white;
}
</style>