buddhism/pages/personalCenter/myAppointment.vue

590 lines
14 KiB
Vue
Raw 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">
<base-background />
<!-- 使用自定义导航栏组件 -->
<custom-navbar ref="customNavbar" title="我的预约" />
<!-- 标签页导航 -->
<view class="tab-container">
<view
:class="{ active: activeTab === 'activity' }"
class="tab-item"
@click="switchTab('activity')"
>
<text class="tab-text">活动预约</text>
</view>
<view
:class="{ active: activeTab === 'meal' }"
class="tab-item"
@click="switchTab('meal')"
>
<text class="tab-text">斋饭预约</text>
</view>
</view>
<!-- 预约列表 -->
<view class="appointment-list">
<view
v-for="(item, index) in appointmentList"
:key="item.id"
class="appointment-card"
>
<!-- 活动标题和状态 -->
<view class="card-header">
<text class="activity-title">{{ item.title }}</text>
<view :class="['status-tag', getStatusClass(Number(item.state))]">
<text class="status-text"
>{{ getStatusText(Number(item.state)) }}
</text>
</view>
</view>
<!-- 活动详情 -->
<view class="card-details">
<view class="detail-item">
<text class="detail-label">活动时间:</text>
<text class="detail-value"
>{{ formatTime(item.slotStarteTime, item.slotEndTime) }}
</text>
</view>
<view class="detail-item">
<text class="detail-label">活动地点:</text>
<text class="detail-value">{{ item.address }}</text>
</view>
<view class="detail-item">
<text class="detail-label">参与人数:</text>
<text class="detail-value">{{ item.number }}人</text>
</view>
</view>
<!-- 操作按钮 -->
<view class="card-actions">
<!-- 只有待核销状态才显示出示码券按钮 -->
<view v-if="Number(item.state) === 1" class="action-btn"></view>
<view
v-if="Number(item.state) === 1"
class="action-btn secondary"
@click="showQRCode(item)"
>
<image :src="urls.qrCodeIcon" class="btn-icon" mode="aspectFit" />
<text class="btn-text">出示码券</text>
</view>
<!-- 只有待核销状态才显示取消预约按钮 -->
<view
v-if="Number(item.state) === 1"
class="action-btn secondary"
@click="cancelAppointment(item)"
>
<image
:src="urls.cancelAppointmentIcon"
class="btn-icon"
mode="aspectFit"
/>
<text class="btn-text">取消预约</text>
</view>
<!-- 已核销状态显示完成提示 -->
<!-- <view v-if="Number(item.state) === 2" class="action-btn completed">-->
<!-- <image-->
<!-- class="btn-icon"-->
<!-- mode="aspectFit"-->
<!-- src="https://api.ccttiot.com/image-1755237410755.png"-->
<!-- />-->
<!-- <text class="btn-text">已完成</text>-->
<!-- </view>-->
<!-- &lt;!&ndash; 取消预约状态显示已取消提示 &ndash;&gt;-->
<!-- <view v-if="Number(item.state) === 3" class="action-btn canceled">-->
<!-- <image-->
<!-- class="btn-icon"-->
<!-- mode="aspectFit"-->
<!-- src="https://api.ccttiot.com/image-1755237410755.png"-->
<!-- />-->
<!-- <text class="btn-text">已取消</text>-->
<!-- </view>-->
</view>
</view>
<!-- 空状态 -->
<view v-if="appointmentList.length === 0 && !loading" class="empty-state">
<image
class="empty-icon"
mode="aspectFit"
src="https://api.ccttiot.com/image-1755237410755.png"
/>
<text class="empty-text">暂无预约记录</text>
</view>
<!-- 加载状态 -->
<view v-if="loading" class="loading-state">
<text class="loading-text">加载中...</text>
</view>
</view>
<!-- 二维码弹窗 -->
<qrcode-modal
:ticket-number="currentTicketNumber"
:title="'核销验证码'"
:value="currentSubscribeId"
:visible="showQRCodeModal"
@close="closeQRCodeModal"
/>
</view>
</template>
<script>
import { CommonEnum } from "@/enum/common.js";
import {
cancelAppointment,
getAppointmentList,
} from "@/api/personalCenter/index.js";
import QRCodeModal from "@/components/qrcode-modal/qrcode-modal.vue";
import BaseBackground from "../../components/base-background/base-background.vue";
export default {
components: {
BaseBackground,
QRCodeModal,
},
data() {
return {
urls: {
qrCodeIcon: "https://api.ccttiot.com/image-1756260317570.png",
cancelAppointmentIcon:
"https://api.ccttiot.com/image-1756260524163.png",
},
CommonEnum,
// 当前激活的标签页
activeTab: "activity",
// 加载状态
loading: false,
// 预约列表数据
appointmentList: [],
// 分页参数
pageParams: {
pageNum: 1,
pageSize: 10,
total: 0,
},
// 二维码弹窗相关
showQRCodeModal: false,
currentSubscribeId: "",
currentTicketNumber: "",
};
},
onLoad() {
// 页面加载时获取预约列表
this.loadAppointmentList();
},
onPullDownRefresh() {
// 下拉刷新
this.refreshList();
},
onReachBottom() {
// 上拉加载更多
this.loadMore();
},
methods: {
// 切换标签页
switchTab(tab) {
if (this.activeTab === tab) return;
this.activeTab = tab;
this.pageParams.pageNum = 1;
this.appointmentList = [];
this.loadAppointmentList();
},
// 加载预约列表
async loadAppointmentList() {
if (this.loading) return;
this.loading = true;
try {
const params = {
pageNum: this.pageParams.pageNum,
pageSize: this.pageParams.pageSize,
type: this.activeTab === "activity" ? 1 : 2, // 1: 活动预约, 2: 斋饭预约
};
const response = await getAppointmentList(params);
if (response.code === 200) {
const { rows, total } = response;
this.appointmentList =
this.pageParams.pageNum === 1
? rows
: [...this.appointmentList, ...rows];
this.pageParams.total = total;
// 调试信息:打印预约数据状态
console.log("预约列表数据:", this.appointmentList);
this.appointmentList.forEach((item, index) => {
console.log(`预约 ${index + 1}:`, {
title: item.title,
state: item.state,
stateType: typeof item.state,
stateNumber: Number(item.state),
});
});
} else {
uni.showToast({
title: response.msg || "获取预约列表失败",
icon: "none",
});
}
} catch (error) {
console.error("获取预约列表失败:", error);
uni.showToast({
title: "网络错误,请重试",
icon: "none",
});
} finally {
this.loading = false;
uni.stopPullDownRefresh();
}
},
// 刷新列表
refreshList() {
this.pageParams.pageNum = 1;
this.loadAppointmentList();
},
// 加载更多
loadMore() {
if (this.appointmentList.length >= this.pageParams.total) {
return;
}
this.pageParams.pageNum++;
this.loadAppointmentList();
},
// 格式化时间
formatTime(startTime, endTime) {
if (!startTime || !endTime) return "时间待定";
const start = new Date(startTime);
const end = new Date(endTime);
const startStr = `${start.getFullYear()}-${String(start.getMonth() + 1).padStart(2, "0")}-${String(start.getDate()).padStart(2, "0")} ${String(start.getHours()).padStart(2, "0")}:${String(start.getMinutes()).padStart(2, "0")}`;
const endStr = `${String(end.getHours()).padStart(2, "0")}:${String(end.getMinutes()).padStart(2, "0")}`;
return `${startStr}-${endStr}`;
},
// 获取状态文本
getStatusText(state) {
const statusMap = {
1: "待核销",
2: "已核销",
3: "已取消",
};
return statusMap[state] || "未知状态";
},
// 获取状态样式类
getStatusClass(state) {
const classMap = {
1: "status-pending", // 待核销 - 橙色
2: "status-verified", // 已核销 - 绿色
3: "status-canceled", // 取消预约 - 灰色
};
return classMap[state] || "status-default";
},
// 显示核销验证码二维码
showQRCode(item) {
// 生成核销码值格式VERIFY:预约ID
const qrValue = `VERIFY:${item.subscribeId || item.id}`;
this.currentSubscribeId = qrValue;
this.currentTicketNumber = item.subscribeId || item.id;
this.showQRCodeModal = true;
},
// 关闭二维码弹窗
closeQRCodeModal() {
this.showQRCodeModal = false;
this.currentSubscribeId = "";
this.currentTicketNumber = "";
},
// 取消预约
cancelAppointment(item) {
uni.showModal({
title: "确认取消",
content: "确定要取消这个预约吗?",
success: async (res) => {
if (res.confirm) {
try {
const response = await cancelAppointment(
item.subscribeId || item.id,
);
if (response.code === 200) {
uni.showToast({
title: "预约已取消",
icon: "success",
});
// 刷新列表
this.refreshList();
} else {
uni.showToast({
title: response.msg || "取消失败",
icon: "none",
});
}
} catch (error) {
console.error("取消预约失败:", error);
uni.showToast({
title: "取消失败,请重试",
icon: "none",
});
}
}
},
});
},
},
};
</script>
<style lang="scss" scoped>
.page {
width: 100%;
display: flex;
align-items: center;
flex-direction: column;
min-height: 100vh;
}
// 标签页容器
.tab-container {
width: 750rpx;
margin-top: 24rpx;
display: flex;
background: #fff;
&.first-child {
border-right: 2px solid #c7a26d;
}
}
.tab-item {
flex: 1;
height: 116rpx;
display: flex;
align-items: center;
justify-content: center;
border-radius: 12rpx;
transition: all 0.3s ease;
.tab-text {
font-size: 32rpx;
color: #808080;
font-weight: 400;
}
&.active {
//background: #c7a26d;
.tab-text {
color: #c7a26d;
font-weight: 500;
height: 116rpx;
line-height: 116rpx;
border-bottom: 2px solid #c7a26d;
}
}
}
// 预约列表
.appointment-list {
width: 650rpx;
margin-top: 24rpx;
padding-bottom: 40rpx;
}
.appointment-card {
background: #fff;
border-radius: 20rpx;
padding: 40rpx;
margin-bottom: 24rpx;
border: 2rpx solid #e8e0d0;
box-shadow: 0 4rpx 12rpx rgba(0, 0, 0, 0.05);
}
// 卡片头部
.card-header {
display: flex;
align-items: center;
justify-content: space-between;
margin-bottom: 30rpx;
}
.activity-title {
flex: 1;
font-size: 36rpx;
font-weight: 600;
color: $text-font-color-1;
line-height: 1.4;
}
.status-tag {
padding: 8rpx 16rpx;
border-radius: 12rpx;
margin-left: 20rpx;
&.status-pending {
background: #fef3e0; // 待核销 - 橙色
}
}
.status-text {
font-size: 24rpx;
color: #c7a26d;
font-weight: 400;
}
// 卡片详情
.card-details {
margin-bottom: 40rpx;
}
.detail-item {
display: flex;
align-items: flex-start;
margin-bottom: 20rpx;
&:last-child {
padding-bottom: 24rpx;
margin-bottom: 0;
border-bottom: #d8d8d8 solid 1px;
}
}
.detail-label {
width: 160rpx;
font-size: 28rpx;
color: #695347;
font-weight: 400;
flex-shrink: 0;
}
.detail-value {
flex: 1;
font-size: 28rpx;
color: $text-font-color-1;
font-weight: 400;
line-height: 1.4;
text-align: right;
}
// 操作按钮
.card-actions {
display: flex;
gap: 20rpx;
justify-content: right;
}
.action-btn {
height: 70rpx;
width: 200rpx;
display: flex;
align-items: center;
justify-content: center;
border-radius: 16rpx;
transition: all 0.3s ease;
&.primary {
background: #c7a26d;
.btn-text {
color: #fff;
}
&:active {
background: #b8945a;
transform: scale(0.98);
}
}
&.secondary {
border: 2rpx solid #d8d8d8;
.btn-text {
color: #3d3d3d;
font-weight: 400;
}
&:active {
background: #f0e8d8;
transform: scale(0.98);
}
}
&.completed {
background: #00b894;
border: 2rpx solid #00b894;
.btn-text {
color: #fff;
}
}
&.canceled {
background: #95a5a6;
border: 2rpx solid #95a5a6;
.btn-text {
color: #fff;
}
}
}
.btn-icon {
width: 32rpx;
height: 32rpx;
margin-right: 12rpx;
}
.btn-text {
font-size: 28rpx;
font-weight: 500;
}
// 空状态
.empty-state {
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
padding: 120rpx 0;
}
.empty-icon {
width: 120rpx;
height: 120rpx;
margin-bottom: 30rpx;
opacity: 0.5;
}
.empty-text {
font-size: 28rpx;
color: #8b7355;
font-weight: 400;
}
// 加载状态
.loading-state {
display: flex;
align-items: center;
justify-content: center;
padding: 60rpx 0;
}
.loading-text {
font-size: 28rpx;
color: #8b7355;
font-weight: 400;
}
</style>