ylj20011123 35e40eb558 update
2026-02-28 18:25:12 +08:00

657 lines
18 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>
<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 来自活动详情 detailPAY_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>