bindbox-mini/components/activity/LotteryResultPopup.vue

411 lines
9.5 KiB
Vue

<template>
<view v-if="visible" class="lottery-overlay" @touchmove.stop.prevent>
<!-- 背景光效 -->
<view class="bg-glow"></view>
<view class="bg-rays"></view>
<!-- 彩带粒子 -->
<view class="confetti-container">
<view v-for="i in 20" :key="i" class="confetti" :style="getConfettiStyle(i)"></view>
</view>
<!-- 主内容区 -->
<view class="lottery-content">
<!-- 中奖标题 -->
<view class="title-area">
<view class="crown-icon">🎉</view>
<text class="main-title">恭喜中奖</text>
</view>
<!-- 奖品展示区 -->
<scroll-view scroll-y class="prizes-scroll">
<view class="prizes-grid">
<view
v-for="(item, index) in groupedResults"
:key="index"
class="prize-card"
:style="{ animationDelay: `${0.2 + index * 0.15}s` }"
>
<!-- 光效边框 -->
<view class="card-glow-border"></view>
<!-- 卡片内容 -->
<view class="card-inner">
<view class="qty-badge" v-if="item.quantity > 1">x{{ item.quantity }}</view>
<view class="image-wrap">
<image
v-if="item.image"
class="prize-img"
:src="item.image"
mode="aspectFill"
@tap="previewImage(item.image)"
/>
<view v-else class="prize-placeholder">🎁</view>
</view>
<view class="prize-details">
<text class="prize-name">{{ item.title }}</text>
</view>
</view>
</view>
</view>
</scroll-view>
<!-- 底部按钮 -->
<view class="action-area">
<view class="claim-btn" @tap="handleClose">
<view class="btn-glow"></view>
<view class="btn-inner">
<text class="btn-icon"></text>
<text class="btn-text">收下奖励</text>
</view>
</view>
</view>
</view>
</view>
</template>
<script setup>
import { computed } from 'vue'
const props = defineProps({
visible: { type: Boolean, default: false },
results: { type: Array, default: () => [] }
})
const emit = defineEmits(['update:visible', 'close'])
const groupedResults = computed(() => {
const map = new Map()
const arr = Array.isArray(props.results) ? props.results : []
arr.forEach(item => {
const key = item.title || item.name || '神秘奖品'
if (map.has(key)) {
map.get(key).quantity++
} else {
map.set(key, {
title: key,
image: item.image || item.img || item.pic || '',
quantity: 1
})
}
})
return Array.from(map.values())
})
function getConfettiStyle(i) {
const colors = ['#FF6B35', '#FFD93D', '#6BCB77', '#4D96FF', '#FF6B6B', '#C9B1FF']
const left = Math.random() * 100
const delay = Math.random() * 2
const duration = 2 + Math.random() * 2
const size = 8 + Math.random() * 8
return {
left: `${left}%`,
animationDelay: `${delay}s`,
animationDuration: `${duration}s`,
width: `${size}rpx`,
height: `${size * 1.5}rpx`,
background: colors[i % colors.length]
}
}
function handleClose() {
emit('update:visible', false)
emit('close')
}
function previewImage(url) {
if (url) uni.previewImage({ urls: [url], current: url })
}
</script>
<style lang="scss" scoped>
.lottery-overlay {
position: fixed;
top: 0; left: 0; right: 0; bottom: 0;
z-index: 2000;
display: flex;
align-items: center;
justify-content: center;
background: radial-gradient(ellipse at center, rgba(30, 20, 50, 0.95) 0%, rgba(10, 5, 20, 0.98) 100%);
animation: fadeIn 0.3s ease-out;
}
@keyframes fadeIn {
from { opacity: 0; }
to { opacity: 1; }
}
/* 背景光效 */
.bg-glow {
position: absolute;
top: 20%; left: 50%;
transform: translateX(-50%);
width: 600rpx; height: 600rpx;
background: radial-gradient(circle, rgba(255, 180, 100, 0.4) 0%, transparent 70%);
filter: blur(60rpx);
animation: pulse 3s ease-in-out infinite;
}
.bg-rays {
position: absolute;
top: 15%; left: 50%;
transform: translateX(-50%);
width: 800rpx; height: 800rpx;
background: conic-gradient(from 0deg, transparent, rgba(255, 200, 100, 0.1), transparent, rgba(255, 200, 100, 0.1), transparent);
animation: rotate 20s linear infinite;
}
@keyframes rotate { to { transform: translateX(-50%) rotate(360deg); } }
@keyframes pulse { 0%, 100% { opacity: 0.6; transform: translateX(-50%) scale(1); } 50% { opacity: 1; transform: translateX(-50%) scale(1.1); } }
/* 彩带 */
.confetti-container {
position: absolute;
top: 0; left: 0; right: 0;
height: 100%;
overflow: hidden;
pointer-events: none;
}
.confetti {
position: absolute;
top: -20rpx;
border-radius: 4rpx;
animation: confettiFall 3s linear infinite;
}
@keyframes confettiFall {
0% { transform: translateY(-20rpx) rotate(0deg); opacity: 1; }
100% { transform: translateY(100vh) rotate(720deg); opacity: 0; }
}
/* 主内容 */
.lottery-content {
position: relative;
width: 88%;
max-height: 80vh;
display: flex;
flex-direction: column;
align-items: center;
animation: contentPop 0.5s cubic-bezier(0.34, 1.56, 0.64, 1);
}
@keyframes contentPop {
from { opacity: 0; transform: scale(0.8) translateY(40rpx); }
to { opacity: 1; transform: scale(1) translateY(0); }
}
/* 标题区 */
.title-area {
position: relative;
text-align: center;
margin-bottom: 40rpx;
z-index: 10;
}
.crown-icon {
font-size: 80rpx;
display: block;
animation: bounce 2s ease-in-out infinite;
}
@keyframes bounce {
0%, 100% { transform: translateY(0); }
50% { transform: translateY(-10rpx); }
}
.main-title {
font-size: 56rpx;
font-weight: 900;
color: #fff;
text-shadow: 0 0 40rpx rgba(255, 180, 100, 0.8), 0 4rpx 20rpx rgba(0, 0, 0, 0.5);
display: block;
letter-spacing: 8rpx;
}
/* 奖品滚动区 */
.prizes-scroll {
width: 100%;
max-height: 50vh;
padding: 0 10rpx;
}
.prizes-grid {
display: flex;
flex-wrap: wrap;
gap: 24rpx;
justify-content: center;
padding: 20rpx 0;
}
/* 奖品卡片 */
.prize-card {
position: relative;
width: calc(50% - 12rpx);
max-width: 300rpx;
animation: cardReveal 0.6s ease-out backwards;
}
@keyframes cardReveal {
from { opacity: 0; transform: scale(0.8) rotateY(-30deg); }
to { opacity: 1; transform: scale(1) rotateY(0); }
}
.card-glow-border {
position: absolute;
inset: -4rpx;
background: linear-gradient(135deg, #FFD700, #FF8C00, #FFD700, #FF6347, #FFD700);
background-size: 400% 400%;
border-radius: 28rpx;
animation: borderGlow 3s ease infinite;
z-index: 0;
}
@keyframes borderGlow {
0%, 100% { background-position: 0% 50%; }
50% { background-position: 100% 50%; }
}
.card-inner {
position: relative;
background: linear-gradient(145deg, rgba(255, 255, 255, 0.98), rgba(255, 248, 240, 0.95));
border-radius: 24rpx;
padding: 24rpx;
display: flex;
flex-direction: column;
align-items: center;
z-index: 1;
}
.qty-badge {
position: absolute;
top: -12rpx; right: -12rpx;
background: linear-gradient(135deg, #FF6B35, #FF8C00);
color: #fff;
font-size: 24rpx;
font-weight: 800;
padding: 8rpx 16rpx;
border-radius: 20rpx;
box-shadow: 0 4rpx 16rpx rgba(255, 107, 53, 0.5);
z-index: 10;
}
.image-wrap {
width: 160rpx; height: 160rpx;
border-radius: 16rpx;
overflow: hidden;
background: linear-gradient(145deg, #FFF8F3, #FFE8D1);
display: flex;
align-items: center;
justify-content: center;
box-shadow: 0 8rpx 24rpx rgba(0, 0, 0, 0.1);
}
.prize-img {
width: 100%; height: 100%;
}
.prize-placeholder {
font-size: 64rpx;
}
.prize-details {
margin-top: 16rpx;
text-align: center;
width: 100%;
}
.prize-name {
font-size: 24rpx;
font-weight: 700;
color: #333;
display: -webkit-box;
-webkit-line-clamp: 2;
-webkit-box-orient: vertical;
overflow: hidden;
line-height: 1.4;
}
/* 底部按钮 - 重新设计 */
.action-area {
width: 100%;
padding: 40rpx 20rpx 20rpx;
}
.claim-btn {
position: relative;
width: 100%;
height: 110rpx;
display: flex;
align-items: center;
justify-content: center;
&:active .btn-inner {
transform: scale(0.96);
}
}
.btn-glow {
position: absolute;
inset: 0;
background: linear-gradient(135deg, #FFD700, #FF8C00, #FF6B35);
border-radius: 55rpx;
filter: blur(15rpx);
opacity: 0.6;
animation: btnPulse 2s ease-in-out infinite;
}
@keyframes btnPulse {
0%, 100% { opacity: 0.4; transform: scale(1); }
50% { opacity: 0.7; transform: scale(1.02); }
}
.btn-inner {
position: relative;
width: 100%;
height: 100%;
background: linear-gradient(135deg, #FFD700 0%, #FF8C00 50%, #FF6B35 100%);
border-radius: 55rpx;
display: flex;
align-items: center;
justify-content: center;
gap: 12rpx;
box-shadow:
0 8rpx 32rpx rgba(255, 140, 0, 0.5),
inset 0 2rpx 0 rgba(255, 255, 255, 0.4),
inset 0 -2rpx 0 rgba(0, 0, 0, 0.1);
transition: transform 0.2s ease;
overflow: hidden;
&::before {
content: '';
position: absolute;
top: 0; left: -100%;
width: 100%; height: 100%;
background: linear-gradient(90deg, transparent, rgba(255, 255, 255, 0.3), transparent);
animation: btnShine 2.5s ease-in-out infinite;
}
}
@keyframes btnShine {
0% { left: -100%; }
50%, 100% { left: 100%; }
}
.btn-icon {
font-size: 36rpx;
}
.btn-text {
font-size: 34rpx;
font-weight: 800;
color: #fff;
text-shadow: 0 2rpx 4rpx rgba(0, 0, 0, 0.2);
letter-spacing: 4rpx;
}
</style>