兼容新版的对对碰,调整了更多的排序方式等内容
This commit is contained in:
parent
952a2a2fe7
commit
a4dbfd14b7
@ -9,7 +9,9 @@
|
|||||||
<view v-if="grouped && rewardGroups.length > 0">
|
<view v-if="grouped && rewardGroups.length > 0">
|
||||||
<view class="prize-level-row" v-for="group in rewardGroups" :key="group.level">
|
<view class="prize-level-row" v-for="group in rewardGroups" :key="group.level">
|
||||||
<view class="level-header-row">
|
<view class="level-header-row">
|
||||||
<view class="level-badge" :class="{ 'badge-boss': group.level === 'BOSS' }">{{ group.level }}赏</view>
|
<view class="level-badge" :class="{ 'badge-boss': group.level === 'BOSS' }">
|
||||||
|
{{ isMatchingGroup(group.level) ? group.level : `${group.level}赏` }}
|
||||||
|
</view>
|
||||||
<text class="level-prob">总概率 {{ group.totalPercent }}%</text>
|
<text class="level-prob">总概率 {{ group.totalPercent }}%</text>
|
||||||
</view>
|
</view>
|
||||||
<scroll-view class="preview-scroll" scroll-x>
|
<scroll-view class="preview-scroll" scroll-x>
|
||||||
@ -58,6 +60,10 @@ const props = defineProps({
|
|||||||
type: Boolean,
|
type: Boolean,
|
||||||
default: false
|
default: false
|
||||||
},
|
},
|
||||||
|
playType: {
|
||||||
|
type: String,
|
||||||
|
default: 'normal'
|
||||||
|
},
|
||||||
emptyText: {
|
emptyText: {
|
||||||
type: String,
|
type: String,
|
||||||
default: '暂无奖品配置'
|
default: '暂无奖品配置'
|
||||||
@ -66,9 +72,14 @@ const props = defineProps({
|
|||||||
|
|
||||||
defineEmits(['view-all'])
|
defineEmits(['view-all'])
|
||||||
|
|
||||||
|
// 判断是否为对对碰分组(包含"对子"字样)
|
||||||
|
const isMatchingGroup = (level) => {
|
||||||
|
return String(level || '').includes('对子')
|
||||||
|
}
|
||||||
|
|
||||||
const rewardGroups = computed(() => {
|
const rewardGroups = computed(() => {
|
||||||
if (!props.grouped) return []
|
if (!props.grouped) return []
|
||||||
return groupRewardsByLevel(props.rewards)
|
return groupRewardsByLevel(props.rewards, props.playType)
|
||||||
})
|
})
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
|||||||
@ -46,7 +46,8 @@
|
|||||||
<RewardsPreview
|
<RewardsPreview
|
||||||
v-if="tabActive === 'pool'"
|
v-if="tabActive === 'pool'"
|
||||||
:rewards="previewRewards"
|
:rewards="previewRewards"
|
||||||
:grouped="detail?.play_type !== 'match'"
|
:grouped="!['match', 'matching'].includes(detail?.play_type)"
|
||||||
|
:play-type="detail?.play_type || 'normal'"
|
||||||
@view-all="openRewardsPopup"
|
@view-all="openRewardsPopup"
|
||||||
/>
|
/>
|
||||||
|
|
||||||
@ -317,11 +318,18 @@ const currentIssueRewards = computed(() => {
|
|||||||
|
|
||||||
// 用于奖池预览的 rewards(已排序)
|
// 用于奖池预览的 rewards(已排序)
|
||||||
const previewRewards = computed(() => {
|
const previewRewards = computed(() => {
|
||||||
const isMatchType = detail.value?.play_type === 'match'
|
const isMatchType = ['match', 'matching'].includes(detail.value?.play_type)
|
||||||
|
|
||||||
if (isMatchType) {
|
if (isMatchType) {
|
||||||
// 对对碰模式:按 min_score 升序
|
// 对对碰模式:按 min_score 升序
|
||||||
return [...currentIssueRewards.value].sort((a, b) => (a.min_score - b.min_score))
|
const sorted = [...currentIssueRewards.value].sort((a, b) => (a.min_score - b.min_score))
|
||||||
|
console.log('[对对碰] previewRewards 排序后:', sorted.map(r => ({
|
||||||
|
name: r.title,
|
||||||
|
min_score: r.min_score,
|
||||||
|
weight: r.weight,
|
||||||
|
percent: r.percent
|
||||||
|
})))
|
||||||
|
return sorted
|
||||||
} else {
|
} else {
|
||||||
// 普通模式:返回原数组
|
// 普通模式:返回原数组
|
||||||
return currentIssueRewards.value
|
return currentIssueRewards.value
|
||||||
@ -329,7 +337,7 @@ const previewRewards = computed(() => {
|
|||||||
})
|
})
|
||||||
|
|
||||||
const rewardGroups = computed(() => {
|
const rewardGroups = computed(() => {
|
||||||
const isMatchType = detail.value?.play_type === 'match'
|
const isMatchType = ['match', 'matching'].includes(detail.value?.play_type)
|
||||||
|
|
||||||
// 对对碰模式:不分组,直接按 min_score 平铺所有奖品
|
// 对对碰模式:不分组,直接按 min_score 平铺所有奖品
|
||||||
if (isMatchType) {
|
if (isMatchType) {
|
||||||
@ -337,11 +345,20 @@ const rewardGroups = computed(() => {
|
|||||||
const sortedRewards = [...currentIssueRewards.value].sort((a, b) => (a.min_score - b.min_score))
|
const sortedRewards = [...currentIssueRewards.value].sort((a, b) => (a.min_score - b.min_score))
|
||||||
|
|
||||||
// 将每个奖品作为一个单独的分组
|
// 将每个奖品作为一个单独的分组
|
||||||
return sortedRewards.map(item => ({
|
const groups = sortedRewards.map(item => ({
|
||||||
level: `${item.min_score}对子`,
|
level: `${item.min_score}对子`,
|
||||||
rewards: [item],
|
rewards: [item],
|
||||||
totalPercent: item.percent.toFixed(1)
|
totalPercent: item.percent.toFixed(1)
|
||||||
}))
|
}))
|
||||||
|
|
||||||
|
console.log('[对对碰] rewardGroups 分组后:', groups.map(g => ({
|
||||||
|
level: g.level,
|
||||||
|
count: g.rewards.length,
|
||||||
|
totalPercent: g.totalPercent,
|
||||||
|
firstItem: g.rewards[0]?.title
|
||||||
|
})))
|
||||||
|
|
||||||
|
return groups
|
||||||
}
|
}
|
||||||
|
|
||||||
// 普通模式:按原来的分组逻辑
|
// 普通模式:按原来的分组逻辑
|
||||||
@ -568,7 +585,7 @@ function normalizeRewards(list, playType = 'normal') {
|
|||||||
}))
|
}))
|
||||||
|
|
||||||
// 根据 play_type 决定排序方式
|
// 根据 play_type 决定排序方式
|
||||||
if (playType === 'match') {
|
if (['match', 'matching'].includes(playType)) {
|
||||||
// 对对碰:按 min_score 升序排列,不过滤 min_score=0 的奖品
|
// 对对碰:按 min_score 升序排列,不过滤 min_score=0 的奖品
|
||||||
enriched.sort((a, b) => (a.min_score - b.min_score))
|
enriched.sort((a, b) => (a.min_score - b.min_score))
|
||||||
} else {
|
} else {
|
||||||
|
|||||||
@ -66,7 +66,15 @@
|
|||||||
<!-- 占位 -->
|
<!-- 占位 -->
|
||||||
<view class="header-placeholder"></view>
|
<view class="header-placeholder"></view>
|
||||||
|
|
||||||
<view v-if="loading && !items.length" class="loading-wrap"><view class="spinner"></view></view>
|
<view v-if="loading && !items.length" class="loading-container">
|
||||||
|
<view class="loading-animation">
|
||||||
|
<view class="circle circle-1"></view>
|
||||||
|
<view class="circle circle-2"></view>
|
||||||
|
<view class="circle circle-3"></view>
|
||||||
|
<view class="circle circle-4"></view>
|
||||||
|
</view>
|
||||||
|
<text class="loading-text">{{ loadingText }}</text>
|
||||||
|
</view>
|
||||||
|
|
||||||
<view class="content-container" v-else>
|
<view class="content-container" v-else>
|
||||||
<!-- 商品视图 (网格) -->
|
<!-- 商品视图 (网格) -->
|
||||||
@ -138,7 +146,7 @@
|
|||||||
<!-- 加载更多 -->
|
<!-- 加载更多 -->
|
||||||
<view v-if="loading && items.length > 0" class="loading-more">
|
<view v-if="loading && items.length > 0" class="loading-more">
|
||||||
<view class="spinner"></view>
|
<view class="spinner"></view>
|
||||||
<text>加载更多...</text>
|
<text>{{ loadingText }}</text>
|
||||||
</view>
|
</view>
|
||||||
<view v-else-if="!hasMore && items.length > 0" class="no-more">- 到底啦 -</view>
|
<view v-else-if="!hasMore && items.length > 0" class="no-more">- 到底啦 -</view>
|
||||||
</view>
|
</view>
|
||||||
@ -147,7 +155,7 @@
|
|||||||
|
|
||||||
<script setup>
|
<script setup>
|
||||||
import { onShow, onReachBottom } from '@dcloudio/uni-app'
|
import { onShow, onReachBottom } from '@dcloudio/uni-app'
|
||||||
import { ref, watch } from 'vue'
|
import { ref, watch, onUnmounted } from 'vue'
|
||||||
import { getStoreItems, redeemProductByPoints, redeemCouponByPoints, redeemItemCardByPoints } from '../../api/appUser'
|
import { getStoreItems, redeemProductByPoints, redeemCouponByPoints, redeemItemCardByPoints } from '../../api/appUser'
|
||||||
|
|
||||||
const loading = ref(false)
|
const loading = ref(false)
|
||||||
@ -159,6 +167,50 @@ const page = ref(1)
|
|||||||
const pageSize = 20
|
const pageSize = 20
|
||||||
const hasMore = ref(true)
|
const hasMore = ref(true)
|
||||||
|
|
||||||
|
// 趣味加载文字数组
|
||||||
|
const loadingTexts = [
|
||||||
|
'请稍等,正在努力加载中。。。',
|
||||||
|
'精彩马上就来,请稍候片刻~',
|
||||||
|
'正在搜寻更多好物,别急哦~',
|
||||||
|
'加载中,好东西值得等待~',
|
||||||
|
'正在为您准备惊喜,马上就好~',
|
||||||
|
'小憩一下,精彩内容即将呈现~',
|
||||||
|
'努力加载中,比心❤️~',
|
||||||
|
'正在赶来,马上就到~',
|
||||||
|
'稍安勿躁,美好值得等待~',
|
||||||
|
'加载进度99%... 开个玩笑😄'
|
||||||
|
]
|
||||||
|
const loadingText = ref(loadingTexts[0])
|
||||||
|
let loadingTextInterval = null
|
||||||
|
|
||||||
|
// 开始切换加载文字
|
||||||
|
function startLoadingTextRotation() {
|
||||||
|
if (loadingTextInterval) return
|
||||||
|
let index = 0
|
||||||
|
loadingText.value = loadingTexts[index]
|
||||||
|
loadingTextInterval = setInterval(() => {
|
||||||
|
index = (index + 1) % loadingTexts.length
|
||||||
|
loadingText.value = loadingTexts[index]
|
||||||
|
}, 2000) // 每2秒切换一次
|
||||||
|
}
|
||||||
|
|
||||||
|
// 停止切换加载文字
|
||||||
|
function stopLoadingTextRotation() {
|
||||||
|
if (loadingTextInterval) {
|
||||||
|
clearInterval(loadingTextInterval)
|
||||||
|
loadingTextInterval = null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 监听 loading 状态,自动启停文字轮播
|
||||||
|
watch(loading, (newVal) => {
|
||||||
|
if (newVal) {
|
||||||
|
startLoadingTextRotation()
|
||||||
|
} else {
|
||||||
|
stopLoadingTextRotation()
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
// 价格筛选相关
|
// 价格筛选相关
|
||||||
const priceMin = ref('')
|
const priceMin = ref('')
|
||||||
const priceMax = ref('')
|
const priceMax = ref('')
|
||||||
@ -174,8 +226,8 @@ const activePriceRange = ref('all')
|
|||||||
|
|
||||||
const tabs = [
|
const tabs = [
|
||||||
{ id: 'product', name: '商品' },
|
{ id: 'product', name: '商品' },
|
||||||
{ id: 'coupon', name: '优惠券' },
|
{ id: 'coupon', name: '优惠券' }
|
||||||
{ id: 'item_card', name: '道具卡' }
|
// { id: 'item_card', name: '道具卡' } // 暂不开放
|
||||||
]
|
]
|
||||||
|
|
||||||
function cleanUrl(u) {
|
function cleanUrl(u) {
|
||||||
@ -379,6 +431,11 @@ onReachBottom(() => {
|
|||||||
|
|
||||||
watch(keyword, () => applyFilters())
|
watch(keyword, () => applyFilters())
|
||||||
|
|
||||||
|
// 组件卸载时清理定时器
|
||||||
|
onUnmounted(() => {
|
||||||
|
stopLoadingTextRotation()
|
||||||
|
})
|
||||||
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style lang="scss" scoped>
|
<style lang="scss" scoped>
|
||||||
@ -630,6 +687,96 @@ watch(keyword, () => applyFilters())
|
|||||||
.empty-img { width: 240rpx; opacity: 0.6; }
|
.empty-img { width: 240rpx; opacity: 0.6; }
|
||||||
.empty-text { color: $text-tertiary; font-size: 26rpx; margin-top: 20rpx; }
|
.empty-text { color: $text-tertiary; font-size: 26rpx; margin-top: 20rpx; }
|
||||||
|
|
||||||
|
/* 酷炫加载动画 */
|
||||||
|
.loading-container {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
padding-top: 150rpx;
|
||||||
|
}
|
||||||
|
|
||||||
|
.loading-animation {
|
||||||
|
position: relative;
|
||||||
|
width: 160rpx;
|
||||||
|
height: 160rpx;
|
||||||
|
margin-bottom: 40rpx;
|
||||||
|
}
|
||||||
|
|
||||||
|
.circle {
|
||||||
|
position: absolute;
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
border-radius: 50%;
|
||||||
|
border: 4rpx solid transparent;
|
||||||
|
}
|
||||||
|
|
||||||
|
.circle-1 {
|
||||||
|
border-top-color: #FF6B6B;
|
||||||
|
border-right-color: #FF6B6B;
|
||||||
|
animation: rotate 1.5s cubic-bezier(0.68, -0.55, 0.265, 1.55) infinite;
|
||||||
|
}
|
||||||
|
|
||||||
|
.circle-2 {
|
||||||
|
width: 80%;
|
||||||
|
height: 80%;
|
||||||
|
top: 10%;
|
||||||
|
left: 10%;
|
||||||
|
border-top-color: #4ECDC4;
|
||||||
|
border-left-color: #4ECDC4;
|
||||||
|
animation: rotate 2s cubic-bezier(0.68, -0.55, 0.265, 1.55) infinite reverse;
|
||||||
|
}
|
||||||
|
|
||||||
|
.circle-3 {
|
||||||
|
width: 60%;
|
||||||
|
height: 60%;
|
||||||
|
top: 20%;
|
||||||
|
left: 20%;
|
||||||
|
border-bottom-color: #FFE66D;
|
||||||
|
border-left-color: #FFE66D;
|
||||||
|
animation: rotate 2.5s cubic-bezier(0.68, -0.55, 0.265, 1.55) infinite;
|
||||||
|
}
|
||||||
|
|
||||||
|
.circle-4 {
|
||||||
|
width: 40%;
|
||||||
|
height: 40%;
|
||||||
|
top: 30%;
|
||||||
|
left: 30%;
|
||||||
|
border-top-color: #95E1D3;
|
||||||
|
border-bottom-color: #95E1D3;
|
||||||
|
animation: rotate 3s cubic-bezier(0.68, -0.55, 0.265, 1.55) infinite reverse;
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes rotate {
|
||||||
|
0% {
|
||||||
|
transform: rotate(0deg) scale(1);
|
||||||
|
opacity: 1;
|
||||||
|
}
|
||||||
|
50% {
|
||||||
|
transform: rotate(180deg) scale(1.1);
|
||||||
|
opacity: 0.8;
|
||||||
|
}
|
||||||
|
100% {
|
||||||
|
transform: rotate(360deg) scale(1);
|
||||||
|
opacity: 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.loading-text {
|
||||||
|
font-size: 28rpx;
|
||||||
|
color: $text-secondary;
|
||||||
|
animation: pulse 1.5s ease-in-out infinite;
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes pulse {
|
||||||
|
0%, 100% {
|
||||||
|
opacity: 0.6;
|
||||||
|
}
|
||||||
|
50% {
|
||||||
|
opacity: 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
.loading-wrap { padding: 100rpx 0; display: flex; justify-content: center; }
|
.loading-wrap { padding: 100rpx 0; display: flex; justify-content: center; }
|
||||||
.spinner {
|
.spinner {
|
||||||
width: 48rpx; height: 48rpx; border: 4rpx solid #eee; border-top-color: $brand-primary;
|
width: 48rpx; height: 48rpx; border: 4rpx solid #eee; border-top-color: $brand-primary;
|
||||||
|
|||||||
@ -147,9 +147,12 @@ export function pickLatestIssueId(list) {
|
|||||||
/**
|
/**
|
||||||
* 按等级分组奖励
|
* 按等级分组奖励
|
||||||
* @param {Array} rewards - 奖励列表
|
* @param {Array} rewards - 奖励列表
|
||||||
|
* @param {string} playType - 活动类型 ('match', 'matching' 对对碰模式,其他为普通模式)
|
||||||
* @returns {Array} 分组后的奖励
|
* @returns {Array} 分组后的奖励
|
||||||
*/
|
*/
|
||||||
export function groupRewardsByLevel(rewards) {
|
export function groupRewardsByLevel(rewards, playType = 'normal') {
|
||||||
|
const isMatchType = ['match', 'matching'].includes(playType)
|
||||||
|
|
||||||
const groups = {}
|
const groups = {}
|
||||||
; (rewards || []).forEach(item => {
|
; (rewards || []).forEach(item => {
|
||||||
let level = item.level || '赏'
|
let level = item.level || '赏'
|
||||||
@ -160,17 +163,34 @@ export function groupRewardsByLevel(rewards) {
|
|||||||
if (!groups[level]) groups[level] = []
|
if (!groups[level]) groups[level] = []
|
||||||
groups[level].push(item)
|
groups[level].push(item)
|
||||||
})
|
})
|
||||||
|
|
||||||
return Object.keys(groups).sort((a, b) => {
|
return Object.keys(groups).sort((a, b) => {
|
||||||
if (a === 'Last' || a === 'BOSS') return -1
|
// Last 和 BOSS 优先(仅限普通模式)
|
||||||
if (b === 'Last' || b === 'BOSS') return 1
|
if (!isMatchType) {
|
||||||
// 分组之间按该组最小 weight 排序(升序)
|
if (a === 'Last' || a === 'BOSS') return -1
|
||||||
|
if (b === 'Last' || b === 'BOSS') return 1
|
||||||
|
}
|
||||||
|
|
||||||
|
// 对对碰模式:按 min_score 升序排序
|
||||||
|
if (isMatchType) {
|
||||||
|
const extractScore = (key) => {
|
||||||
|
const match = key.match(/(\d+)对子/)
|
||||||
|
return match ? parseInt(match[1], 10) : 0
|
||||||
|
}
|
||||||
|
return extractScore(a) - extractScore(b)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 普通模式:分组之间按该组最小 weight 排序(升序)
|
||||||
const minWeightA = Math.min(...groups[a].map(item => item.weight || 0))
|
const minWeightA = Math.min(...groups[a].map(item => item.weight || 0))
|
||||||
const minWeightB = Math.min(...groups[b].map(item => item.weight || 0))
|
const minWeightB = Math.min(...groups[b].map(item => item.weight || 0))
|
||||||
return minWeightA - minWeightB
|
return minWeightA - minWeightB
|
||||||
}).map(key => {
|
}).map(key => {
|
||||||
const levelRewards = groups[key]
|
const levelRewards = groups[key]
|
||||||
// 确保分组内的奖品按 weight 升序排列(从小到大)
|
// 对对碰模式:保持 min_score 升序(已在 previewRewards 排序)
|
||||||
levelRewards.sort((a, b) => (a.weight - b.weight))
|
// 普通模式:确保分组内的奖品按 weight 升序排列(从小到大)
|
||||||
|
if (!isMatchType) {
|
||||||
|
levelRewards.sort((a, b) => (a.weight - b.weight))
|
||||||
|
}
|
||||||
const total = levelRewards.reduce((sum, item) => sum + (Number(item.percent) || 0), 0)
|
const total = levelRewards.reduce((sum, item) => sum + (Number(item.percent) || 0), 0)
|
||||||
return {
|
return {
|
||||||
level: key,
|
level: key,
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user