145 lines
8.4 KiB
Vue
145 lines
8.4 KiB
Vue
<!-- 未来社区 - 活动详情 -->
|
|
<template>
|
|
<view class="ad-page">
|
|
<view class="fc-nav" :style="{ paddingTop: statusBarTop + 'px' }">
|
|
<view class="fc-nav-bar">
|
|
<view class="fc-back" @tap="goBack"><u-icon name="arrow-left" color="#1a1a1a" size="38"></u-icon></view>
|
|
<text class="fc-nav-title">活动详情</text>
|
|
<view class="ad-star" @tap="onFollow" v-if="act"><u-icon :name="followed ? 'star-fill' : 'star'" :color="followed ? '#ff9a12' : '#9098a0'" size="40"></u-icon></view>
|
|
</view>
|
|
</view>
|
|
|
|
<view class="ad-body" v-if="act">
|
|
<!-- 封面 -->
|
|
<view class="ad-banner">
|
|
<image v-if="act.image" :src="act.image" class="ad-banner-img" mode="aspectFill" />
|
|
<view v-else class="ad-banner-img" :style="{ background: sceneBg(act.scene) }"></view>
|
|
<text class="ad-badge" :style="statusStyle(act.status)">{{ statusLabel(act.status) }}</text>
|
|
</view>
|
|
|
|
<!-- 摘要 -->
|
|
<view class="ad-summary">
|
|
<text class="ad-title">{{ act.title }}</text>
|
|
<view class="ad-sum-row">
|
|
<view class="ad-dist"><u-icon name="map" color="#9098a0" size="24"></u-icon><text>{{ act.distance || '社区内' }}</text></view>
|
|
<view class="ad-call" v-if="act.phone" @tap="onCall"><u-icon name="phone" color="#e9272d" size="24"></u-icon><text>联系电话</text></view>
|
|
</view>
|
|
<text class="ad-addr">{{ act.address || act.place }}</text>
|
|
</view>
|
|
|
|
<!-- 活动说明 -->
|
|
<view class="ad-card">
|
|
<text class="ad-card-tt">活动说明</text>
|
|
<view class="ad-info-row"><u-icon name="account" color="#9098a0" size="26"></u-icon><text class="ad-lb">报名人数</text><text class="ad-vl">{{ act.enrolled || act.quota }}</text></view>
|
|
<view class="ad-info-row"><u-icon name="clock" color="#9098a0" size="26"></u-icon><text class="ad-lb">报名时间</text><text class="ad-vl">{{ act.signupRange || act.deadline }}</text></view>
|
|
<view class="ad-info-row"><u-icon name="calendar" color="#9098a0" size="26"></u-icon><text class="ad-lb">活动时间</text><text class="ad-vl">{{ act.time }}</text></view>
|
|
<view class="ad-info-row"><u-icon name="integral" color="#9098a0" size="26"></u-icon><text class="ad-lb">报名费用</text><text class="ad-vl">{{ act.fee || '免费' }}</text></view>
|
|
<view class="ad-info-row"><u-icon name="server-man" color="#9098a0" size="26"></u-icon><text class="ad-lb">组织方</text><text class="ad-vl">{{ act.organizer || '九九社区' }}</text></view>
|
|
<view class="ad-info-row"><u-icon name="phone" color="#9098a0" size="26"></u-icon><text class="ad-lb">联系电话</text><text class="ad-vl">{{ act.phone || '暂无' }}</text></view>
|
|
</view>
|
|
|
|
<!-- 活动介绍 -->
|
|
<view class="ad-card">
|
|
<text class="ad-card-tt">活动介绍</text>
|
|
<rich-text class="ad-content" :nodes="act.content"></rich-text>
|
|
<view class="ad-points" v-if="act.points && act.points.length">
|
|
<view class="ad-point" v-for="(p, i) in act.points" :key="i"><text class="ad-dot">•</text><text class="ad-pt">{{ p }}</text></view>
|
|
</view>
|
|
</view>
|
|
|
|
<!-- 参与须知 -->
|
|
<view class="ad-card">
|
|
<text class="ad-card-tt">参与须知</text>
|
|
<view class="ad-point" v-for="(p, i) in ACTIVITY_REGISTER_NOTICE" :key="i"><text class="ad-dot">•</text><text class="ad-pt">{{ p }}</text></view>
|
|
</view>
|
|
|
|
<view class="fc-spacer"></view>
|
|
</view>
|
|
<view v-else class="fc-empty">活动不存在</view>
|
|
|
|
<view class="ad-foot" v-if="act">
|
|
<view class="ad-foot-fee"><text class="ad-foot-lb">报名费用</text><text class="ad-foot-vl">{{ act.fee || '免费' }}</text></view>
|
|
<text class="ad-join" :class="{ done: joined, ended: act.status === 'ended' }" @tap="onJoin">{{ joinLabel }}</text>
|
|
</view>
|
|
</view>
|
|
</template>
|
|
|
|
<script>
|
|
import { findActivity, ACTIVITY_STATUS_META, ACTIVITY_SCENE_BG, ACTIVITY_REGISTER_NOTICE, isActivityJoined, toggleActivityJoin, isActivityFollowed, toggleActivityFollow } from '../data.js'
|
|
|
|
export default {
|
|
data() { return { statusBarTop: 20, act: null, joined: false, followed: false, ACTIVITY_REGISTER_NOTICE } },
|
|
onLoad(options) {
|
|
const sys = uni.getSystemInfoSync()
|
|
this.statusBarTop = sys.statusBarHeight || 20
|
|
this.act = findActivity(options && options.id) || null
|
|
if (this.act) { this.joined = isActivityJoined(this.act.id); this.followed = isActivityFollowed(this.act.id) }
|
|
},
|
|
computed: {
|
|
joinLabel() {
|
|
if (!this.act) return ''
|
|
if (this.act.status === 'ended') return '活动已结束'
|
|
return this.joined ? '已报名 · 点击取消' : '立即报名'
|
|
}
|
|
},
|
|
methods: {
|
|
statusLabel(s) { return (ACTIVITY_STATUS_META[s] || {}).label || '' },
|
|
statusStyle(s) { const m = ACTIVITY_STATUS_META[s] || {}; return { color: m.color, background: m.bg } },
|
|
sceneBg(s) { return ACTIVITY_SCENE_BG[s] || ACTIVITY_SCENE_BG.handicraft },
|
|
goBack() { uni.navigateBack({ delta: 1 }) },
|
|
onCall() { uni.showToast({ title: '联系电话:' + this.act.phone, icon: 'none' }) },
|
|
onFollow() { this.followed = toggleActivityFollow(this.act.id); uni.showToast({ title: this.followed ? '已关注活动' : '已取消关注', icon: 'none' }) },
|
|
onJoin() {
|
|
if (this.act.status === 'ended') { uni.showToast({ title: '活动已结束', icon: 'none' }); return }
|
|
this.joined = toggleActivityJoin(this.act.id)
|
|
uni.showToast({ title: this.joined ? '报名成功' : '已取消报名', icon: 'none' })
|
|
}
|
|
}
|
|
}
|
|
</script>
|
|
|
|
<style scoped>
|
|
page { background: #f5f6f7; }
|
|
.ad-page { min-height: 100vh; background: #f5f6f7; padding-bottom: 140rpx; }
|
|
.fc-nav { position: sticky; top: 0; z-index: 100; background: #fff; padding: 0 24rpx; }
|
|
.fc-nav-bar { position: relative; display: flex; align-items: center; justify-content: center; height: 88rpx; }
|
|
.fc-back { position: absolute; left: 0; top: 0; height: 88rpx; display: flex; align-items: center; }
|
|
.fc-nav-title { font-size: 34rpx; font-weight: 700; color: #1a1a1a; }
|
|
.ad-star { position: absolute; right: 0; top: 0; height: 88rpx; display: flex; align-items: center; }
|
|
|
|
.ad-body { padding: 20rpx; }
|
|
.ad-banner { position: relative; border-radius: 20rpx; overflow: hidden; }
|
|
.ad-banner-img { width: 100%; height: 360rpx; display: block; }
|
|
.ad-badge { position: absolute; left: 20rpx; top: 20rpx; font-size: 22rpx; padding: 4rpx 16rpx; border-radius: 6rpx; }
|
|
|
|
.ad-summary { background: #fff; border-radius: 20rpx; padding: 26rpx; margin-top: 16rpx; }
|
|
.ad-title { font-size: 38rpx; font-weight: 800; color: #1a1a1a; }
|
|
.ad-sum-row { display: flex; align-items: center; justify-content: space-between; margin-top: 16rpx; }
|
|
.ad-dist { display: flex; align-items: center; font-size: 24rpx; color: #6b7078; }
|
|
.ad-dist text { margin-left: 8rpx; }
|
|
.ad-call { display: flex; align-items: center; font-size: 24rpx; color: #e9272d; }
|
|
.ad-call text { margin-left: 6rpx; }
|
|
.ad-addr { display: block; margin-top: 14rpx; font-size: 24rpx; color: #9098a0; }
|
|
|
|
.ad-card { background: #fff; border-radius: 20rpx; padding: 26rpx; margin-top: 16rpx; }
|
|
.ad-card-tt { display: block; font-size: 30rpx; font-weight: 700; color: #1a1a1a; margin-bottom: 18rpx; }
|
|
.ad-info-row { display: flex; align-items: center; padding: 14rpx 0; }
|
|
.ad-lb { width: 130rpx; margin-left: 14rpx; font-size: 26rpx; color: #9098a0; }
|
|
.ad-vl { flex: 1; font-size: 26rpx; color: #333; }
|
|
.ad-content { display: block; font-size: 28rpx; color: #333; line-height: 1.8; }
|
|
.ad-points { margin-top: 12rpx; }
|
|
.ad-point { display: flex; margin-top: 12rpx; }
|
|
.ad-dot { color: #e9272d; margin-right: 12rpx; font-size: 28rpx; line-height: 1.6; }
|
|
.ad-pt { flex: 1; font-size: 26rpx; color: #555; line-height: 1.6; }
|
|
|
|
.ad-foot { position: fixed; left: 0; right: 0; bottom: 0; background: #fff; display: flex; align-items: center; padding: 14rpx 24rpx calc(14rpx + env(safe-area-inset-bottom)); box-shadow: 0 -4rpx 20rpx rgba(0,0,0,0.04); }
|
|
.ad-foot-fee { flex: 1; display: flex; flex-direction: column; }
|
|
.ad-foot-lb { font-size: 20rpx; color: #9098a0; }
|
|
.ad-foot-vl { font-size: 34rpx; color: #e9272d; font-weight: 700; }
|
|
.ad-join { flex-shrink: 0; background: linear-gradient(90deg, #ff5b5b 0%, #ef111b 100%); color: #fff; font-size: 30rpx; font-weight: 700; padding: 20rpx 70rpx; border-radius: 44rpx; }
|
|
.ad-join.done { background: #fff0ee; color: #ef111b; }
|
|
.ad-join.ended { background: #eef0f3; color: #9aa0a8; }
|
|
.fc-empty { text-align: center; color: #9aa0a8; font-size: 26rpx; padding: 120rpx 0; }
|
|
.fc-spacer { height: 20rpx; }
|
|
</style>
|