feat: 优化抽奖活动页面UI,新增奖池分级展示和购买记录功能。
This commit is contained in:
parent
a18845c849
commit
148c62a983
@ -74,6 +74,10 @@ export function getActivityIssueRewards(activity_id, issue_id) {
|
||||
return authRequest({ url: `/api/app/activities/${activity_id}/issues/${issue_id}/rewards`, method: 'GET' })
|
||||
}
|
||||
|
||||
export function getIssueDrawLogs(activity_id, issue_id) {
|
||||
return authRequest({ url: `/api/app/activities/${activity_id}/issues/${issue_id}/draw_logs`, method: 'GET' })
|
||||
}
|
||||
|
||||
export function drawActivityIssue(activity_id, issue_id) {
|
||||
return authRequest({ url: `/api/app/activities/${activity_id}/issues/${issue_id}/draw`, method: 'POST' })
|
||||
}
|
||||
|
||||
@ -476,39 +476,40 @@ async function onPaymentConfirm(paymentData) {
|
||||
text-shadow: 0 2rpx 4rpx rgba(0,0,0,0.1);
|
||||
}
|
||||
|
||||
/* ============= 底部操作栏 ============= */
|
||||
/* ============= 底部操作栏 - 高级重置 ============= */
|
||||
.action-bar {
|
||||
position: fixed;
|
||||
bottom: calc(40rpx + env(safe-area-inset-bottom));
|
||||
left: 30rpx;
|
||||
right: 30rpx;
|
||||
background: rgba($bg-card, 0.9);
|
||||
backdrop-filter: blur(20rpx);
|
||||
padding: 20rpx 30rpx;
|
||||
box-shadow: $shadow-lg;
|
||||
background: rgba(255, 255, 255, 0.85);
|
||||
backdrop-filter: blur(30rpx);
|
||||
padding: 24rpx 40rpx;
|
||||
box-shadow: 0 16rpx 48rpx rgba(0, 0, 0, 0.12);
|
||||
border-radius: 999rpx;
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
z-index: 100;
|
||||
border: 1rpx solid rgba($bg-card, 0.5);
|
||||
animation: slideUp 0.4s ease-out backwards;
|
||||
border: 1rpx solid rgba(255, 255, 255, 0.6);
|
||||
animation: slideUp 0.4s cubic-bezier(0.23, 1, 0.32, 1) backwards;
|
||||
}
|
||||
|
||||
/* 选择信息行 */
|
||||
.selection-info {
|
||||
font-size: 26rpx;
|
||||
font-size: 28rpx;
|
||||
color: $text-main;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
font-weight: 600;
|
||||
align-items: baseline;
|
||||
font-weight: 800;
|
||||
}
|
||||
.highlight {
|
||||
color: $brand-primary;
|
||||
font-weight: 800;
|
||||
font-size: 36rpx;
|
||||
font-weight: 900;
|
||||
font-size: 40rpx;
|
||||
margin: 0 8rpx;
|
||||
font-family: 'DIN Alternate', sans-serif;
|
||||
}
|
||||
|
||||
/* 按钮组 */
|
||||
@ -519,54 +520,75 @@ async function onPaymentConfirm(paymentData) {
|
||||
|
||||
/* 通用按钮样式 */
|
||||
.btn-common {
|
||||
height: 80rpx;
|
||||
line-height: 80rpx;
|
||||
padding: 0 48rpx;
|
||||
height: 88rpx;
|
||||
line-height: 88rpx;
|
||||
padding: 0 56rpx;
|
||||
border-radius: 999rpx;
|
||||
font-size: 28rpx;
|
||||
font-weight: 700;
|
||||
font-size: 30rpx;
|
||||
font-weight: 900;
|
||||
margin: 0;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
transition: all 0.2s;
|
||||
transition: all 0.3s cubic-bezier(0.175, 0.885, 0.32, 1.275);
|
||||
border: none;
|
||||
|
||||
&:active {
|
||||
transform: scale(0.96);
|
||||
transform: scale(0.92);
|
||||
}
|
||||
}
|
||||
|
||||
/* 购买按钮 */
|
||||
/* 购买按钮 - 品牌渐变 + 流光 */
|
||||
.btn-buy {
|
||||
background: $gradient-brand !important;
|
||||
color: #FFFFFF !important;
|
||||
box-shadow: 0 8rpx 20rpx rgba($brand-primary, 0.3);
|
||||
box-shadow: 0 12rpx 32rpx rgba($brand-primary, 0.35);
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
|
||||
/* 脉冲动画 */
|
||||
animation: pulse 2s infinite;
|
||||
}
|
||||
|
||||
/* 随机按钮 */
|
||||
.btn-random {
|
||||
background: $bg-secondary !important;
|
||||
color: $text-main !important;
|
||||
box-shadow: none;
|
||||
border: 1rpx solid transparent;
|
||||
|
||||
&:active {
|
||||
background: #E5E7EB !important;
|
||||
&::before {
|
||||
content: '';
|
||||
position: absolute;
|
||||
top: -50%;
|
||||
left: -150%;
|
||||
width: 200%;
|
||||
height: 200%;
|
||||
background: linear-gradient(
|
||||
120deg,
|
||||
rgba(255, 255, 255, 0) 30%,
|
||||
rgba(255, 255, 255, 0.4) 50%,
|
||||
rgba(255, 255, 255, 0) 70%
|
||||
);
|
||||
transform: rotate(25deg);
|
||||
animation: btnShine 4s infinite cubic-bezier(0.19, 1, 0.22, 1);
|
||||
pointer-events: none;
|
||||
}
|
||||
}
|
||||
|
||||
/* 随机按钮 - 轻量化设计 */
|
||||
.btn-random {
|
||||
background: #1A1A1A !important;
|
||||
color: $accent-gold !important;
|
||||
box-shadow: 0 8rpx 20rpx rgba(0, 0, 0, 0.15);
|
||||
|
||||
&:active {
|
||||
background: #333 !important;
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes btnShine {
|
||||
0% { left: -150%; }
|
||||
100% { left: 150%; }
|
||||
}
|
||||
|
||||
@keyframes slideUp {
|
||||
from { transform: translateY(100%); opacity: 0; }
|
||||
from { transform: translateY(120rpx); opacity: 0; }
|
||||
to { transform: translateY(0); opacity: 1; }
|
||||
}
|
||||
|
||||
@keyframes pulse {
|
||||
0% { box-shadow: 0 0 0 0 rgba($brand-primary, 0.4); }
|
||||
70% { box-shadow: 0 0 0 20rpx rgba($brand-primary, 0); }
|
||||
100% { box-shadow: 0 0 0 0 rgba($brand-primary, 0); }
|
||||
@keyframes scaleIn {
|
||||
from { transform: scale(0.95); opacity: 0; }
|
||||
to { transform: scale(1); opacity: 1; }
|
||||
}
|
||||
|
||||
@keyframes float {
|
||||
|
||||
@ -25,6 +25,16 @@
|
||||
<view class="tag-item">理性消费</view>
|
||||
</view>
|
||||
</view>
|
||||
<view class="header-actions">
|
||||
<view class="action-btn" @tap="showRules">
|
||||
<view class="action-icon rules-icon"></view>
|
||||
<text class="action-label">规则</text>
|
||||
</view>
|
||||
<view class="action-btn" @tap="goCabinet">
|
||||
<view class="action-icon cabinet-icon"></view>
|
||||
<text class="action-label">盒柜</text>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<view class="section-container animate-enter stagger-1">
|
||||
@ -51,18 +61,6 @@
|
||||
</view>
|
||||
|
||||
<view class="section-container animate-enter stagger-2">
|
||||
<view class="issue-header">
|
||||
<view class="issue-switch-btn" @tap="prevIssue">
|
||||
<text class="arrow">◀</text>
|
||||
</view>
|
||||
<view class="issue-info-center">
|
||||
<text class="issue-current-text">{{ currentIssueTitle }}</text>
|
||||
<text class="issue-status-badge">{{ currentIssueStatusText }}</text>
|
||||
</view>
|
||||
<view class="issue-switch-btn" @tap="nextIssue">
|
||||
<text class="arrow">▶</text>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<view class="modern-tabs">
|
||||
<view class="tab-item" :class="{ active: tabActive === 'pool' }" @tap="tabActive = 'pool'">
|
||||
@ -80,13 +78,21 @@
|
||||
<text class="section-title">奖池配置</text>
|
||||
<text class="section-more" @tap="openRewardsPopup">查看全部</text>
|
||||
</view>
|
||||
<scroll-view v-if="currentIssueRewards.length > 0" class="preview-scroll" scroll-x>
|
||||
<view class="preview-item" v-for="(item, idx) in currentIssueRewards" :key="item.id || idx">
|
||||
<view class="prize-tag" :class="{ 'tag-boss': item.boss }">{{ item.boss ? 'BOSS' : '赏' }}</view>
|
||||
<image class="preview-img" :src="item.image" mode="aspectFill" />
|
||||
<view class="preview-name">{{ item.title }}</view>
|
||||
<view v-if="rewardGroups.length > 0">
|
||||
<view class="prize-level-row" v-for="group in rewardGroups" :key="group.level">
|
||||
<view class="level-header-row">
|
||||
<view class="level-badge" :class="{ 'badge-boss': group.level === 'BOSS' }">{{ group.level }}赏</view>
|
||||
<text class="level-prob">总概率 {{ group.totalPercent }}%</text>
|
||||
</view>
|
||||
<scroll-view class="preview-scroll" scroll-x>
|
||||
<view class="preview-item" v-for="(item, idx) in group.rewards" :key="item.id || idx">
|
||||
<view class="prize-tag" :class="{ 'tag-boss': item.boss }">{{ item.boss ? 'BOSS' : group.level }}</view>
|
||||
<image class="preview-img" :src="item.image" mode="aspectFill" />
|
||||
<view class="preview-name">{{ item.title }}</view>
|
||||
</view>
|
||||
</scroll-view>
|
||||
</view>
|
||||
</scroll-view>
|
||||
</view>
|
||||
<view v-else class="empty-state">
|
||||
<text class="empty-icon">📭</text>
|
||||
<text class="empty-text">暂无奖励配置</text>
|
||||
@ -101,7 +107,6 @@
|
||||
<view class="record-title">{{ it.title }}</view>
|
||||
<view class="record-meta">
|
||||
<text class="record-count">x{{ it.count }}</text>
|
||||
<text v-if="it.percent !== undefined">占比 {{ it.percent }}%</text>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
@ -185,17 +190,25 @@
|
||||
<text class="rewards-close" @tap="closeRewardsPopup">×</text>
|
||||
</view>
|
||||
<scroll-view scroll-y class="rewards-list">
|
||||
<view v-for="(item, idx) in currentIssueRewards" :key="item.id || idx" class="rewards-item">
|
||||
<image class="rewards-thumb" :src="item.image" mode="aspectFill" />
|
||||
<view class="rewards-info">
|
||||
<view class="rewards-name-row">
|
||||
<text class="rewards-name">{{ item.title || '-' }}</text>
|
||||
<view class="rewards-tag" v-if="item.boss">BOSS</view>
|
||||
<view v-if="rewardGroups.length > 0">
|
||||
<view class="rewards-group-v2" v-for="group in rewardGroups" :key="group.level">
|
||||
<view class="group-header-row">
|
||||
<text class="group-badge" :class="{ 'badge-boss': group.level === 'BOSS' }">{{ group.level }}赏</text>
|
||||
<text class="group-total-prob">该档总概率 {{ group.totalPercent }}%</text>
|
||||
</view>
|
||||
<view v-for="(item, idx) in group.rewards" :key="item.id || idx" class="rewards-item">
|
||||
<image class="rewards-thumb" :src="item.image" mode="aspectFill" />
|
||||
<view class="rewards-info">
|
||||
<view class="rewards-name-row">
|
||||
<text class="rewards-name">{{ item.title || '-' }}</text>
|
||||
<view class="rewards-tag" v-if="item.boss">BOSS</view>
|
||||
</view>
|
||||
<text class="rewards-percent">单项概率 {{ formatPercent(item.percent) }}</text>
|
||||
</view>
|
||||
</view>
|
||||
<text class="rewards-percent">概率 {{ formatPercent(item.percent) }}</text>
|
||||
</view>
|
||||
</view>
|
||||
<view v-if="!currentIssueRewards.length" class="rewards-empty">暂无奖品数据</view>
|
||||
<view v-else class="rewards-empty">暂无奖品数据</view>
|
||||
</scroll-view>
|
||||
</view>
|
||||
</view>
|
||||
@ -214,7 +227,7 @@
|
||||
import { ref, computed } from 'vue'
|
||||
import { onLoad } from '@dcloudio/uni-app'
|
||||
import PaymentPopup from '../../../components/PaymentPopup.vue'
|
||||
import { getActivityDetail, getActivityIssues, getActivityIssueRewards, getUserCoupons, getItemCards, createWechatOrder, getMatchingCardTypes, createMatchingPreorder, checkMatchingGame } from '../../../api/appUser'
|
||||
import { getActivityDetail, getActivityIssues, getActivityIssueRewards, getUserCoupons, getItemCards, createWechatOrder, getMatchingCardTypes, createMatchingPreorder, checkMatchingGame, getIssueDrawLogs } from '../../../api/appUser'
|
||||
|
||||
const detail = ref({})
|
||||
const statusText = ref('')
|
||||
@ -328,10 +341,33 @@ const currentIssueRewards = computed(() => {
|
||||
const m = rewardsMap.value || {}
|
||||
return (iid && Array.isArray(m[iid])) ? m[iid] : []
|
||||
})
|
||||
|
||||
const rewardGroups = computed(() => {
|
||||
const groups = {}
|
||||
currentIssueRewards.value.forEach(item => {
|
||||
const level = item.level || '赏'
|
||||
if (!groups[level]) groups[level] = []
|
||||
groups[level].push(item)
|
||||
})
|
||||
return Object.keys(groups).sort((a, b) => {
|
||||
if (a === 'BOSS') return -1
|
||||
if (b === 'BOSS') return 1
|
||||
return a.localeCompare(b)
|
||||
}).map(key => {
|
||||
const rewards = groups[key]
|
||||
const total = rewards.reduce((sum, item) => sum + (Number(item.percent) || 0), 0)
|
||||
return {
|
||||
level: key,
|
||||
rewards: rewards,
|
||||
totalPercent: total.toFixed(1)
|
||||
}
|
||||
})
|
||||
})
|
||||
const currentIssueTitle = computed(() => {
|
||||
const arr = issues.value || []
|
||||
const cur = arr[selectedIssueIndex.value]
|
||||
return (cur && (cur.title || ('第' + (cur.no || '-') + '期'))) || '-'
|
||||
// 对对碰不显示“期数”,优先显示标题,兜底显示“奖池”
|
||||
return (cur && (cur.title || '奖池')) || '-'
|
||||
})
|
||||
const currentIssueStatusText = computed(() => {
|
||||
const arr = issues.value || []
|
||||
@ -502,7 +538,8 @@ function normalizeRewards(list) {
|
||||
title: i.name ?? i.title ?? '',
|
||||
image: cleanUrl(i.product_image ?? i.image ?? i.img ?? i.pic ?? i.banner ?? ''),
|
||||
weight: Number(i.weight) || 0,
|
||||
boss: detectBoss(i)
|
||||
boss: detectBoss(i),
|
||||
level: levelToAlpha(i.prize_level ?? i.level ?? (detectBoss(i) ? 'BOSS' : '赏'))
|
||||
}))
|
||||
const total = items.reduce((acc, it) => acc + (it.weight > 0 ? it.weight : 0), 0)
|
||||
const enriched = items.map(it => ({
|
||||
@ -555,6 +592,10 @@ async function fetchIssues(id) {
|
||||
const latestId = pickLatestIssueId(issues.value)
|
||||
setSelectedById(latestId)
|
||||
await fetchRewardsForIssues(id)
|
||||
// 获取购买记录
|
||||
if (currentIssueId.value) {
|
||||
fetchWinRecords(id, currentIssueId.value)
|
||||
}
|
||||
}
|
||||
|
||||
function pickLatestIssueId(list) {
|
||||
@ -596,12 +637,50 @@ function nextIssue() {
|
||||
syncResumeGame(activityId.value)
|
||||
}
|
||||
|
||||
async function fetchWinRecords(actId, issId) {
|
||||
if (!actId || !issId) return
|
||||
try {
|
||||
const res = await getIssueDrawLogs(actId, issId)
|
||||
const list = (res && res.list) || (Array.isArray(res) ? res : [])
|
||||
// 聚合同一奖品的记录
|
||||
const aggregate = {}
|
||||
list.forEach(it => {
|
||||
const key = it.reward_id || it.id
|
||||
if (!aggregate[key]) {
|
||||
aggregate[key] = {
|
||||
id: key,
|
||||
title: it.reward_name || it.title || it.name || '-',
|
||||
image: it.reward_image || it.image || '',
|
||||
count: 0
|
||||
}
|
||||
}
|
||||
aggregate[key].count += 1
|
||||
})
|
||||
const total = list.length || 1
|
||||
winRecords.value = Object.values(aggregate).map(it => ({
|
||||
...it,
|
||||
percent: ((it.count / total) * 100).toFixed(1)
|
||||
}))
|
||||
} catch (e) {
|
||||
console.error('fetchWinRecords error', e)
|
||||
winRecords.value = []
|
||||
}
|
||||
}
|
||||
|
||||
function formatPercent(v) {
|
||||
const n = Number(v)
|
||||
if (!Number.isFinite(n)) return '0%'
|
||||
return `${n}%`
|
||||
}
|
||||
|
||||
function levelToAlpha(level) {
|
||||
if (level === 'BOSS') return 'BOSS'
|
||||
const n = Number(level)
|
||||
if (isNaN(n) || n <= 0) return String(level || '赏')
|
||||
// 1 -> A, 2 -> B ... 26 -> Z
|
||||
return String.fromCharCode(64 + n)
|
||||
}
|
||||
|
||||
function openRewardsPopup() {
|
||||
rewardsVisible.value = true
|
||||
}
|
||||
@ -609,6 +688,18 @@ function closeRewardsPopup() {
|
||||
rewardsVisible.value = false
|
||||
}
|
||||
|
||||
function showRules() {
|
||||
uni.showModal({
|
||||
title: '活动规则',
|
||||
content: detail.value.rules || '1. 选择卡牌类型进行对对碰\\n2. 每次抽取随机获得奖品\\n3. 奖池与概率以页面展示为准',
|
||||
showCancel: false
|
||||
})
|
||||
}
|
||||
|
||||
function goCabinet() {
|
||||
uni.switchTab({ url: '/pages/cabinet/index' })
|
||||
}
|
||||
|
||||
function onPreviewBanner() {
|
||||
const url = detail.value.banner || ''
|
||||
if (url) uni.previewImage({ urls: [url], current: url })
|
||||
@ -1266,10 +1357,10 @@ onLoad((opts) => {
|
||||
}
|
||||
.header-info {
|
||||
flex: 1;
|
||||
min-width: 0;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: flex-start;
|
||||
min-height: 180rpx;
|
||||
justify-content: center;
|
||||
padding: 6rpx 0;
|
||||
}
|
||||
.header-title {
|
||||
@ -1306,6 +1397,52 @@ onLoad((opts) => {
|
||||
border: 1rpx solid rgba($brand-primary, 0.1);
|
||||
}
|
||||
|
||||
.header-actions {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 28rpx;
|
||||
margin-left: 16rpx;
|
||||
padding-left: 24rpx;
|
||||
border-left: 2rpx solid #E8E8E8;
|
||||
justify-content: center;
|
||||
align-self: stretch;
|
||||
}
|
||||
.action-btn {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
&:active {
|
||||
opacity: 0.6;
|
||||
}
|
||||
}
|
||||
.action-icon {
|
||||
width: 44rpx;
|
||||
height: 44rpx;
|
||||
margin-bottom: 8rpx;
|
||||
background-size: contain;
|
||||
background-repeat: no-repeat;
|
||||
background-position: center;
|
||||
}
|
||||
.rules-icon {
|
||||
background-color: #999;
|
||||
mask-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='currentColor'%3E%3Cpath d='M19 3H5c-1.1 0-2 .9-2 2v14c0 1.1.9 2 2 2h14c1.1 0 2-.9 2-2V5c0-1.1-.9-2-2-2zm-5 14H7v-2h7v2zm3-4H7v-2h10v2zm0-4H7V7h10v2z'/%3E%3C/svg%3E");
|
||||
-webkit-mask-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='currentColor'%3E%3Cpath d='M19 3H5c-1.1 0-2 .9-2 2v14c0 1.1.9 2 2 2h14c1.1 0 2-.9 2-2V5c0-1.1-.9-2-2-2zm-5 14H7v-2h7v2zm3-4H7v-2h10v2zm0-4H7V7h10v2z'/%3E%3C/svg%3E");
|
||||
mask-size: cover;
|
||||
-webkit-mask-size: cover;
|
||||
}
|
||||
.cabinet-icon {
|
||||
background-color: #999;
|
||||
mask-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='currentColor'%3E%3Cpath d='M20 3H4c-1.1 0-2 .9-2 2v16l4-4h14c1.1 0 2-.9 2-2V5c0-1.1-.9-2-2-2zm-9 11H7v-2h4v2zm6-4H7V8h10v2z'/%3E%3C/svg%3E");
|
||||
-webkit-mask-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='currentColor'%3E%3Cpath d='M20 3H4c-1.1 0-2 .9-2 2v16l4-4h14c1.1 0 2-.9 2-2V5c0-1.1-.9-2-2-2zm-9 11H7v-2h4v2zm6-4H7V8h10v2z'/%3E%3C/svg%3E");
|
||||
mask-size: cover;
|
||||
-webkit-mask-size: cover;
|
||||
}
|
||||
.action-label {
|
||||
font-size: 22rpx;
|
||||
color: #666;
|
||||
letter-spacing: 1rpx;
|
||||
}
|
||||
|
||||
.section-container {
|
||||
margin: 0 $spacing-lg $spacing-lg;
|
||||
background: rgba(255, 255, 255, 0.9);
|
||||
@ -1384,14 +1521,12 @@ onLoad((opts) => {
|
||||
|
||||
.preview-scroll {
|
||||
white-space: nowrap;
|
||||
margin: 0 -$spacing-lg;
|
||||
padding: 0 $spacing-lg;
|
||||
width: calc(100% + 40rpx);
|
||||
width: 100%;
|
||||
}
|
||||
.preview-item {
|
||||
display: inline-block;
|
||||
width: 160rpx;
|
||||
margin-right: $spacing-lg;
|
||||
width: 180rpx;
|
||||
margin-right: $spacing-md;
|
||||
vertical-align: top;
|
||||
position: relative;
|
||||
transition: transform 0.2s;
|
||||
@ -1401,12 +1536,12 @@ onLoad((opts) => {
|
||||
}
|
||||
|
||||
&:last-child {
|
||||
margin-right: 40rpx;
|
||||
margin-right: 0;
|
||||
}
|
||||
}
|
||||
.preview-img {
|
||||
width: 160rpx;
|
||||
height: 160rpx;
|
||||
width: 180rpx;
|
||||
height: 180rpx;
|
||||
border-radius: $radius-lg;
|
||||
background: $bg-secondary;
|
||||
margin-bottom: $spacing-sm;
|
||||
@ -1414,7 +1549,7 @@ onLoad((opts) => {
|
||||
border: 1rpx solid rgba(0,0,0,0.03);
|
||||
}
|
||||
.preview-name {
|
||||
font-size: $font-sm;
|
||||
font-size: $font-xs;
|
||||
color: $text-secondary;
|
||||
width: 100%;
|
||||
overflow: hidden;
|
||||
@ -1423,6 +1558,47 @@ onLoad((opts) => {
|
||||
text-align: center;
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
.prize-level-row {
|
||||
margin-bottom: $spacing-lg;
|
||||
background: rgba(0,0,0,0.02);
|
||||
padding: $spacing-md;
|
||||
border-radius: $radius-lg;
|
||||
&:last-child { margin-bottom: 0; }
|
||||
}
|
||||
|
||||
.level-header-row {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
margin-bottom: $spacing-md;
|
||||
}
|
||||
|
||||
.level-badge {
|
||||
display: inline-block;
|
||||
font-size: $font-xs;
|
||||
font-weight: 900;
|
||||
color: $text-main;
|
||||
background: #F0F0F0;
|
||||
padding: 4rpx 16rpx;
|
||||
border-radius: 8rpx;
|
||||
font-style: italic;
|
||||
border: 1rpx solid rgba(0,0,0,0.05);
|
||||
box-shadow: $shadow-xs;
|
||||
|
||||
&.badge-boss {
|
||||
background: $gradient-gold;
|
||||
color: #78350F;
|
||||
border-color: rgba(217, 119, 6, 0.3);
|
||||
}
|
||||
}
|
||||
|
||||
.level-prob {
|
||||
font-size: 22rpx;
|
||||
color: $brand-primary;
|
||||
font-weight: 800;
|
||||
opacity: 0.9;
|
||||
}
|
||||
.prize-tag {
|
||||
position: absolute;
|
||||
top: 10rpx;
|
||||
@ -1542,6 +1718,7 @@ onLoad((opts) => {
|
||||
font-weight: 900;
|
||||
background: $gradient-brand;
|
||||
-webkit-background-clip: text;
|
||||
background-clip: text;
|
||||
-webkit-text-fill-color: transparent;
|
||||
display: inline-block;
|
||||
}
|
||||
@ -1831,24 +2008,28 @@ onLoad((opts) => {
|
||||
.empty-icon { font-size: 80rpx; margin-bottom: $spacing-lg; opacity: 0.5; }
|
||||
.empty-text { font-size: $font-md; }
|
||||
|
||||
/* Float Bar */
|
||||
/* 底部悬浮操作栏 - 高级重置 */
|
||||
.float-bar {
|
||||
position: fixed;
|
||||
left: 0; right: 0; bottom: 0;
|
||||
padding: $spacing-lg $spacing-xl;
|
||||
padding-bottom: calc($spacing-lg + env(safe-area-inset-bottom));
|
||||
background: rgba(255, 255, 255, 0.9);
|
||||
backdrop-filter: blur(20px);
|
||||
box-shadow: 0 -8rpx 30rpx rgba(0, 0, 0, 0.05);
|
||||
left: 32rpx;
|
||||
right: 32rpx;
|
||||
bottom: calc(40rpx + env(safe-area-inset-bottom));
|
||||
z-index: 100;
|
||||
animation: slideUp 0.4s ease-out backwards;
|
||||
animation: slideUp 0.6s cubic-bezier(0.23, 1, 0.32, 1) backwards;
|
||||
}
|
||||
|
||||
.float-bar-inner {
|
||||
background: rgba(255, 255, 255, 0.85);
|
||||
backdrop-filter: blur(30rpx);
|
||||
padding: 24rpx 40rpx;
|
||||
border-radius: 999rpx;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
gap: $spacing-lg;
|
||||
box-shadow: 0 16rpx 48rpx rgba(0, 0, 0, 0.12);
|
||||
border: 1rpx solid rgba(255, 255, 255, 0.6);
|
||||
}
|
||||
|
||||
.float-price {
|
||||
display: flex;
|
||||
align-items: baseline;
|
||||
@ -1856,9 +2037,9 @@ onLoad((opts) => {
|
||||
font-weight: 800;
|
||||
min-width: 0;
|
||||
}
|
||||
.currency { font-size: $font-md; color: $brand-primary; }
|
||||
.amount { font-size: 44rpx; margin: 0 6rpx; color: $brand-primary; font-family: 'DIN Alternate', sans-serif; }
|
||||
.unit { font-size: $font-sm; color: $text-tertiary; font-weight: 600; }
|
||||
.float-price .currency { font-size: 26rpx; margin-right: 4rpx; color: $brand-primary; }
|
||||
.float-price .amount { font-size: 44rpx; font-weight: 900; font-family: 'DIN Alternate', sans-serif; color: $brand-primary; }
|
||||
.float-price .unit { font-size: 24rpx; color: $text-sub; margin-left: 4rpx; font-weight: 600; }
|
||||
|
||||
.rewards-overlay { position: fixed; left: 0; right: 0; top: 0; bottom: 0; z-index: 9000; }
|
||||
.rewards-mask {
|
||||
@ -1901,6 +2082,43 @@ onLoad((opts) => {
|
||||
max-height: 60vh;
|
||||
padding: $spacing-lg;
|
||||
}
|
||||
|
||||
.rewards-group-v2 {
|
||||
margin-bottom: $spacing-xl;
|
||||
&:last-child { margin-bottom: 0; }
|
||||
}
|
||||
|
||||
.group-header-row {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
margin-bottom: $spacing-md;
|
||||
padding: 0 4rpx;
|
||||
}
|
||||
|
||||
.group-badge {
|
||||
font-size: $font-xs;
|
||||
font-weight: 900;
|
||||
color: $text-main;
|
||||
background: #F0F0F0;
|
||||
padding: 4rpx 16rpx;
|
||||
border-radius: 8rpx;
|
||||
font-style: italic;
|
||||
border: 1rpx solid rgba(0,0,0,0.05);
|
||||
box-shadow: $shadow-xs;
|
||||
|
||||
&.badge-boss {
|
||||
background: $gradient-gold;
|
||||
color: #78350F;
|
||||
border-color: rgba(217, 119, 6, 0.3);
|
||||
}
|
||||
}
|
||||
|
||||
.group-total-prob {
|
||||
font-size: 24rpx;
|
||||
color: $brand-primary;
|
||||
font-weight: 800;
|
||||
}
|
||||
.rewards-item {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
@ -1978,44 +2196,57 @@ onLoad((opts) => {
|
||||
to { transform: translateY(0); opacity: 1; }
|
||||
}
|
||||
.action-btn {
|
||||
height: 96rpx;
|
||||
border-radius: $radius-round;
|
||||
height: 88rpx;
|
||||
line-height: 88rpx;
|
||||
padding: 0 56rpx;
|
||||
border-radius: 999rpx;
|
||||
font-size: 30rpx;
|
||||
font-weight: 900;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
font-size: $font-xl;
|
||||
font-weight: 800;
|
||||
transition: all 0.3s cubic-bezier(0.175, 0.885, 0.32, 1.275);
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
transition: all 0.2s;
|
||||
|
||||
&.primary {
|
||||
background: $gradient-brand;
|
||||
color: #fff;
|
||||
box-shadow: $shadow-warm;
|
||||
}
|
||||
|
||||
&.secondary {
|
||||
background: rgba($bg-card, 0.9);
|
||||
color: $text-main;
|
||||
border: 2rpx solid rgba($brand-primary, 0.25);
|
||||
box-shadow: $shadow-sm;
|
||||
padding: 0 40rpx;
|
||||
background: $gradient-brand !important;
|
||||
color: #fff !important;
|
||||
box-shadow: 0 12rpx 32rpx rgba($brand-primary, 0.35);
|
||||
|
||||
&::before {
|
||||
content: '';
|
||||
position: absolute;
|
||||
top: -50%;
|
||||
left: -150%;
|
||||
width: 200%;
|
||||
height: 200%;
|
||||
background: linear-gradient(
|
||||
120deg,
|
||||
rgba(255, 255, 255, 0) 30%,
|
||||
rgba(255, 255, 255, 0.4) 50%,
|
||||
rgba(255, 255, 255, 0) 70%
|
||||
);
|
||||
transform: rotate(25deg);
|
||||
animation: btnShine 4s infinite cubic-bezier(0.19, 1, 0.22, 1);
|
||||
pointer-events: none;
|
||||
}
|
||||
}
|
||||
|
||||
&:active { transform: scale(0.98); }
|
||||
}
|
||||
.btn-shine {
|
||||
position: absolute;
|
||||
top: 0; left: -100%; width: 50%; height: 100%;
|
||||
background: linear-gradient(90deg, transparent, rgba(255,255,255,0.3), transparent);
|
||||
transform: skewX(-20deg);
|
||||
animation: shine 3s infinite;
|
||||
&.secondary {
|
||||
background: #1A1A1A !important;
|
||||
color: $accent-gold !important;
|
||||
box-shadow: 0 8rpx 20rpx rgba(0, 0, 0, 0.15);
|
||||
}
|
||||
|
||||
&:active {
|
||||
transform: scale(0.92);
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes shine {
|
||||
0% { left: -100%; }
|
||||
50%, 100% { left: 200%; }
|
||||
@keyframes btnShine {
|
||||
0% { left: -150%; }
|
||||
100% { left: 150%; }
|
||||
}
|
||||
|
||||
.flip-overlay {
|
||||
|
||||
@ -214,6 +214,7 @@ $local-gold: #FFD700; // 特殊金色,比全局更亮
|
||||
text-shadow: 0 4rpx 16rpx rgba(0,0,0,0.6);
|
||||
background: linear-gradient(180deg, #fff, #b3b3b3);
|
||||
-webkit-background-clip: text;
|
||||
background-clip: text;
|
||||
color: transparent;
|
||||
letter-spacing: 2rpx;
|
||||
}
|
||||
@ -327,59 +328,71 @@ $local-gold: #FFD700; // 特殊金色,比全局更亮
|
||||
}
|
||||
|
||||
.action-area {
|
||||
background: $bg-dark-card;
|
||||
backdrop-filter: blur(40rpx);
|
||||
padding: 24rpx 32rpx;
|
||||
border-radius: 100rpx;
|
||||
position: fixed;
|
||||
left: 40rpx;
|
||||
right: 40rpx;
|
||||
bottom: calc(40rpx + env(safe-area-inset-bottom));
|
||||
background: rgba(26, 26, 26, 0.85);
|
||||
backdrop-filter: blur(30rpx);
|
||||
padding: 24rpx 40rpx;
|
||||
border-radius: 999rpx;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
border: 1px solid $border-dark;
|
||||
box-shadow: 0 20rpx 60rpx rgba(0,0,0,0.5);
|
||||
margin-bottom: calc(env(safe-area-inset-bottom) + 20rpx);
|
||||
animation: slideUp 0.6s ease-out backwards;
|
||||
animation-delay: 0.3s;
|
||||
border: 1rpx solid rgba(255, 255, 255, 0.1);
|
||||
box-shadow: 0 16rpx 48rpx rgba(0, 0, 0, 0.4);
|
||||
z-index: 100;
|
||||
animation: slideUp 0.6s cubic-bezier(0.23, 1, 0.32, 1) backwards;
|
||||
}
|
||||
|
||||
.challenge-btn {
|
||||
background: $gradient-brand;
|
||||
color: #fff;
|
||||
background: $gradient-brand !important;
|
||||
color: #fff !important;
|
||||
font-weight: 900;
|
||||
border-radius: 100rpx;
|
||||
border-radius: 999rpx;
|
||||
padding: 0 60rpx;
|
||||
height: 88rpx;
|
||||
line-height: 88rpx;
|
||||
font-size: 32rpx;
|
||||
box-shadow: 0 8rpx 24rpx rgba(255, 107, 0, 0.3);
|
||||
box-shadow: 0 12rpx 32rpx rgba(255, 107, 0, 0.3);
|
||||
border: none;
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
transition: all 0.2s;
|
||||
transition: all 0.3s cubic-bezier(0.175, 0.885, 0.32, 1.275);
|
||||
width: 100%;
|
||||
|
||||
&::after {
|
||||
&::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: shimmer 3s infinite;
|
||||
top: -50%;
|
||||
left: -150%;
|
||||
width: 200%;
|
||||
height: 200%;
|
||||
background: linear-gradient(
|
||||
120deg,
|
||||
rgba(255, 255, 255, 0) 30%,
|
||||
rgba(255, 255, 255, 0.4) 50%,
|
||||
rgba(255, 255, 255, 0) 70%
|
||||
);
|
||||
transform: rotate(25deg);
|
||||
animation: btnShine 4s infinite cubic-bezier(0.19, 1, 0.22, 1);
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
&:active {
|
||||
transform: scale(0.96);
|
||||
box-shadow: 0 4rpx 12rpx rgba(255, 107, 0, 0.2);
|
||||
transform: scale(0.94);
|
||||
}
|
||||
|
||||
&.disabled {
|
||||
background: #555;
|
||||
color: #999;
|
||||
background: #333 !important;
|
||||
color: #666 !important;
|
||||
box-shadow: none;
|
||||
&::after { display: none; }
|
||||
&::before { display: none; }
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes shimmer {
|
||||
0% { left: -100%; }
|
||||
50%, 100% { left: 200%; }
|
||||
@keyframes btnShine {
|
||||
0% { left: -150%; }
|
||||
100% { left: 150%; }
|
||||
}
|
||||
</style>
|
||||
|
||||
@ -27,28 +27,71 @@
|
||||
</view>
|
||||
<view class="header-actions">
|
||||
<view class="action-btn" @tap="showRules">
|
||||
<text class="icon">📋</text>
|
||||
<text>规则</text>
|
||||
<view class="action-icon rules-icon"></view>
|
||||
<text class="action-label">规则</text>
|
||||
</view>
|
||||
<view class="action-btn" @tap="goCabinet">
|
||||
<text class="icon">📦</text>
|
||||
<text>盒柜</text>
|
||||
<view class="action-icon cabinet-icon"></view>
|
||||
<text class="action-label">盒柜</text>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<view class="section-container animate-enter stagger-1" v-if="currentIssueRewards.length > 0">
|
||||
<view class="section-header">
|
||||
<text class="section-title">奖池一览</text>
|
||||
<text class="section-more" @tap="openRewardsPopup">查看全部</text>
|
||||
</view>
|
||||
<scroll-view class="preview-scroll" scroll-x>
|
||||
<view class="preview-item" v-for="(item, idx) in currentIssueRewards" :key="item.id || idx">
|
||||
<view class="prize-tag" :class="{ 'tag-boss': item.boss }">{{ item.boss ? 'BOSS' : '赏' }}</view>
|
||||
<image class="preview-img" :src="item.image" mode="aspectFill" />
|
||||
<view class="preview-name">{{ item.title }}</view>
|
||||
<view class="section-container animate-enter stagger-1">
|
||||
<view class="modern-tabs">
|
||||
<view class="tab-item" :class="{ active: tabActive === 'pool' }" @tap="tabActive = 'pool'">
|
||||
本机奖池
|
||||
<view v-if="tabActive === 'pool'" class="active-dot"></view>
|
||||
</view>
|
||||
</scroll-view>
|
||||
<view class="tab-item" :class="{ active: tabActive === 'records' }" @tap="tabActive = 'records'">
|
||||
购买记录
|
||||
<view v-if="tabActive === 'records'" class="active-dot"></view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<view v-show="tabActive === 'pool'">
|
||||
<view class="section-header">
|
||||
<text class="section-title">奖池配置</text>
|
||||
<text class="section-more" @tap="openRewardsPopup">查看全部</text>
|
||||
</view>
|
||||
<view v-if="rewardGroups.length > 0">
|
||||
<view class="prize-level-row" v-for="group in rewardGroups" :key="group.level">
|
||||
<view class="level-header-row">
|
||||
<view class="level-badge" :class="{ 'badge-boss': group.level === 'BOSS' }">{{ group.level }}赏</view>
|
||||
<text class="level-prob">总概率 {{ group.totalPercent }}%</text>
|
||||
</view>
|
||||
<scroll-view class="preview-scroll" scroll-x>
|
||||
<view class="preview-item" v-for="(item, idx) in group.rewards" :key="item.id || idx">
|
||||
<view class="prize-tag" :class="{ 'tag-boss': item.boss }">{{ item.boss ? 'BOSS' : group.level }}</view>
|
||||
<image class="preview-img" :src="item.image" mode="aspectFill" />
|
||||
<view class="preview-name">{{ item.title }}</view>
|
||||
</view>
|
||||
</scroll-view>
|
||||
</view>
|
||||
</view>
|
||||
<view v-else class="empty-state">
|
||||
<text class="empty-icon">📭</text>
|
||||
<text class="empty-text">暂无奖池配置</text>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<view v-show="tabActive === 'records'">
|
||||
<view class="records-list" v-if="winRecords.length">
|
||||
<view v-for="(it, idx) in winRecords" :key="it.id" class="record-item">
|
||||
<image class="record-img" :src="it.image" mode="aspectFill" />
|
||||
<view class="record-info">
|
||||
<view class="record-title">{{ it.title }}</view>
|
||||
<view class="record-meta">
|
||||
<text class="record-count">x{{ it.count }}</text>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
<view class="empty-state" v-else>
|
||||
<text class="empty-icon">📝</text>
|
||||
<text class="empty-text">暂无购买记录</text>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<view style="height: 220rpx;"></view>
|
||||
@ -83,17 +126,25 @@
|
||||
<text class="rewards-close" @tap="closeRewardsPopup">×</text>
|
||||
</view>
|
||||
<scroll-view scroll-y class="rewards-list">
|
||||
<view v-for="(item, idx) in rewardsForPopup" :key="item.id || idx" class="rewards-item">
|
||||
<image class="rewards-thumb" :src="item.image" mode="aspectFill" />
|
||||
<view class="rewards-info">
|
||||
<view class="rewards-name-row">
|
||||
<text class="rewards-name">{{ item.title || '-' }}</text>
|
||||
<view class="rewards-tag" v-if="item.boss">BOSS</view>
|
||||
<view v-if="rewardGroups.length > 0">
|
||||
<view class="rewards-group-v2" v-for="group in rewardGroups" :key="group.level">
|
||||
<view class="group-header-row">
|
||||
<text class="group-badge" :class="{ 'badge-boss': group.level === 'BOSS' }">{{ group.level }}赏</text>
|
||||
<text class="group-total-prob">该档总概率 {{ group.totalPercent }}%</text>
|
||||
</view>
|
||||
<view v-for="(item, idx) in group.rewards" :key="item.id || idx" class="rewards-item">
|
||||
<image class="rewards-thumb" :src="item.image" mode="aspectFill" />
|
||||
<view class="rewards-info">
|
||||
<view class="rewards-name-row">
|
||||
<text class="rewards-name">{{ item.title || '-' }}</text>
|
||||
<view class="rewards-tag" v-if="item.boss">BOSS</view>
|
||||
</view>
|
||||
<text class="rewards-percent">单项概率 {{ formatPercent(item.percent) }}</text>
|
||||
</view>
|
||||
</view>
|
||||
<text class="rewards-percent">概率 {{ formatPercent(item.percent) }}</text>
|
||||
</view>
|
||||
</view>
|
||||
<view v-if="!rewardsForPopup.length" class="rewards-empty">暂无奖池数据</view>
|
||||
<view v-else class="rewards-empty">暂无奖池数据</view>
|
||||
</scroll-view>
|
||||
</view>
|
||||
</view>
|
||||
@ -120,11 +171,13 @@ import { ref, computed, nextTick } from 'vue'
|
||||
import FlipGrid from '../../../components/FlipGrid.vue'
|
||||
import { onLoad } from '@dcloudio/uni-app'
|
||||
import PaymentPopup from '../../../components/PaymentPopup.vue'
|
||||
import { getActivityDetail, getActivityIssues, getActivityIssueRewards, joinLottery, createWechatOrder, getLotteryResult, getItemCards, getUserCoupons } from '../../../api/appUser'
|
||||
import { getActivityDetail, getActivityIssues, getActivityIssueRewards, joinLottery, createWechatOrder, getLotteryResult, getItemCards, getUserCoupons, getIssueDrawLogs } from '../../../api/appUser'
|
||||
|
||||
const detail = ref({})
|
||||
const statusText = ref('')
|
||||
const rewardsVisible = ref(false)
|
||||
const tabActive = ref('pool')
|
||||
const winRecords = ref([])
|
||||
const issues = ref([])
|
||||
const rewardsMap = ref({})
|
||||
const currentIssueId = ref('')
|
||||
@ -137,8 +190,8 @@ const coverUrl = computed(() => cleanUrl(detail.value && (detail.value.image ||
|
||||
const currentIssueTitle = computed(() => {
|
||||
const arr = issues.value || []
|
||||
const cur = arr[selectedIssueIndex.value]
|
||||
const t = (cur && (cur.title || ('第' + (cur.no || '-') + '期'))) || '-'
|
||||
return t
|
||||
// 不显示“期数”,优先显示标题,兜底显示“奖池”
|
||||
return (cur && (cur.title || '奖池')) || '-'
|
||||
})
|
||||
const points = ref(0)
|
||||
const flipRef = ref(null)
|
||||
@ -147,6 +200,28 @@ const rewardsForPopup = computed(() => {
|
||||
const arr = currentIssueRewards.value || []
|
||||
return Array.isArray(arr) ? arr : []
|
||||
})
|
||||
const rewardGroups = computed(() => {
|
||||
const groups = {}
|
||||
currentIssueRewards.value.forEach(item => {
|
||||
const level = item.level || '赏'
|
||||
if (!groups[level]) groups[level] = []
|
||||
groups[level].push(item)
|
||||
})
|
||||
return Object.keys(groups).sort((a, b) => {
|
||||
if (a === 'BOSS') return -1
|
||||
if (b === 'BOSS') return 1
|
||||
// Alphabetical sort (A, B, C...)
|
||||
return a.localeCompare(b)
|
||||
}).map(key => {
|
||||
const rewards = groups[key]
|
||||
const total = rewards.reduce((sum, item) => sum + (Number(item.percent) || 0), 0)
|
||||
return {
|
||||
level: key,
|
||||
rewards: rewards,
|
||||
totalPercent: total.toFixed(1)
|
||||
}
|
||||
})
|
||||
})
|
||||
const paymentVisible = ref(false)
|
||||
const paymentAmount = ref('0.00')
|
||||
const coupons = ref([])
|
||||
@ -169,6 +244,15 @@ function formatPercent(v) {
|
||||
return `${n}%`
|
||||
}
|
||||
|
||||
function levelToAlpha(level) {
|
||||
if (level === 'BOSS') return 'BOSS'
|
||||
const n = Number(level)
|
||||
if (isNaN(n) || n <= 0) return String(level || '赏')
|
||||
// 1 -> A, 2 -> B ... 26 -> Z
|
||||
// Only handle up to 26 levels for now as it's rare to have more
|
||||
return String.fromCharCode(64 + n)
|
||||
}
|
||||
|
||||
function openRewardsPopup() {
|
||||
rewardsVisible.value = true
|
||||
}
|
||||
@ -235,7 +319,8 @@ function normalizeRewards(list) {
|
||||
title: i.name ?? i.title ?? '',
|
||||
image: cleanUrl(i.product_image ?? i.image ?? i.img ?? i.pic ?? i.banner ?? ''),
|
||||
weight: Number(i.weight) || 0,
|
||||
boss: detectBoss(i)
|
||||
boss: detectBoss(i),
|
||||
level: levelToAlpha(i.prize_level ?? i.level ?? (detectBoss(i) ? 'BOSS' : '赏'))
|
||||
}))
|
||||
const total = items.reduce((acc, it) => acc + (it.weight > 0 ? it.weight : 0), 0)
|
||||
const enriched = items.map(it => ({
|
||||
@ -288,6 +373,10 @@ async function fetchIssues(id) {
|
||||
const latestId = pickLatestIssueId(issues.value)
|
||||
setSelectedById(latestId)
|
||||
await fetchRewardsForIssues(id)
|
||||
// 获取购买记录
|
||||
if (currentIssueId.value) {
|
||||
fetchWinRecords(id, currentIssueId.value)
|
||||
}
|
||||
}
|
||||
|
||||
function pickLatestIssueId(list) {
|
||||
@ -328,6 +417,36 @@ function nextIssue() {
|
||||
currentIssueId.value = (cur && cur.id) || ''
|
||||
}
|
||||
|
||||
async function fetchWinRecords(actId, issId) {
|
||||
if (!actId || !issId) return
|
||||
try {
|
||||
const res = await getIssueDrawLogs(actId, issId)
|
||||
const list = (res && res.list) || (Array.isArray(res) ? res : [])
|
||||
// 聚合同一奖品的记录
|
||||
const aggregate = {}
|
||||
list.forEach(it => {
|
||||
const key = it.reward_id || it.id
|
||||
if (!aggregate[key]) {
|
||||
aggregate[key] = {
|
||||
id: key,
|
||||
title: it.reward_name || it.title || it.name || '-',
|
||||
image: it.reward_image || it.image || '',
|
||||
count: 0
|
||||
}
|
||||
}
|
||||
aggregate[key].count += 1
|
||||
})
|
||||
const total = list.length || 1
|
||||
winRecords.value = Object.values(aggregate).map(it => ({
|
||||
...it,
|
||||
percent: ((it.count / total) * 100).toFixed(1)
|
||||
}))
|
||||
} catch (e) {
|
||||
console.error('fetchWinRecords error', e)
|
||||
winRecords.value = []
|
||||
}
|
||||
}
|
||||
|
||||
function onPreviewBanner() {
|
||||
const url = detail.value.banner || ''
|
||||
if (url) uni.previewImage({ urls: [url], current: url })
|
||||
@ -618,10 +737,10 @@ function closeFlip() { showFlip.value = false }
|
||||
}
|
||||
.header-info {
|
||||
flex: 1;
|
||||
min-width: 0;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: flex-start;
|
||||
min-height: 180rpx;
|
||||
justify-content: center;
|
||||
padding: 6rpx 0;
|
||||
}
|
||||
.header-title {
|
||||
@ -660,29 +779,47 @@ function closeFlip() { showFlip.value = false }
|
||||
.header-actions {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: $spacing-lg;
|
||||
margin-left: 20rpx;
|
||||
padding-left: $spacing-lg;
|
||||
border-left: 1rpx solid rgba(0,0,0,0.06);
|
||||
gap: 28rpx;
|
||||
margin-left: 16rpx;
|
||||
padding-left: 24rpx;
|
||||
border-left: 2rpx solid #E8E8E8;
|
||||
justify-content: center;
|
||||
height: 140rpx;
|
||||
align-self: stretch;
|
||||
}
|
||||
.action-btn {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
font-size: $font-xs;
|
||||
color: $text-sub;
|
||||
transition: all 0.2s;
|
||||
&:active {
|
||||
transform: scale(0.9);
|
||||
color: $text-main;
|
||||
opacity: 0.6;
|
||||
}
|
||||
}
|
||||
.action-btn .icon {
|
||||
font-size: $font-xl;
|
||||
margin-bottom: 6rpx;
|
||||
filter: grayscale(0.2);
|
||||
.action-icon {
|
||||
width: 44rpx;
|
||||
height: 44rpx;
|
||||
margin-bottom: 8rpx;
|
||||
background-size: contain;
|
||||
background-repeat: no-repeat;
|
||||
background-position: center;
|
||||
}
|
||||
.rules-icon {
|
||||
background-color: #999;
|
||||
mask-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='currentColor'%3E%3Cpath d='M19 3H5c-1.1 0-2 .9-2 2v14c0 1.1.9 2 2 2h14c1.1 0 2-.9 2-2V5c0-1.1-.9-2-2-2zm-5 14H7v-2h7v2zm3-4H7v-2h10v2zm0-4H7V7h10v2z'/%3E%3C/svg%3E");
|
||||
-webkit-mask-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='currentColor'%3E%3Cpath d='M19 3H5c-1.1 0-2 .9-2 2v14c0 1.1.9 2 2 2h14c1.1 0 2-.9 2-2V5c0-1.1-.9-2-2-2zm-5 14H7v-2h7v2zm3-4H7v-2h10v2zm0-4H7V7h10v2z'/%3E%3C/svg%3E");
|
||||
mask-size: cover;
|
||||
-webkit-mask-size: cover;
|
||||
}
|
||||
.cabinet-icon {
|
||||
background-color: #999;
|
||||
mask-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='currentColor'%3E%3Cpath d='M20 3H4c-1.1 0-2 .9-2 2v16l4-4h14c1.1 0 2-.9 2-2V5c0-1.1-.9-2-2-2zm-9 11H7v-2h4v2zm6-4H7V8h10v2z'/%3E%3C/svg%3E");
|
||||
-webkit-mask-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='currentColor'%3E%3Cpath d='M20 3H4c-1.1 0-2 .9-2 2v16l4-4h14c1.1 0 2-.9 2-2V5c0-1.1-.9-2-2-2zm-9 11H7v-2h4v2zm6-4H7V8h10v2z'/%3E%3C/svg%3E");
|
||||
mask-size: cover;
|
||||
-webkit-mask-size: cover;
|
||||
}
|
||||
.action-label {
|
||||
font-size: 22rpx;
|
||||
color: #666;
|
||||
letter-spacing: 1rpx;
|
||||
}
|
||||
|
||||
.section-container {
|
||||
@ -693,6 +830,39 @@ function closeFlip() { showFlip.value = false }
|
||||
box-shadow: $shadow-sm;
|
||||
backdrop-filter: blur(10rpx);
|
||||
}
|
||||
|
||||
.modern-tabs {
|
||||
display: flex;
|
||||
background: $bg-secondary;
|
||||
padding: 8rpx;
|
||||
border-radius: $radius-lg;
|
||||
margin-bottom: $spacing-lg;
|
||||
}
|
||||
.tab-item {
|
||||
flex: 1;
|
||||
text-align: center;
|
||||
padding: $spacing-md 0;
|
||||
font-size: $font-md;
|
||||
color: $text-sub;
|
||||
border-radius: $radius-md;
|
||||
font-weight: 600;
|
||||
transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
|
||||
position: relative;
|
||||
|
||||
&.active {
|
||||
background: #FFFFFF;
|
||||
color: $brand-primary;
|
||||
box-shadow: $shadow-sm;
|
||||
}
|
||||
}
|
||||
.active-dot {
|
||||
width: 8rpx; height: 8rpx;
|
||||
background: $brand-primary;
|
||||
border-radius: 50%;
|
||||
position: absolute;
|
||||
bottom: 8rpx; left: 50%; transform: translateX(-50%);
|
||||
}
|
||||
|
||||
.section-header {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
@ -733,31 +903,29 @@ function closeFlip() { showFlip.value = false }
|
||||
|
||||
.preview-scroll {
|
||||
white-space: nowrap;
|
||||
margin: 0 -$spacing-lg;
|
||||
padding: 0 $spacing-lg;
|
||||
width: calc(100% + 40rpx);
|
||||
width: 100%;
|
||||
}
|
||||
.preview-item {
|
||||
display: inline-block;
|
||||
width: 200rpx;
|
||||
margin-right: $spacing-lg;
|
||||
width: 180rpx;
|
||||
margin-right: $spacing-md;
|
||||
vertical-align: top;
|
||||
position: relative;
|
||||
transition: transform 0.2s;
|
||||
&:active { transform: scale(0.96); }
|
||||
&:last-child { margin-right: 40rpx; }
|
||||
&:last-child { margin-right: 0; }
|
||||
}
|
||||
.preview-img {
|
||||
width: 200rpx;
|
||||
height: 200rpx;
|
||||
width: 180rpx;
|
||||
height: 180rpx;
|
||||
border-radius: $radius-lg;
|
||||
background: $bg-secondary;
|
||||
margin-bottom: $spacing-md;
|
||||
margin-bottom: $spacing-sm;
|
||||
box-shadow: $shadow-sm;
|
||||
border: 1rpx solid rgba(0,0,0,0.03);
|
||||
}
|
||||
.preview-name {
|
||||
font-size: $font-sm;
|
||||
font-size: $font-xs;
|
||||
color: $text-secondary;
|
||||
width: 100%;
|
||||
overflow: hidden;
|
||||
@ -768,16 +936,16 @@ function closeFlip() { showFlip.value = false }
|
||||
}
|
||||
.prize-tag {
|
||||
position: absolute;
|
||||
top: 10rpx;
|
||||
left: 10rpx;
|
||||
top: 8rpx;
|
||||
left: 8rpx;
|
||||
background: rgba(0,0,0,0.6);
|
||||
color: #fff;
|
||||
font-size: $font-xs;
|
||||
padding: 4rpx $spacing-sm;
|
||||
border-radius: $radius-sm;
|
||||
font-size: $font-xxs;
|
||||
padding: 2rpx $spacing-xs;
|
||||
border-radius: 4rpx;
|
||||
z-index: 10;
|
||||
font-weight: 700;
|
||||
backdrop-filter: blur(4rpx);
|
||||
backdrop-filter: blur(4px);
|
||||
transform: scale(0.9);
|
||||
transform-origin: top left;
|
||||
}
|
||||
@ -786,6 +954,47 @@ function closeFlip() { showFlip.value = false }
|
||||
box-shadow: 0 4rpx 12rpx rgba($brand-primary, 0.4);
|
||||
}
|
||||
|
||||
/* 新增:等级分组样式 */
|
||||
.prize-level-row {
|
||||
margin-bottom: $spacing-lg;
|
||||
background: rgba(0,0,0,0.02);
|
||||
padding: $spacing-md;
|
||||
border-radius: $radius-lg;
|
||||
&:last-child { margin-bottom: 0; }
|
||||
}
|
||||
|
||||
.level-header-row {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
margin-bottom: $spacing-md;
|
||||
}
|
||||
|
||||
.level-badge {
|
||||
display: inline-block;
|
||||
font-size: $font-xs;
|
||||
font-weight: 900;
|
||||
color: $text-main;
|
||||
background: #F0F0F0;
|
||||
padding: 4rpx 16rpx;
|
||||
border-radius: 8rpx;
|
||||
font-style: italic;
|
||||
border: 1rpx solid rgba(0,0,0,0.05);
|
||||
box-shadow: $shadow-xs;
|
||||
}
|
||||
|
||||
.level-prob {
|
||||
font-size: 22rpx;
|
||||
color: $brand-primary;
|
||||
font-weight: 800;
|
||||
}
|
||||
|
||||
.level-badge.badge-boss {
|
||||
background: $gradient-brand;
|
||||
color: #fff;
|
||||
border: none;
|
||||
}
|
||||
|
||||
.selector-container {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
@ -893,6 +1102,7 @@ function closeFlip() { showFlip.value = false }
|
||||
margin-bottom: $spacing-sm;
|
||||
display: -webkit-box;
|
||||
-webkit-line-clamp: 2;
|
||||
line-clamp: 2;
|
||||
-webkit-box-orient: vertical;
|
||||
overflow: hidden;
|
||||
line-height: 1.4;
|
||||
@ -997,23 +1207,23 @@ function closeFlip() { showFlip.value = false }
|
||||
border: 1rpx solid rgba(255, 255, 255, 0.2);
|
||||
}
|
||||
|
||||
/* 底部多档位抽赏按钮 */
|
||||
/* 底部多档位抽赏按钮 - 高级重置 */
|
||||
.bottom-actions {
|
||||
position: fixed;
|
||||
left: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
display: flex;
|
||||
gap: $spacing-md;
|
||||
padding: $spacing-lg $spacing-lg;
|
||||
padding-bottom: calc($spacing-lg + env(safe-area-inset-bottom));
|
||||
background: rgba(255, 255, 255, 0.9);
|
||||
backdrop-filter: blur(20rpx);
|
||||
box-shadow: 0 -8rpx 24rpx rgba(0, 0, 0, 0.08);
|
||||
gap: 20rpx;
|
||||
padding: 32rpx 32rpx;
|
||||
padding-bottom: calc(32rpx + env(safe-area-inset-bottom));
|
||||
background: rgba(255, 255, 255, 0.85);
|
||||
backdrop-filter: blur(30rpx);
|
||||
box-shadow: 0 -12rpx 40rpx rgba(0, 0, 0, 0.08);
|
||||
z-index: 999;
|
||||
animation: slideUp $transition-slow $ease-out backwards;
|
||||
border-top: 1rpx solid rgba(0,0,0,0.05);
|
||||
border-top: 1rpx solid rgba(255, 255, 255, 0.8);
|
||||
}
|
||||
|
||||
.tier-btn {
|
||||
flex: 1;
|
||||
min-width: 0;
|
||||
@ -1021,92 +1231,80 @@ function closeFlip() { showFlip.value = false }
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
padding: $spacing-md $spacing-xs;
|
||||
background: $bg-card;
|
||||
border: 1rpx solid $border-color-light;
|
||||
border-radius: $radius-lg;
|
||||
box-shadow: $shadow-sm;
|
||||
transition: all $transition-fast;
|
||||
padding: 24rpx 10rpx;
|
||||
background: #FFF;
|
||||
border: 2rpx solid rgba($brand-primary, 0.1);
|
||||
border-radius: 28rpx;
|
||||
box-shadow: 0 4rpx 12rpx rgba(0,0,0,0.03);
|
||||
transition: all 0.3s cubic-bezier(0.25, 0.8, 0.25, 1);
|
||||
margin: 0;
|
||||
line-height: normal;
|
||||
|
||||
&:active {
|
||||
transform: scale(0.95);
|
||||
background: $bg-page;
|
||||
transform: scale(0.92);
|
||||
background: #F9F9F9;
|
||||
box-shadow: none;
|
||||
}
|
||||
}
|
||||
.tier-price {
|
||||
font-size: $font-lg;
|
||||
font-weight: 800;
|
||||
color: $text-main;
|
||||
font-family: 'DIN Alternate', sans-serif;
|
||||
}
|
||||
.tier-label {
|
||||
font-size: $font-xs;
|
||||
color: $text-sub;
|
||||
margin-top: 4rpx;
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
.tier-price {
|
||||
font-size: 34rpx;
|
||||
font-weight: 900;
|
||||
color: $text-main;
|
||||
font-family: 'DIN Alternate', sans-serif;
|
||||
letter-spacing: -1rpx;
|
||||
}
|
||||
|
||||
.tier-label {
|
||||
font-size: 22rpx;
|
||||
color: $brand-primary;
|
||||
margin-top: 6rpx;
|
||||
font-weight: 800;
|
||||
font-style: italic;
|
||||
}
|
||||
|
||||
/* 热门/最高档位 - 高级动效 */
|
||||
.tier-hot {
|
||||
background: $gradient-red;
|
||||
border: none;
|
||||
box-shadow: 0 6rpx 14rpx rgba($accent-red, 0.12);
|
||||
background: $gradient-brand !important;
|
||||
border: none !important;
|
||||
box-shadow: 0 12rpx 32rpx rgba($brand-primary, 0.35) !important;
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
border-radius: $radius-lg;
|
||||
transform: translateZ(0);
|
||||
|
||||
.tier-price, .tier-label {
|
||||
color: #fff !important;
|
||||
position: relative;
|
||||
z-index: 2;
|
||||
.tier-price {
|
||||
color: #FFF !important;
|
||||
text-shadow: 0 2rpx 4rpx rgba(0, 0, 0, 0.1);
|
||||
}
|
||||
|
||||
.tier-label {
|
||||
color: rgba(255, 255, 255, 0.9) !important;
|
||||
text-shadow: 0 1rpx 2rpx rgba(0, 0, 0, 0.1);
|
||||
}
|
||||
|
||||
/* 流光效果 */
|
||||
&::before {
|
||||
content: '';
|
||||
position: absolute;
|
||||
left: -40%;
|
||||
top: 0;
|
||||
width: 60%;
|
||||
height: 100%;
|
||||
background: linear-gradient(90deg, rgba(255,255,255,0) 0%, rgba(255,255,255,0.22) 50%, rgba(255,255,255,0) 100%);
|
||||
transform: skewX(-18deg);
|
||||
opacity: 0.9;
|
||||
top: -50%;
|
||||
left: -150%;
|
||||
width: 200%;
|
||||
height: 200%;
|
||||
background: linear-gradient(
|
||||
120deg,
|
||||
rgba(255, 255, 255, 0) 30%,
|
||||
rgba(255, 255, 255, 0.4) 50%,
|
||||
rgba(255, 255, 255, 0) 70%
|
||||
);
|
||||
transform: rotate(25deg);
|
||||
animation: hotSweep 4s infinite cubic-bezier(0.19, 1, 0.22, 1);
|
||||
pointer-events: none;
|
||||
z-index: 1;
|
||||
pointer-events: none;
|
||||
animation: hotShine 2.6s ease-in-out infinite;
|
||||
}
|
||||
|
||||
&::after {
|
||||
content: 'HOT';
|
||||
position: absolute;
|
||||
top: 8rpx;
|
||||
right: 8rpx;
|
||||
background: linear-gradient(135deg, rgba(255,255,255,0.25), rgba(255,255,255,0.05));
|
||||
color: #fff;
|
||||
font-size: 20rpx;
|
||||
font-weight: 800;
|
||||
padding: 3rpx 10rpx;
|
||||
border-radius: 999rpx;
|
||||
border: 1rpx solid rgba(255,255,255,0.35);
|
||||
text-shadow: 0 1rpx 2rpx rgba(0,0,0,0.18);
|
||||
z-index: 3;
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
&:active {
|
||||
opacity: 0.9;
|
||||
transform: scale(0.96);
|
||||
}
|
||||
}
|
||||
.tier-hot .tier-price, .tier-hot .tier-label {
|
||||
color: #FFFFFF;
|
||||
}
|
||||
|
||||
@keyframes hotShine {
|
||||
0% { transform: translateX(-10%) skewX(-18deg); opacity: 0; }
|
||||
15% { opacity: 0.9; }
|
||||
55% { transform: translateX(220%) skewX(-18deg); opacity: 0.35; }
|
||||
100% { transform: translateX(220%) skewX(-18deg); opacity: 0; }
|
||||
@keyframes hotSweep {
|
||||
0% { left: -150%; }
|
||||
100% { left: 150%; }
|
||||
}
|
||||
|
||||
.rewards-overlay { position: fixed; left: 0; right: 0; top: 0; bottom: 0; z-index: 9000; }
|
||||
@ -1150,6 +1348,43 @@ function closeFlip() { showFlip.value = false }
|
||||
max-height: 60vh;
|
||||
padding: $spacing-lg;
|
||||
}
|
||||
|
||||
.rewards-group-v2 {
|
||||
margin-bottom: $spacing-xl;
|
||||
&:last-child { margin-bottom: 0; }
|
||||
}
|
||||
|
||||
.group-header-row {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
margin-bottom: $spacing-md;
|
||||
padding: 0 4rpx;
|
||||
}
|
||||
|
||||
.group-badge {
|
||||
font-size: $font-xs;
|
||||
font-weight: 900;
|
||||
color: $text-main;
|
||||
background: #F0F0F0;
|
||||
padding: 4rpx 16rpx;
|
||||
border-radius: 8rpx;
|
||||
font-style: italic;
|
||||
border: 1rpx solid rgba(0,0,0,0.05);
|
||||
box-shadow: $shadow-xs;
|
||||
|
||||
&.badge-boss {
|
||||
background: $gradient-gold;
|
||||
color: #78350F;
|
||||
border-color: rgba(217, 119, 6, 0.3);
|
||||
}
|
||||
}
|
||||
|
||||
.group-total-prob {
|
||||
font-size: 24rpx;
|
||||
color: $brand-primary;
|
||||
font-weight: 800;
|
||||
}
|
||||
.rewards-item {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
@ -1257,4 +1492,67 @@ function closeFlip() { showFlip.value = false }
|
||||
transform: scale(0.95);
|
||||
}
|
||||
}
|
||||
|
||||
/* ============================================
|
||||
Purchase Records Styles
|
||||
============================================ */
|
||||
.records-list {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: $spacing-lg;
|
||||
}
|
||||
.record-item {
|
||||
display: flex;
|
||||
background: #FFFFFF;
|
||||
padding: $spacing-lg;
|
||||
border-radius: $radius-lg;
|
||||
box-shadow: $shadow-sm;
|
||||
align-items: center;
|
||||
}
|
||||
.record-img {
|
||||
width: 100rpx; height: 100rpx;
|
||||
border-radius: $radius-md;
|
||||
background: $bg-secondary;
|
||||
margin-right: $spacing-lg;
|
||||
}
|
||||
.record-info {
|
||||
flex: 1;
|
||||
}
|
||||
.record-title {
|
||||
font-size: $font-md;
|
||||
font-weight: 600;
|
||||
color: $text-main;
|
||||
margin-bottom: $spacing-xs;
|
||||
}
|
||||
.record-meta {
|
||||
display: flex;
|
||||
gap: $spacing-md;
|
||||
font-size: $font-sm;
|
||||
color: $text-sub;
|
||||
}
|
||||
.record-count {
|
||||
background: rgba($brand-primary, 0.1);
|
||||
color: $brand-primary;
|
||||
padding: 2rpx $spacing-sm;
|
||||
border-radius: $radius-sm;
|
||||
}
|
||||
|
||||
/* Empty State */
|
||||
.empty-state {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
padding: $spacing-xl * 2;
|
||||
color: $text-tertiary;
|
||||
}
|
||||
.empty-icon {
|
||||
font-size: 80rpx;
|
||||
margin-bottom: $spacing-md;
|
||||
opacity: 0.6;
|
||||
}
|
||||
.empty-text {
|
||||
font-size: $font-md;
|
||||
color: $text-sub;
|
||||
}
|
||||
</style>
|
||||
|
||||
@ -38,30 +38,67 @@
|
||||
</view>
|
||||
</view>
|
||||
<view class="header-actions">
|
||||
<view class="action-btn" @tap="showRules">
|
||||
<text class="icon">📋</text>
|
||||
<text>规则</text>
|
||||
</view>
|
||||
<view class="action-btn" @tap="goCabinet">
|
||||
<text class="icon">📦</text>
|
||||
<text>盒柜</text>
|
||||
</view>
|
||||
<view class="action-btn" @tap="showRules">
|
||||
<view class="action-icon rules-icon"></view>
|
||||
<text class="action-label">规则</text>
|
||||
</view>
|
||||
<view class="action-btn" @tap="goCabinet">
|
||||
<view class="action-icon cabinet-icon"></view>
|
||||
<text class="action-label">盒柜</text>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 赏品概览 -->
|
||||
<view class="section-container animate-enter stagger-1" v-if="currentIssueRewards.length > 0">
|
||||
<view class="section-header">
|
||||
<text class="section-title">赏品一览</text>
|
||||
<text class="section-more" @tap="openRewardsPopup">查看全部</text>
|
||||
</view>
|
||||
<scroll-view class="preview-scroll" scroll-x>
|
||||
<view class="preview-item" v-for="(item, idx) in currentIssueRewards" :key="idx">
|
||||
<view class="prize-tag" :class="{ 'tag-boss': item.boss }">{{ item.boss ? 'BOSS' : (item.grade || '赏') }}</view>
|
||||
<image class="preview-img" :src="item.image" mode="aspectFill" />
|
||||
<view class="preview-name">{{ item.title }}</view>
|
||||
<view class="section-container animate-enter stagger-1">
|
||||
<view class="modern-tabs">
|
||||
<view class="tab-item" :class="{ active: tabActive === 'pool' }" @tap="tabActive = 'pool'">
|
||||
本机奖池
|
||||
<view v-if="tabActive === 'pool'" class="active-dot"></view>
|
||||
</view>
|
||||
</scroll-view>
|
||||
<view class="tab-item" :class="{ active: tabActive === 'records' }" @tap="tabActive = 'records'">
|
||||
购买记录
|
||||
<view v-if="tabActive === 'records'" class="active-dot"></view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<view v-show="tabActive === 'pool'">
|
||||
<view class="section-header">
|
||||
<text class="section-title">奖品配置</text>
|
||||
<text class="section-more" @tap="openRewardsPopup">查看全部</text>
|
||||
</view>
|
||||
<view v-if="currentIssueRewards.length > 0">
|
||||
<scroll-view class="preview-scroll" scroll-x>
|
||||
<view class="preview-item" v-for="(item, idx) in currentIssueRewards" :key="idx">
|
||||
<view class="prize-tag" :class="{ 'tag-boss': item.boss }">{{ item.boss ? 'BOSS' : (item.grade || '赏') }}</view>
|
||||
<image class="preview-img" :src="item.image" mode="aspectFill" />
|
||||
<view class="preview-name">{{ item.title }}</view>
|
||||
</view>
|
||||
</scroll-view>
|
||||
</view>
|
||||
<view v-else class="empty-state">
|
||||
<text class="empty-icon">📭</text>
|
||||
<text class="empty-text">暂无奖品配置</text>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<view v-show="tabActive === 'records'">
|
||||
<view class="records-list" v-if="winRecords.length">
|
||||
<view v-for="(it, idx) in winRecords" :key="it.id" class="record-item">
|
||||
<image class="record-img" :src="it.image" mode="aspectFill" />
|
||||
<view class="record-info">
|
||||
<view class="record-title">{{ it.title }}</view>
|
||||
<view class="record-meta">
|
||||
<text class="record-count">x{{ it.count }}</text>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
<view class="empty-state" v-else>
|
||||
<text class="empty-icon">📝</text>
|
||||
<text class="empty-text">暂无购买记录</text>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 选号区域 -->
|
||||
@ -142,7 +179,7 @@ import { ref, computed } from 'vue'
|
||||
import { onLoad, onUnload } from '@dcloudio/uni-app'
|
||||
import FlipGrid from '../../../components/FlipGrid.vue'
|
||||
import YifanSelector from '@/components/YifanSelector.vue'
|
||||
import { getActivityDetail, getActivityIssues, getActivityIssueRewards } from '../../../api/appUser'
|
||||
import { getActivityDetail, getActivityIssues, getActivityIssueRewards, getIssueDrawLogs } from '../../../api/appUser'
|
||||
|
||||
const detail = ref({})
|
||||
const issues = ref([])
|
||||
@ -379,6 +416,10 @@ async function fetchIssues(id) {
|
||||
const latestId = pickLatestIssueId(issues.value)
|
||||
setSelectedById(latestId)
|
||||
await fetchRewardsForIssues(id)
|
||||
// 获取购买记录
|
||||
if (currentIssueId.value) {
|
||||
fetchWinRecords(id, currentIssueId.value)
|
||||
}
|
||||
}
|
||||
|
||||
function pickLatestIssueId(list) {
|
||||
@ -419,7 +460,35 @@ function nextIssue() {
|
||||
currentIssueId.value = (cur && cur.id) || ''
|
||||
}
|
||||
|
||||
|
||||
async function fetchWinRecords(actId, issId) {
|
||||
if (!actId || !issId) return
|
||||
try {
|
||||
const res = await getIssueDrawLogs(actId, issId)
|
||||
const list = (res && res.list) || (Array.isArray(res) ? res : [])
|
||||
// 聚合同一奖品的记录
|
||||
const aggregate = {}
|
||||
list.forEach(it => {
|
||||
const key = it.reward_id || it.id
|
||||
if (!aggregate[key]) {
|
||||
aggregate[key] = {
|
||||
id: key,
|
||||
title: it.reward_name || it.title || it.name || '-',
|
||||
image: it.reward_image || it.image || '',
|
||||
count: 0
|
||||
}
|
||||
}
|
||||
aggregate[key].count += 1
|
||||
})
|
||||
const total = list.length || 1
|
||||
winRecords.value = Object.values(aggregate).map(it => ({
|
||||
...it,
|
||||
percent: ((it.count / total) * 100).toFixed(1)
|
||||
}))
|
||||
} catch (e) {
|
||||
console.error('fetchWinRecords error', e)
|
||||
winRecords.value = []
|
||||
}
|
||||
}
|
||||
|
||||
function onPreviewBanner() {
|
||||
const url = coverUrl.value || ''
|
||||
@ -612,10 +681,10 @@ onUnload(() => {
|
||||
}
|
||||
.header-info {
|
||||
flex: 1;
|
||||
min-width: 0;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: flex-start;
|
||||
min-height: 180rpx;
|
||||
justify-content: center;
|
||||
padding: 6rpx 0;
|
||||
}
|
||||
.header-title {
|
||||
@ -672,30 +741,47 @@ onUnload(() => {
|
||||
.header-actions {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: $spacing-lg;
|
||||
margin-left: 20rpx;
|
||||
padding-left: $spacing-lg;
|
||||
border-left: 1rpx solid rgba(0,0,0,0.06);
|
||||
gap: 28rpx;
|
||||
margin-left: 16rpx;
|
||||
padding-left: 24rpx;
|
||||
border-left: 2rpx solid #E8E8E8;
|
||||
justify-content: center;
|
||||
height: 140rpx;
|
||||
align-self: stretch;
|
||||
}
|
||||
.action-btn {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
font-size: $font-xs;
|
||||
color: $text-sub;
|
||||
transition: all 0.2s;
|
||||
|
||||
&:active {
|
||||
transform: scale(0.9);
|
||||
color: $text-main;
|
||||
opacity: 0.6;
|
||||
}
|
||||
}
|
||||
.action-btn .icon {
|
||||
font-size: $font-xl;
|
||||
margin-bottom: 6rpx;
|
||||
filter: grayscale(0.2);
|
||||
.action-icon {
|
||||
width: 44rpx;
|
||||
height: 44rpx;
|
||||
margin-bottom: 8rpx;
|
||||
background-size: contain;
|
||||
background-repeat: no-repeat;
|
||||
background-position: center;
|
||||
}
|
||||
.rules-icon {
|
||||
background-color: #999;
|
||||
mask-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='currentColor'%3E%3Cpath d='M19 3H5c-1.1 0-2 .9-2 2v14c0 1.1.9 2 2 2h14c1.1 0 2-.9 2-2V5c0-1.1-.9-2-2-2zm-5 14H7v-2h7v2zm3-4H7v-2h10v2zm0-4H7V7h10v2z'/%3E%3C/svg%3E");
|
||||
-webkit-mask-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='currentColor'%3E%3Cpath d='M19 3H5c-1.1 0-2 .9-2 2v14c0 1.1.9 2 2 2h14c1.1 0 2-.9 2-2V5c0-1.1-.9-2-2-2zm-5 14H7v-2h7v2zm3-4H7v-2h10v2zm0-4H7V7h10v2z'/%3E%3C/svg%3E");
|
||||
mask-size: cover;
|
||||
-webkit-mask-size: cover;
|
||||
}
|
||||
.cabinet-icon {
|
||||
background-color: #999;
|
||||
mask-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='currentColor'%3E%3Cpath d='M20 3H4c-1.1 0-2 .9-2 2v16l4-4h14c1.1 0 2-.9 2-2V5c0-1.1-.9-2-2-2zm-9 11H7v-2h4v2zm6-4H7V8h10v2z'/%3E%3C/svg%3E");
|
||||
-webkit-mask-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='currentColor'%3E%3Cpath d='M20 3H4c-1.1 0-2 .9-2 2v16l4-4h14c1.1 0 2-.9 2-2V5c0-1.1-.9-2-2-2zm-9 11H7v-2h4v2zm6-4H7V8h10v2z'/%3E%3C/svg%3E");
|
||||
mask-size: cover;
|
||||
-webkit-mask-size: cover;
|
||||
}
|
||||
.action-label {
|
||||
font-size: 22rpx;
|
||||
color: #666;
|
||||
letter-spacing: 1rpx;
|
||||
}
|
||||
|
||||
/* 通用板块容器 */
|
||||
@ -1044,4 +1130,99 @@ onUnload(() => {
|
||||
color: $text-tertiary;
|
||||
font-size: $font-sm;
|
||||
}
|
||||
|
||||
/* ============================================
|
||||
Tabs & Purchase Records Styles
|
||||
============================================ */
|
||||
.modern-tabs {
|
||||
display: flex;
|
||||
background: $bg-secondary;
|
||||
padding: 8rpx;
|
||||
border-radius: $radius-lg;
|
||||
margin-bottom: $spacing-lg;
|
||||
}
|
||||
.tab-item {
|
||||
flex: 1;
|
||||
text-align: center;
|
||||
padding: $spacing-md 0;
|
||||
font-size: $font-md;
|
||||
color: $text-sub;
|
||||
border-radius: $radius-md;
|
||||
font-weight: 600;
|
||||
transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
|
||||
position: relative;
|
||||
|
||||
&.active {
|
||||
background: #FFFFFF;
|
||||
color: $brand-primary;
|
||||
box-shadow: $shadow-sm;
|
||||
}
|
||||
}
|
||||
.active-dot {
|
||||
width: 8rpx; height: 8rpx;
|
||||
background: $brand-primary;
|
||||
border-radius: 50%;
|
||||
position: absolute;
|
||||
bottom: 8rpx; left: 50%; transform: translateX(-50%);
|
||||
}
|
||||
|
||||
.records-list {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: $spacing-lg;
|
||||
}
|
||||
.record-item {
|
||||
display: flex;
|
||||
background: #FFFFFF;
|
||||
padding: $spacing-lg;
|
||||
border-radius: $radius-lg;
|
||||
box-shadow: $shadow-sm;
|
||||
align-items: center;
|
||||
}
|
||||
.record-img {
|
||||
width: 100rpx; height: 100rpx;
|
||||
border-radius: $radius-md;
|
||||
background: $bg-secondary;
|
||||
margin-right: $spacing-lg;
|
||||
}
|
||||
.record-info {
|
||||
flex: 1;
|
||||
}
|
||||
.record-title {
|
||||
font-size: $font-md;
|
||||
font-weight: 600;
|
||||
color: $text-main;
|
||||
margin-bottom: $spacing-xs;
|
||||
}
|
||||
.record-meta {
|
||||
display: flex;
|
||||
gap: $spacing-md;
|
||||
font-size: $font-sm;
|
||||
color: $text-sub;
|
||||
}
|
||||
.record-count {
|
||||
background: rgba($brand-primary, 0.1);
|
||||
color: $brand-primary;
|
||||
padding: 2rpx $spacing-sm;
|
||||
border-radius: $radius-sm;
|
||||
}
|
||||
|
||||
/* Empty State */
|
||||
.empty-state {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
padding: $spacing-xl * 2;
|
||||
color: $text-tertiary;
|
||||
}
|
||||
.empty-icon {
|
||||
font-size: 80rpx;
|
||||
margin-bottom: $spacing-md;
|
||||
opacity: 0.6;
|
||||
}
|
||||
.empty-text {
|
||||
font-size: $font-md;
|
||||
color: $text-sub;
|
||||
}
|
||||
</style>
|
||||
|
||||
@ -115,13 +115,16 @@ export default {
|
||||
}
|
||||
})
|
||||
|
||||
// 构建游戏URL
|
||||
// 构建游戏URL,传递安全认证参数
|
||||
const gameBaseUrl = 'https://game.1024tool.vip'
|
||||
const gameUrl = `${gameBaseUrl}?ticket=${res.ticket_token}`
|
||||
const gameToken = encodeURIComponent(res.game_token)
|
||||
const nakamaServer = encodeURIComponent(res.nakama_server)
|
||||
const nakamaKey = encodeURIComponent(res.nakama_key)
|
||||
const gameUrl = `${gameBaseUrl}?game_token=${gameToken}&nakama_server=${nakamaServer}&nakama_key=${nakamaKey}`
|
||||
|
||||
// 跳转到webview
|
||||
uni.navigateTo({
|
||||
url: `/pages/game/webview?url=${encodeURIComponent(gameUrl)}&ticket=${res.ticket_token}`
|
||||
url: `/pages/game/webview?url=${encodeURIComponent(gameUrl)}`
|
||||
})
|
||||
} catch (e) {
|
||||
uni.showToast({
|
||||
|
||||
@ -1,7 +1,11 @@
|
||||
<template>
|
||||
<view class="page">
|
||||
<!-- 背景装饰 -->
|
||||
<view class="bg-decoration"></view>
|
||||
<!-- 品牌级背景装饰系统 -->
|
||||
<view class="premium-bg">
|
||||
<view class="bg-shape circle-1"></view>
|
||||
<view class="bg-shape circle-2"></view>
|
||||
<view class="bg-shape mesh-gradient"></view>
|
||||
</view>
|
||||
|
||||
<!-- 顶部导航栏 (搜索) -->
|
||||
<view class="nav-header">
|
||||
@ -13,28 +17,36 @@
|
||||
|
||||
<!-- 滚动区域 -->
|
||||
<scroll-view class="main-content" scroll-y>
|
||||
<!-- Banner 区域 -->
|
||||
<view class="banner-box">
|
||||
<swiper class="banner-swiper" circular autoplay interval="4000" duration="500">
|
||||
<swiper-item v-for="b in displayBanners" :key="b.id">
|
||||
<image v-if="b.image" class="banner-image" :src="b.image" mode="aspectFill" @tap="onBannerTap(b)" />
|
||||
<view v-else class="banner-fallback">
|
||||
<text class="banner-fallback-text">{{ b.title || '柯大鸭潮玩 V6.0' }}</text>
|
||||
<text class="banner-tag">功能更新UI优化全面来袭</text>
|
||||
<!-- 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" @tap="onNoticeTap">
|
||||
<view class="notice-tag">通知</view>
|
||||
<swiper class="notice-swiper" vertical circular autoplay interval="3000" duration="300">
|
||||
<!-- 品牌动态栏 (极简风格) -->
|
||||
<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-more">查看详情</view>
|
||||
<view class="notice-arrow"></view>
|
||||
</view>
|
||||
|
||||
|
||||
@ -129,7 +141,8 @@ export default {
|
||||
notices: [],
|
||||
banners: [],
|
||||
activities: [],
|
||||
selectedGroupName: ''
|
||||
selectedGroupName: '',
|
||||
bannerIndex: 0
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
@ -182,6 +195,9 @@ export default {
|
||||
this.loadHomeData()
|
||||
},
|
||||
methods: {
|
||||
onBannerChange(e) {
|
||||
this.bannerIndex = e.detail.current
|
||||
},
|
||||
onSelectGroup(name) {
|
||||
this.selectedGroupName = String(name || '')
|
||||
},
|
||||
@ -341,10 +357,54 @@ export default {
|
||||
|
||||
.page {
|
||||
padding: 0;
|
||||
background-color: $bg-page;
|
||||
background-color: #F8F9FB;
|
||||
min-height: 100vh;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
position: relative;
|
||||
overflow-x: hidden;
|
||||
}
|
||||
|
||||
/* ========== 品牌级背景系统 ========== */
|
||||
.premium-bg {
|
||||
position: fixed;
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
z-index: 0;
|
||||
pointer-events: none;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.bg-shape {
|
||||
position: absolute;
|
||||
filter: blur(100rpx);
|
||||
opacity: 0.6;
|
||||
}
|
||||
|
||||
.circle-1 {
|
||||
width: 600rpx;
|
||||
height: 600rpx;
|
||||
background: radial-gradient(circle, rgba(255, 107, 0, 0.15) 0%, transparent 70%);
|
||||
top: -200rpx;
|
||||
right: -100rpx;
|
||||
animation: float 10s ease-in-out infinite alternate;
|
||||
}
|
||||
|
||||
.circle-2 {
|
||||
width: 500rpx;
|
||||
height: 500rpx;
|
||||
background: radial-gradient(circle, rgba(255, 193, 7, 0.1) 0%, transparent 70%);
|
||||
bottom: 200rpx;
|
||||
left: -100rpx;
|
||||
animation: float 12s ease-in-out infinite alternate-reverse;
|
||||
}
|
||||
|
||||
.mesh-gradient {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
background: linear-gradient(135deg, rgba(255, 107, 0, 0.03) 0%, rgba(255, 255, 255, 0) 50%, rgba(255, 193, 7, 0.03) 100%);
|
||||
}
|
||||
|
||||
/* ========== 顶部导航栏 ========== */
|
||||
@ -352,192 +412,203 @@ export default {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
padding: $spacing-md $spacing-lg;
|
||||
background: transparent; /* 透明背景,依靠内容区的渐变 */
|
||||
padding-top: calc($spacing-md + env(safe-area-inset-top));
|
||||
gap: $spacing-lg;
|
||||
z-index: 10;
|
||||
}
|
||||
|
||||
.brand-logo {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
position: sticky;
|
||||
top: 0;
|
||||
}
|
||||
|
||||
.brand-text {
|
||||
font-size: 40rpx;
|
||||
font-size: 44rpx;
|
||||
font-weight: 900;
|
||||
color: $text-main;
|
||||
font-style: italic;
|
||||
letter-spacing: -1rpx;
|
||||
text-shadow: 0 2rpx 4rpx rgba(255, 255, 255, 0.5);
|
||||
letter-spacing: -2rpx;
|
||||
background: linear-gradient(135deg, #1A1A1A 0%, #444 100%);
|
||||
-webkit-background-clip: text;
|
||||
background-clip: text;
|
||||
-webkit-text-fill-color: transparent;
|
||||
}
|
||||
|
||||
.brand-star {
|
||||
font-size: 24rpx;
|
||||
margin-left: 4rpx;
|
||||
margin-top: -16rpx;
|
||||
animation: pulse 2s infinite;
|
||||
font-size: 28rpx;
|
||||
margin-left: 6rpx;
|
||||
}
|
||||
|
||||
.search-bar {
|
||||
flex: 1;
|
||||
height: 72rpx;
|
||||
background: rgba(255, 255, 255, 0.8);
|
||||
backdrop-filter: blur(10px);
|
||||
border-radius: $radius-round;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
padding: 0 $spacing-lg;
|
||||
border: 1rpx solid rgba(255, 255, 255, 0.6);
|
||||
box-shadow: $shadow-sm;
|
||||
transition: all 0.3s ease;
|
||||
}
|
||||
|
||||
.search-bar:active {
|
||||
background: #fff;
|
||||
transform: scale(0.98);
|
||||
}
|
||||
|
||||
.search-icon { margin-right: 12rpx; font-size: 32rpx; }
|
||||
.search-placeholder { color: $text-tertiary; font-size: 28rpx; font-weight: 500; }
|
||||
|
||||
|
||||
/* ========== 滚动主内容区 ========== */
|
||||
.main-content {
|
||||
flex: 1;
|
||||
/* background: linear-gradient(180deg, $bg-secondary 0%, $bg-page 400rpx); */ /* 移除原有背景,使用全局背景装饰 */
|
||||
position: relative;
|
||||
z-index: 1; /* 确保内容在背景装饰之上 */
|
||||
z-index: 1;
|
||||
padding-top: $spacing-sm;
|
||||
}
|
||||
|
||||
/* Logo Banner */
|
||||
.banner-box {
|
||||
margin: 0 $spacing-lg $spacing-lg;
|
||||
border-radius: $radius-lg;
|
||||
overflow: hidden;
|
||||
box-shadow: $shadow-float;
|
||||
transform: translateZ(0); /* 开启硬件加速 */
|
||||
animation: fadeInUp 0.6s ease-out;
|
||||
/* Banner Container (Modern Floating) */
|
||||
.banner-container {
|
||||
padding: 0 $spacing-lg $spacing-xl;
|
||||
position: relative;
|
||||
z-index: 2;
|
||||
}
|
||||
|
||||
.banner-swiper, .banner-image, .banner-fallback {
|
||||
.banner-swiper {
|
||||
height: 360rpx;
|
||||
overflow: visible; /* 让阴影不被切断 */
|
||||
}
|
||||
|
||||
.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(255, 107, 0, 0.1);
|
||||
}
|
||||
|
||||
.banner-card.active {
|
||||
transform: scale(1);
|
||||
box-shadow: $shadow-float;
|
||||
}
|
||||
|
||||
.banner-image {
|
||||
width: 100%;
|
||||
height: 340rpx; /* 略微增高 */
|
||||
height: 100%;
|
||||
display: block;
|
||||
}
|
||||
|
||||
.banner-fallback {
|
||||
background: linear-gradient(135deg, $bg-secondary, #FFF0D6);
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
background: linear-gradient(135deg, $brand-primary, $brand-secondary);
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
border: 4rpx dashed $brand-primary-light;
|
||||
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: 48rpx;
|
||||
font-size: 52rpx;
|
||||
font-weight: 900;
|
||||
color: $brand-primary;
|
||||
color: #fff;
|
||||
font-style: italic;
|
||||
margin-bottom: 12rpx;
|
||||
text-shadow: 0 2rpx 0 rgba(255,255,255,1);
|
||||
margin-bottom: 12rpx;
|
||||
letter-spacing: 2rpx;
|
||||
z-index: 1;
|
||||
}
|
||||
|
||||
.banner-tag {
|
||||
background: #1A1A1A;
|
||||
color: $accent-gold;
|
||||
padding: 6rpx 20rpx;
|
||||
background: rgba(0,0,0,0.2);
|
||||
color: #fff;
|
||||
padding: 8rpx 24rpx;
|
||||
border-radius: $radius-round;
|
||||
font-size: 24rpx;
|
||||
font-weight: 700;
|
||||
font-size: 22rpx;
|
||||
font-weight: 700;
|
||||
backdrop-filter: blur(4px);
|
||||
z-index: 1;
|
||||
}
|
||||
|
||||
/* 通知栏 */
|
||||
.notice-bar {
|
||||
/* 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: #FFFFFF;
|
||||
border-radius: $radius-round;
|
||||
padding: 16rpx 24rpx;
|
||||
border-radius: 32rpx;
|
||||
padding: 24rpx 32rpx;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 16rpx;
|
||||
gap: 20rpx;
|
||||
box-shadow: $shadow-sm;
|
||||
animation: fadeInUp 0.6s ease-out 0.1s backwards;
|
||||
}
|
||||
|
||||
.notice-tag {
|
||||
background: $gradient-brand;
|
||||
color: #fff;
|
||||
font-size: 20rpx;
|
||||
padding: 4rpx 12rpx;
|
||||
border-radius: 8rpx;
|
||||
font-weight: 700;
|
||||
box-shadow: 0 2rpx 6rpx rgba($brand-primary, 0.3);
|
||||
border: 1rpx solid rgba(0,0,0,0.02);
|
||||
}
|
||||
|
||||
.notice-icon { font-size: 32rpx; }
|
||||
.notice-swiper { flex: 1; height: 36rpx; }
|
||||
.notice-item {
|
||||
font-size: 26rpx;
|
||||
color: $text-main;
|
||||
line-height: 36rpx;
|
||||
overflow: hidden;
|
||||
white-space: nowrap;
|
||||
text-overflow: ellipsis;
|
||||
font-weight: 500;
|
||||
font-weight: 600;
|
||||
}
|
||||
.notice-arrow {
|
||||
width: 12rpx;
|
||||
height: 12rpx;
|
||||
border-top: 3rpx solid #DDD;
|
||||
border-right: 3rpx solid #DDD;
|
||||
transform: rotate(45deg);
|
||||
}
|
||||
.notice-more { font-size: 24rpx; color: $text-sub; display: flex; align-items: center; }
|
||||
.notice-more::after { content: '›'; margin-left: 4rpx; font-size: 32rpx; line-height: 24rpx; }
|
||||
|
||||
|
||||
/* 玩法专区 - 方案B:2+3 杂志风布局 */
|
||||
/* 玩法专区 - 极质设计 */
|
||||
.gameplay-section {
|
||||
padding: 0 $spacing-lg;
|
||||
margin-bottom: $spacing-xl;
|
||||
animation: fadeInUp 0.6s ease-out 0.2s backwards;
|
||||
position: relative;
|
||||
z-index: 2;
|
||||
}
|
||||
|
||||
.section-header {
|
||||
margin-bottom: 20rpx;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
margin-bottom: 24rpx;
|
||||
}
|
||||
|
||||
.section-title {
|
||||
font-size: 36rpx;
|
||||
font-size: 38rpx;
|
||||
font-weight: 900;
|
||||
color: $text-main;
|
||||
position: relative;
|
||||
z-index: 1;
|
||||
padding-left: 12rpx;
|
||||
font-style: italic;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
/* 标题装饰竖线 */
|
||||
.section-title::before {
|
||||
content: '';
|
||||
position: absolute;
|
||||
left: 0;
|
||||
top: 50%;
|
||||
transform: translateY(-50%) skewX(-10deg);
|
||||
width: 8rpx;
|
||||
height: 32rpx;
|
||||
background: $brand-primary;
|
||||
border-radius: 2rpx;
|
||||
margin-right: 16rpx;
|
||||
border-radius: 4rpx;
|
||||
transform: skewX(-15deg);
|
||||
}
|
||||
|
||||
.gameplay-grid-v2 {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 20rpx;
|
||||
gap: 24rpx;
|
||||
}
|
||||
|
||||
/* 上排 */
|
||||
.grid-row-top {
|
||||
display: flex;
|
||||
gap: 20rpx;
|
||||
height: 220rpx; /* 增加高度,展示更多细节 */
|
||||
gap: 24rpx;
|
||||
height: 190rpx;
|
||||
}
|
||||
|
||||
.game-card-large {
|
||||
@ -545,7 +616,7 @@ export default {
|
||||
border-radius: $radius-lg;
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
padding: 28rpx;
|
||||
padding: 22rpx;
|
||||
box-shadow: $shadow-card;
|
||||
transition: transform 0.2s;
|
||||
}
|
||||
@ -558,7 +629,7 @@ export default {
|
||||
.grid-row-bottom {
|
||||
display: flex;
|
||||
gap: 20rpx;
|
||||
height: 160rpx;
|
||||
height: 130rpx;
|
||||
}
|
||||
|
||||
.game-card-small {
|
||||
@ -566,7 +637,7 @@ export default {
|
||||
border-radius: $radius-md;
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
padding: 20rpx;
|
||||
padding: 16rpx;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: center;
|
||||
@ -591,19 +662,19 @@ export default {
|
||||
}
|
||||
|
||||
.card-title-large {
|
||||
font-size: 40rpx;
|
||||
font-size: 34rpx;
|
||||
font-weight: 900;
|
||||
color: #FFF;
|
||||
font-style: italic;
|
||||
margin-bottom: 16rpx;
|
||||
margin-bottom: 12rpx;
|
||||
text-shadow: 0 4rpx 8rpx rgba(0,0,0,0.1);
|
||||
}
|
||||
|
||||
.card-tag-large {
|
||||
font-size: 22rpx;
|
||||
font-size: 20rpx;
|
||||
background: rgba(255, 255, 255, 0.9);
|
||||
color: $text-main;
|
||||
padding: 6rpx 16rpx;
|
||||
padding: 4rpx 14rpx;
|
||||
border-radius: $radius-round;
|
||||
font-weight: 800;
|
||||
box-shadow: 0 4rpx 12rpx rgba(0,0,0,0.1);
|
||||
@ -613,10 +684,10 @@ export default {
|
||||
|
||||
.card-mascot-large {
|
||||
position: absolute;
|
||||
right: -20rpx;
|
||||
bottom: -30rpx;
|
||||
width: 180rpx;
|
||||
height: 180rpx;
|
||||
right: -10rpx;
|
||||
bottom: -20rpx;
|
||||
width: 140rpx;
|
||||
height: 140rpx;
|
||||
transform: rotate(10deg);
|
||||
filter: drop-shadow(0 8rpx 16rpx rgba(0,0,0,0.2));
|
||||
}
|
||||
@ -640,8 +711,8 @@ export default {
|
||||
position: absolute;
|
||||
right: -10rpx;
|
||||
bottom: -10rpx;
|
||||
width: 100rpx;
|
||||
height: 100rpx;
|
||||
width: 80rpx;
|
||||
height: 80rpx;
|
||||
opacity: 0.9;
|
||||
transform: rotate(-10deg);
|
||||
}
|
||||
@ -675,7 +746,10 @@ export default {
|
||||
.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;
|
||||
@ -684,28 +758,29 @@ export default {
|
||||
|
||||
.activity-item {
|
||||
background: #FFFFFF;
|
||||
border-radius: $radius-lg;
|
||||
border-radius: 32rpx;
|
||||
overflow: hidden;
|
||||
box-shadow: $shadow-card;
|
||||
box-shadow: 0 4rpx 20rpx rgba(0,0,0,0.03);
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
transition: transform 0.2s;
|
||||
transition: all 0.3s ease;
|
||||
}
|
||||
|
||||
.activity-item:active {
|
||||
transform: scale(0.98);
|
||||
transform: translateY(4rpx);
|
||||
}
|
||||
|
||||
.activity-thumb-box {
|
||||
position: relative;
|
||||
width: 100%;
|
||||
padding-bottom: 100%; /* 1:1 正方形 */
|
||||
padding-bottom: 100%;
|
||||
}
|
||||
|
||||
.activity-thumb {
|
||||
position: absolute;
|
||||
top: 0; left: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
background: #EEE;
|
||||
}
|
||||
|
||||
.activity-tag-hot {
|
||||
@ -715,11 +790,10 @@ export default {
|
||||
background: $gradient-brand;
|
||||
color: #fff;
|
||||
font-size: 20rpx;
|
||||
padding: 6rpx 14rpx;
|
||||
border-radius: 8rpx;
|
||||
padding: 6rpx 16rpx;
|
||||
border-radius: 12rpx;
|
||||
font-weight: 800;
|
||||
box-shadow: 0 4rpx 10rpx rgba($brand-primary, 0.3);
|
||||
animation: pulse 2s infinite;
|
||||
box-shadow: 0 4rpx 12rpx rgba(255,107,0,0.3);
|
||||
}
|
||||
|
||||
.activity-info {
|
||||
@ -740,6 +814,7 @@ export default {
|
||||
text-overflow: ellipsis;
|
||||
display: -webkit-box;
|
||||
-webkit-line-clamp: 2;
|
||||
line-clamp: 2;
|
||||
-webkit-box-orient: vertical;
|
||||
}
|
||||
|
||||
@ -751,27 +826,23 @@ export default {
|
||||
|
||||
.activity-desc {
|
||||
font-size: 26rpx;
|
||||
color: $accent-red; /* 价格/热度颜色 */
|
||||
color: $accent-red;
|
||||
font-weight: 800;
|
||||
}
|
||||
|
||||
.activity-btn-go {
|
||||
background: $text-main;
|
||||
color: #FFD700;
|
||||
font-size: 20rpx;
|
||||
background: #1A1A1A;
|
||||
color: $accent-gold;
|
||||
font-size: 22rpx;
|
||||
font-weight: 900;
|
||||
padding: 8rpx 24rpx;
|
||||
padding: 10rpx 28rpx;
|
||||
border-radius: $radius-round;
|
||||
box-shadow: 0 4rpx 12rpx rgba(0,0,0,0.2);
|
||||
box-shadow: 0 6rpx 16rpx rgba(0,0,0,0.1);
|
||||
}
|
||||
|
||||
/* 空状态 */
|
||||
.activity-empty {
|
||||
text-align: center;
|
||||
padding: 80rpx 0;
|
||||
color: $text-tertiary;
|
||||
font-size: 28rpx;
|
||||
}
|
||||
/* ============================================
|
||||
🌌 动画与高级动效
|
||||
============================================ */
|
||||
|
||||
@keyframes pulse {
|
||||
0% { transform: scale(1); opacity: 1; }
|
||||
@ -780,170 +851,28 @@ export default {
|
||||
}
|
||||
|
||||
@keyframes fadeInUp {
|
||||
from {
|
||||
opacity: 0;
|
||||
transform: translateY(30rpx);
|
||||
}
|
||||
to {
|
||||
opacity: 1;
|
||||
transform: translateY(0);
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes scaleIn {
|
||||
from {
|
||||
opacity: 0;
|
||||
transform: scale(0.95);
|
||||
}
|
||||
to {
|
||||
opacity: 1;
|
||||
transform: scale(1);
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes shimmer {
|
||||
0% { background-position: -200% 0; }
|
||||
100% { background-position: 200% 0; }
|
||||
from { opacity: 0; transform: translateY(40rpx); }
|
||||
to { opacity: 1; transform: translateY(0); }
|
||||
}
|
||||
|
||||
@keyframes float {
|
||||
0%, 100% { transform: translateY(0); }
|
||||
50% { transform: translateY(-8rpx); }
|
||||
50% { transform: translateY(-20rpx); }
|
||||
}
|
||||
|
||||
/* Banner 入场动画 */
|
||||
.banner-box {
|
||||
animation: scaleIn 0.5s ease-out;
|
||||
}
|
||||
.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; }
|
||||
|
||||
/* 通知栏滑入 */
|
||||
.notice-bar {
|
||||
animation: fadeInUp 0.4s ease-out 0.1s both;
|
||||
}
|
||||
.brand-star { animation: pulse 2s infinite; }
|
||||
|
||||
/* 玩法卡片交错入场 */
|
||||
.game-card-large:first-child {
|
||||
animation: fadeInUp 0.4s ease-out 0.2s both;
|
||||
}
|
||||
.game-card-large:last-child {
|
||||
animation: fadeInUp 0.4s ease-out 0.3s both;
|
||||
}
|
||||
.game-card-small:nth-child(1) {
|
||||
animation: fadeInUp 0.4s ease-out 0.35s both;
|
||||
}
|
||||
.game-card-small:nth-child(2) {
|
||||
animation: fadeInUp 0.4s ease-out 0.4s both;
|
||||
}
|
||||
.game-card-small:nth-child(3) {
|
||||
animation: fadeInUp 0.4s ease-out 0.45s both;
|
||||
}
|
||||
|
||||
/* 活动卡片交错入场 */
|
||||
.activity-item:nth-child(1) { animation: fadeInUp 0.4s ease-out 0.2s both; }
|
||||
.activity-item:nth-child(2) { animation: fadeInUp 0.4s ease-out 0.25s both; }
|
||||
.activity-item:nth-child(3) { animation: fadeInUp 0.4s ease-out 0.3s both; }
|
||||
.activity-item:nth-child(4) { animation: fadeInUp 0.4s ease-out 0.35s both; }
|
||||
.activity-item:nth-child(n+5) { animation: fadeInUp 0.4s ease-out 0.4s both; }
|
||||
|
||||
/* 玻璃态搜索栏 */
|
||||
.search-bar {
|
||||
background: rgba(255, 255, 255, 0.85);
|
||||
backdrop-filter: blur(20rpx);
|
||||
-webkit-backdrop-filter: blur(20rpx);
|
||||
border: 1rpx solid rgba(255, 255, 255, 0.6);
|
||||
box-shadow: 0 4rpx 16rpx rgba(0, 0, 0, 0.04);
|
||||
transition: all 0.3s ease;
|
||||
}
|
||||
.search-bar:active {
|
||||
transform: scale(0.98);
|
||||
box-shadow: 0 2rpx 8rpx rgba(0, 0, 0, 0.08);
|
||||
}
|
||||
|
||||
/* 卡片点击效果 */
|
||||
.game-card-large,
|
||||
.game-card-small {
|
||||
transition: all 0.25s cubic-bezier(0.25, 0.46, 0.45, 0.94);
|
||||
cursor: pointer;
|
||||
}
|
||||
.game-card-large:active {
|
||||
transform: scale(0.97);
|
||||
box-shadow: 0 2rpx 8rpx rgba(0,0,0,0.1);
|
||||
}
|
||||
.game-card-small:active {
|
||||
transform: scale(0.95);
|
||||
box-shadow: 0 2rpx 6rpx rgba(0,0,0,0.08);
|
||||
}
|
||||
|
||||
/* 活动卡片悬浮效果 */
|
||||
.activity-item {
|
||||
transition: all 0.3s cubic-bezier(0.25, 0.46, 0.45, 0.94);
|
||||
}
|
||||
.activity-item:active {
|
||||
transform: scale(0.97) translateY(4rpx);
|
||||
box-shadow: 0 4rpx 12rpx rgba(0,0,0,0.08);
|
||||
}
|
||||
|
||||
/* GO 按钮脉冲动画 */
|
||||
.activity-btn-go {
|
||||
animation: pulse 2s infinite;
|
||||
transition: all 0.2s ease;
|
||||
}
|
||||
.activity-btn-go:active {
|
||||
animation: none;
|
||||
transform: scale(0.9);
|
||||
}
|
||||
|
||||
/* 图片加载动画 */
|
||||
.activity-thumb {
|
||||
background: linear-gradient(90deg, #f0f0f0 25%, #e8e8e8 50%, #f0f0f0 75%);
|
||||
background-size: 200% 100%;
|
||||
animation: shimmer 1.5s infinite;
|
||||
}
|
||||
|
||||
/* 热门标签浮动效果 */
|
||||
.activity-tag-hot {
|
||||
animation: float 3s ease-in-out infinite;
|
||||
}
|
||||
|
||||
/* Banner 图片悬浮效果 */
|
||||
.banner-image {
|
||||
transition: transform 0.4s ease;
|
||||
}
|
||||
.banner-box:active .banner-image {
|
||||
transform: scale(1.02);
|
||||
}
|
||||
|
||||
/* 通知栏点击效果 */
|
||||
.notice-bar {
|
||||
transition: all 0.2s ease;
|
||||
}
|
||||
.notice-bar:active {
|
||||
background: #F0F0F0;
|
||||
transform: scale(0.99);
|
||||
}
|
||||
|
||||
/* 品牌标志动画 */
|
||||
.brand-star {
|
||||
animation: float 2s ease-in-out infinite;
|
||||
}
|
||||
|
||||
/* Section 标题增强 */
|
||||
.section-header {
|
||||
animation: fadeInUp 0.4s ease-out 0.15s both;
|
||||
}
|
||||
|
||||
/* 增强阴影效果 */
|
||||
.banner-box {
|
||||
box-shadow: 0 12rpx 40rpx rgba(0,0,0,0.08);
|
||||
}
|
||||
.activity-item {
|
||||
box-shadow: 0 8rpx 32rpx rgba(0,0,0,0.06);
|
||||
}
|
||||
|
||||
/* 热门标签增强 */
|
||||
.activity-tag-hot {
|
||||
background: linear-gradient(135deg, rgba(255,77,79,0.9), rgba(255,107,53,0.9));
|
||||
color: #fff;
|
||||
text-shadow: 0 1rpx 2rpx rgba(0,0,0,0.2);
|
||||
/* 兼容性修复 */
|
||||
.brand-text {
|
||||
background-clip: text;
|
||||
}
|
||||
</style>
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user