<template>
|
<view class="page-wrapper">
|
<div class="bg-top"></div>
|
|
<div class="content-wrapper">
|
<view class="p-4 w-95-percent">
|
<uni-segmented-control :current="currentTab" :values="tabs" @clickItem="onTabChange" style-type="button"
|
active-color="#3b82f6" class="mb-4" />
|
</view>
|
<!-- 加载提示 -->
|
<view v-if="loading" class="text-center text-gray-500 py-8">
|
加载中...
|
</view>
|
<!-- 预约信息卡片列表 -->
|
<scroll-view scroll-y class="h-calc-100vh-120px w-95-percent">
|
<view v-for="(item, index) in filteredsitebooking" :key="index"
|
class="bg-white rounded-lg shadow p-4 mb-4 relative">
|
<!-- 取消按钮(仅 已预约 状态显示) -->
|
<view v-if="item.status === '已预约'" class="absolute top-2 right-2">
|
<button class="bg-red-500 text-white text-sm px-2 py-1 rounded"
|
@click="cancelReservation(item)">
|
取消预约
|
</button>
|
|
</view>
|
<!-- 卡片内容 -->
|
<view class="space-y-2">
|
<view class="flex justify-between">
|
<text class="font-bold text-lg">{{ item.sitename }}</text>
|
<text v-if="item.status != '已预约'" class="text-sm text-gray-500">{{ item.status }}</text>
|
</view>
|
<view class="text-sm space-y-2">
|
<text class="text-gray-600">预约日期:</text>
|
<text>{{ item.visitdate }}</text>
|
</view>
|
<view class="text-sm space-y-2">
|
<text class="text-gray-600">时段:</text>
|
<text>{{ item.visittimeam && item.visittimeam !== '' ? '上午(09:00-12:00)' : '下午(13:00-17:00)' }}</text>
|
</view>
|
<view class="text-sm space-y-2">
|
<text class="text-gray-600">预约人:</text>
|
<text>{{ item.contactperson }}</text>
|
</view>
|
<view class="text-sm space-y-2">
|
<text class="text-gray-600">电话:</text>
|
<text>{{ item.contactphone }}</text>
|
</view>
|
<view class="text-sm space-y-2">
|
<text class="text-gray-600">同行人数:</text>
|
<text>{{ item.personcount }} 人</text>
|
</view>
|
<view v-if="item.status === '已使用'" class="text-sm space-y-2">
|
<text class="text-gray-600">使用时间:</text>
|
<text>{{ item.usedtime }}</text>
|
</view>
|
<view v-if="item.status === '已取消'" class="text-sm space-y-2">
|
<text class="text-gray-600">取消时间:</text>
|
<text>{{ item.canceltime }}</text>
|
</view>
|
<button v-if="item.status === '已预约'"
|
class="bg-blue-500 text-white text-base px-2 py-1 rounded space-y-2"
|
@click="verifyReservation(item)">
|
现场核销
|
</button>
|
</view>
|
</view>
|
<!-- 无数据提示 -->
|
<view v-if="!filteredsitebooking.length" class="text-center text-gray-500 py-8">
|
暂无{{ tabs[currentTab] }}的预约信息
|
</view>
|
</scroll-view>
|
</div>
|
<div class="bg-bottom"></div>
|
</view>
|
</template>
|
|
<script>
|
import {
|
ref,
|
computed,
|
onMounted
|
} from 'vue'
|
import {
|
apiService
|
} from '@/apiservice/apiService'
|
import {
|
useUserStore
|
} from '@/stores/user'
|
|
const userStore = useUserStore()
|
|
export default {
|
setup() {
|
// Tab 数据
|
const tabs = ['已预约', '已使用', '已取消', '已过期'];
|
const currentTab = ref(0);
|
let sitebooking = ref([]);
|
const loading = ref(false);
|
|
// 获取预约列表
|
const fetchsitebooking = async () => {
|
loading.value = true;
|
try {
|
const res = await apiService.bookingListByUuid(userStore.uuid)
|
if (res.code === 1000) {
|
sitebooking.value = res.data
|
|
} else {
|
console.warn('查询失败:', res.message)
|
}
|
|
} catch (error) {
|
console.error('获取预约信息错误:', error);
|
uni.showToast({
|
title: '网络错误',
|
icon: 'none'
|
});
|
} finally {
|
loading.value = false;
|
}
|
};
|
|
// 根据当前 Tab 过滤数据
|
const filteredsitebooking = computed(() => {
|
return sitebooking.value.filter(item => item.status === tabs[currentTab.value]);
|
});
|
|
const getNowString = () => {
|
const now = new Date();
|
const pad = (n) => n.toString().padStart(2, '0');
|
return `${now.getFullYear()}-${pad(now.getMonth() + 1)}-${pad(now.getDate())} ` +
|
`${pad(now.getHours())}:${pad(now.getMinutes())}:${pad(now.getSeconds())}`;
|
};
|
|
// Tab 切换
|
const onTabChange = (e) => {
|
currentTab.value = e.currentIndex;
|
};
|
|
// 取消预约
|
const cancelReservation = async (item) => {
|
uni.showModal({
|
title: '提示',
|
content: '确定取消预约吗?',
|
success: async (res) => {
|
if (res.confirm) {
|
try {
|
console.log(item);
|
|
const cancelRes = await apiService.bookingCancel({
|
id: item.id,
|
canceltime: getNowString(),
|
status: '已取消'
|
});
|
|
console.log(cancelRes);
|
|
if (cancelRes.code === 1000) {
|
uni.showToast({
|
title: '预约已取消',
|
icon: 'success'
|
});
|
// 刷新数据
|
await fetchsitebooking();
|
} else {
|
uni.showToast({
|
title: cancelRes.message || '取消失败',
|
icon: 'none'
|
});
|
}
|
} catch (error) {
|
console.error('取消预约错误:', error);
|
uni.showToast({
|
title: '取消失败,请重试',
|
icon: 'none'
|
});
|
}
|
}
|
}
|
});
|
};
|
// 现场核销
|
const verifyReservation = async (item) => {
|
try {
|
console.log(item)
|
const scanRes = await new Promise((resolve, reject) => {
|
uni.scanCode({
|
scanType: ['qrCode'],
|
success: resolve,
|
fail: reject
|
});
|
});
|
|
console.log(scanRes.result)
|
const match = scanRes.result.match(/farm\.evcc\.top.*?sitename=([^&]+)/);
|
|
if (!match) {
|
uni.showToast({
|
title: '二维码无效,请扫描正确的核销码',
|
icon: 'none'
|
});
|
return;
|
}
|
|
const qrSitename = decodeURIComponent(match[1]);
|
|
// 2️⃣ 获取当前时间
|
const now = new Date();
|
const todayStr = now.toISOString().split('T')[0]; // yyyy-mm-dd
|
const currentHour = now.getHours();
|
const isAm = !!item.visittimeam && item.visittimeam !== '';
|
|
// 3️⃣ 按顺序验证所有条件
|
let message = '';
|
|
if (qrSitename !== item.sitename) {
|
message = `二维码场馆不匹配(扫码场馆:${qrSitename})`;
|
} else if (item.visitdate !== todayStr) {
|
message = `未到预约日期(预约日期:${item.visitdate})`;
|
} else if (isAm && currentHour >= 12) {
|
message = '上午时段已过,无法核销';
|
} else if (!isAm && currentHour < 12) {
|
message = '还未到下午时段,请稍后核销';
|
}
|
|
// 如果有 message,直接提示客户,不做任何请求
|
if (message) {
|
uni.showModal({
|
title: '无法核销',
|
content: message,
|
showCancel: false,
|
confirmText: '我知道了'
|
});
|
return;
|
}
|
const verifyRes = await apiService.bookingUse({
|
id: item.id,
|
usedtime: getNowString(),
|
status: '已使用'
|
})
|
|
if (verifyRes.code === 1000) {
|
uni.showModal({
|
title: '核销成功',
|
content: '预约已核销,请工作人员确认',
|
showCancel: false,
|
confirmText: '确定',
|
success: async () => {
|
await fetchsitebooking(); // 刷新数据
|
}
|
});
|
} else {
|
uni.showToast({
|
title: verifyRes.message || '核销失败',
|
icon: 'none'
|
});
|
}
|
} catch (error) {
|
console.error('核销错误:', error);
|
uni.showToast({
|
title: '核销失败,请重试',
|
icon: 'none'
|
});
|
}
|
};
|
|
// 页面加载时获取数据
|
onMounted(() => {
|
fetchsitebooking();
|
});
|
|
return {
|
tabs,
|
currentTab,
|
sitebooking,
|
filteredsitebooking,
|
loading,
|
onTabChange,
|
cancelReservation,
|
verifyReservation
|
};
|
}
|
};
|
</script>
|
|
<style>
|
.min-h-screen {
|
min-height: 100vh;
|
}
|
|
.bg-gray-100 {
|
background-color: #f3f4f6;
|
}
|
|
.ml-5 {
|
left: 10rpx;
|
}
|
|
.w-95-percent {
|
width: 95%;
|
}
|
|
.mr-5 {
|
right: 10rpx;
|
}
|
|
.mb-4 {
|
margin-bottom: 1rem;
|
}
|
|
.text-center {
|
text-align: center;
|
}
|
|
.text-gray-500 {
|
color: #6b7280;
|
}
|
|
.text-gray-600 {
|
color: #4b5563;
|
}
|
|
.py-8 {
|
padding-top: 2rem;
|
/* 32px */
|
padding-bottom: 2rem;
|
}
|
|
.h-calc-100vh-120px {
|
height: calc(100vh - 120px);
|
}
|
|
.px-4 {
|
padding-left: 1rem;
|
padding-right: 1rem;
|
}
|
|
.bg-white {
|
background-color: #ffffff;
|
}
|
|
.rounded-lg {
|
border-radius: 0.5rem;
|
/* 8px */
|
}
|
|
.shadow {
|
box-shadow: 0 1px 3px rgba(0, 0, 0, 0.1), 0 1px 2px rgba(0, 0, 0, 0.06);
|
}
|
|
.p-4 {
|
padding: 1rem;
|
}
|
|
.mb-4 {
|
margin-bottom: 1rem;
|
}
|
|
.mt-10 {
|
margin-top: 2rem;
|
}
|
|
.relative {
|
position: relative;
|
}
|
|
.absolute {
|
position: absolute;
|
}
|
|
.top-2 {
|
top: 0.5rem;
|
/* 8px */
|
}
|
|
.right-2 {
|
right: 0.5rem;
|
/* 8px */
|
}
|
|
.bg-red-500 {
|
background-color: #ef4444;
|
}
|
|
.bg-blue-500 {
|
background-color: #3b82f6;
|
}
|
|
.text-white {
|
color: #ffffff;
|
}
|
|
.text-sm {
|
font-size: 0.875rem;
|
/* 14px */
|
line-height: 1.25rem;
|
/* 20px */
|
}
|
|
.text-base {
|
font-size: 1rem;
|
/* 14px */
|
line-height: 1.5rem;
|
/* 20px */
|
}
|
|
.px-2 {
|
padding-left: 0.5rem;
|
padding-right: 0.5rem;
|
}
|
|
.py-1 {
|
padding-top: 0.25rem;
|
padding-bottom: 0.25rem;
|
}
|
|
.rounded {
|
border-radius: 0.25rem;
|
/* 4px */
|
}
|
|
.space-y-2 {
|
margin-top: 0.5rem;
|
/* 8px */
|
}
|
|
.flex {
|
display: flex;
|
}
|
|
.justify-between {
|
justify-content: space-between;
|
}
|
|
.font-bold {
|
font-weight: 700;
|
}
|
|
.text-lg {
|
font-size: 1.125rem;
|
/* 18px */
|
line-height: 1.75rem;
|
/* 28px */
|
}
|
|
.page-wrapper {
|
position: relative;
|
min-height: 100vh;
|
/* 确保小内容时填满视口 */
|
overflow: hidden;
|
}
|
|
/* 顶部背景 */
|
.bg-top {
|
position: absolute;
|
top: 0;
|
left: 0;
|
width: 100%;
|
height: 500rpx;
|
/* 可用固定像素或图片高度 */
|
background: url('https://farm.evcc.top/Uploads/yuanboyuan/background/bg_top.jpg') no-repeat top center;
|
background-size: cover;
|
z-index: 0;
|
}
|
|
/* 底部背景 */
|
.bg-bottom {
|
position: absolute;
|
bottom: 0;
|
left: 0;
|
width: 100%;
|
height: 500rpx;
|
/* 可用固定像素或图片高度 */
|
background: url('https://farm.evcc.top/Uploads/yuanboyuan/background/bg_bottom.jpg') no-repeat bottom center;
|
background-size: cover;
|
z-index: 0;
|
}
|
|
/* 内容层 */
|
.content-wrapper {
|
position: relative;
|
z-index: 1;
|
padding: 2rem 1rem;
|
}
|
</style>
|