446 lines
11 KiB
Vue
446 lines
11 KiB
Vue
<template>
|
|
<view class="page">
|
|
<custom-navbar ref="customNavbar" title="活动详情" />
|
|
<view :style="{ backgroundColor: CommonEnum.BASE_COLOR }" class="header">
|
|
<!-- 状态展示 -->
|
|
<status-display v-if="loading" loading-text="加载中..." type="loading" />
|
|
|
|
<!-- 错误状态 -->
|
|
<status-display
|
|
v-if="error"
|
|
:error-text="error"
|
|
type="error"
|
|
@retry="loadPageData"
|
|
/>
|
|
|
|
<!-- 活动详情内容 -->
|
|
<view v-if="!loading && !error && activityData" class="activity-detail">
|
|
<!-- 活动标题区域 -->
|
|
<view class="activity-header">
|
|
<view class="title-section">
|
|
<text class="main-title">{{ activityData.title }}</text>
|
|
<view class="meta-info">
|
|
<text class="date"
|
|
>{{ formatDate(activityData.createTime) }}
|
|
</text>
|
|
<text class="read-count"
|
|
>阅读{{ activityData.readNum || 0 }}
|
|
</text>
|
|
</view>
|
|
</view>
|
|
</view>
|
|
|
|
<!-- 活动信息卡片 -->
|
|
<activity-card
|
|
:activity="formattedActivity"
|
|
:show-register-button="false"
|
|
:show-title="false"
|
|
@register="handleRegister"
|
|
@card-click="handleCardClick"
|
|
/>
|
|
|
|
<!-- 参与统计 -->
|
|
<view class="participation-stats">
|
|
<!-- <view class="stat-item">-->
|
|
<!-- <text class="stat-label">活动参与人数:</text>-->
|
|
<!-- <text class="stat-value">{{ activityData.attendance || 0 }}</text>-->
|
|
<!-- </view>-->
|
|
<view class="stat-item">
|
|
<text class="stat-label">已报名:</text>
|
|
<text class="stat-value"
|
|
>{{ activityData.currentBooking || 0 }}
|
|
</text>
|
|
</view>
|
|
</view>
|
|
|
|
<!-- 活动描述 -->
|
|
<view v-if="activityData.content" class="activity-description">
|
|
<view class="description-title">
|
|
<text class="title-text">活动详情</text>
|
|
</view>
|
|
<view class="description-content">
|
|
<rich-text
|
|
:nodes="removeBackgroundStyle(activityData.content)"
|
|
class="content-html"
|
|
></rich-text>
|
|
</view>
|
|
</view>
|
|
|
|
<!-- 底部操作按钮 -->
|
|
<view class="bottom-actions">
|
|
<view class="action-button phone-button" @click="handlePhoneCall">
|
|
<image
|
|
:src="CommonEnum.PHONE"
|
|
class="button-icon"
|
|
mode="aspectFit"
|
|
></image>
|
|
<text class="button-text">电话</text>
|
|
</view>
|
|
<view class="action-button location-button" @click="handleLocation">
|
|
<image
|
|
:src="CommonEnum.ADDRESS"
|
|
class="button-icon"
|
|
mode="aspectFit"
|
|
></image>
|
|
<text class="button-text">地址</text>
|
|
</view>
|
|
<view class="action-button register-button" @click="handleRegister">
|
|
<text class="button-text">立即报名</text>
|
|
</view>
|
|
</view>
|
|
</view>
|
|
</view>
|
|
</view>
|
|
</template>
|
|
|
|
<script>
|
|
import CustomNavbar from "../../components/custom-navbar/custom-navbar.vue";
|
|
import CommonEnum from "../../enum/common";
|
|
import StatusDisplay from "../../components/status-display/status-display.vue";
|
|
import ActivityCard from "../../components/activity-card/activity-card.vue";
|
|
import activityApi from "../../api/activity/activity.js";
|
|
import activityFormatter from "../../utils/activity-data-formatter.js";
|
|
import { removeBackgroundStyle } from "../../composables/clearnBackgroundColor";
|
|
import { checkLogin } from "../../composables/goToLogin";
|
|
|
|
export default {
|
|
components: {
|
|
CustomNavbar,
|
|
StatusDisplay,
|
|
ActivityCard,
|
|
},
|
|
data() {
|
|
return {
|
|
CommonEnum,
|
|
loading: false,
|
|
error: "",
|
|
activityData: null,
|
|
actId: "",
|
|
};
|
|
},
|
|
computed: {
|
|
// 格式化后的活动数据
|
|
formattedActivity() {
|
|
if (!this.activityData) return null;
|
|
return activityFormatter.formatActivityDetail(this.activityData);
|
|
},
|
|
},
|
|
onLoad(options) {
|
|
// 获取传入的活动ID
|
|
if (options.actId) {
|
|
this.actId = options.actId;
|
|
this.loadPageData();
|
|
} else {
|
|
this.error = "缺少活动ID参数";
|
|
}
|
|
},
|
|
methods: {
|
|
removeBackgroundStyle,
|
|
// 加载页面数据
|
|
async loadPageData() {
|
|
if (!this.actId) return;
|
|
|
|
this.loading = true;
|
|
this.error = "";
|
|
|
|
try {
|
|
const response = await activityApi.getActivityDetail(this.actId);
|
|
|
|
if (response.code === 200 && response.data) {
|
|
this.activityData = response.data;
|
|
} else {
|
|
this.error = response.msg || "获取活动详情失败";
|
|
}
|
|
} catch (error) {
|
|
console.error("获取活动详情失败:", error);
|
|
this.error = "网络错误,请稍后重试";
|
|
} finally {
|
|
this.loading = false;
|
|
}
|
|
},
|
|
|
|
// 格式化日期
|
|
formatDate(dateString) {
|
|
if (!dateString) return "";
|
|
|
|
try {
|
|
// 将 "yyyy-MM-dd HH:mm:ss" 格式转换为 "yyyy-MM-ddTHH:mm:ss" 格式以兼容iOS
|
|
const iosCompatibleDate = dateString.replace(" ", "T");
|
|
const date = new Date(iosCompatibleDate);
|
|
const year = date.getFullYear();
|
|
const month = String(date.getMonth() + 1).padStart(2, "0");
|
|
const day = String(date.getDate()).padStart(2, "0");
|
|
const hours = String(date.getHours()).padStart(2, "0");
|
|
const minutes = String(date.getMinutes()).padStart(2, "0");
|
|
|
|
return `${year}-${month}-${day} ${hours}:${minutes}`;
|
|
} catch (error) {
|
|
console.error("格式化日期失败:", error);
|
|
return dateString;
|
|
}
|
|
},
|
|
|
|
// 处理卡片点击
|
|
handleCardClick(activity) {
|
|
console.log("点击活动卡片:", activity);
|
|
// 在详情页中点击卡片不执行任何操作
|
|
},
|
|
|
|
// 处理报名
|
|
handleRegister() {
|
|
if (!this.activityData) return;
|
|
|
|
console.log("报名活动:", this.activityData);
|
|
|
|
if (!checkLogin()) {
|
|
return;
|
|
}
|
|
|
|
// 直接跳转到活动报名页面
|
|
uni.navigateTo({
|
|
url: `/pages/activity/application?actId=${this.activityData.id}`,
|
|
});
|
|
},
|
|
|
|
// 处理电话拨打
|
|
handlePhoneCall() {
|
|
if (!this.activityData || !this.activityData.phone) {
|
|
uni.showToast({
|
|
title: "暂无联系电话",
|
|
icon: "none",
|
|
});
|
|
return;
|
|
}
|
|
|
|
uni.showModal({
|
|
title: "拨打电话",
|
|
content: `确定要拨打 ${this.activityData.phone} 吗?`,
|
|
success: (res) => {
|
|
if (res.confirm) {
|
|
uni.makePhoneCall({
|
|
phoneNumber: this.activityData.phone,
|
|
fail: (err) => {
|
|
console.error("拨打电话失败:", err);
|
|
uni.showToast({
|
|
title: "拨打电话失败",
|
|
icon: "none",
|
|
});
|
|
},
|
|
});
|
|
}
|
|
},
|
|
});
|
|
},
|
|
|
|
// 处理地址查看
|
|
handleLocation() {
|
|
if (!this.activityData || !this.activityData.address) {
|
|
uni.showToast({
|
|
title: "暂无地址信息",
|
|
icon: "none",
|
|
});
|
|
return;
|
|
}
|
|
|
|
// 如果有经纬度信息,打开地图
|
|
if (this.activityData.lon && this.activityData.lat) {
|
|
uni.openLocation({
|
|
latitude: this.activityData.lat,
|
|
longitude: this.activityData.lon,
|
|
name: this.activityData.title,
|
|
address: this.activityData.address,
|
|
fail: (err) => {
|
|
console.error("打开地图失败:", err);
|
|
// 如果打开地图失败,显示地址信息
|
|
this.showAddressInfo();
|
|
},
|
|
});
|
|
} else {
|
|
// 没有经纬度信息,显示地址详情
|
|
this.showAddressInfo();
|
|
}
|
|
},
|
|
|
|
// 显示地址信息
|
|
showAddressInfo() {
|
|
uni.showModal({
|
|
title: "活动地址",
|
|
content: this.activityData.address,
|
|
showCancel: false,
|
|
confirmText: "知道了",
|
|
});
|
|
},
|
|
},
|
|
};
|
|
</script>
|
|
|
|
<style lang="scss">
|
|
page {
|
|
background: #faf8f3;
|
|
}
|
|
|
|
.header {
|
|
width: 100%;
|
|
min-height: 100vh;
|
|
display: flex;
|
|
align-items: flex-start;
|
|
flex-direction: column;
|
|
padding: 0 15rpx;
|
|
padding-bottom: 200rpx; /* 为固定底部按钮留出空间 */
|
|
}
|
|
|
|
.activity-detail {
|
|
width: 100%;
|
|
}
|
|
|
|
.activity-header {
|
|
width: 100%;
|
|
padding: 30rpx 0 20rpx 24rpx;
|
|
|
|
.title-section {
|
|
.main-title {
|
|
font-size: 44rpx;
|
|
font-weight: 500;
|
|
color: #3d3d3d;
|
|
line-height: 60rpx;
|
|
margin-bottom: 20rpx;
|
|
}
|
|
|
|
.meta-info {
|
|
display: flex;
|
|
align-items: center;
|
|
gap: 30rpx;
|
|
|
|
.date {
|
|
font-weight: 400;
|
|
font-size: 24rpx;
|
|
color: #808080;
|
|
}
|
|
|
|
.read-count {
|
|
font-weight: 400;
|
|
font-size: 24rpx;
|
|
color: #808080;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
.participation-stats {
|
|
width: 100%;
|
|
display: flex;
|
|
border-radius: 16rpx;
|
|
padding: 30rpx 30rpx 30rpx 0;
|
|
margin-bottom: 30rpx;
|
|
|
|
.stat-item {
|
|
display: flex;
|
|
align-items: center;
|
|
margin-bottom: 0;
|
|
|
|
&:last-child {
|
|
margin-left: 50rpx;
|
|
}
|
|
|
|
.stat-label {
|
|
font-size: 30rpx;
|
|
color: #333;
|
|
font-weight: 500;
|
|
}
|
|
|
|
.stat-value {
|
|
font-size: 30rpx;
|
|
color: #e74c3c;
|
|
font-weight: bold;
|
|
}
|
|
}
|
|
}
|
|
|
|
.activity-description {
|
|
width: 100%;
|
|
border-radius: 16rpx;
|
|
padding: 30rpx;
|
|
margin-bottom: 30rpx;
|
|
|
|
.description-title {
|
|
margin-bottom: 20rpx;
|
|
|
|
.title-text {
|
|
font-size: 32rpx;
|
|
font-weight: bold;
|
|
color: #333;
|
|
}
|
|
}
|
|
|
|
.description-content {
|
|
.content-html {
|
|
font-size: 28rpx;
|
|
color: #666;
|
|
line-height: 1.6;
|
|
text-align: justify;
|
|
}
|
|
}
|
|
}
|
|
|
|
.bottom-actions {
|
|
position: fixed;
|
|
bottom: 0;
|
|
left: 0;
|
|
right: 0;
|
|
width: 100%;
|
|
display: flex;
|
|
align-items: center;
|
|
gap: 20rpx;
|
|
height: 62px;
|
|
padding: 30rpx 40rpx;
|
|
background: #fff;
|
|
border-top: 1rpx solid #f0f0f0;
|
|
border-radius: 20rpx 20rpx 0 0;
|
|
//margin-bottom: 12rpx;
|
|
|
|
.action-button {
|
|
display: flex;
|
|
flex-direction: column;
|
|
align-items: center;
|
|
justify-content: center;
|
|
border-radius: 12rpx;
|
|
|
|
&.phone-button,
|
|
&.location-button {
|
|
flex: 1;
|
|
min-height: 120rpx;
|
|
background: transparent;
|
|
|
|
.button-icon {
|
|
width: 48rpx;
|
|
height: 48rpx;
|
|
margin-bottom: 12rpx;
|
|
filter: brightness(0) saturate(100%) invert(27%) sepia(51%)
|
|
saturate(2878%) hue-rotate(346deg) brightness(104%) contrast(97%);
|
|
}
|
|
|
|
.button-text {
|
|
font-size: 26rpx;
|
|
color: #522510;
|
|
font-weight: 400;
|
|
}
|
|
}
|
|
|
|
&.register-button {
|
|
flex: 4;
|
|
background: #a24242;
|
|
border-radius: 60rpx;
|
|
padding: 0 40rpx;
|
|
width: 227px;
|
|
height: 38px;
|
|
|
|
.button-text {
|
|
font-size: 32rpx;
|
|
color: #fff;
|
|
font-weight: bold;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
</style>
|