888 lines
24 KiB
Vue
888 lines
24 KiB
Vue
<template>
|
||
<view class="page">
|
||
<!-- 开屏动画 -->
|
||
<SplashScreen />
|
||
|
||
<!-- 品牌级背景装饰系统 - 统一漂浮光球 -->
|
||
<view class="bg-decoration"></view>
|
||
|
||
<!-- 自定义 tabBar -->
|
||
<!-- #ifdef MP-TOUTIAO -->
|
||
<customTabBarToutiao />
|
||
<!-- #endif -->
|
||
<!-- #ifndef MP-TOUTIAO -->
|
||
<customTabBar />
|
||
<!-- #endif -->
|
||
|
||
<!-- 顶部导航栏 (搜索) -->
|
||
<view class="nav-header">
|
||
<!-- 品牌标识已按需移除 -->
|
||
</view>
|
||
|
||
<!-- 滚动区域 -->
|
||
<scroll-view class="main-content" scroll-y>
|
||
<!-- Banner 区域 (现代级浮动设计) -->
|
||
<view class="banner-container">
|
||
<swiper class="banner-swiper" circular autoplay interval="5000" duration="600" :indicator-dots="false" @change="onBannerChange">
|
||
<swiper-item v-for="(b, index) in displayBanners" :key="b.id">
|
||
<view class="banner-card" :class="{ 'active': bannerIndex === index }">
|
||
<image v-if="b.image" class="banner-image" :src="b.image" mode="aspectFill" @tap="onBannerTap(b)" />
|
||
<view v-else class="banner-fallback">
|
||
<view class="fallback-glow"></view>
|
||
<text class="banner-fallback-text">{{ b.title || 'KE DAYA TOYS' }}</text>
|
||
<view class="banner-tag">UI/UX PREMIUM 6.0</view>
|
||
</view>
|
||
</view>
|
||
</swiper-item>
|
||
</swiper>
|
||
<!-- 自定义指示器 -->
|
||
<view class="banner-indicator">
|
||
<view v-for="(b, index) in displayBanners" :key="'dot' + index"
|
||
class="indicator-dot" :class="{ 'active': bannerIndex === index }"></view>
|
||
</view>
|
||
</view>
|
||
|
||
<!-- 品牌动态栏 (极简风格) -->
|
||
<view class="notice-bar-v2" @tap="onNoticeTap">
|
||
<view class="notice-icon">📢</view>
|
||
<swiper class="notice-swiper" vertical circular autoplay interval="3500">
|
||
<swiper-item v-for="n in displayNotices" :key="n.id">
|
||
<view class="notice-item">{{ n.text }}</view>
|
||
</swiper-item>
|
||
</swiper>
|
||
<view class="notice-arrow"></view>
|
||
</view>
|
||
|
||
|
||
|
||
<!-- 玩法分类专区 -->
|
||
<!-- #ifndef MP-TOUTIAO -->
|
||
<view class="gameplay-section">
|
||
<view class="section-header">
|
||
<text class="section-title">玩法分类</text>
|
||
</view>
|
||
<view class="gameplay-grid-v2">
|
||
<!-- 上排:两大核心 -->
|
||
<view class="grid-row-top">
|
||
<view class="game-card-large card-yifan" @tap="navigateTo('/pages-activity/activity/list/index?category=一番赏')">
|
||
<view class="card-bg-decoration"></view>
|
||
<view class="card-content-large">
|
||
<text class="card-title-large">一番赏</text>
|
||
<view class="card-tag-large">欧皇擂台</view>
|
||
<image class="card-mascot-large" src="https://via.placeholder.com/150/90EE90/000000?text=YI" mode="aspectFit" />
|
||
</view>
|
||
</view>
|
||
<view class="game-card-large card-wuxian" @tap="navigateTo('/pages-activity/activity/list/index?category=无限赏')">
|
||
<view class="card-content-large">
|
||
<text class="card-title-large">无限赏</text>
|
||
<view class="card-tag-large yellow">一发入魂</view>
|
||
<image class="card-mascot-large" src="https://via.placeholder.com/150/FFD700/000000?text=WU" mode="aspectFit" />
|
||
</view>
|
||
</view>
|
||
</view>
|
||
|
||
<!-- 下排:三小功能 -->
|
||
<view class="grid-row-bottom">
|
||
<view class="game-card-small card-match" @tap="navigateTo('/pages-activity/activity/list/index?category=对对碰')">
|
||
<text class="card-title-small">对对碰</text>
|
||
<text class="card-subtitle-small">碰一碰消除</text>
|
||
<image class="card-icon-small" src="https://via.placeholder.com/80/FFB6C1/000000?text=Match" mode="aspectFit" />
|
||
</view>
|
||
|
||
<view class="game-card-small card-tower" @tap="navigateTo('/pages-game/game/minesweeper/index')">
|
||
<text class="card-title-small">扫雷</text>
|
||
<text class="card-subtitle-small">福利挑战</text>
|
||
<image class="card-icon-small" src="https://via.placeholder.com/80/9370DB/000000?text=Mine" mode="aspectFit" />
|
||
</view>
|
||
|
||
<view class="game-card-small card-more" @tap="navigateTo('#')">
|
||
<text class="card-title-small">更多</text>
|
||
<text class="card-subtitle-small">敬请期待</text>
|
||
<image class="card-icon-small" src="https://via.placeholder.com/80/E0E0E0/000000?text=More" mode="aspectFit" />
|
||
</view>
|
||
</view>
|
||
</view>
|
||
</view>
|
||
<!-- #endif -->
|
||
|
||
<!-- 推荐活动列表 -->
|
||
<!-- #ifndef MP-TOUTIAO -->
|
||
<view class="activity-section">
|
||
<view class="section-header">
|
||
<text class="section-title">推荐活动</text>
|
||
</view>
|
||
|
||
<view v-if="activeGroupItems.length" class="activity-grid-list">
|
||
<view class="activity-item" v-for="a in activeGroupItems" :key="a.id" @tap="onActivityTap(a)">
|
||
<view class="activity-thumb-box">
|
||
<image v-if="a.image" class="activity-thumb" :src="a.image" mode="aspectFill" />
|
||
<view v-else class="banner-fallback mini">
|
||
<text class="banner-fallback-text mini">{{ a.title }}</text>
|
||
</view>
|
||
<!-- 热门标签 -->
|
||
<view class="activity-tag-hot">HOT</view>
|
||
</view>
|
||
<view class="activity-info">
|
||
<text class="activity-name">{{ a.title }}</text>
|
||
<view class="activity-row">
|
||
<text class="activity-desc" v-if="a.subtitle">{{ a.subtitle }}</text>
|
||
<view class="activity-btn-go">GO</view>
|
||
</view>
|
||
</view>
|
||
</view>
|
||
</view>
|
||
<view v-else class="activity-empty">暂无更多活动</view>
|
||
</view>
|
||
<!-- #endif -->
|
||
|
||
<!-- 底部垫高 - 避开TabBar -->
|
||
<view style="height: 140rpx"></view>
|
||
</scroll-view>
|
||
</view>
|
||
</template>
|
||
|
||
<script>
|
||
import { authRequest, request } from '../../utils/request.js'
|
||
import SplashScreen from '@/components/SplashScreen.vue'
|
||
import { checkPhoneBound, checkPhoneBoundSync } from '../../utils/checkPhone.js'
|
||
// #ifdef MP-TOUTIAO
|
||
import customTabBarToutiao from '@/components/app-tab-bar-toutiao.vue'
|
||
// #endif
|
||
// #ifndef MP-TOUTIAO
|
||
import customTabBar from '@/components/app-tab-bar.vue'
|
||
// #endif
|
||
|
||
export default {
|
||
components: {
|
||
SplashScreen
|
||
// #ifdef MP-TOUTIAO
|
||
, customTabBarToutiao
|
||
// #endif
|
||
// #ifndef MP-TOUTIAO
|
||
, customTabBar
|
||
// #endif
|
||
},
|
||
data() {
|
||
return {
|
||
notices: [],
|
||
banners: [],
|
||
activities: [],
|
||
selectedGroupName: '',
|
||
bannerIndex: 0,
|
||
isHomeLoading: false
|
||
}
|
||
},
|
||
computed: {
|
||
displayNotices() {
|
||
if (Array.isArray(this.notices) && this.notices.length) return this.notices
|
||
return [
|
||
{ id: 'n1', text: '欢迎光临' },
|
||
{ id: 'n2', text: '最新活动敬请期待' }
|
||
]
|
||
},
|
||
displayBanners() {
|
||
if (Array.isArray(this.banners) && this.banners.length) return this.banners
|
||
return [
|
||
{ id: 'ph-1', title: '精彩内容即将上线', image: '' },
|
||
{ id: 'ph-2', title: '敬请期待', image: '' },
|
||
{ id: 'ph-3', title: '更多活动请关注', image: '' }
|
||
]
|
||
},
|
||
activityGroups() {
|
||
const list = Array.isArray(this.activities) ? this.activities : []
|
||
const map = new Map()
|
||
list.forEach(a => {
|
||
const key = (a.category_name || '').trim() || '其他'
|
||
if (!map.has(key)) map.set(key, [])
|
||
map.get(key).push(a)
|
||
})
|
||
return Array.from(map.entries()).map(([name, items]) => ({ name, items }))
|
||
},
|
||
activeGroupItems() {
|
||
// Return ALL activities without filtering by group
|
||
return Array.isArray(this.activities) ? this.activities : []
|
||
}
|
||
},
|
||
onLoad() {
|
||
// #ifdef MP-TOUTIAO
|
||
// 抖音平台屏蔽首页,自动跳转到盒柜
|
||
uni.switchTab({
|
||
url: '/pages/cabinet/index'
|
||
})
|
||
return
|
||
// #endif
|
||
|
||
// 检查手机号绑定状态(快速检查本地缓存)
|
||
if (!checkPhoneBoundSync()) return
|
||
|
||
// 延迟 200ms 首次加载,让 Token/Session 有机会就绪
|
||
// 同时避免页面动画卡顿
|
||
setTimeout(() => {
|
||
this.loadHomeData()
|
||
}, 200)
|
||
},
|
||
onPullDownRefresh() {
|
||
this.loadHomeData(true)
|
||
},
|
||
onShow() {
|
||
// 只有非首次进入或数据为空时才触发刷新,避免 onLoad/onShow 双重触发
|
||
if (this.activities.length === 0 && !this.isHomeLoading) {
|
||
this.loadHomeData()
|
||
}
|
||
},
|
||
methods: {
|
||
onBannerChange(e) {
|
||
this.bannerIndex = e.detail.current
|
||
},
|
||
onSelectGroup(name) {
|
||
this.selectedGroupName = String(name || '')
|
||
},
|
||
updateSelectedGroup() {
|
||
// No-op as we now show all
|
||
},
|
||
toArray(x) { return Array.isArray(x) ? x : [] },
|
||
unwrap(list) {
|
||
if (Array.isArray(list)) return list
|
||
const obj = list || {}
|
||
const arr = obj.list || obj.items || obj.data || []
|
||
return Array.isArray(arr) ? arr : []
|
||
},
|
||
cleanUrl(u) {
|
||
const s = String(u || '').trim()
|
||
const m = s.match(/https?:\/\/[^\s'"`]+/)
|
||
if (m && m[0]) return m[0]
|
||
return s.replace(/[`'\"]/g, '').trim()
|
||
},
|
||
apiGet(url) {
|
||
const token = uni.getStorageSync('token')
|
||
const fn = token ? authRequest : request
|
||
return fn({ url })
|
||
},
|
||
normalizeNotices(list) {
|
||
const arr = this.unwrap(list)
|
||
return arr.map((i, idx) => ({
|
||
id: i.id ?? String(idx),
|
||
text: i.content ?? i.text ?? i.title ?? ''
|
||
})).filter(i => i.text)
|
||
},
|
||
normalizeBanners(list) {
|
||
const arr = this.unwrap(list)
|
||
const mapped = arr.map((i, idx) => ({
|
||
id: i.id ?? String(idx),
|
||
title: i.title ?? '',
|
||
image: this.cleanUrl(i.imageUrl ?? i.image_url ?? i.image ?? i.img ?? i.pic ?? ''),
|
||
link: this.cleanUrl(i.linkUrl ?? i.link_url ?? i.link ?? i.url ?? ''),
|
||
sort: typeof i.sort === 'number' ? i.sort : 0
|
||
})).filter(i => i.image)
|
||
mapped.sort((a, b) => a.sort - b.sort)
|
||
return mapped
|
||
},
|
||
normalizeActivities(list) {
|
||
const arr = this.unwrap(list)
|
||
const mapped = arr.map((i, idx) => ({
|
||
id: i.id ?? String(idx),
|
||
image: this.cleanUrl(i.image ?? i.banner ?? i.coverUrl ?? i.cover_url ?? i.img ?? i.pic ?? ''),
|
||
title: i.title ?? i.name ?? '',
|
||
subtitle: this.buildActivitySubtitle(i),
|
||
link: this.cleanUrl(i.linkUrl ?? i.link_url ?? i.link ?? i.url ?? ''),
|
||
category_name: (i.category_name ?? i.categoryName ?? '').trim(),
|
||
category_id: i.activity_category_id ?? i.category_id ?? i.categoryId ?? null
|
||
})).filter(i => i.image || i.title)
|
||
return mapped
|
||
},
|
||
buildActivitySubtitle(i) {
|
||
const base = i.subTitle ?? i.sub_title ?? i.subtitle ?? i.desc ?? i.description ?? ''
|
||
if (base) return base
|
||
const cat = i.category_name ?? i.categoryName ?? ''
|
||
const price = (i.price_draw !== undefined && i.price_draw !== null) ? `¥${(Number(i.price_draw || 0) / 100).toFixed(2)}` : ''
|
||
const parts = [cat, price].filter(Boolean)
|
||
return parts.join(' · ')
|
||
},
|
||
async loadHomeData(isRefresh = false) {
|
||
if (this.isHomeLoading && !isRefresh) return
|
||
this.isHomeLoading = true
|
||
|
||
// 定义重试函数
|
||
const fetchWithRetry = async (url, retries = 3) => {
|
||
for (let i = 0; i < retries; i++) {
|
||
try {
|
||
const res = await this.apiGet(url)
|
||
if (res) return res
|
||
// 如果返回空,认为是失败,尝试重试
|
||
if (i < retries - 1) await new Promise(r => setTimeout(r, 1000))
|
||
} catch (e) {
|
||
console.error(`Fetch ${url} failed, attempt ${i + 1}`, e)
|
||
if (i < retries - 1) await new Promise(r => setTimeout(r, 1000))
|
||
}
|
||
}
|
||
return null
|
||
}
|
||
|
||
// 同时发起请求,大幅提升首屏加载速度
|
||
try {
|
||
const [nData, bData, acData] = await Promise.all([
|
||
fetchWithRetry('/api/app/notices'),
|
||
fetchWithRetry('/api/app/banners'),
|
||
fetchWithRetry('/api/app/activities')
|
||
])
|
||
|
||
if (nData) this.notices = this.normalizeNotices(nData)
|
||
if (bData) this.banners = this.normalizeBanners(bData)
|
||
|
||
// 只有获取到有效活动数据才更新,保留旧数据兜底
|
||
if (acData) {
|
||
const validActivities = this.normalizeActivities(acData)
|
||
if (validActivities.length > 0) {
|
||
this.activities = validActivities
|
||
}
|
||
}
|
||
} catch (e) {
|
||
console.error('Home data load failed', e)
|
||
if (isRefresh) {
|
||
uni.showToast({ title: '刷新失败,请稍后重试', icon: 'none' })
|
||
}
|
||
} finally {
|
||
this.isHomeLoading = false
|
||
if (isRefresh) {
|
||
uni.stopPullDownRefresh()
|
||
uni.showToast({ title: '刷新成功', icon: 'none' })
|
||
}
|
||
}
|
||
},
|
||
onBannerTap(b) {
|
||
const imgs = (Array.isArray(this.banners) ? this.banners : []).map(x => x.image).filter(Boolean)
|
||
const current = b && b.image
|
||
if (current) {
|
||
uni.previewImage({ urls: imgs.length ? imgs : [current], current })
|
||
return
|
||
}
|
||
if (b.link && /^\/.+/.test(b.link)) {
|
||
uni.navigateTo({ url: b.link })
|
||
}
|
||
},
|
||
onActivityTap(a) {
|
||
const name = (a.category_name || a.categoryName || '').trim()
|
||
const id = a.id
|
||
let path = ''
|
||
if (name.includes('一番赏')) path = '/pages-activity/activity/yifanshang/index'
|
||
else if (name.includes('无限赏')) path = '/pages-activity/activity/wuxianshang/index'
|
||
else if (name.includes('对对碰')) path = '/pages-activity/activity/duiduipeng/index'
|
||
else if (name.includes('爬塔')) path = '/pages-activity/activity/pata/index'
|
||
|
||
if (path && id) {
|
||
uni.navigateTo({ url: `${path}?id=${id}` })
|
||
return
|
||
}
|
||
if (a.link && /^\/.+/.test(a.link)) {
|
||
uni.navigateTo({ url: a.link })
|
||
}
|
||
},
|
||
navigateTo(url) {
|
||
if(url === '#') return
|
||
uni.navigateTo({ url })
|
||
},
|
||
onNoticeTap() {
|
||
const content = this.displayNotices.map(n => n.text).join('\n')
|
||
uni.showModal({
|
||
title: '系统通知',
|
||
content: content || '暂无通知',
|
||
showCancel: false,
|
||
confirmText: '知道了'
|
||
})
|
||
}
|
||
},
|
||
// 分享给好友
|
||
onShareAppMessage() {
|
||
const inviteCode = uni.getStorageSync('invite_code') || (uni.getStorageSync('user_info') || {}).invite_code || ''
|
||
return {
|
||
title: '柯大鸭 - 开箱惊喜等你来',
|
||
path: `/pages/index/index?invite_code=${inviteCode}`,
|
||
imageUrl: '/static/logo.png'
|
||
}
|
||
},
|
||
// 分享到朋友圈
|
||
onShareTimeline() {
|
||
const inviteCode = uni.getStorageSync('invite_code') || (uni.getStorageSync('user_info') || {}).invite_code || ''
|
||
return {
|
||
title: '柯大鸭 - 开箱惊喜等你来',
|
||
query: `invite_code=${inviteCode}`,
|
||
imageUrl: '/static/logo.png'
|
||
}
|
||
}
|
||
}
|
||
</script>
|
||
|
||
<style lang="scss">
|
||
/* ============================================
|
||
柯大鸭 - 首页样式 (V6.0 Pro Refined)
|
||
============================================ */
|
||
|
||
.page {
|
||
padding: 0;
|
||
background-color: $bg-page;
|
||
min-height: 100vh;
|
||
display: flex;
|
||
flex-direction: column;
|
||
position: relative;
|
||
overflow: hidden;
|
||
}
|
||
|
||
/* ========== 顶部导航栏 ========== */
|
||
.nav-header {
|
||
height: env(safe-area-inset-top);
|
||
z-index: 10;
|
||
position: sticky;
|
||
top: 0;
|
||
background: rgba($bg-page, 0.8);
|
||
backdrop-filter: blur(10rpx);
|
||
}
|
||
|
||
|
||
/* ========== 滚动主内容区 ========== */
|
||
|
||
|
||
/* ========== 滚动主内容区 ========== */
|
||
.main-content {
|
||
flex: 1;
|
||
position: relative;
|
||
z-index: 1;
|
||
}
|
||
|
||
/* Banner Container (Modern Floating) */
|
||
.banner-container {
|
||
padding: $spacing-sm $spacing-lg $spacing-xl;
|
||
position: relative;
|
||
z-index: 2;
|
||
}
|
||
|
||
.banner-swiper {
|
||
height: 360rpx;
|
||
}
|
||
|
||
.banner-card {
|
||
height: 100%;
|
||
margin: 0 4rpx;
|
||
border-radius: 32rpx;
|
||
overflow: hidden;
|
||
position: relative;
|
||
transform: scale(0.96);
|
||
transition: all 0.5s $ease-out;
|
||
box-shadow: 0 16rpx 48rpx rgba(0, 0, 0, 0.08);
|
||
}
|
||
|
||
.banner-card.active {
|
||
transform: scale(1);
|
||
box-shadow: $shadow-float;
|
||
}
|
||
|
||
.banner-image {
|
||
width: 100%;
|
||
height: 100%;
|
||
display: block;
|
||
}
|
||
|
||
.banner-fallback {
|
||
width: 100%;
|
||
height: 100%;
|
||
background: linear-gradient(135deg, $brand-primary, $brand-secondary);
|
||
display: flex;
|
||
flex-direction: column;
|
||
align-items: center;
|
||
justify-content: center;
|
||
position: relative;
|
||
}
|
||
|
||
.fallback-glow {
|
||
position: absolute;
|
||
width: 100%;
|
||
height: 100%;
|
||
background: radial-gradient(circle at center, rgba(255,255,255,0.2) 0%, transparent 80%);
|
||
}
|
||
|
||
.banner-fallback-text {
|
||
font-size: 52rpx;
|
||
font-weight: 900;
|
||
color: #fff;
|
||
font-style: italic;
|
||
margin-bottom: 12rpx;
|
||
letter-spacing: 2rpx;
|
||
z-index: 1;
|
||
}
|
||
|
||
.banner-tag {
|
||
background: rgba(0,0,0,0.2);
|
||
color: #fff;
|
||
padding: 8rpx 24rpx;
|
||
border-radius: $radius-round;
|
||
font-size: 22rpx;
|
||
font-weight: 700;
|
||
backdrop-filter: blur(4px);
|
||
z-index: 1;
|
||
}
|
||
|
||
/* Indicator */
|
||
.banner-indicator {
|
||
display: flex;
|
||
justify-content: center;
|
||
gap: 12rpx;
|
||
margin-top: -16rpx;
|
||
}
|
||
|
||
.indicator-dot {
|
||
width: 12rpx;
|
||
height: 6rpx;
|
||
background: rgba(0,0,0,0.1);
|
||
border-radius: 4rpx;
|
||
transition: all 0.3s ease;
|
||
}
|
||
|
||
.indicator-dot.active {
|
||
width: 32rpx;
|
||
background: $brand-primary;
|
||
}
|
||
|
||
/* Notice Bar V2 (Minimalist) */
|
||
.notice-bar-v2 {
|
||
margin: 0 $spacing-lg $spacing-xl;
|
||
background: rgba(255, 255, 255, 0.7);
|
||
backdrop-filter: blur(10rpx);
|
||
border-radius: 32rpx;
|
||
padding: 24rpx 32rpx;
|
||
display: flex;
|
||
align-items: center;
|
||
gap: 20rpx;
|
||
box-shadow: $shadow-sm;
|
||
border: 1px solid rgba(255, 255, 255, 0.6);
|
||
}
|
||
|
||
.notice-icon { font-size: 32rpx; }
|
||
.notice-swiper { flex: 1; height: 36rpx; }
|
||
.notice-item {
|
||
font-size: 26rpx;
|
||
color: $text-main;
|
||
line-height: 36rpx;
|
||
font-weight: 600;
|
||
}
|
||
.notice-arrow {
|
||
width: 12rpx;
|
||
height: 12rpx;
|
||
border-top: 3rpx solid #DDD;
|
||
border-right: 3rpx solid #DDD;
|
||
transform: rotate(45deg);
|
||
}
|
||
|
||
|
||
/* 玩法专区 - 极质设计 */
|
||
.gameplay-section {
|
||
padding: 0 $spacing-lg;
|
||
margin-bottom: $spacing-xl;
|
||
position: relative;
|
||
z-index: 2;
|
||
}
|
||
|
||
.section-header {
|
||
margin-bottom: 24rpx;
|
||
}
|
||
|
||
.section-title {
|
||
font-size: 38rpx;
|
||
font-weight: 900;
|
||
color: $text-main;
|
||
font-style: italic;
|
||
display: flex;
|
||
align-items: center;
|
||
text-shadow: 0 2rpx 8rpx rgba(0,0,0,0.05);
|
||
}
|
||
|
||
.section-title::before {
|
||
content: '';
|
||
width: 8rpx;
|
||
height: 32rpx;
|
||
background: $brand-primary;
|
||
margin-right: 16rpx;
|
||
border-radius: 4rpx;
|
||
transform: skewX(-15deg);
|
||
}
|
||
|
||
.gameplay-grid-v2 {
|
||
display: flex;
|
||
flex-direction: column;
|
||
gap: 24rpx;
|
||
}
|
||
|
||
.grid-row-top {
|
||
display: flex;
|
||
gap: 24rpx;
|
||
height: 190rpx;
|
||
}
|
||
|
||
.game-card-large {
|
||
flex: 1;
|
||
border-radius: $radius-lg;
|
||
position: relative;
|
||
overflow: hidden;
|
||
padding: 22rpx;
|
||
box-shadow: $shadow-card;
|
||
transition: transform 0.2s;
|
||
border: 1px solid rgba(255, 255, 255, 0.2);
|
||
}
|
||
|
||
.game-card-large:active {
|
||
transform: scale(0.98);
|
||
}
|
||
|
||
/* 下排 */
|
||
.grid-row-bottom {
|
||
display: flex;
|
||
gap: 20rpx;
|
||
height: 130rpx;
|
||
}
|
||
|
||
.game-card-small {
|
||
flex: 1;
|
||
border-radius: $radius-md;
|
||
position: relative;
|
||
overflow: hidden;
|
||
padding: 16rpx;
|
||
display: flex;
|
||
flex-direction: column;
|
||
justify-content: center;
|
||
box-shadow: $shadow-sm;
|
||
background: white;
|
||
transition: all 0.2s;
|
||
}
|
||
.game-card-small:active {
|
||
transform: scale(0.96);
|
||
box-shadow: none;
|
||
}
|
||
|
||
/* 内容样式 - 大卡片 */
|
||
.card-content-large {
|
||
position: relative;
|
||
z-index: 2;
|
||
height: 100%;
|
||
display: flex;
|
||
flex-direction: column;
|
||
justify-content: center;
|
||
align-items: flex-start;
|
||
}
|
||
|
||
.card-title-large {
|
||
font-size: 34rpx;
|
||
font-weight: 900;
|
||
color: #FFF;
|
||
font-style: italic;
|
||
margin-bottom: 12rpx;
|
||
text-shadow: 0 4rpx 8rpx rgba(0,0,0,0.1);
|
||
}
|
||
|
||
.card-tag-large {
|
||
font-size: 20rpx;
|
||
background: rgba(255, 255, 255, 0.9);
|
||
color: $text-main;
|
||
padding: 4rpx 14rpx;
|
||
border-radius: $radius-round;
|
||
font-weight: 800;
|
||
box-shadow: 0 4rpx 12rpx rgba(0,0,0,0.1);
|
||
backdrop-filter: blur(4px);
|
||
}
|
||
.card-tag-large.yellow { color: #D97706; }
|
||
|
||
.card-mascot-large {
|
||
position: absolute;
|
||
right: -10rpx;
|
||
bottom: -20rpx;
|
||
width: 140rpx;
|
||
height: 140rpx;
|
||
transform: rotate(10deg);
|
||
filter: drop-shadow(0 8rpx 16rpx rgba(0,0,0,0.2));
|
||
}
|
||
|
||
/* 内容样式 - 小卡片 */
|
||
.card-title-small {
|
||
font-size: 30rpx;
|
||
font-weight: 800;
|
||
color: $text-main;
|
||
margin-bottom: 6rpx;
|
||
z-index: 2;
|
||
}
|
||
|
||
.card-subtitle-small {
|
||
font-size: 22rpx;
|
||
color: $text-sub;
|
||
z-index: 2;
|
||
}
|
||
|
||
.card-icon-small {
|
||
position: absolute;
|
||
right: -10rpx;
|
||
bottom: -10rpx;
|
||
width: 80rpx;
|
||
height: 80rpx;
|
||
opacity: 0.9;
|
||
transform: rotate(-10deg);
|
||
}
|
||
|
||
/* 背景配色 - 优化后的渐变 */
|
||
.card-yifan {
|
||
background: linear-gradient(135deg, $brand-primary 0%, $brand-secondary 100%); /* 品牌橙渐变 */
|
||
}
|
||
|
||
.card-wuxian {
|
||
background: $gradient-gold; /* 质感金渐变 */
|
||
}
|
||
|
||
.card-match {
|
||
background: linear-gradient(135deg, #FF9A9E 0%, #FECFEF 100%); /* 柔和粉 */
|
||
}
|
||
.card-match .card-title-small { color: $accent-pink; }
|
||
|
||
.card-tower {
|
||
background: linear-gradient(135deg, #E0C3FC 0%, #8EC5FC 100%); /* 梦幻紫蓝 */
|
||
}
|
||
.card-tower .card-title-small { color: $accent-purple; }
|
||
|
||
.card-more {
|
||
background: linear-gradient(135deg, $bg-secondary 0%, #E5E7EB 100%); /* 金属灰 */
|
||
}
|
||
.card-more .card-title-small { color: $text-sub; }
|
||
|
||
|
||
/* 推荐活动列表 */
|
||
.activity-section {
|
||
padding: 0 $spacing-lg;
|
||
animation: fadeInUp 0.6s ease-out 0.3s backwards;
|
||
position: relative;
|
||
z-index: 2;
|
||
}
|
||
|
||
.activity-grid-list {
|
||
display: grid;
|
||
grid-template-columns: 1fr 1fr;
|
||
gap: 24rpx;
|
||
}
|
||
|
||
.activity-item {
|
||
background: rgba(255, 255, 255, 0.7);
|
||
backdrop-filter: blur(10rpx);
|
||
border-radius: $radius-xl;
|
||
overflow: hidden;
|
||
box-shadow: $shadow-sm;
|
||
display: flex;
|
||
flex-direction: column;
|
||
transition: all 0.3s ease;
|
||
border: 1px solid rgba(255, 255, 255, 0.6);
|
||
}
|
||
|
||
.activity-item:active {
|
||
transform: translateY(4rpx);
|
||
box-shadow: none;
|
||
}
|
||
|
||
.activity-thumb-box {
|
||
position: relative;
|
||
width: 100%;
|
||
padding-bottom: 100%;
|
||
}
|
||
|
||
.activity-thumb {
|
||
position: absolute;
|
||
top: 0; left: 0;
|
||
width: 100%;
|
||
height: 100%;
|
||
}
|
||
|
||
.activity-tag-hot {
|
||
position: absolute;
|
||
top: 16rpx;
|
||
left: 16rpx;
|
||
background: $gradient-brand;
|
||
color: #fff;
|
||
font-size: 20rpx;
|
||
padding: 6rpx 16rpx;
|
||
border-radius: 12rpx;
|
||
font-weight: 800;
|
||
box-shadow: 0 4rpx 12rpx rgba(255,107,0,0.3);
|
||
}
|
||
|
||
.activity-info {
|
||
padding: 24rpx;
|
||
flex: 1;
|
||
display: flex;
|
||
flex-direction: column;
|
||
justify-content: space-between;
|
||
}
|
||
|
||
.activity-name {
|
||
font-size: 28rpx;
|
||
font-weight: 700;
|
||
color: $text-main;
|
||
margin-bottom: 20rpx;
|
||
line-height: 1.4;
|
||
overflow: hidden;
|
||
text-overflow: ellipsis;
|
||
display: -webkit-box;
|
||
-webkit-line-clamp: 2;
|
||
line-clamp: 2;
|
||
-webkit-box-orient: vertical;
|
||
}
|
||
|
||
.activity-row {
|
||
display: flex;
|
||
justify-content: space-between;
|
||
align-items: center;
|
||
}
|
||
|
||
.activity-desc {
|
||
font-size: 26rpx;
|
||
color: $accent-red;
|
||
font-weight: 800;
|
||
}
|
||
|
||
.activity-btn-go {
|
||
background: #1A1A1A;
|
||
color: $accent-gold;
|
||
font-size: 22rpx;
|
||
font-weight: 900;
|
||
padding: 10rpx 28rpx;
|
||
border-radius: $radius-round;
|
||
box-shadow: 0 6rpx 16rpx rgba(0,0,0,0.1);
|
||
}
|
||
|
||
/* ============================================
|
||
🌌 动画与高级动效
|
||
============================================ */
|
||
|
||
@keyframes pulse {
|
||
0% { transform: scale(1); opacity: 1; }
|
||
50% { transform: scale(1.05); opacity: 0.9; }
|
||
100% { transform: scale(1); opacity: 1; }
|
||
}
|
||
|
||
@keyframes fadeInUp {
|
||
from { opacity: 0; transform: translateY(40rpx); }
|
||
to { opacity: 1; transform: translateY(0); }
|
||
}
|
||
|
||
@keyframes float {
|
||
0%, 100% { transform: translateY(0); }
|
||
50% { transform: translateY(-20rpx); }
|
||
}
|
||
|
||
.banner-container { animation: fadeInUp 0.6s $ease-out; }
|
||
.notice-bar-v2 { animation: fadeInUp 0.6s $ease-out 0.15s both; }
|
||
.gameplay-section { animation: fadeInUp 0.6s $ease-out 0.3s both; }
|
||
.activity-section { animation: fadeInUp 0.6s $ease-out 0.45s both; }
|
||
|
||
.brand-star { animation: pulse 2s infinite; }
|
||
|
||
.activity-btn-go:active {
|
||
transform: scale(0.9);
|
||
}
|
||
|
||
/* 兼容性修复 */
|
||
.brand-text {
|
||
background-clip: text;
|
||
}
|
||
</style>
|