Files
rent/apps/miniapp/src/pages/activity/index.vue
T
2026-05-15 11:28:02 +08:00

541 lines
12 KiB
Vue
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<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>