兼容新版的对对碰,调整了更多的排序方式等内容
This commit is contained in:
parent
952a2a2fe7
commit
a4dbfd14b7
@ -9,7 +9,9 @@
|
||||
<view v-if="grouped && 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>
|
||||
<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>
|
||||
</view>
|
||||
<scroll-view class="preview-scroll" scroll-x>
|
||||
@ -58,6 +60,10 @@ const props = defineProps({
|
||||
type: Boolean,
|
||||
default: false
|
||||
},
|
||||
playType: {
|
||||
type: String,
|
||||
default: 'normal'
|
||||
},
|
||||
emptyText: {
|
||||
type: String,
|
||||
default: '暂无奖品配置'
|
||||
@ -66,9 +72,14 @@ const props = defineProps({
|
||||
|
||||
defineEmits(['view-all'])
|
||||
|
||||
// 判断是否为对对碰分组(包含"对子"字样)
|
||||
const isMatchingGroup = (level) => {
|
||||
return String(level || '').includes('对子')
|
||||
}
|
||||
|
||||
const rewardGroups = computed(() => {
|
||||
if (!props.grouped) return []
|
||||
return groupRewardsByLevel(props.rewards)
|
||||
return groupRewardsByLevel(props.rewards, props.playType)
|
||||
})
|
||||
</script>
|
||||
|
||||
|
||||
@ -46,7 +46,8 @@
|
||||
<RewardsPreview
|
||||
v-if="tabActive === 'pool'"
|
||||
:rewards="previewRewards"
|
||||
:grouped="detail?.play_type !== 'match'"
|
||||
:grouped="!['match', 'matching'].includes(detail?.play_type)"
|
||||
:play-type="detail?.play_type || 'normal'"
|
||||
@view-all="openRewardsPopup"
|
||||
/>
|
||||
|
||||
@ -317,11 +318,18 @@ const currentIssueRewards = computed(() => {
|
||||
|
||||
// 用于奖池预览的 rewards(已排序)
|
||||
const previewRewards = computed(() => {
|
||||
const isMatchType = detail.value?.play_type === 'match'
|
||||
const isMatchType = ['match', 'matching'].includes(detail.value?.play_type)
|
||||
|
||||
if (isMatchType) {
|
||||
// 对对碰模式:按 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 {
|
||||
// 普通模式:返回原数组
|
||||
return currentIssueRewards.value
|
||||
@ -329,7 +337,7 @@ const previewRewards = computed(() => {
|
||||
})
|
||||
|
||||
const rewardGroups = computed(() => {
|
||||
const isMatchType = detail.value?.play_type === 'match'
|
||||
const isMatchType = ['match', 'matching'].includes(detail.value?.play_type)
|
||||
|
||||
// 对对碰模式:不分组,直接按 min_score 平铺所有奖品
|
||||
if (isMatchType) {
|
||||
@ -337,11 +345,20 @@ const rewardGroups = computed(() => {
|
||||
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}对子`,
|
||||
rewards: [item],
|
||||
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 决定排序方式
|
||||
if (playType === 'match') {
|
||||
if (['match', 'matching'].includes(playType)) {
|
||||
// 对对碰:按 min_score 升序排列,不过滤 min_score=0 的奖品
|
||||
enriched.sort((a, b) => (a.min_score - b.min_score))
|
||||
} else {
|
||||
|
||||
@ -66,7 +66,15 @@
|
||||
<!-- 占位 -->
|
||||
<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>
|
||||
<!-- 商品视图 (网格) -->
|
||||
@ -138,7 +146,7 @@
|
||||
<!-- 加载更多 -->
|
||||
<view v-if="loading && items.length > 0" class="loading-more">
|
||||
<view class="spinner"></view>
|
||||
<text>加载更多...</text>
|
||||
<text>{{ loadingText }}</text>
|
||||
</view>
|
||||
<view v-else-if="!hasMore && items.length > 0" class="no-more">- 到底啦 -</view>
|
||||
</view>
|
||||
@ -147,7 +155,7 @@
|
||||
|
||||
<script setup>
|
||||
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'
|
||||
|
||||
const loading = ref(false)
|
||||
@ -159,6 +167,50 @@ const page = ref(1)
|
||||
const pageSize = 20
|
||||
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 priceMax = ref('')
|
||||
@ -174,8 +226,8 @@ const activePriceRange = ref('all')
|
||||
|
||||
const tabs = [
|
||||
{ id: 'product', name: '商品' },
|
||||
{ id: 'coupon', name: '优惠券' },
|
||||
{ id: 'item_card', name: '道具卡' }
|
||||
{ id: 'coupon', name: '优惠券' }
|
||||
// { id: 'item_card', name: '道具卡' } // 暂不开放
|
||||
]
|
||||
|
||||
function cleanUrl(u) {
|
||||
@ -379,6 +431,11 @@ onReachBottom(() => {
|
||||
|
||||
watch(keyword, () => applyFilters())
|
||||
|
||||
// 组件卸载时清理定时器
|
||||
onUnmounted(() => {
|
||||
stopLoadingTextRotation()
|
||||
})
|
||||
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
@ -630,6 +687,96 @@ watch(keyword, () => applyFilters())
|
||||
.empty-img { width: 240rpx; opacity: 0.6; }
|
||||
.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; }
|
||||
.spinner {
|
||||
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 {string} playType - 活动类型 ('match', 'matching' 对对碰模式,其他为普通模式)
|
||||
* @returns {Array} 分组后的奖励
|
||||
*/
|
||||
export function groupRewardsByLevel(rewards) {
|
||||
export function groupRewardsByLevel(rewards, playType = 'normal') {
|
||||
const isMatchType = ['match', 'matching'].includes(playType)
|
||||
|
||||
const groups = {}
|
||||
; (rewards || []).forEach(item => {
|
||||
let level = item.level || '赏'
|
||||
@ -160,17 +163,34 @@ export function groupRewardsByLevel(rewards) {
|
||||
if (!groups[level]) groups[level] = []
|
||||
groups[level].push(item)
|
||||
})
|
||||
|
||||
return Object.keys(groups).sort((a, b) => {
|
||||
if (a === 'Last' || a === 'BOSS') return -1
|
||||
if (b === 'Last' || b === 'BOSS') return 1
|
||||
// 分组之间按该组最小 weight 排序(升序)
|
||||
// Last 和 BOSS 优先(仅限普通模式)
|
||||
if (!isMatchType) {
|
||||
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 minWeightB = Math.min(...groups[b].map(item => item.weight || 0))
|
||||
return minWeightA - minWeightB
|
||||
}).map(key => {
|
||||
const levelRewards = groups[key]
|
||||
// 确保分组内的奖品按 weight 升序排列(从小到大)
|
||||
levelRewards.sort((a, b) => (a.weight - b.weight))
|
||||
// 对对碰模式:保持 min_score 升序(已在 previewRewards 排序)
|
||||
// 普通模式:确保分组内的奖品按 weight 升序排列(从小到大)
|
||||
if (!isMatchType) {
|
||||
levelRewards.sort((a, b) => (a.weight - b.weight))
|
||||
}
|
||||
const total = levelRewards.reduce((sum, item) => sum + (Number(item.percent) || 0), 0)
|
||||
return {
|
||||
level: key,
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user