657 lines
18 KiB
Vue
657 lines
18 KiB
Vue
<template>
|
||
<div class="event-detail-page">
|
||
<!-- 活动基本信息 -->
|
||
<div class="event-header">
|
||
<div class="event-status" v-if="activityStatusTag" :class="activityStatusTag.cssClass">
|
||
{{ activityStatusTag.text }}
|
||
</div>
|
||
<div class="event-title">{{ eventDetail.ACTIVITY_NAME }}</div>
|
||
|
||
<div class="event-meta">
|
||
<div class="meta-item">
|
||
<span class="meta-icon">📅</span>
|
||
<span class="meta-text">{{ eventDetail.ACTIVITY_STARTDATE ? eventDetail.ACTIVITY_STARTDATE : ""
|
||
}}{{ eventDetail.ACTIVITY_ENDDATE ? `-` + eventDetail.ACTIVITY_ENDDATE : "" }}</span>
|
||
</div>
|
||
<div class="meta-item">
|
||
<span class="meta-icon">📍</span>
|
||
<span class="meta-text">{{ eventDetail.ACTIVITY_LOCATION }}</span>
|
||
</div>
|
||
<div class="meta-item">
|
||
<span class="meta-icon">👥</span>
|
||
<span class="meta-text">
|
||
{{ eventDetail.MAXIMUM_CAPACITY ? eventDetail.MAXIMUM_CAPACITY : '不限' }}人
|
||
</span>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- 活动封面 -->
|
||
<div class="event-cover">
|
||
<swiper>
|
||
<swiper-item v-for="(item, imgIndex) in eventDetail.ImageList" :key="imgIndex">
|
||
<image class="cover-image" :src="item.ImageUrl" mode="aspectFill" />
|
||
</swiper-item>
|
||
</swiper>
|
||
</div>
|
||
|
||
<!-- 活动内容 -->
|
||
<div class="content-section">
|
||
<div class="section-title">活动介绍</div>
|
||
<text class="content-text">{{ eventDetail.ACTIVITY_INFO }}</text>
|
||
</div>
|
||
|
||
<!-- 底部操作区域 -->
|
||
<div class="action-section" v-if="pageStatus !== 'init'">
|
||
<!-- 已参加 -->
|
||
<div v-if="pageStatus === 'attended'" class="status-bar status-bar-attended">
|
||
<view class="status-icon">✓</view>
|
||
<text class="status-label">已参加活动</text>
|
||
</div>
|
||
|
||
<!-- 报名了但未参加 -->
|
||
<div v-else-if="pageStatus === 'missed'" class="status-bar status-bar-missed">
|
||
<view class="status-icon">!</view>
|
||
<text class="status-label">未参加活动</text>
|
||
</div>
|
||
|
||
<!-- 已成功报名 -->
|
||
<div v-else-if="pageStatus === 'registered'" class="registered-bar">
|
||
<div class="status-info">
|
||
<view class="status-icon">✓</view>
|
||
<text class="label">已成功报名</text>
|
||
</div>
|
||
<div class="btn-cancel" @click="handleCancelRegistration">取消报名</div>
|
||
</div>
|
||
|
||
<!-- 报名待付款 -->
|
||
<div v-else-if="pageStatus === 'pendingPay'" class="registered-bar registered-bar-pending">
|
||
<div class="status-info">
|
||
<view class="status-icon pending-icon">💰</view>
|
||
<text class="label pending-label">待付款</text>
|
||
</div>
|
||
<div class="btn-pay" @click="handleGoPay">去付款</div>
|
||
</div>
|
||
|
||
<!-- 已取消报名 -->
|
||
<div v-else-if="pageStatus === 'cancelled'"
|
||
:class="['action-btn', canReRegister ? 'action-normal' : 'action-disabled']" @click="handleReRegister">
|
||
<span class="btn-text">{{ canReRegister ? '重新报名' : '已取消报名' }}</span>
|
||
</div>
|
||
|
||
<!-- 报名未开始 -->
|
||
<div v-else-if="pageStatus === 'notStarted'" class="action-btn action-disabled">
|
||
<span class="btn-text">报名未开始</span>
|
||
</div>
|
||
|
||
<!-- 报名已截止 -->
|
||
<div v-else-if="pageStatus === 'ended'" class="action-btn action-disabled">
|
||
<span class="btn-text">报名已截止</span>
|
||
</div>
|
||
|
||
|
||
<!-- 报名进行中(立即报名) -->
|
||
<div v-else-if="pageStatus === 'open'" class="action-btn action-normal" @click="handleGoRegister">
|
||
<span class="btn-text">立即报名</span>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</template>
|
||
|
||
<script>
|
||
import { mapGetters } from "vuex";
|
||
export default {
|
||
data() {
|
||
return {
|
||
eventId: "",
|
||
eventDetail: {},
|
||
// 页面状态:init | notStarted | ended | open | registered | pendingPay | cancelled | missed | attended
|
||
pageStatus: "init",
|
||
// 活动进度标签(右上角)
|
||
activityStatusTag: null,
|
||
// 用户报名记录详情(用于取消/重新报名)
|
||
statusDetail: {},
|
||
isFirst: true,
|
||
};
|
||
},
|
||
computed: {
|
||
...mapGetters({
|
||
user: "user",
|
||
}),
|
||
// 已取消的用户是否还能重新报名(报名窗口仍在)
|
||
canReRegister() {
|
||
if (!this.eventDetail.SIGNUP_ENDDATE) return true;
|
||
const signEnd = new Date(this.eventDetail.SIGNUP_ENDDATE.replace(/-/g, '/')).getTime();
|
||
return signEnd > Date.now();
|
||
},
|
||
// 是否是付费活动
|
||
isPaidActivity() {
|
||
return this.eventDetail.ACTIVITY_TYPE == 3000;
|
||
},
|
||
},
|
||
onLoad(query) {
|
||
let id = '';
|
||
if (query.q) {
|
||
const url = decodeURIComponent(query.q);
|
||
const queryString = url.split('?')[1];
|
||
if (queryString) {
|
||
const params = {};
|
||
queryString.split('&').forEach(item => {
|
||
const [k, v] = item.split('=');
|
||
params[k] = v;
|
||
});
|
||
id = params.id;
|
||
}
|
||
}
|
||
this.eventId = query.id || id;
|
||
this.loadEventDetail();
|
||
},
|
||
onShow() {
|
||
if (!this.isFirst) {
|
||
this.loadEventDetail();
|
||
}
|
||
},
|
||
methods: {
|
||
// 加载活动详情
|
||
async loadEventDetail() {
|
||
// 立即归零,防止残留数据闪烁
|
||
this.pageStatus = "init";
|
||
this.eventDetail = {};
|
||
this.activityStatusTag = null;
|
||
|
||
const req = {
|
||
ACTIVITYId: this.eventId,
|
||
type: 'encryption'
|
||
};
|
||
uni.showLoading({ title: '加载中...', mask: true });
|
||
const data = await this.$api.$posMemberPost("/Member/GetACTIVITYDetail", req);
|
||
uni.hideLoading();
|
||
|
||
const detail = data.Result_Data;
|
||
console.log('detaildetail', detail);
|
||
|
||
if (!detail) return;
|
||
|
||
const now = Date.now();
|
||
|
||
// ========== 第一步:计算活动进度标签(右上角) ==========
|
||
const actStart = detail.ACTIVITY_STARTDATE ? new Date(detail.ACTIVITY_STARTDATE.replace(/-/g, '/')).getTime() : 0;
|
||
const actEnd = detail.ACTIVITY_ENDDATE ? new Date(detail.ACTIVITY_ENDDATE.replace(/-/g, '/')).getTime() : 0;
|
||
|
||
if (actEnd > 0 && actEnd < now) {
|
||
this.activityStatusTag = { text: '已结束', cssClass: 'status-ended' };
|
||
} else if (actStart > 0 && actStart > now) {
|
||
this.activityStatusTag = { text: '未开始', cssClass: 'status-unstarted' };
|
||
} else {
|
||
this.activityStatusTag = { text: '进行中', cssClass: 'status-active' };
|
||
}
|
||
|
||
// ========== 第二步:查询用户报名状态 ==========
|
||
const userRecord = await this.fetchUserRegistration();
|
||
console.log('userRecorduserRecord', userRecord);
|
||
|
||
// ========== 第三步:综合判定 pageStatus ==========
|
||
const signStart = detail.SIGNUP_STARTDATE ? new Date(detail.SIGNUP_STARTDATE.replace(/-/g, '/')).getTime() : 0;
|
||
const signEnd = detail.SIGNUP_ENDDATE ? new Date(detail.SIGNUP_ENDDATE.replace(/-/g, '/')).getTime() : 0;
|
||
const isPaid = detail.ACTIVITY_TYPE == 3000;
|
||
|
||
// ACTIVITY_STATE 来自活动详情 detail,PAY_STATUS 来自报名记录 userRecord
|
||
// 只有用户有报名记录时,才按 ACTIVITY_STATE 判断用户状态
|
||
const state = userRecord && detail.ACTIVITY_STATE != null ? Number(detail.ACTIVITY_STATE) : null;
|
||
const payStatus = userRecord && userRecord.PAY_STATUS != null ? Number(userRecord.PAY_STATUS) : null;
|
||
console.log('state=', state, 'payStatus=', payStatus, 'isPaid=', isPaid, 'userRecord=', userRecord);
|
||
|
||
if (state === 9) {
|
||
// 已参加
|
||
this.pageStatus = 'attended';
|
||
} else if (state === 0) {
|
||
// 已取消报名
|
||
this.pageStatus = 'cancelled';
|
||
} else if (state === 1) {
|
||
// 已报名,区分付费/免费
|
||
if (isPaid && payStatus === 2) {
|
||
this.pageStatus = 'cancelled'; // 退款 = 已取消
|
||
} else if (isPaid && payStatus === 1) {
|
||
// 付费活动且已付款 → 按正常报名处理
|
||
if (actEnd > 0 && actEnd < now) {
|
||
this.pageStatus = 'missed'; // 活动已结束但未参加
|
||
} else {
|
||
this.pageStatus = 'registered'; // 已成功报名
|
||
}
|
||
} else if (isPaid) {
|
||
// 付费活动,PAY_STATUS 为 null 或 0 都视为待付款
|
||
this.pageStatus = 'pendingPay';
|
||
} else {
|
||
// 免费活动,不看 PAY_STATUS
|
||
if (actEnd > 0 && actEnd < now) {
|
||
this.pageStatus = 'missed'; // 活动已结束但未参加
|
||
} else {
|
||
this.pageStatus = 'registered'; // 已成功报名
|
||
}
|
||
}
|
||
} else {
|
||
// 无报名记录(state 为 null),按报名时间窗口判定
|
||
if (signEnd > 0 && signEnd < now) {
|
||
this.pageStatus = 'ended'; // 报名已截止
|
||
} else if (signStart > 0 && signStart > now) {
|
||
this.pageStatus = 'notStarted'; // 报名未开始
|
||
} else {
|
||
this.pageStatus = 'open'; // 报名进行中
|
||
}
|
||
}
|
||
console.log('this.pageStatusthis.pageStatus', this.pageStatus);
|
||
|
||
// ========== 第四步:存储数据 ==========
|
||
this.eventDetail = detail;
|
||
this.isFirst = false;
|
||
},
|
||
|
||
// 查询用户报名记录
|
||
async fetchUserRegistration() {
|
||
if (!this.user || !this.user.MEMBERSHIP_ID) return null;
|
||
|
||
const req = {
|
||
SearchParameter: {
|
||
ACTIVITY_IDS: this.eventId,
|
||
MEMBERSHIP_IDS: this.user.MEMBERSHIP_ID,
|
||
},
|
||
PageIndex: 1,
|
||
PageSize: 9,
|
||
type: 'encryption'
|
||
};
|
||
const data = await this.$api.$posMemberPost("/Member/GetACTIVITYDETAILList", req);
|
||
const list = data.Result_Data && data.Result_Data.List;
|
||
|
||
if (list && list.length > 0) {
|
||
this.statusDetail = list[0];
|
||
return list[0];
|
||
}
|
||
this.statusDetail = {};
|
||
return null;
|
||
},
|
||
|
||
// ====== 按钮操作 ======
|
||
|
||
// 跳转报名页
|
||
handleGoRegister() {
|
||
// 检查是否满额
|
||
const isFull = this.eventDetail.MAXIMUM_CAPACITY &&
|
||
Number(this.eventDetail.currentParticipants || 0) >= Number(this.eventDetail.MAXIMUM_CAPACITY);
|
||
if (isFull) {
|
||
uni.showToast({ title: "报名人数已满", icon: 'none' });
|
||
return;
|
||
}
|
||
uni.navigateTo({
|
||
url: `/pages/eventRegistration/registration?eventId=${this.eventId}`
|
||
});
|
||
},
|
||
|
||
// 取消报名
|
||
handleCancelRegistration() {
|
||
uni.showModal({
|
||
title: "注意",
|
||
content: "您已报名成功,确认要取消报名?",
|
||
success: async (res) => {
|
||
if (res.confirm) {
|
||
const req = {
|
||
...this.statusDetail,
|
||
ACTIVITYDETAIL_STATE: 0,
|
||
type: 'encryption'
|
||
};
|
||
const data = await this.$api.$posMemberPost("/Member/SynchroACTIVITYDETAIL", req);
|
||
if (data.Result_Code === 100) {
|
||
uni.showToast({ title: '取消成功!', icon: 'none' });
|
||
this.loadEventDetail();
|
||
} else {
|
||
uni.showToast({ title: data.Result_Desc, icon: 'none' });
|
||
}
|
||
}
|
||
},
|
||
});
|
||
},
|
||
|
||
// 重新报名
|
||
handleReRegister() {
|
||
if (!this.canReRegister) {
|
||
uni.showToast({ title: "报名已截止,无法重新报名", icon: 'none' });
|
||
return;
|
||
}
|
||
uni.showModal({
|
||
title: "注意",
|
||
content: "您有报名记录,是否重新报名?",
|
||
success: async (res) => {
|
||
if (res.confirm) {
|
||
const req = {
|
||
...this.statusDetail,
|
||
ACTIVITYDETAIL_STATE: 1,
|
||
type: 'encryption'
|
||
};
|
||
const data = await this.$api.$posMemberPost("/Member/SynchroACTIVITYDETAIL", req);
|
||
if (data.Result_Code === 100) {
|
||
uni.showToast({ title: '重新报名成功!', icon: 'none' });
|
||
this.loadEventDetail();
|
||
} else {
|
||
uni.showToast({ title: data.Result_Desc, icon: 'none' });
|
||
}
|
||
}
|
||
},
|
||
});
|
||
},
|
||
|
||
// 去付款
|
||
handleGoPay() {
|
||
uni.showToast({ title: '请前往订单管理页面进行付款', icon: 'none' });
|
||
},
|
||
}
|
||
};
|
||
</script>
|
||
|
||
<style scoped lang="less">
|
||
.event-detail-page {
|
||
width: 100vw;
|
||
min-height: 100vh;
|
||
background: #f8fafc;
|
||
overflow-y: scroll;
|
||
|
||
.event-header {
|
||
background: white;
|
||
padding: 32rpx;
|
||
margin: 32rpx 32rpx;
|
||
border-radius: 24rpx;
|
||
box-shadow: 0 4rpx 20rpx rgba(0, 0, 0, 0.08);
|
||
position: relative;
|
||
z-index: 2;
|
||
|
||
.event-title {
|
||
font-size: 30rpx;
|
||
font-weight: 600;
|
||
color: #1f2937;
|
||
line-height: 1.4;
|
||
margin-bottom: 12rpx;
|
||
padding-right: 120rpx;
|
||
}
|
||
|
||
.event-status {
|
||
position: absolute;
|
||
top: 26rpx;
|
||
right: 32rpx;
|
||
padding: 6rpx 20rpx;
|
||
border-radius: 12rpx;
|
||
font-size: 26rpx;
|
||
font-weight: 600;
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: center;
|
||
z-index: 5;
|
||
|
||
&.status-active {
|
||
background: #f0fdf4;
|
||
color: #16a34a;
|
||
border: 1rpx solid #bbf7d0;
|
||
}
|
||
|
||
&.status-ended,
|
||
&.status-unstarted {
|
||
background: #f3f4f6;
|
||
color: #6b7280;
|
||
border: 1rpx solid #e5e7eb;
|
||
}
|
||
}
|
||
|
||
.event-meta {
|
||
.meta-item {
|
||
display: flex;
|
||
align-items: center;
|
||
margin-bottom: 12rpx;
|
||
|
||
&:last-child {
|
||
margin-bottom: 0;
|
||
}
|
||
|
||
.meta-icon {
|
||
font-size: 24rpx;
|
||
margin-right: 16rpx;
|
||
width: 40rpx;
|
||
text-align: center;
|
||
}
|
||
|
||
.meta-text {
|
||
font-size: 24rpx;
|
||
color: #374151;
|
||
line-height: 1.4;
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
.event-cover {
|
||
position: relative;
|
||
width: calc(100% - 64rpx);
|
||
height: 300rpx;
|
||
margin: 0 32rpx 32rpx;
|
||
border-radius: 24rpx;
|
||
overflow: hidden;
|
||
|
||
.cover-image {
|
||
width: 100%;
|
||
height: 100%;
|
||
object-fit: cover;
|
||
border-radius: 24rpx;
|
||
}
|
||
}
|
||
}
|
||
|
||
.content-section {
|
||
background: white;
|
||
margin: 0 32rpx 32rpx;
|
||
padding: 32rpx;
|
||
border-radius: 24rpx;
|
||
box-shadow: 0 4rpx 16rpx rgba(0, 0, 0, 0.06);
|
||
margin-bottom: calc(24rpx + env(safe-area-inset-bottom) + 24rpx + 28rpx + 48rpx + 24rpx);
|
||
|
||
.section-title {
|
||
font-size: 28rpx;
|
||
font-weight: 600;
|
||
color: #1f2937;
|
||
margin-bottom: 12rpx;
|
||
position: relative;
|
||
padding-left: 16rpx;
|
||
|
||
&::before {
|
||
content: '';
|
||
position: absolute;
|
||
left: 0;
|
||
top: 8rpx;
|
||
bottom: 8rpx;
|
||
width: 6rpx;
|
||
background: #22c55e;
|
||
border-radius: 3rpx;
|
||
}
|
||
}
|
||
|
||
.content-text {
|
||
font-size: 24rpx;
|
||
color: #374151;
|
||
line-height: 1.6;
|
||
white-space: pre-wrap;
|
||
}
|
||
}
|
||
|
||
/* ========== 底部操作区域 ========== */
|
||
.action-section {
|
||
position: fixed;
|
||
left: 0;
|
||
right: 0;
|
||
bottom: 0;
|
||
background: white;
|
||
border-top: 1rpx solid #f0f0f0;
|
||
padding: 24rpx 32rpx calc(24rpx + env(safe-area-inset-bottom));
|
||
z-index: 100;
|
||
|
||
/* 通用按钮 */
|
||
.action-btn {
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: center;
|
||
padding: 24rpx;
|
||
border-radius: 48rpx;
|
||
font-size: 28rpx;
|
||
font-weight: 600;
|
||
transition: all 0.3s ease;
|
||
border: none;
|
||
|
||
&.action-normal {
|
||
background: linear-gradient(135deg, #22c55e, #16a34a);
|
||
color: white;
|
||
|
||
&:active {
|
||
opacity: 0.9;
|
||
}
|
||
}
|
||
|
||
&.action-disabled {
|
||
background: linear-gradient(135deg, #9ca3af, #6b7280);
|
||
color: white;
|
||
}
|
||
}
|
||
|
||
/* 已成功报名状态栏 */
|
||
.registered-bar {
|
||
display: flex;
|
||
justify-content: space-between;
|
||
align-items: center;
|
||
padding: 12rpx 32rpx;
|
||
background: #f0fdf4;
|
||
border-radius: 48rpx;
|
||
border: 1rpx solid #bbf7d0;
|
||
|
||
.status-info {
|
||
display: flex;
|
||
align-items: center;
|
||
|
||
.status-icon {
|
||
width: 32rpx;
|
||
height: 32rpx;
|
||
background: #22c55e;
|
||
color: white;
|
||
border-radius: 50%;
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: center;
|
||
font-size: 20rpx;
|
||
margin-right: 16rpx;
|
||
font-weight: bold;
|
||
}
|
||
|
||
.pending-icon {
|
||
background: transparent;
|
||
width: auto;
|
||
height: auto;
|
||
font-size: 28rpx;
|
||
}
|
||
|
||
.label {
|
||
font-size: 28rpx;
|
||
color: #15803d;
|
||
font-weight: 600;
|
||
}
|
||
|
||
.pending-label {
|
||
color: #d97706;
|
||
}
|
||
}
|
||
|
||
.btn-cancel {
|
||
padding: 12rpx 28rpx;
|
||
border-radius: 32rpx;
|
||
background: white;
|
||
color: #6b7280;
|
||
font-size: 22rpx;
|
||
font-weight: 500;
|
||
border: 1rpx solid #e5e7eb;
|
||
|
||
&:active {
|
||
opacity: 0.7;
|
||
}
|
||
}
|
||
|
||
.btn-pay {
|
||
padding: 12rpx 28rpx;
|
||
border-radius: 32rpx;
|
||
background: linear-gradient(135deg, #f59e0b, #d97706);
|
||
color: white;
|
||
font-size: 22rpx;
|
||
font-weight: 600;
|
||
|
||
&:active {
|
||
opacity: 0.9;
|
||
}
|
||
}
|
||
|
||
/* 待付款变体 */
|
||
&.registered-bar-pending {
|
||
background: #fffbeb;
|
||
border-color: #fde68a;
|
||
}
|
||
}
|
||
|
||
/* 纯展示状态栏(已参加/未参加) */
|
||
.status-bar {
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: center;
|
||
padding: 24rpx;
|
||
border-radius: 48rpx;
|
||
font-size: 28rpx;
|
||
font-weight: 600;
|
||
|
||
.status-icon {
|
||
width: 36rpx;
|
||
height: 36rpx;
|
||
border-radius: 50%;
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: center;
|
||
font-size: 22rpx;
|
||
margin-right: 16rpx;
|
||
font-weight: bold;
|
||
color: white;
|
||
}
|
||
|
||
.status-label {
|
||
font-size: 28rpx;
|
||
font-weight: 600;
|
||
}
|
||
|
||
&.status-bar-attended {
|
||
background: #f0fdf4;
|
||
border: 1rpx solid #bbf7d0;
|
||
|
||
.status-icon {
|
||
background: #22c55e;
|
||
}
|
||
|
||
.status-label {
|
||
color: #15803d;
|
||
}
|
||
}
|
||
|
||
&.status-bar-missed {
|
||
background: #fef2f2;
|
||
border: 1rpx solid #fecaca;
|
||
|
||
.status-icon {
|
||
background: #f87171;
|
||
}
|
||
|
||
.status-label {
|
||
color: #dc2626;
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
.event-detail-page::-webkit-scrollbar {
|
||
display: none;
|
||
}
|
||
</style> |