541 lines
12 KiB
Vue
541 lines
12 KiB
Vue
<template>
|
||
<view class="page-activity">
|
||
<!-- 顶部标题区 -->
|
||
<view class="header-section">
|
||
<view class="header-icon">
|
||
<u-icon name="gift-fill" :size="28" color="#FF6B35" />
|
||
</view>
|
||
<view class="header-content">
|
||
<text class="header-title">精彩活动</text>
|
||
<text class="header-subtitle">参与活动,赢取丰厚奖励</text>
|
||
</view>
|
||
</view>
|
||
|
||
<!-- 活动列表 -->
|
||
<view class="activity-list">
|
||
<!-- 邀请活动卡片 -->
|
||
<view
|
||
v-for="activity in activities"
|
||
:key="activity.id"
|
||
class="activity-card"
|
||
:class="getCardClass(activity.type)"
|
||
@tap="handleActivityClick(activity)"
|
||
>
|
||
<!-- 背景装饰 -->
|
||
<view class="card-bg-decoration">
|
||
<view class="decoration-circle decoration-circle-1"></view>
|
||
<view class="decoration-circle decoration-circle-2"></view>
|
||
</view>
|
||
|
||
<!-- 卡片内容 -->
|
||
<view class="card-main">
|
||
<view class="card-header">
|
||
<view class="activity-icon" :class="`icon-${activity.type}`">
|
||
<text class="icon-emoji">{{ activity.icon }}</text>
|
||
</view>
|
||
<view class="activity-info">
|
||
<text class="activity-title">{{ activity.title }}</text>
|
||
<text class="activity-desc">{{ activity.description }}</text>
|
||
</view>
|
||
</view>
|
||
|
||
<!-- 活动标签 -->
|
||
<view v-if="activity.type === 'invite' && activity.config" class="activity-tags">
|
||
<view v-if="activity.config.firstOrderRate" class="tag-item tag-highlight">
|
||
<u-icon name="star-fill" :size="14" color="#FF6B35" />
|
||
<text class="tag-text">首单返{{ (activity.config.firstOrderRate * 100).toFixed(1) }}%</text>
|
||
</view>
|
||
<view v-if="activity.config.maxCashback" class="tag-item">
|
||
<u-icon name="rmb-circle-fill" :size="14" color="#FFB800" />
|
||
<text class="tag-text">最高¥{{ activity.config.maxCashback }}</text>
|
||
</view>
|
||
<view v-if="activity.config.withdrawThreshold" class="tag-item">
|
||
<u-icon name="checkmark-circle-fill" :size="14" color="#52C41A" />
|
||
<text class="tag-text">满{{ activity.config.withdrawThreshold }}元可提现</text>
|
||
</view>
|
||
</view>
|
||
|
||
</view>
|
||
</view>
|
||
|
||
<!-- 更多活动占位卡片 -->
|
||
<view v-if="activities.length > 0" class="coming-soon-card">
|
||
<view class="coming-icon">
|
||
<u-icon name="clock" :size="40" color="#CCCCCC" />
|
||
</view>
|
||
<text class="coming-title">更多活动</text>
|
||
<text class="coming-desc">敬请期待</text>
|
||
</view>
|
||
|
||
<!-- 空状态 -->
|
||
<view v-if="activities.length === 0 && !loading" class="empty-state">
|
||
<view class="empty-icon-wrapper">
|
||
<u-icon name="calendar" :size="80" color="#CCCCCC" />
|
||
</view>
|
||
<text class="empty-title">暂无活动</text>
|
||
<text class="empty-desc">精彩活动即将上线,敬请期待</text>
|
||
</view>
|
||
|
||
<!-- 加载状态 -->
|
||
<view v-if="loading" class="loading-state">
|
||
<u-icon name="loading" :size="24" color="#999" />
|
||
<text class="loading-text">加载中...</text>
|
||
</view>
|
||
</view>
|
||
|
||
<!-- 底部提示 -->
|
||
<view v-if="activities.length > 0" class="bottom-tip">
|
||
<u-icon name="info-circle" :size="14" color="#999" />
|
||
<text class="tip-text">活动最终解释权归平台所有</text>
|
||
</view>
|
||
</view>
|
||
</template>
|
||
|
||
<script setup lang="ts">
|
||
import { ref, onMounted } from 'vue';
|
||
import { getActivityList } from '@/api/user/invite';
|
||
|
||
interface Activity {
|
||
id: number;
|
||
type: string;
|
||
title: string;
|
||
description: string;
|
||
icon: string;
|
||
enabled: boolean;
|
||
config?: {
|
||
maxCashback?: number;
|
||
firstOrderRate?: number;
|
||
secondOrderRate?: number;
|
||
withdrawThreshold?: number;
|
||
minCashback?: number;
|
||
maxOrderIndex?: number;
|
||
};
|
||
}
|
||
|
||
const activities = ref<Activity[]>([]);
|
||
const loading = ref(false);
|
||
|
||
onMounted(() => {
|
||
fetchActivities();
|
||
});
|
||
|
||
async function fetchActivities() {
|
||
loading.value = true;
|
||
try {
|
||
const res = await getActivityList();
|
||
const list = Array.isArray(res.data) ? res.data : (res.data?.list || []);
|
||
|
||
// 转换后端数据格式为前端需要的格式
|
||
activities.value = list
|
||
.filter((item: any) => item.enabled)
|
||
.map((item: any) => ({
|
||
id: item.id,
|
||
type: item.type === 'invite_cashback' ? 'invite' : item.type,
|
||
title: item.name || '活动',
|
||
description: getActivityDescription(item.type),
|
||
icon: getActivityIcon(item.type),
|
||
enabled: item.enabled,
|
||
config: item.config,
|
||
}));
|
||
} catch (e) {
|
||
console.error('获取活动列表失败:', e);
|
||
uni.showToast({ title: '加载失败,请重试', icon: 'none' });
|
||
} finally {
|
||
loading.value = false;
|
||
}
|
||
}
|
||
|
||
function getActivityDescription(type: string) {
|
||
const descMap: Record<string, string> = {
|
||
invite_cashback: '邀请好友下单,双方都有奖励',
|
||
coupon: '领取优惠券,享受更多优惠',
|
||
discount: '限时折扣,不容错过',
|
||
};
|
||
return descMap[type] || '参与活动,赢取奖励';
|
||
}
|
||
|
||
function getActivityIcon(type: string) {
|
||
const iconMap: Record<string, string> = {
|
||
invite_cashback: '🎁',
|
||
coupon: '🎫',
|
||
discount: '💰',
|
||
};
|
||
return iconMap[type] || '🎉';
|
||
}
|
||
|
||
function getCardClass(type: string) {
|
||
const classMap: Record<string, string> = {
|
||
invite: 'card-invite',
|
||
coupon: 'card-coupon',
|
||
discount: 'card-discount',
|
||
};
|
||
return classMap[type] || 'card-default';
|
||
}
|
||
|
||
function getButtonColor(type: string) {
|
||
const colorMap: Record<string, string> = {
|
||
invite: '#ffffff',
|
||
coupon: '#FF6B35',
|
||
discount: '#FF6B35',
|
||
};
|
||
return colorMap[type] || '#FF6B35';
|
||
}
|
||
|
||
function handleActivityClick(activity: Activity) {
|
||
if (!activity.enabled) {
|
||
uni.showToast({ title: '活动暂未开启', icon: 'none' });
|
||
return;
|
||
}
|
||
|
||
if (activity.type === 'invite') {
|
||
uni.navigateTo({ url: '/pages/invite/index' });
|
||
} else {
|
||
uni.showToast({ title: '活动详情开发中', icon: 'none' });
|
||
}
|
||
}
|
||
</script>
|
||
|
||
<style lang="scss" scoped>
|
||
@import '@/static/styles/design-tokens.scss';
|
||
@import '@/static/styles/mixins.scss';
|
||
|
||
.page-activity {
|
||
min-height: 100vh;
|
||
background: linear-gradient(180deg, #FFF5F0 0%, #F5F7FA 100%);
|
||
padding-bottom: 40rpx;
|
||
}
|
||
|
||
/* ========== 顶部标题区 ========== */
|
||
.header-section {
|
||
display: flex;
|
||
align-items: center;
|
||
gap: 20rpx;
|
||
padding: 40rpx 32rpx 32rpx;
|
||
}
|
||
|
||
.header-icon {
|
||
width: 72rpx;
|
||
height: 72rpx;
|
||
background: linear-gradient(135deg, #FFF5F0 0%, #FFE8DD 100%);
|
||
border-radius: 50%;
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: center;
|
||
box-shadow: 0 8rpx 24rpx rgba(255, 107, 53, 0.15);
|
||
}
|
||
|
||
.header-content {
|
||
flex: 1;
|
||
display: flex;
|
||
flex-direction: column;
|
||
gap: 8rpx;
|
||
}
|
||
|
||
.header-title {
|
||
font-size: 40rpx;
|
||
font-weight: 700;
|
||
color: #1A1A1A;
|
||
line-height: 1.2;
|
||
}
|
||
|
||
.header-subtitle {
|
||
font-size: 26rpx;
|
||
color: #666;
|
||
line-height: 1.4;
|
||
}
|
||
|
||
/* ========== 活动列表 ========== */
|
||
.activity-list {
|
||
padding: 0 24rpx;
|
||
display: flex;
|
||
flex-direction: column;
|
||
gap: 20rpx;
|
||
}
|
||
|
||
/* ========== 活动卡片 ========== */
|
||
.activity-card {
|
||
position: relative;
|
||
border-radius: 24rpx;
|
||
padding: 32rpx;
|
||
overflow: hidden;
|
||
box-shadow: 0 8rpx 24rpx rgba(0, 0, 0, 0.08);
|
||
transition: all 0.3s ease;
|
||
|
||
&:active {
|
||
transform: scale(0.98);
|
||
}
|
||
}
|
||
|
||
/* 邀请活动卡片 */
|
||
.card-invite {
|
||
background: linear-gradient(135deg, #FF8C5A 0%, #FF6B35 100%);
|
||
}
|
||
|
||
/* 优惠券活动卡片 */
|
||
.card-coupon {
|
||
background: linear-gradient(135deg, #4A90E2 0%, #357ABD 100%);
|
||
}
|
||
|
||
/* 折扣活动卡片 */
|
||
.card-discount {
|
||
background: linear-gradient(135deg, #52C41A 0%, #3D9614 100%);
|
||
}
|
||
|
||
/* 默认卡片 */
|
||
.card-default {
|
||
background: linear-gradient(135deg, #8B5CF6 0%, #7C3AED 100%);
|
||
}
|
||
|
||
/* 背景装饰 */
|
||
.card-bg-decoration {
|
||
position: absolute;
|
||
top: 0;
|
||
left: 0;
|
||
right: 0;
|
||
bottom: 0;
|
||
overflow: hidden;
|
||
pointer-events: none;
|
||
}
|
||
|
||
.decoration-circle {
|
||
position: absolute;
|
||
border-radius: 50%;
|
||
background: rgba(255, 255, 255, 0.1);
|
||
}
|
||
|
||
.decoration-circle-1 {
|
||
width: 200rpx;
|
||
height: 200rpx;
|
||
top: -80rpx;
|
||
right: -60rpx;
|
||
}
|
||
|
||
.decoration-circle-2 {
|
||
width: 120rpx;
|
||
height: 120rpx;
|
||
bottom: -40rpx;
|
||
left: -30rpx;
|
||
}
|
||
|
||
/* 卡片主体 */
|
||
.card-main {
|
||
position: relative;
|
||
z-index: 1;
|
||
}
|
||
|
||
.card-header {
|
||
display: flex;
|
||
align-items: flex-start;
|
||
gap: 20rpx;
|
||
margin-bottom: 24rpx;
|
||
}
|
||
|
||
.activity-icon {
|
||
width: 80rpx;
|
||
height: 80rpx;
|
||
border-radius: 20rpx;
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: center;
|
||
flex-shrink: 0;
|
||
background: rgba(255, 255, 255, 0.2);
|
||
backdrop-filter: blur(10rpx);
|
||
}
|
||
|
||
.icon-emoji {
|
||
font-size: 48rpx;
|
||
line-height: 1;
|
||
}
|
||
|
||
.activity-info {
|
||
flex: 1;
|
||
display: flex;
|
||
flex-direction: column;
|
||
gap: 8rpx;
|
||
padding-top: 8rpx;
|
||
}
|
||
|
||
.activity-title {
|
||
font-size: 36rpx;
|
||
font-weight: 700;
|
||
color: #ffffff;
|
||
line-height: 1.3;
|
||
}
|
||
|
||
.activity-desc {
|
||
font-size: 26rpx;
|
||
color: rgba(255, 255, 255, 0.9);
|
||
line-height: 1.5;
|
||
}
|
||
|
||
/* 活动标签 */
|
||
.activity-tags {
|
||
display: flex;
|
||
flex-wrap: wrap;
|
||
gap: 12rpx;
|
||
margin-bottom: 24rpx;
|
||
}
|
||
|
||
.tag-item {
|
||
display: flex;
|
||
align-items: center;
|
||
gap: 6rpx;
|
||
padding: 10rpx 16rpx;
|
||
background: rgba(255, 255, 255, 0.2);
|
||
backdrop-filter: blur(10rpx);
|
||
border-radius: 20rpx;
|
||
border: 2rpx solid rgba(255, 255, 255, 0.3);
|
||
}
|
||
|
||
.tag-highlight {
|
||
background: rgba(255, 255, 255, 0.3);
|
||
border-color: rgba(255, 255, 255, 0.5);
|
||
}
|
||
|
||
.tag-text {
|
||
font-size: 24rpx;
|
||
color: #ffffff;
|
||
font-weight: 600;
|
||
line-height: 1;
|
||
}
|
||
|
||
/* 操作按钮 */
|
||
.card-action {
|
||
display: flex;
|
||
justify-content: flex-end;
|
||
}
|
||
|
||
.action-btn {
|
||
display: flex;
|
||
align-items: center;
|
||
gap: 8rpx;
|
||
padding: 16rpx 32rpx;
|
||
background: rgba(255, 255, 255, 0.25);
|
||
backdrop-filter: blur(10rpx);
|
||
border-radius: 40rpx;
|
||
border: 2rpx solid rgba(255, 255, 255, 0.4);
|
||
transition: all 0.3s ease;
|
||
|
||
&:active {
|
||
background: rgba(255, 255, 255, 0.35);
|
||
}
|
||
}
|
||
|
||
.card-invite .action-btn {
|
||
background: rgba(255, 255, 255, 1);
|
||
border-color: transparent;
|
||
|
||
.btn-text {
|
||
color: #FF6B35;
|
||
}
|
||
|
||
&:active {
|
||
background: rgba(255, 255, 255, 0.9);
|
||
}
|
||
}
|
||
|
||
.btn-text {
|
||
font-size: 28rpx;
|
||
color: #ffffff;
|
||
font-weight: 600;
|
||
}
|
||
|
||
/* ========== 即将上线卡片 ========== */
|
||
.coming-soon-card {
|
||
display: flex;
|
||
flex-direction: column;
|
||
align-items: center;
|
||
justify-content: center;
|
||
padding: 60rpx 32rpx;
|
||
background: #ffffff;
|
||
border-radius: 24rpx;
|
||
border: 2rpx dashed #E5E5E5;
|
||
}
|
||
|
||
.coming-icon {
|
||
width: 100rpx;
|
||
height: 100rpx;
|
||
background: #F5F7FA;
|
||
border-radius: 50%;
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: center;
|
||
margin-bottom: 20rpx;
|
||
}
|
||
|
||
.coming-title {
|
||
font-size: 30rpx;
|
||
font-weight: 600;
|
||
color: #666;
|
||
margin-bottom: 8rpx;
|
||
}
|
||
|
||
.coming-desc {
|
||
font-size: 24rpx;
|
||
color: #999;
|
||
}
|
||
|
||
/* ========== 空状态 ========== */
|
||
.empty-state {
|
||
display: flex;
|
||
flex-direction: column;
|
||
align-items: center;
|
||
justify-content: center;
|
||
padding: 120rpx 32rpx;
|
||
}
|
||
|
||
.empty-icon-wrapper {
|
||
width: 160rpx;
|
||
height: 160rpx;
|
||
background: #F5F7FA;
|
||
border-radius: 50%;
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: center;
|
||
margin-bottom: 32rpx;
|
||
}
|
||
|
||
.empty-title {
|
||
font-size: 32rpx;
|
||
font-weight: 600;
|
||
color: #666;
|
||
margin-bottom: 12rpx;
|
||
}
|
||
|
||
.empty-desc {
|
||
font-size: 26rpx;
|
||
color: #999;
|
||
text-align: center;
|
||
line-height: 1.6;
|
||
}
|
||
|
||
/* ========== 加载状态 ========== */
|
||
.loading-state {
|
||
display: flex;
|
||
flex-direction: column;
|
||
align-items: center;
|
||
justify-content: center;
|
||
gap: 16rpx;
|
||
padding: 80rpx 0;
|
||
}
|
||
|
||
.loading-text {
|
||
font-size: 26rpx;
|
||
color: #999;
|
||
}
|
||
|
||
/* ========== 底部提示 ========== */
|
||
.bottom-tip {
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: center;
|
||
gap: 8rpx;
|
||
padding: 32rpx 24rpx;
|
||
}
|
||
|
||
.tip-text {
|
||
font-size: 24rpx;
|
||
color: #999;
|
||
}
|
||
</style>
|