qiuyuan 42b6d2db33 1
2025-08-07 12:22:19 +08:00

451 lines
10 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="activity-detail-container">
<!-- 顶部图片轮播 -->
<view class="banner-container">
<swiper class="activity-banner" :autoplay="false" indicator-dots indicator-color="rgba(255,255,255,0.5)"
indicator-active-color="#5b9cf8">
<swiper-item v-for="(img, index) in bannerImages" :key="index">
<image :src="img" class="w-full h-full" @click="previewImage(index)" />
</swiper-item>
</swiper>
<view class="banner-mask"></view>
</view>
<!-- 活动基本信息 -->
<view class="activity-base-info">
<view class="title-box">
<text class="title">{{activityInfo.title}}</text>
<view class="status-tag active" v-if="activityInfo.status === 2">
{{'未开始'}}
</view>
<view class="status-tag active" v-else-if="activityInfo.status === 3">
{{'进行中'}}
</view>
<view class="status-tag active" v-else="activityInfo.status === 4">
{{'已结束'}}
</view>
</view>
<view class="info-grid">
<view class="info-item">
<u-icon name="star" size="26" color="#5b9cf8"></u-icon>
<view class="info-text"><text class="info-label">活动类型</text>{{activityInfo.categoryName || '待确认'}} </view>
</view>
<view class="info-item">
<u-icon name="clock" size="26" color="#5b9cf8"></u-icon>
<view class="info-text"><text class="info-label">开始时间</text>{{formatTime(activityInfo.startAt,"YYYY-MM-DD HH:mm:ss") }}</view>
</view>
<view class="info-item">
<u-icon name="clock-fill" size="26" color="#ff5a5f"></u-icon>
<view class="info-text"><text class="info-label">结束时间</text>{{ formatTime(activityInfo.endAt,"YYYY-MM-DD HH:mm:ss") }}</view>
</view>
<view class="info-item highlight">
<u-icon name="warning" size="26" color="#ff9500"></u-icon>
<view class="info-text"><text class="info-label">报名截止</text>{{ formatTime(activityInfo.endSignupAt,"YYYY-MM-DD HH:mm:ss") }}</view>
</view>
</view>
</view>
<!-- 活动详情卡片 -->
<view class="detail-card">
<view class="detail-section">
<view class="section-title">
<view class="title-icon"></view>
<text>活动详情</text>
</view>
<view class="section-content rich-text">
<rich-text :nodes="activityInfo.content"></rich-text>
<!-- <image src="/static/route-map.jpg" mode="widthFix" class="route-map"
@click="previewImage(bannerImages.length)"></image> -->
</view>
</view>
</view>
<!-- 底部操作栏 -->
<view class="action-bar safe-area-inset-bottom">
<button class="share-btn" open-type="share">
<u-icon name="share" size="32" color="#fff"></u-icon>
<text class="share-text">分享</text>
</button>
<button class="signup-btn" @click="handleSignUp">立即报名</button>
</view>
</view>
</template>
<script>
import {
get,
post
} from '@/utils/request';
import {
IMAGE_BASE_URL,
BASE_URL
} from '@/utils/config';
import {
formatTime,
formatRelativeTime
} from '@/utils/timeFormat';
export default {
data() {
return {
formatTime,
IMAGE_BASE_URL,
Id: null,
isFavorite: false,
isSignedUp: false,
bannerImages: [],
activityInfo: {},
// timeline: [
// {time: '19:00-19:20', desc: '签到领取装备'},
// {time: '19:20-19:30', desc: '热身运动'},
// {time: '19:30-20:15', desc: '集体夜跑'},
// {time: '20:15-20:30', desc: '拉伸放松'}
// ],
// pointRules: [
// '完成全程跑步可获得50积分',
// '连续参加4次额外奖励100积分',
// '邀请好友参加每人奖励30积分',
// '积分每月1日清零请及时兑换'
// ],
// tips: [
// '建议穿着舒适运动服装和跑鞋',
// '活动前1小时请勿大量进食',
// '请自带水杯,现场提供饮用水',
// '如遇雨天,活动将延期举行',
// '未成年人需家长陪同参加'
// ],
dotColors: ['#36CFC9', '#5AC8FA', '#FF9500', '#FF5A5F']
}
},
onLoad(options) {
if (options && options.Id) {
this.Id = options.Id;
}
},
mounted() {
this.getActivitiesDetail();
},
methods: {
toggleFavorite() {
this.isFavorite = !this.isFavorite
uni.showToast({
title: this.isFavorite ? '已收藏' : '已取消收藏',
icon: 'none'
})
},
async handleSignUp() {
try {
const res = await get(`/api/v1/app_auth/activities/${this.Id}`);
if (!res.success) {
uni.showToast({
title: res.msg || '报名失败',
icon: 'error'
})
} else {
uni.showToast({
title: '报名成功!',
icon: 'success'
})
}
} catch (err) {
console.error('获取详情失败:', err);
}
},
onShareAppMessage() {
return {
title: this.activityInfo.title,
path: `/pages/activity/detail?id=${this.Id}`,
imageUrl: this.bannerImages[0] || '/static/logo.png'
}
},
previewImage(index) {
uni.previewImage({
current: index,
urls: [...this.bannerImages, '/static/route-map.jpg']
})
},
async getActivitiesDetail() {
try {
const res = await get(`/api/v1/apps/home/activities/${this.Id}`);
if (!res || !res.success) {
throw new Error('获取详情失败');
}
let imgs = res.data.images;
imgs.forEach(item => {
if (!item.startsWith('http') && !item.startsWith('https')) {
item = IMAGE_BASE_URL + item;
this.bannerImages.push(item);
}
});
this.activityInfo = {
...res.data
};
} catch (err) {
console.error('获取详情失败:', err);
}
}
}
}
</script>
<style lang="scss" scoped>
$primary-color: #5b9cf8;
$secondary-color: #f7f8fa;
$text-color: #333;
$light-text: #666;
$lighter-text: #999;
$border-color: #eee;
$border-radius: 16rpx;
$shadow: 0 4rpx 24rpx rgba(0, 0, 0, 0.08);
$highlight-color: #fff8e6;
.activity-detail-container {
padding-bottom: 160rpx;
background-color: $secondary-color;
color: $text-color;
.banner-container {
position: relative;
height: 500rpx;
overflow: hidden;
.activity-banner {
width: 100%;
height: 100%;
}
.activity-banner image {
width: 100%;
height: 100%;
display: block;
}
.banner-mask {
position: absolute;
bottom: 0;
left: 0;
right: 0;
height: 120rpx;
background: linear-gradient(to top, rgba(0,0,0,0.3), transparent);
}
}
.activity-base-info {
padding: 30rpx;
margin: -40rpx 20rpx 20rpx;
background-color: #fff;
border-radius: $border-radius;
box-shadow: $shadow;
position: relative;
z-index: 2;
.title-box {
display: flex;
align-items: center;
margin-bottom: 30rpx;
padding-bottom: 20rpx;
border-bottom: 1rpx solid rgba($border-color, 0.8);
.title {
flex: 1;
font-size: 38rpx;
font-weight: bold;
line-height: 1.4;
margin-right: 20rpx;
}
.status-tag {
padding: 8rpx 20rpx;
border-radius: 30rpx;
font-size: 24rpx;
flex-shrink: 0;
&.active {
background-color: rgba($primary-color, 0.1);
color: $primary-color;
}
}
}
.info-grid {
display: grid;
grid-template-columns: repeat(1, 1fr);
gap: 24rpx;
.info-item {
display: flex;
align-items: center; /* 垂直居中 */
&.highlight {
background-color: #fff8e6;
padding: 16rpx;
border-radius: 12rpx;
margin:0rpx -16rpx;
}
.u-icon {
flex-shrink: 0; /* 防止图标被压缩 */
}
.info-text {
font-size: 28rpx;
line-height: 1.5;
margin-left: 12rpx;
display: flex;
align-items: center; /* 确保内部文本垂直居中 */
.info-label {
color: #999;
margin-right: 8rpx;
}
}
}
}
}
.detail-card {
background-color: #fff;
border-radius: $border-radius;
padding: 30rpx;
margin: 0 20rpx 20rpx;
box-shadow: $shadow;
.detail-section {
margin-bottom: 40rpx;
.section-title {
display: flex;
align-items: center;
margin-bottom: 24rpx;
padding-bottom: 16rpx;
border-bottom: 1rpx solid rgba($border-color, 0.5);
.title-icon {
width: 8rpx;
height: 32rpx;
background-color: $primary-color;
border-radius: 4rpx;
margin-right: 16rpx;
}
text {
font-size: 32rpx;
font-weight: bold;
}
}
.section-content {
font-size: 30rpx;
line-height: 1.8;
color: $light-text;
&.rich-text {
p {
margin-bottom: 24rpx;
line-height: 1.8;
}
img {
max-width: 100%;
border-radius: 8rpx;
margin: 20rpx 0;
}
}
.route-map {
width: 100%;
margin-top: 30rpx;
border-radius: $border-radius;
box-shadow: 0 2rpx 12rpx rgba(0, 0, 0, 0.1);
}
}
}
}
.action-bar {
position: fixed;
bottom: 0;
left: 0;
right: 0;
height: 120rpx;
background-color: #fff;
display: flex;
align-items: center;
justify-content: space-between;
padding: 0 30rpx;
box-shadow: 0 -4rpx 20rpx rgba(0, 0, 0, 0.08);
z-index: 100;
// &.safe-area-inset-bottom {
// padding-bottom: env(safe-area-inset-bottom);
// }
.share-btn {
display: flex;
align-items: center;
justify-content: center;
width: 160rpx;
height: 80rpx;
background: linear-gradient(135deg, #6e8cfa, #5b9cf8);
border-radius: 40rpx;
color: #fff;
font-size: 28rpx;
margin-right: 20rpx;
border: none;
box-shadow: 0 4rpx 12rpx rgba(91, 156, 248, 0.2);
transition: all 0.2s ease;
.share-text {
margin-left: 10rpx;
}
&:active {
transform: scale(0.98);
opacity: 0.9;
}
}
.share-btn::after {
border: none;
}
.signup-btn {
flex: 1;
height: 80rpx;
background: linear-gradient(135deg, #ff7e5f, #ff5a5f);
color: #fff;
border-radius: 40rpx;
font-size: 32rpx;
font-weight: 500;
display: flex;
align-items: center;
justify-content: center;
box-shadow: 0 4rpx 12rpx rgba(255, 90, 95, 0.2);
transition: all 0.2s ease;
border: none;
&::after {
border: none;
}
&:active {
transform: scale(0.98);
opacity: 0.9;
}
}
}
}
@media (min-width: 768px) {
.activity-detail-container {
max-width: 750px;
margin: 0 auto;
}
}
</style>