feat(stargalaxy): add PodiumCard for TOP 1-3 with gold/silver/bronze labels

This commit is contained in:
zheng020 2026-06-10 15:20:18 +08:00
parent a870943dc1
commit 2bd5733ca0

View File

@ -0,0 +1,163 @@
<template>
<view class="podium-card" :class="['podium-' + rank]" :style="cardStyle" @click="handleClick">
<!-- 钻石渐变外框 -->
<view class="diamond-frame" :style="frameStyle"></view>
<!-- 藏品主图不规则圆角 -->
<view class="cover-wrap">
<image
class="cover-image"
:src="item.cover_url || item.cover_image || ''"
mode="aspectFill"
/>
<!-- 青绿色高光 overlay -->
<view class="cover-highlight"></view>
</view>
<!-- 钻石渐变边框层 -->
<view class="diamond-border"></view>
<!-- 皇冠 TOP 1 -->
<view v-if="rank === 4" class="crown">👑</view>
<!-- TOP N 标签cover 下方居中 -->
<view class="top-label" :style="labelStyle">TOP {{ displayRank }}</view>
</view>
</template>
<script setup>
import { computed } from 'vue'
const props = defineProps({
item: { type: Object, required: true },
rank: { type: Number, required: true, validator: v => v >= 4 && v <= 6 },
size: { type: Object, default: () => ({ width: 120, height: 130 }) },
})
const emit = defineEmits(['cardClick'])
// rank 4/5/6 1/2/3TOP 1/2/3
const displayRank = computed(() => props.rank - 3)
// rank
const labelGradients = {
4: 'radial-gradient(ellipse, #FFD700, #FFF6A8 30%, #DAA520 100%)', //
5: 'radial-gradient(ellipse, #C0C0C0, #E8E8E8 50%, #7A7A7A)', //
6: 'radial-gradient(ellipse, #CD7F32, #E8A45C 50%, #A0522D)', //
}
const labelSizes = {
4: { w: 96, h: 22, font: 13 },
5: { w: 78, h: 18, font: 11 },
6: { w: 78, h: 18, font: 11 },
}
// //
const frameGradients = {
4: 'radial-gradient(ellipse at -10% 5%, #FFD700 0%, #FF3939 32%, #FFEDA5 59%, #FF6B6B 100%)',
5: 'radial-gradient(ellipse at -10% 5%, #C0C0C0 0%, #FF6B6B 32%, #E8E8E8 59%, #9A9A9A 100%)',
6: 'radial-gradient(ellipse at -10% 5%, #CD7F32 0%, #FF3939 32%, #E8A45C 59%, #A0522D 100%)',
}
const cardStyle = {
width: props.size.width + 'rpx',
height: props.size.height + 'rpx',
}
const frameStyle = {
background: frameGradients[props.rank],
}
const labelStyle = {
width: labelSizes[props.rank].w + 'rpx',
height: labelSizes[props.rank].h + 'rpx',
fontSize: labelSizes[props.rank].font + 'rpx',
background: labelGradients[props.rank],
}
function handleClick() {
emit('cardClick', props.item)
}
</script>
<style scoped>
.podium-card {
position: relative;
display: flex;
align-items: center;
justify-content: center;
}
.diamond-frame {
position: absolute;
inset: 0;
border-radius: 8rpx 44rpx 8rpx 38rpx;
filter: blur(6rpx);
opacity: 0.7;
}
.cover-wrap {
position: absolute;
inset: 10rpx;
border-radius: 6rpx 40rpx 6rpx 34rpx;
overflow: hidden;
background: #fff;
box-shadow: 4rpx 4rpx 28rpx rgba(127, 7, 7, 0.5);
}
.cover-image {
width: 100%;
height: 100%;
position: absolute;
inset: 0;
}
.cover-highlight {
position: absolute;
top: 0;
left: 0;
width: 50%;
height: 42%;
background: linear-gradient(180deg, rgba(83, 244, 211, 0.4) 1%, transparent 70%);
pointer-events: none;
}
.diamond-border {
position: absolute;
inset: 8rpx;
border: 4rpx solid transparent;
border-radius: 8rpx 42rpx 8rpx 36rpx;
background: linear-gradient(135deg, #86BEFF, #FF3939, #88FFCE, #4D9AF8) border-box;
-webkit-mask: linear-gradient(#fff 0 0) padding-box, linear-gradient(#fff 0 0);
-webkit-mask-composite: xor;
mask-composite: exclude;
pointer-events: none;
}
.crown {
position: absolute;
top: -44rpx;
left: 50%;
transform: translateX(-50%);
font-size: 44rpx;
animation: crownPulse 2s ease-in-out infinite;
z-index: 5;
}
.top-label {
position: absolute;
bottom: -4rpx;
left: 50%;
transform: translateX(-50%);
border-radius: 11rpx;
display: flex;
align-items: center;
justify-content: center;
font-weight: 900;
color: #fff;
text-shadow: 0 1rpx 3rpx rgba(0, 0, 0, 0.5);
box-shadow: 0 6rpx 16rpx rgba(255, 140, 0, 0.5);
z-index: 6;
letter-spacing: 1rpx;
}
</style>