| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410 |
- <template>
- <view class="section">
- <!-- 头部 -->
- <view class="section-header">
- <view class="section-header-left">
- <view class="section-icon-wrap">
- <text class="section-icon">{{ dimensionIcon }}</text>
- </view>
- <text class="section-title">推荐活动</text>
- </view>
- <view class="section-more" @click="$emit('moreActivities')">
- <text class="section-more-text">更多</text>
- <text class="section-more-arrow">›</text>
- </view>
- </view>
- <!-- 空状态 -->
- <view v-if="!displayActivities || displayActivities.length === 0" class="empty-state">
- <text class="empty-icon">{{ dimensionIcon }}</text>
- <text class="empty-tip">{{ isLoggedIn ? '暂无相关活动' : '登录后查看更多活动' }}</text>
- </view>
- <!-- 活动卡片列表 -->
- <view class="activity-list" v-if="displayActivities && displayActivities.length > 0">
- <view
- class="activity-card"
- v-for="(act, index) in displayActivities"
- :key="act.id"
- @click.stop="$emit('activityClick', act)"
- >
- <!-- 封面图区域 -->
- <view class="card-cover">
- <image
- class="card-cover-img"
- :src="act.coverImage || '/static/default-activity.png'"
- mode="aspectFill"
- />
- <!-- 渐变遮罩 -->
- <view class="card-cover-mask"></view>
- <!-- 序号标签 -->
- <view class="card-rank" v-if="displayActivities.length > 1">
- <text class="card-rank-num">{{ index + 1 }}</text>
- </view>
- <!-- 状态标签 -->
- <view class="card-status" :class="'status-' + (act.status || 'upcoming')">
- <text class="card-status-dot" v-if="act.status === 'ongoing'"></text>
- <text class="card-status-text">{{ statusText(act.status) }}</text>
- </view>
- <!-- 标题 -->
- <view class="card-title-wrap">
- <text class="card-title">{{ act.title }}</text>
- </view>
- </view>
- <!-- 内容区域 -->
- <view class="card-content">
- <!-- 时间和维度标签 -->
- <view class="card-meta-row">
- <view class="card-time">
- <text class="meta-icon">🕒</text>
- <text class="meta-text">{{ formatTime(act.startTime) }}</text>
- </view>
- <view class="card-badges" v-if="act._badges && act._badges.length">
- <text
- class="card-badge"
- v-for="badge in act._badges"
- :key="badge.name"
- :style="{ color: badge.color, borderColor: badge.color + '40' }"
- >
- {{ badge.name }}
- </text>
- </view>
- </view>
- <!-- 底部:价格和按钮 -->
- <view class="card-footer">
- <view class="card-price-wrap">
- <text class="card-price" v-if="act.priceLabel">{{ act.priceLabel }}</text>
- <text class="card-price" v-else-if="act.price && act.price > 0">{{ formatPriceWithSymbol(act.price) }}</text>
- <text class="card-price-free" v-else>免费</text>
- </view>
- <view class="card-action-btn">
- <text class="card-action-text">了解详情</text>
- </view>
- </view>
- </view>
- </view>
- </view>
- </view>
- </template>
- <script>
- var DIMENSION_MAP = {
- body: { name: '身', color: '#FF8C42', icon: '🌏' },
- mind: { name: '心', color: '#FF6B9D', icon: '🔥' },
- wisdom: { name: '智', color: '#6366F1', icon: '⚡' },
- action: { name: '行', color: '#10B981', icon: '🌿' },
- wealth: { name: '富', color: '#F59E0B', icon: '💧' }
- }
- export default {
- props: {
- dimensionCode: { type: String, required: true },
- activities: { type: Array, default: function() { return [] } },
- isLoggedIn: { type: Boolean, default: false }
- },
- data: function() {
- return {}
- },
- computed: {
- displayActivities: function() {
- var self = this
- return (this.activities || []).slice(0, 3).map(function(item) {
- item._badges = self.parseWeights(item.dimensionWeights)
- return item
- })
- },
- dimensionIcon: function() {
- var map = { body: '🌏', mind: '🔥', wisdom: '⚡', action: '🌿', wealth: '💧' }
- return map[this.dimensionCode] || '🎯'
- }
- },
- methods: {
- parseWeights: function(str) {
- if (!str) return []
- try {
- var obj = JSON.parse(str)
- var result = []
- for (var key in obj) {
- if (obj[key] > 0 && DIMENSION_MAP[key]) {
- result.push({ name: DIMENSION_MAP[key].name, color: DIMENSION_MAP[key].color, weight: obj[key] })
- }
- }
- return result
- } catch (e) {
- return []
- }
- },
- statusText: function(status) {
- var map = { upcoming: '即将开始', ongoing: '进行中', ended: '已结束', cancelled: '已取消' }
- return map[status] || '即将开始'
- },
- formatTime: function(timeStr) {
- if (!timeStr) return '待定'
- var d = new Date(timeStr)
- if (isNaN(d.getTime())) return timeStr
- var month = d.getMonth() + 1
- var day = d.getDate()
- var hour = d.getHours()
- var minute = d.getMinutes() < 10 ? '0' + d.getMinutes() : d.getMinutes()
- return month + '月' + day + '日 ' + hour + ':' + minute
- },
- formatPriceWithSymbol: function(price) {
- if (price == null) return '免费'
- return '¥' + (Number(price) / 100).toFixed(0)
- }
- }
- }
- </script>
- <style scoped>
- .section {
- margin: 24rpx 20rpx;
- }
- /* ===== 头部 ===== */
- .section-header {
- display: flex;
- flex-direction: row;
- justify-content: space-between;
- align-items: center;
- margin-bottom: 24rpx;
- }
- .section-header-left {
- display: flex;
- flex-direction: row;
- align-items: center;
- }
- .section-icon-wrap {
- width: 44rpx;
- height: 44rpx;
- border-radius: 12rpx;
- background: linear-gradient(135deg, #FF6B9D, #FF8FB1);
- display: flex;
- align-items: center;
- justify-content: center;
- margin-right: 12rpx;
- }
- .section-icon {
- font-size: 24rpx;
- }
- .section-title {
- font-size: 32rpx;
- font-weight: 700;
- color: #1E293B;
- letter-spacing: 1rpx;
- }
- .section-more {
- display: flex;
- flex-direction: row;
- align-items: center;
- padding: 8rpx 20rpx;
- background: #F1F5F9;
- border-radius: 24rpx;
- }
- .section-more-text {
- font-size: 24rpx;
- color: #64748B;
- margin-right: 4rpx;
- }
- .section-more-arrow {
- font-size: 20rpx;
- color: #94A3B8;
- }
- /* ===== 空状态 ===== */
- .empty-state {
- display: flex;
- flex-direction: column;
- align-items: center;
- padding: 60rpx 0;
- }
- .empty-icon {
- font-size: 64rpx;
- margin-bottom: 16rpx;
- }
- .empty-tip {
- font-size: 26rpx;
- color: #94A3B8;
- }
- /* ===== 活动列表 ===== */
- .activity-list {
- display: flex;
- flex-direction: column;
- gap: 24rpx;
- }
- /* ===== 活动卡片 ===== */
- .activity-card {
- background: #FFFFFF;
- border-radius: 24rpx;
- overflow: hidden;
- box-shadow: 0 4rpx 20rpx rgba(0, 0, 0, 0.06), 0 1rpx 3rpx rgba(0, 0, 0, 0.04);
- transition: transform 0.2s ease;
- }
- .activity-card:active {
- transform: scale(0.98);
- }
- /* 封面图 */
- .card-cover {
- position: relative;
- width: 100%;
- height: 320rpx;
- overflow: hidden;
- }
- .card-cover-img {
- width: 100%;
- height: 100%;
- background: #F1F5F9;
- }
- .card-cover-mask {
- position: absolute;
- bottom: 0;
- left: 0;
- right: 0;
- height: 200rpx;
- background: linear-gradient(to top, rgba(0, 0, 0, 0.5), transparent);
- }
- .card-rank {
- position: absolute;
- top: 16rpx;
- left: 16rpx;
- width: 48rpx;
- height: 48rpx;
- border-radius: 14rpx;
- background: rgba(0, 0, 0, 0.5);
- backdrop-filter: blur(10rpx);
- display: flex;
- align-items: center;
- justify-content: center;
- }
- .card-rank-num {
- font-size: 24rpx;
- font-weight: 700;
- color: #FFFFFF;
- }
- .card-status {
- position: absolute;
- top: 16rpx;
- right: 16rpx;
- display: flex;
- flex-direction: row;
- align-items: center;
- padding: 6rpx 16rpx;
- border-radius: 20rpx;
- background: rgba(0, 0, 0, 0.4);
- backdrop-filter: blur(10rpx);
- }
- .card-status-dot {
- width: 8rpx;
- height: 8rpx;
- border-radius: 50%;
- background: #22C55E;
- margin-right: 6rpx;
- animation: pulse 2s infinite;
- }
- @keyframes pulse {
- 0%, 100% { opacity: 1; }
- 50% { opacity: 0.4; }
- }
- .card-status-text {
- font-size: 22rpx;
- color: #FFFFFF;
- font-weight: 500;
- }
- .card-title-wrap {
- position: absolute;
- bottom: 16rpx;
- left: 20rpx;
- right: 20rpx;
- }
- .card-title {
- font-size: 32rpx;
- font-weight: 700;
- color: #FFFFFF;
- text-shadow: 0 2rpx 8rpx rgba(0, 0, 0, 0.3);
- display: -webkit-box;
- -webkit-line-clamp: 2;
- -webkit-box-orient: vertical;
- overflow: hidden;
- }
- /* 内容区域 */
- .card-content {
- padding: 20rpx;
- }
- .card-meta-row {
- display: flex;
- flex-direction: row;
- align-items: center;
- margin-bottom: 16rpx;
- }
- .card-time {
- display: flex;
- flex-direction: row;
- align-items: center;
- margin-right: 16rpx;
- }
- .meta-icon {
- font-size: 22rpx;
- margin-right: 6rpx;
- }
- .meta-text {
- font-size: 24rpx;
- color: #64748B;
- }
- .card-badges {
- display: flex;
- flex-direction: row;
- flex-wrap: wrap;
- gap: 8rpx;
- }
- .card-badge {
- font-size: 20rpx;
- padding: 4rpx 12rpx;
- border-radius: 20rpx;
- border: 1rpx solid;
- font-weight: 500;
- }
- /* 底部 */
- .card-footer {
- display: flex;
- flex-direction: row;
- justify-content: space-between;
- align-items: center;
- padding-top: 16rpx;
- border-top: 1rpx solid #F1F5F9;
- }
- .card-price-wrap {
- display: flex;
- flex-direction: row;
- align-items: baseline;
- }
- .card-price {
- font-size: 30rpx;
- font-weight: 700;
- color: #FF6B9D;
- }
- .card-price-free {
- font-size: 28rpx;
- font-weight: 600;
- color: #22C55E;
- }
- .card-action-btn {
- padding: 10rpx 24rpx;
- background: linear-gradient(135deg, #FF6B9D, #FF8FB1);
- border-radius: 24rpx;
- }
- .card-action-text {
- font-size: 22rpx;
- color: #FFFFFF;
- font-weight: 600;
- }
- .card-action-btn:active {
- opacity: 0.8;
- }
- </style>
|