diff --git a/api/appUser.js b/api/appUser.js
index b5c2114..7eac8e3 100644
--- a/api/appUser.js
+++ b/api/appUser.js
@@ -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' })
}
diff --git a/components/YifanSelector.vue b/components/YifanSelector.vue
index 255684a..791d1a9 100644
--- a/components/YifanSelector.vue
+++ b/components/YifanSelector.vue
@@ -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 {
diff --git a/pages/activity/duiduipeng/index.vue b/pages/activity/duiduipeng/index.vue
index 564caf1..ca95bcd 100644
--- a/pages/activity/duiduipeng/index.vue
+++ b/pages/activity/duiduipeng/index.vue
@@ -25,6 +25,16 @@
理性消费
+
@@ -51,18 +61,6 @@
-
@@ -80,13 +78,21 @@
奖池配置
查看全部
-
-
- {{ item.boss ? 'BOSS' : '赏' }}
-
- {{ item.title }}
+
+
+
+
+
+ {{ item.boss ? 'BOSS' : group.level }}
+
+ {{ item.title }}
+
+
-
+
📭
暂无奖励配置
@@ -101,7 +107,6 @@
{{ it.title }}
x{{ it.count }}
- 占比 {{ it.percent }}%
@@ -185,17 +190,25 @@
×
-
-
-
-
- {{ item.title || '-' }}
- BOSS
+
+
+
+
+
+
+
+ {{ item.title || '-' }}
+ BOSS
+
+ 单项概率 {{ formatPercent(item.percent) }}
+
- 概率 {{ formatPercent(item.percent) }}
- 暂无奖品数据
+ 暂无奖品数据
@@ -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 {
diff --git a/pages/activity/pata/index.vue b/pages/activity/pata/index.vue
index 7f25c2f..79703a6 100644
--- a/pages/activity/pata/index.vue
+++ b/pages/activity/pata/index.vue
@@ -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%; }
}
diff --git a/pages/activity/wuxianshang/index.vue b/pages/activity/wuxianshang/index.vue
index e422b76..8927bcf 100644
--- a/pages/activity/wuxianshang/index.vue
+++ b/pages/activity/wuxianshang/index.vue
@@ -27,28 +27,71 @@
-
-
-
-
- {{ item.boss ? 'BOSS' : '赏' }}
-
- {{ item.title }}
+
+
+
+ 本机奖池
+
-
+
+ 购买记录
+
+
+
+
+
+
+
+
+
+
+
+ {{ item.boss ? 'BOSS' : group.level }}
+
+ {{ item.title }}
+
+
+
+
+
+ 📭
+ 暂无奖池配置
+
+
+
+
+
+
+
+
+ {{ it.title }}
+
+ x{{ it.count }}
+
+
+
+
+
+ 📝
+ 暂无购买记录
+
+
@@ -83,17 +126,25 @@
×
-
-
-
-
- {{ item.title || '-' }}
- BOSS
+
+
+
+
+
+
+
+ {{ item.title || '-' }}
+ BOSS
+
+ 单项概率 {{ formatPercent(item.percent) }}
+
- 概率 {{ formatPercent(item.percent) }}
- 暂无奖池数据
+ 暂无奖池数据
@@ -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;
+}
diff --git a/pages/activity/yifanshang/index.vue b/pages/activity/yifanshang/index.vue
index 4788479..e117d29 100644
--- a/pages/activity/yifanshang/index.vue
+++ b/pages/activity/yifanshang/index.vue
@@ -38,30 +38,67 @@
-
-
-
-
- {{ item.boss ? 'BOSS' : (item.grade || '赏') }}
-
- {{ item.title }}
+
+
+
+ 本机奖池
+
-
+
+ 购买记录
+
+
+
+
+
+
+
+
+
+ {{ item.boss ? 'BOSS' : (item.grade || '赏') }}
+
+ {{ item.title }}
+
+
+
+
+ 📭
+ 暂无奖品配置
+
+
+
+
+
+
+
+
+ {{ it.title }}
+
+ x{{ it.count }}
+
+
+
+
+
+ 📝
+ 暂无购买记录
+
+
@@ -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;
+}
diff --git a/pages/game/minesweeper/index.vue b/pages/game/minesweeper/index.vue
index 6e80179..656802c 100644
--- a/pages/game/minesweeper/index.vue
+++ b/pages/game/minesweeper/index.vue
@@ -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({
diff --git a/pages/index/index.vue b/pages/index/index.vue
index 9798364..4b87073 100644
--- a/pages/index/index.vue
+++ b/pages/index/index.vue
@@ -1,7 +1,11 @@
-
-
+
+
+
+
+
+