bindbox-mini/components/activity/ActivityHeader.vue

263 lines
6.1 KiB
Vue

<template>
<view class="header-card animate-enter">
<image class="header-cover" :src="coverUrl" mode="aspectFill" />
<view class="header-info">
<view class="header-title">{{ title }}</view>
<view class="header-price-row" v-if="price !== undefined">
<text class="price-symbol">¥</text>
<text class="price-num">{{ formattedPrice }}</text>
<text class="price-unit">{{ priceUnit }}</text>
</view>
<view class="header-time-row" v-if="scheduledTime">
<text class="time-label">本期结束</text>
<text class="time-value">{{ scheduledTime }}</text>
</view>
<view class="header-tags" v-if="tags && tags.length">
<view class="tag-item" v-for="(tag, idx) in tags" :key="idx">{{ tag }}</view>
</view>
</view>
<view class="header-actions">
<view class="action-btn" @tap="$emit('show-rules')">
<view class="action-icon rules-icon"></view>
<text class="action-label">规则</text>
</view>
<view class="action-btn" @tap="$emit('go-cabinet')">
<view class="action-icon cabinet-icon"></view>
<text class="action-label">盒柜</text>
</view>
</view>
</view>
</template>
<script setup>
import { computed } from 'vue'
const props = defineProps({
title: {
type: String,
default: ''
},
price: {
type: Number,
default: undefined
},
priceUnit: {
type: String,
default: '/发'
},
coverUrl: {
type: String,
default: ''
},
tags: {
type: Array,
default: () => []
},
scheduledTime: {
type: String,
default: ''
}
})
defineEmits(['show-rules', 'go-cabinet'])
const formattedPrice = computed(() => {
const cents = Number(props.price || 0)
return (cents / 100).toFixed(2)
})
</script>
<style lang="scss" scoped>
/* ============================================
头部卡片 - 与原始设计完全一致
============================================ */
.header-card {
margin: $spacing-xl $spacing-lg;
background: rgba($bg-card, 0.72);
backdrop-filter: blur(32rpx);
border-radius: $radius-xl;
padding: $spacing-lg;
display: flex;
align-items: center;
box-shadow:
0 1rpx 0 rgba(255,255,255,0.5) inset,
0 -1rpx 0 rgba(0,0,0,0.02) inset,
$shadow-card;
border: none;
position: relative;
overflow: hidden;
&::before {
content: '';
position: absolute;
top: 0;
left: 0;
right: 0;
height: 2rpx;
background: linear-gradient(90deg, transparent, rgba(255,255,255,0.8), transparent);
}
}
.header-cover {
width: 180rpx;
height: 180rpx;
border-radius: $radius-md;
margin-right: $spacing-lg;
background: $bg-secondary;
box-shadow: $shadow-md;
flex-shrink: 0;
}
.header-info {
flex: 1;
min-width: 0;
display: flex;
flex-direction: column;
justify-content: center;
padding: 6rpx 0;
}
.header-title {
font-size: $font-xl;
font-weight: 800;
color: $text-main;
margin-bottom: $spacing-xs;
line-height: 1.3;
overflow: hidden;
text-overflow: ellipsis;
display: -webkit-box;
-webkit-line-clamp: 2;
-webkit-box-orient: vertical;
}
.header-price-row {
display: flex;
align-items: baseline;
color: $brand-primary;
margin-bottom: $spacing-sm;
text-shadow: 0 2rpx 4rpx rgba($brand-primary, 0.1);
}
.price-symbol {
font-size: $font-md;
font-weight: 700;
}
.price-num {
font-size: $font-xxl;
font-weight: 900;
margin: 0 4rpx;
font-family: 'DIN Alternate', sans-serif;
}
.price-unit {
font-size: $font-sm;
color: $text-sub;
margin-left: 4rpx;
}
.header-time-row {
display: flex;
align-items: center;
gap: 12rpx;
margin-bottom: $spacing-sm;
}
.time-label {
font-size: $font-xs;
color: $text-tertiary;
font-weight: 600;
}
.time-value {
font-size: $font-sm;
color: $text-sub;
font-weight: 600;
}
.header-tags {
display: flex;
gap: $spacing-xs;
flex-wrap: wrap;
}
.tag-item {
font-size: $font-xs;
color: $brand-primary-dark;
background: rgba($brand-primary, 0.08);
padding: 4rpx $spacing-sm;
border-radius: $radius-sm;
font-weight: 600;
border: 1rpx solid rgba($brand-primary, 0.1);
}
.header-actions {
display: flex;
flex-direction: column;
gap: 28rpx;
margin-left: 16rpx;
padding-left: 24rpx;
border-left: 2rpx solid #E8E8E8;
justify-content: center;
align-self: stretch;
}
.action-btn {
display: flex;
flex-direction: column;
align-items: center;
&:active {
opacity: 0.6;
}
}
.action-icon {
width: 44rpx;
height: 44rpx;
margin-bottom: 8rpx;
background-size: contain;
background-repeat: no-repeat;
background-position: center;
}
.rules-icon {
background-color: #999;
mask-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='currentColor'%3E%3Cpath d='M19 3H5c-1.1 0-2 .9-2 2v14c0 1.1.9 2 2 2h14c1.1 0 2-.9 2-2V5c0-1.1-.9-2-2-2zm-5 14H7v-2h7v2zm3-4H7v-2h10v2zm0-4H7V7h10v2z'/%3E%3C/svg%3E");
-webkit-mask-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='currentColor'%3E%3Cpath d='M19 3H5c-1.1 0-2 .9-2 2v14c0 1.1.9 2 2 2h14c1.1 0 2-.9 2-2V5c0-1.1-.9-2-2-2zm-5 14H7v-2h7v2zm3-4H7v-2h10v2zm0-4H7V7h10v2z'/%3E%3C/svg%3E");
mask-size: cover;
-webkit-mask-size: cover;
}
.cabinet-icon {
background-color: #999;
mask-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='currentColor'%3E%3Cpath d='M20 3H4c-1.1 0-2 .9-2 2v16l4-4h14c1.1 0 2-.9 2-2V5c0-1.1-.9-2-2-2zm-9 11H7v-2h4v2zm6-4H7V8h10v2z'/%3E%3C/svg%3E");
-webkit-mask-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='currentColor'%3E%3Cpath d='M20 3H4c-1.1 0-2 .9-2 2v16l4-4h14c1.1 0 2-.9 2-2V5c0-1.1-.9-2-2-2zm-9 11H7v-2h4v2zm6-4H7V8h10v2z'/%3E%3C/svg%3E");
mask-size: cover;
-webkit-mask-size: cover;
}
.action-label {
font-size: 22rpx;
color: #666;
letter-spacing: 1rpx;
}
/* 入场动画 */
.animate-enter {
animation: slideUp 0.5s ease-out both;
}
@keyframes slideUp {
from {
opacity: 0;
transform: translateY(20rpx);
}
to {
opacity: 1;
transform: translateY(0);
}
}
</style>