290 lines
6.1 KiB
Vue
290 lines
6.1 KiB
Vue
<template>
|
||
<view v-if="visible && activity" class="prize-claim-overlay" @touchmove.stop.prevent>
|
||
<view class="prize-claim-mask" @tap="handleClose"></view>
|
||
<view class="prize-claim-panel" @tap.stop>
|
||
<view class="prize-claim-hero">
|
||
<view class="hero-glow"></view>
|
||
<view class="hero-top">
|
||
<view class="hero-badge">奖励发放</view>
|
||
<text class="prize-claim-close" @tap="handleClose">×</text>
|
||
</view>
|
||
<view class="hero-content">
|
||
<text class="hero-title">奖励已到账,待你领取</text>
|
||
<text class="hero-reason">{{ activity.reason }}</text>
|
||
</view>
|
||
</view>
|
||
|
||
<view class="prize-claim-body">
|
||
<view class="section-title">奖品内容</view>
|
||
<view class="reward-list">
|
||
<view
|
||
v-for="(item, index) in activity.rewards || []"
|
||
:key="`${item.reward_type}-${item.reward_ref_id}-${index}`"
|
||
class="reward-item"
|
||
>
|
||
<view class="reward-thumb-wrap">
|
||
<image v-if="item.image" class="reward-thumb" :src="item.image" mode="aspectFill" />
|
||
<view v-else class="reward-thumb-empty">{{ typeShortLabel(item.reward_type) }}</view>
|
||
</view>
|
||
<view class="reward-main">
|
||
<text class="reward-name">{{ item.name || item.reward_type }}</text>
|
||
<view class="reward-meta-row">
|
||
<text class="reward-type">{{ typeLabel(item.reward_type) }}</text>
|
||
<text v-if="item.value_cents > 0" class="reward-value">单价 ¥{{ (item.value_cents / 100).toFixed(2) }}</text>
|
||
</view>
|
||
</view>
|
||
<view class="reward-side">
|
||
<text class="reward-quantity">x{{ item.quantity }}</text>
|
||
</view>
|
||
</view>
|
||
</view>
|
||
</view>
|
||
|
||
<view class="prize-claim-footer">
|
||
<button class="claim-button" :disabled="loading" @tap="handleClaim">
|
||
{{ loading ? '领取中...' : '立即领取' }}
|
||
</button>
|
||
</view>
|
||
</view>
|
||
</view>
|
||
</template>
|
||
|
||
<script setup>
|
||
const props = defineProps({
|
||
visible: { type: Boolean, default: false },
|
||
activity: { type: Object, default: null },
|
||
loading: { type: Boolean, default: false }
|
||
})
|
||
|
||
const emit = defineEmits(['update:visible', 'claim', 'close'])
|
||
|
||
function handleClose() {
|
||
emit('close')
|
||
emit('update:visible', false)
|
||
}
|
||
|
||
function handleClaim() {
|
||
if (!props.loading) emit('claim')
|
||
}
|
||
|
||
function typeLabel(type) {
|
||
if (type === 'product') return '商品'
|
||
if (type === 'coupon') return '优惠券'
|
||
if (type === 'item_card') return '道具卡'
|
||
return type
|
||
}
|
||
|
||
function typeShortLabel(type) {
|
||
if (type === 'product') return '商品'
|
||
if (type === 'coupon') return '券'
|
||
if (type === 'item_card') return '卡'
|
||
return '奖'
|
||
}
|
||
</script>
|
||
|
||
<style lang="scss" scoped>
|
||
.prize-claim-overlay {
|
||
position: fixed;
|
||
inset: 0;
|
||
z-index: 1001;
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: center;
|
||
}
|
||
|
||
.prize-claim-mask {
|
||
position: absolute;
|
||
inset: 0;
|
||
background: rgba(0, 0, 0, 0.55);
|
||
backdrop-filter: blur(6rpx);
|
||
}
|
||
|
||
.prize-claim-panel {
|
||
position: relative;
|
||
width: 88%;
|
||
max-height: 78vh;
|
||
background: $bg-card;
|
||
border-radius: $radius-xl;
|
||
overflow: hidden;
|
||
box-shadow: $shadow-lg;
|
||
animation: slideUp 0.25s ease-out;
|
||
}
|
||
|
||
.prize-claim-hero {
|
||
position: relative;
|
||
padding: $spacing-lg $spacing-lg $spacing-xl;
|
||
background: linear-gradient(135deg, $brand-primary 0%, $brand-primary-light 100%);
|
||
}
|
||
|
||
.hero-glow {
|
||
position: absolute;
|
||
right: -80rpx;
|
||
top: -80rpx;
|
||
width: 260rpx;
|
||
height: 260rpx;
|
||
border-radius: 50%;
|
||
background: rgba(255, 255, 255, 0.16);
|
||
}
|
||
|
||
.hero-top {
|
||
position: relative;
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: space-between;
|
||
margin-bottom: $spacing-lg;
|
||
}
|
||
|
||
.hero-badge {
|
||
padding: 8rpx 18rpx;
|
||
border-radius: 999rpx;
|
||
background: rgba(255, 255, 255, 0.18);
|
||
color: #fff;
|
||
font-size: $font-xs;
|
||
font-weight: 700;
|
||
}
|
||
|
||
.prize-claim-close {
|
||
color: rgba(255, 255, 255, 0.9);
|
||
font-size: 48rpx;
|
||
line-height: 1;
|
||
}
|
||
|
||
.hero-content {
|
||
position: relative;
|
||
}
|
||
|
||
.hero-title {
|
||
display: block;
|
||
font-size: 40rpx;
|
||
font-weight: 700;
|
||
color: #fff;
|
||
line-height: 1.3;
|
||
margin-bottom: 12rpx;
|
||
}
|
||
|
||
.hero-reason {
|
||
display: block;
|
||
font-size: $font-md;
|
||
color: rgba(255, 255, 255, 0.92);
|
||
line-height: 1.6;
|
||
}
|
||
|
||
.prize-claim-body {
|
||
padding: $spacing-lg;
|
||
}
|
||
|
||
.section-title {
|
||
font-size: $font-md;
|
||
font-weight: 700;
|
||
color: $text-main;
|
||
margin-bottom: $spacing-md;
|
||
}
|
||
|
||
.reward-list {
|
||
display: flex;
|
||
flex-direction: column;
|
||
gap: $spacing-md;
|
||
}
|
||
|
||
.reward-item {
|
||
display: flex;
|
||
align-items: center;
|
||
gap: $spacing-md;
|
||
background: $bg-page;
|
||
border-radius: $radius-lg;
|
||
padding: $spacing-md;
|
||
}
|
||
|
||
.reward-thumb-wrap {
|
||
width: 112rpx;
|
||
height: 112rpx;
|
||
border-radius: $radius-md;
|
||
overflow: hidden;
|
||
flex-shrink: 0;
|
||
background: #fff;
|
||
box-shadow: $shadow-sm;
|
||
}
|
||
|
||
.reward-thumb {
|
||
width: 100%;
|
||
height: 100%;
|
||
}
|
||
|
||
.reward-thumb-empty {
|
||
width: 100%;
|
||
height: 100%;
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: center;
|
||
font-size: $font-sm;
|
||
color: $text-sub;
|
||
background: rgba($brand-primary, 0.08);
|
||
}
|
||
|
||
.reward-main {
|
||
flex: 1;
|
||
display: flex;
|
||
flex-direction: column;
|
||
gap: 10rpx;
|
||
min-width: 0;
|
||
}
|
||
|
||
.reward-name {
|
||
font-size: $font-md;
|
||
color: $text-main;
|
||
font-weight: 700;
|
||
line-height: 1.4;
|
||
}
|
||
|
||
.reward-meta-row {
|
||
display: flex;
|
||
flex-wrap: wrap;
|
||
gap: 12rpx;
|
||
}
|
||
|
||
.reward-type,
|
||
.reward-value,
|
||
.reward-quantity {
|
||
font-size: $font-sm;
|
||
color: $text-sub;
|
||
}
|
||
|
||
.reward-value {
|
||
color: $brand-primary-dark;
|
||
font-weight: 600;
|
||
}
|
||
|
||
.reward-side {
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: center;
|
||
min-width: 64rpx;
|
||
}
|
||
|
||
.reward-quantity {
|
||
font-size: $font-md;
|
||
font-weight: 700;
|
||
color: $brand-primary;
|
||
}
|
||
|
||
.prize-claim-footer {
|
||
padding: 0 $spacing-lg $spacing-lg;
|
||
}
|
||
|
||
.claim-button {
|
||
width: 100%;
|
||
border: none;
|
||
border-radius: $radius-round;
|
||
background: linear-gradient(135deg, $brand-primary, $brand-primary-light);
|
||
color: #fff;
|
||
font-size: $font-md;
|
||
font-weight: 700;
|
||
padding: 24rpx 0;
|
||
box-shadow: $shadow-warm;
|
||
}
|
||
|
||
.claim-button[disabled] {
|
||
opacity: 0.65;
|
||
}
|
||
</style>
|