feat(新页面): 扑克王牌
This commit is contained in:
parent
32d27c5818
commit
f130c124db
@ -16,6 +16,7 @@ export const PUBLIC_PATHS = Object.freeze([
|
||||
'/invitation/invite-new-user',
|
||||
'/recharge-reward',
|
||||
'/login-reward',
|
||||
'/activities/poker-ace',
|
||||
'/activities/lucky-dollars-season4',
|
||||
'/activities/lesser-bairam',
|
||||
'/activities/lucky-dollars-season3',
|
||||
@ -35,7 +36,9 @@ export const PUBLIC_PATHS = Object.freeze([
|
||||
])
|
||||
|
||||
export function normalizePath(path = '') {
|
||||
const normalizedPath = String(path || '/').split('?')[0].trim()
|
||||
const normalizedPath = String(path || '/')
|
||||
.split('?')[0]
|
||||
.trim()
|
||||
|
||||
if (!normalizedPath) {
|
||||
return '/'
|
||||
|
||||
@ -395,12 +395,18 @@ const router = createRouter({
|
||||
},
|
||||
|
||||
// 限时活动
|
||||
{
|
||||
path: '/activities/poker-ace',
|
||||
name: 'pokerAce',
|
||||
component: () => import('../views/Activities/PokerAce/index.vue'),
|
||||
meta: { requiresAuth: true },
|
||||
}, //2026年x月x日上午5点结束
|
||||
{
|
||||
path: '/activities/lucky-dollars-season4',
|
||||
name: 'lucky-dollars-season4',
|
||||
component: () => import('../views/Activities/LuckyDollars/Season4/index.vue'),
|
||||
meta: { requiresAuth: true },
|
||||
}, //2026年x月x日上午5点结束
|
||||
}, //2026年4月30日上午5点结束
|
||||
{
|
||||
path: '/activities/lesser-bairam',
|
||||
name: 'lesser-bairam',
|
||||
|
||||
@ -60,6 +60,7 @@ const ACTIVITIES = [
|
||||
'/recharge-reward', //充值奖励
|
||||
'/login-reward', // 登录奖励
|
||||
|
||||
'/activities/poker-ace', // 扑克王牌排行榜
|
||||
'/activities/lucky-dollars-season4', // 幸运美金活动
|
||||
'/activities/lesser-bairam', // 开斋节打榜
|
||||
'/activities/lucky-dollars-season3', // 斋月打榜
|
||||
|
||||
259
src/views/Activities/PokerAce/components/topUser.vue
Normal file
259
src/views/Activities/PokerAce/components/topUser.vue
Normal file
@ -0,0 +1,259 @@
|
||||
<!-- src/views/Ranking/Overall/components/topUser.vue -->
|
||||
<template>
|
||||
<div style="width: 100%">
|
||||
<div
|
||||
style="
|
||||
position: relative;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
gap: 4px;
|
||||
"
|
||||
:style="{ minHeight: currentLayout.minHeight }"
|
||||
>
|
||||
<!-- 头像 -->
|
||||
<itemCenter
|
||||
style="width: 100%; z-index: 1"
|
||||
:imgUrl="BorderImgUrl"
|
||||
:key="BorderImgUrl"
|
||||
:contentStyle="``"
|
||||
>
|
||||
<img
|
||||
:src="avatarUrl || ''"
|
||||
alt=""
|
||||
style="
|
||||
position: relative;
|
||||
z-index: -1;
|
||||
display: block;
|
||||
width: 70%;
|
||||
aspect-ratio: 1/1;
|
||||
object-fit: cover;
|
||||
"
|
||||
@error="handleAvatarImageError"
|
||||
/>
|
||||
</itemCenter>
|
||||
|
||||
<!-- 名称 -->
|
||||
<div
|
||||
style="width: 100%; min-width: 0; text-align: center; font-weight: 860"
|
||||
class="showText text"
|
||||
>
|
||||
{{ name }}
|
||||
</div>
|
||||
|
||||
<!-- 贡献金币 -->
|
||||
<div
|
||||
style="
|
||||
width: 100%;
|
||||
min-width: 0;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
"
|
||||
>
|
||||
<img
|
||||
v-smart-img
|
||||
src="/src/assets/icon/Azizi/coin.png"
|
||||
alt=""
|
||||
style="display: block; width: 0.95em; flex-shrink: 0"
|
||||
/>
|
||||
<div style="font-weight: 700; color: white" class="showText valueText">
|
||||
{{ displayValue }}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { computed } from 'vue'
|
||||
import { handleAvatarImageError } from '@/utils/image/imageHandler.js'
|
||||
import itemCenter from '@/components/itemCenter.vue'
|
||||
|
||||
const TOP_USER_LAYOUTS = {
|
||||
Wealth: {
|
||||
1: {
|
||||
minHeight: '0vw',
|
||||
avatarBox: { top: '0', height: '110%' },
|
||||
avatarImage: { width: '36%' },
|
||||
name: { bottom: '14%', width: '30%', height: '7.2%' },
|
||||
value: { bottom: '2%', width: '34%', height: '10%' },
|
||||
},
|
||||
2: {
|
||||
minHeight: '0vw',
|
||||
avatarBox: { top: '0', height: '90%' },
|
||||
avatarImage: { width: '42%' },
|
||||
info: { bottom: '0', height: '17%', width: '60%' },
|
||||
},
|
||||
3: {
|
||||
minHeight: '0vw',
|
||||
avatarBox: { top: '0', height: '90%' },
|
||||
avatarImage: { width: '42%' },
|
||||
info: { bottom: '0', height: '17%', width: '60%' },
|
||||
},
|
||||
},
|
||||
Charm: {
|
||||
1: {
|
||||
minHeight: '0vw',
|
||||
avatarBox: { top: '0', height: '90%' },
|
||||
avatarImage: { width: '36%' },
|
||||
name: { bottom: '16%', width: '38%', maxWidth: '38%', height: '7.2%' },
|
||||
value: { bottom: '2%', width: '34%', maxWidth: '34%', height: '10%' },
|
||||
},
|
||||
2: {
|
||||
minHeight: '0vw',
|
||||
avatarBox: { top: '0', height: '90%' },
|
||||
avatarImage: { width: '42%' },
|
||||
info: { bottom: '0', height: '17%', width: '60%' },
|
||||
},
|
||||
3: {
|
||||
minHeight: '0vw',
|
||||
avatarBox: { top: '0', height: '90%' },
|
||||
avatarImage: { width: '42%' },
|
||||
info: { bottom: '0', height: '17%', width: '60%' },
|
||||
},
|
||||
},
|
||||
Room: {
|
||||
1: {
|
||||
minHeight: '0vw',
|
||||
avatarBox: { top: '0', height: '100%' },
|
||||
avatarImage: { width: '27%' },
|
||||
name: { bottom: '24%', width: '30%', height: '7.2%' },
|
||||
value: { bottom: '3%', width: '34%', height: '10%' },
|
||||
},
|
||||
2: {
|
||||
minHeight: '0vw',
|
||||
avatarBox: { top: '0', height: '90%' },
|
||||
avatarImage: { width: '42%' },
|
||||
info: { bottom: '0', height: '17%', width: '60%' },
|
||||
},
|
||||
3: {
|
||||
minHeight: '0vw',
|
||||
avatarBox: { top: '0', height: '90%' },
|
||||
avatarImage: { width: '42%' },
|
||||
info: { bottom: '0', height: '17%', width: '60%' },
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
const props = defineProps({
|
||||
ranking: {
|
||||
type: String,
|
||||
default: '',
|
||||
},
|
||||
Type: {
|
||||
type: String,
|
||||
default: '',
|
||||
},
|
||||
rank: {
|
||||
type: Number,
|
||||
default: 1,
|
||||
},
|
||||
isTopOne: {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
},
|
||||
avatarUrl: {
|
||||
type: String,
|
||||
default: '/src/assets/icon/Azizi/defaultAvatar.png',
|
||||
},
|
||||
BorderImgUrl: {
|
||||
type: String,
|
||||
required: true,
|
||||
},
|
||||
name: {
|
||||
type: String,
|
||||
default: '',
|
||||
},
|
||||
distributionValue: {
|
||||
type: String,
|
||||
default: '',
|
||||
},
|
||||
})
|
||||
|
||||
const currentType = computed(() => {
|
||||
return TOP_USER_LAYOUTS[props.Type] ? props.Type : 'Wealth'
|
||||
})
|
||||
|
||||
const normalizedRank = computed(() => {
|
||||
return Number(props.ranking || props.rank || 1)
|
||||
})
|
||||
|
||||
const currentRank = computed(() => {
|
||||
if (props.isTopOne || normalizedRank.value === 1) {
|
||||
return 1
|
||||
}
|
||||
|
||||
return normalizedRank.value === 3 ? 3 : 2
|
||||
})
|
||||
|
||||
const isFirst = computed(() => {
|
||||
return currentRank.value === 1
|
||||
})
|
||||
|
||||
const currentLayout = computed(() => {
|
||||
return TOP_USER_LAYOUTS[currentType.value][currentRank.value]
|
||||
})
|
||||
|
||||
const avatarBorderRadius = computed(() => {
|
||||
return props.Type === 'Room' ? '0' : '50%'
|
||||
})
|
||||
|
||||
const displayValue = computed(() => {
|
||||
return props.distributionValue || '0'
|
||||
})
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
* {
|
||||
color: black;
|
||||
}
|
||||
|
||||
.showText {
|
||||
color: #fff;
|
||||
text-align: center;
|
||||
overflow: hidden;
|
||||
white-space: nowrap;
|
||||
text-overflow: ellipsis;
|
||||
}
|
||||
|
||||
.top1Text {
|
||||
font-size: 0.86em;
|
||||
}
|
||||
|
||||
.top1ValueText {
|
||||
font-size: 0.72em;
|
||||
}
|
||||
|
||||
.text {
|
||||
font-size: 0.74em;
|
||||
}
|
||||
|
||||
.valueText {
|
||||
font-size: 0.68em;
|
||||
}
|
||||
|
||||
@media screen and (max-width: 360px) {
|
||||
* {
|
||||
font-size: 10px;
|
||||
}
|
||||
}
|
||||
|
||||
@media screen and (min-width: 360px) {
|
||||
* {
|
||||
font-size: 12px;
|
||||
}
|
||||
}
|
||||
|
||||
@media screen and (min-width: 768px) {
|
||||
* {
|
||||
font-size: 24px;
|
||||
}
|
||||
}
|
||||
|
||||
@media screen and (min-width: 1024px) {
|
||||
* {
|
||||
font-size: 32px;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
907
src/views/Activities/PokerAce/index.vue
Normal file
907
src/views/Activities/PokerAce/index.vue
Normal file
@ -0,0 +1,907 @@
|
||||
<template>
|
||||
<div class="fullPage">
|
||||
<!-- 预加载状态 -->
|
||||
<div v-if="isLoading" class="loading-container">
|
||||
<LoadingSpinner />
|
||||
<p>{{ t('loading') }}...</p>
|
||||
</div>
|
||||
|
||||
<!-- 主要内容 -->
|
||||
<div
|
||||
v-show="!isLoading"
|
||||
style="width: 100vw; height: 100vh; overflow-y: auto; position: relative"
|
||||
class="scrollY"
|
||||
>
|
||||
<!-- 页面背景 -->
|
||||
<BackgroundLayer :useCanvas="true" :backgroundImages="[imageUrl('bg')]" />
|
||||
|
||||
<!-- 页面内容 -->
|
||||
<div
|
||||
style="
|
||||
width: 100vw;
|
||||
position: relative;
|
||||
z-index: 2;
|
||||
margin-top: 110vw;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 2vw;
|
||||
"
|
||||
>
|
||||
<img
|
||||
v-smart-img
|
||||
:src="imageUrl('helpBt')"
|
||||
alt=""
|
||||
class="helpBt"
|
||||
@click="openPopup('help')"
|
||||
/>
|
||||
|
||||
<!-- 倒计时 -->
|
||||
<div style="padding: 23vw 4vw 3vw">
|
||||
<itemCenter
|
||||
:imgUrl="imageUrl('timeBg')"
|
||||
style=""
|
||||
:contentStyle="`width:92vw;left:50%;transform: translateX(-50%);`"
|
||||
>
|
||||
<div
|
||||
style="
|
||||
width: 100%;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
direction: ltr;
|
||||
"
|
||||
>
|
||||
<div class="timeBoxItem">
|
||||
<div class="timeText" style="">{{ Days }}D</div>
|
||||
</div>
|
||||
<div class="timeBoxItem">
|
||||
<div class="timeText" style="">{{ Hours }}</div>
|
||||
</div>
|
||||
<div class="timeBoxItem">
|
||||
<div class="timeText" style="">{{ Minutes }}</div>
|
||||
</div>
|
||||
<div class="timeBoxItem">
|
||||
<div class="timeText" style="">{{ Seconds }}</div>
|
||||
</div>
|
||||
</div>
|
||||
</itemCenter>
|
||||
</div>
|
||||
|
||||
<!-- 模块切换按钮 -->
|
||||
<div style="display: flex">
|
||||
<!-- 排行榜模块按钮 -->
|
||||
<div style="flex: 1" @click="handleBt('rank')">
|
||||
<img
|
||||
v-show="RankingShow"
|
||||
v-smart-img
|
||||
:src="imageUrl(getImgName('rankBtActive'))"
|
||||
alt=""
|
||||
style="width: 100%; display: block"
|
||||
/>
|
||||
<img
|
||||
v-show="!RankingShow"
|
||||
v-smart-img
|
||||
:src="imageUrl(getImgName('rankBt'))"
|
||||
alt=""
|
||||
style="width: 100%; display: block"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<!-- 奖励模块按钮 -->
|
||||
<div style="flex: 1" @click="handleBt('reward')">
|
||||
<img
|
||||
v-show="!RankingShow"
|
||||
v-smart-img
|
||||
:src="imageUrl(getImgName('rewardBtActive'))"
|
||||
alt=""
|
||||
style="width: 100%; display: block"
|
||||
/>
|
||||
<img
|
||||
v-show="RankingShow"
|
||||
v-smart-img
|
||||
:src="imageUrl(getImgName('rewardBt'))"
|
||||
alt=""
|
||||
style="width: 100%; display: block"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 排行榜模块 -->
|
||||
<div
|
||||
v-show="rankingShow"
|
||||
style="display: flex; flex-direction: column; align-items: center; gap: 2vw"
|
||||
>
|
||||
<!-- 前三名 -->
|
||||
<itemCenter
|
||||
:imgUrl="imageUrl('topBg')"
|
||||
style="margin-top: 2vw"
|
||||
:contentStyle="topRankingContentStyle"
|
||||
>
|
||||
<!-- 第二名 -->
|
||||
<div
|
||||
style="
|
||||
flex: 1;
|
||||
min-width: 0;
|
||||
|
||||
align-self: stretch;
|
||||
display: flex;
|
||||
align-items: flex-end;
|
||||
"
|
||||
@click.stop="viewUserInfo(RankingHasTop3[1].userId)"
|
||||
>
|
||||
<TopUser
|
||||
ranking="2"
|
||||
:BorderImgUrl="imageUrl('frameTop2')"
|
||||
:avatarUrl="RankingHasTop3[1].userAvatar"
|
||||
:name="RankingHasTop3[1].userName"
|
||||
:distributionValue="RankingHasTop3[1].amount"
|
||||
style="width: 100%"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<!-- 第一名 -->
|
||||
<div
|
||||
style="
|
||||
flex: 1.1;
|
||||
min-width: 0;
|
||||
|
||||
align-self: stretch;
|
||||
display: flex;
|
||||
align-items: flex-end;
|
||||
"
|
||||
@click="viewUserInfo(RankingHasTop3[0].userId)"
|
||||
>
|
||||
<TopUser
|
||||
ranking="1"
|
||||
:BorderImgUrl="imageUrl('frameTop1')"
|
||||
:avatarUrl="RankingHasTop3[0].userAvatar"
|
||||
:name="RankingHasTop3[0].userName"
|
||||
:distributionValue="RankingHasTop3[0].amount"
|
||||
style="width: 100%"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<!-- 第三名 -->
|
||||
<div
|
||||
style="
|
||||
flex: 1;
|
||||
min-width: 0;
|
||||
|
||||
align-self: stretch;
|
||||
display: flex;
|
||||
align-items: flex-end;
|
||||
"
|
||||
@click.stop="viewUserInfo(RankingHasTop3[2].userId)"
|
||||
>
|
||||
<TopUser
|
||||
ranking="3"
|
||||
:BorderImgUrl="imageUrl('frameTop3')"
|
||||
:avatarUrl="RankingHasTop3[2].userAvatar"
|
||||
:name="RankingHasTop3[2].userName"
|
||||
:distributionValue="RankingHasTop3[2].amount"
|
||||
style="width: 100%"
|
||||
/>
|
||||
</div>
|
||||
</itemCenter>
|
||||
|
||||
<!-- 第四名开始 -->
|
||||
<div
|
||||
v-for="(listItem, index) in showRanking"
|
||||
:key="listItem.userId"
|
||||
style="width: 95vw; display: flex; flex-direction: column; gap: 1vw"
|
||||
>
|
||||
<!-- 基本信息 -->
|
||||
<itemCenter
|
||||
style="min-height: 0vw"
|
||||
:imgUrl="imageUrl('rankingItmeBg')"
|
||||
:contentStyle="`padding: 0 3vw`"
|
||||
:lazy="true"
|
||||
@click="viewUserInfo(listItem.userId)"
|
||||
>
|
||||
<!-- 基本信息 -->
|
||||
<div
|
||||
style="
|
||||
flex: 1;
|
||||
min-width: 0;
|
||||
|
||||
display: flex;
|
||||
justify-content: space-around;
|
||||
align-items: center;
|
||||
|
||||
gap: 2vw;
|
||||
"
|
||||
>
|
||||
<!-- 排名 -->
|
||||
<div style="display: flex; justify-content: center">
|
||||
<div style="color: #ffe601; font-weight: 500">
|
||||
{{ listItem?.rank }}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 头像 -->
|
||||
<div style="width: 12vw; margin: 0 1vw; position: relative; z-index: 0">
|
||||
<img
|
||||
:src="listItem?.userAvatar || ''"
|
||||
alt=""
|
||||
style="
|
||||
display: block;
|
||||
width: 100%;
|
||||
|
||||
aspect-ratio: 1/1;
|
||||
border-radius: 50%;
|
||||
object-fit: cover;
|
||||
"
|
||||
@error="handleAvatarImageError"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<!-- 名称、id -->
|
||||
<div style="flex: 1; min-width: 0; display: flex; flex-direction: column; gap: 1vw">
|
||||
<div
|
||||
style="
|
||||
color: #fff;
|
||||
font-weight: 700;
|
||||
overflow: hidden;
|
||||
white-space: nowrap;
|
||||
text-overflow: ellipsis;
|
||||
"
|
||||
class="s4-x3"
|
||||
>
|
||||
{{ listItem?.userName }}
|
||||
</div>
|
||||
<div style="color: #ffedc2; font-weight: 500" class="s4-x3">
|
||||
{{ $t('user_id_prefix') }} {{ listItem?.specialAccount || listItem?.account }}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 贡献值 -->
|
||||
<div
|
||||
style="
|
||||
width: auto;
|
||||
min-width: 0;
|
||||
|
||||
display: flex;
|
||||
align-items: center;
|
||||
"
|
||||
>
|
||||
<img
|
||||
v-smart-img
|
||||
src="/src/assets/icon/Azizi/coin.png"
|
||||
alt=""
|
||||
style="display: block; width: 1.5em; aspect-ratio: 1/1; object-fit: cover"
|
||||
/>
|
||||
<div style="color: #ffedc2; font-weight: 600" class="Textlittle">
|
||||
{{ listItem?.amount || 0 }}
|
||||
</div>
|
||||
</div>
|
||||
</itemCenter>
|
||||
</div>
|
||||
|
||||
<!-- 触发加载功能 -->
|
||||
<div ref="RankingLoadmore"></div>
|
||||
</div>
|
||||
|
||||
<!-- 前三奖励模块 -->
|
||||
<div
|
||||
v-show="rewardsShow"
|
||||
style="
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
gap: 4vw;
|
||||
padding-bottom: 10vw;
|
||||
"
|
||||
>
|
||||
<div
|
||||
v-for="(rewardsTop, index) in rankingRewards"
|
||||
style="display: flex; flex-direction: column; align-items: center; gap: 4vw"
|
||||
>
|
||||
<!-- 排名标题 -->
|
||||
<img :src="imageUrl(`rewardTitleTop${index + 1}`)" alt="" style="width: 50vw" />
|
||||
|
||||
<!-- 奖励列表 -->
|
||||
<div
|
||||
style="padding: 0 2vw; display: grid; grid-template-columns: repeat(3, 1fr); gap: 4vw"
|
||||
>
|
||||
<div
|
||||
v-for="(reward, index) in rewardsTop.rewards"
|
||||
style="
|
||||
height: 100%;
|
||||
align-self: stretch;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
gap: 1vw;
|
||||
"
|
||||
>
|
||||
<!-- 奖品图 -->
|
||||
<itemCenter :imgUrl="imageUrl('giftItemBg')" style="width: 100%">
|
||||
<!-- 奖励图片 -->
|
||||
<img
|
||||
:src="reward.cover || ''"
|
||||
alt=""
|
||||
style="
|
||||
display: block;
|
||||
min-width: 0;
|
||||
width: 80%;
|
||||
height: 100%;
|
||||
object-fit: contain;
|
||||
"
|
||||
@error="(e) => handleRewardImageError(e, reward.type)"
|
||||
/>
|
||||
</itemCenter>
|
||||
|
||||
<!-- 数值 -->
|
||||
<div
|
||||
style="
|
||||
width: 80%;
|
||||
height: 5.5vw;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
"
|
||||
>
|
||||
<div style="min-width: 0; font-size: 1em; font-weight: 600; color: #fff">
|
||||
{{ formatRewardDisplay(reward.type, reward) }}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 我的排名占位 -->
|
||||
<div v-show="rankingShow" style="height: 20vw"></div>
|
||||
|
||||
<!-- 我的排名 -->
|
||||
<itemCenter
|
||||
v-show="rankingShow"
|
||||
style="min-height: 22.3vw; position: fixed; bottom: -1vw; z-index: 3"
|
||||
:imgUrl="imageUrl('myRankingBg')"
|
||||
:contentStyle="`padding: 0 3vw 0`"
|
||||
>
|
||||
<!-- 基本信息 -->
|
||||
<div
|
||||
style="
|
||||
flex: 1;
|
||||
min-width: 0;
|
||||
|
||||
display: flex;
|
||||
justify-content: space-around;
|
||||
align-items: center;
|
||||
|
||||
gap: 3vw;
|
||||
"
|
||||
>
|
||||
<!-- 排名 -->
|
||||
<div style="display: flex; justify-content: center">
|
||||
<div style="color: #ffe601; font-weight: 500">
|
||||
{{ myRanking.rank || 999 }}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 头像 -->
|
||||
<img
|
||||
:src="myRanking.userAvatar || ''"
|
||||
alt=""
|
||||
style="
|
||||
display: block;
|
||||
width: 15vw;
|
||||
aspect-ratio: 1/1;
|
||||
border-radius: 50%;
|
||||
object-fit: cover;
|
||||
"
|
||||
@error="handleAvatarImageError"
|
||||
/>
|
||||
|
||||
<!-- 名称 -->
|
||||
<div style="flex: 1; min-width: 0; display: flex; flex-direction: column">
|
||||
<div style="display: flex; min-width: 0">
|
||||
<div
|
||||
style="
|
||||
color: #fff;
|
||||
font-size: 1em;
|
||||
font-weight: 700;
|
||||
overflow: hidden;
|
||||
white-space: nowrap;
|
||||
text-overflow: ellipsis;
|
||||
direction: ltr;
|
||||
"
|
||||
>
|
||||
{{ myRanking.userName }}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 贡献值 -->
|
||||
<div
|
||||
style="
|
||||
width: auto;
|
||||
min-width: 0;
|
||||
|
||||
display: flex;
|
||||
align-items: center;
|
||||
"
|
||||
>
|
||||
<div style="display: flex; justify-content: center; align-items: center">
|
||||
<img
|
||||
v-smart-img
|
||||
src="/src/assets/icon/Azizi/coin.png"
|
||||
alt=""
|
||||
style="display: block; width: 1.5em; aspect-ratio: 1/1; object-fit: cover"
|
||||
/>
|
||||
<div style="color: #ffedc2; font-weight: 600">
|
||||
{{ myRanking.amount || 0 }}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</itemCenter>
|
||||
|
||||
<!-- 弹窗遮罩层 -->
|
||||
<maskLayer :maskLayerShow="maskLayerShow" @click="closedPopup">
|
||||
<!-- 居中弹窗 -->
|
||||
<div
|
||||
style="
|
||||
width: 100vw;
|
||||
height: 100vh;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
"
|
||||
@click="closedPopup"
|
||||
>
|
||||
<img
|
||||
v-if="helpShow"
|
||||
v-smart-img
|
||||
:src="imageUrl(getImgName('helpInfo'))"
|
||||
alt=""
|
||||
style="display: block; width: 100%"
|
||||
/>
|
||||
</div>
|
||||
</maskLayer>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { computed, nextTick, onMounted, onUnmounted, ref } from 'vue'
|
||||
import { connectToApp } from '@/utils/appConnector.js'
|
||||
import { useI18n } from 'vue-i18n'
|
||||
import { viewUserInfo } from '@/utils/appBridge.js'
|
||||
import { useLangStore } from '@/stores/lang'
|
||||
import { getPngUrl } from '@/config/imagePaths.js'
|
||||
import { preloadImages } from '@/utils/image/imagePreloader.js'
|
||||
import { handleAvatarImageError, handleRewardImageError } from '@/utils/image/imageHandler.js'
|
||||
import { useThrottle } from '@/utils/useDebounce'
|
||||
import { formatRewardDisplay } from '@/utils/rewardFormatter.js'
|
||||
import { apigetRewardConfigs, ranklist } from '@/api/lottery'
|
||||
|
||||
import TopUser from './components/topUser.vue'
|
||||
import BackgroundLayer from '@/components/BackgroundLayer.vue'
|
||||
import itemCenter from '@/components/itemCenter.vue'
|
||||
import maskLayer from '@/components/MaskLayer.vue'
|
||||
|
||||
// 预加载状态
|
||||
const isLoading = ref(true)
|
||||
|
||||
const { t } = useI18n()
|
||||
|
||||
const langStore = useLangStore()
|
||||
// 当前语言类型
|
||||
const currentLangType = computed(() => {
|
||||
return langStore.selectedLang?.type || 'en'
|
||||
})
|
||||
|
||||
// 获取 OSS 图片 URL
|
||||
const imageUrl = (filename) => getPngUrl('Activities/PokerAce/', filename)
|
||||
|
||||
// 根据语言获取图片名
|
||||
const getImgName = (filename) => {
|
||||
return currentLangType.value === 'en' ? filename : `${filename}_${currentLangType.value}`
|
||||
}
|
||||
|
||||
const topRankingContentStyle = computed(() => {
|
||||
const baseStyle = 'gap:8vw;padding: 0 9vw 26vw;'
|
||||
return currentLangType.value === 'ar' ? `${baseStyle}flex-direction:row-reverse` : baseStyle
|
||||
})
|
||||
|
||||
// 弹窗展示
|
||||
const helpShow = ref(false) // 帮助
|
||||
const maskLayerShow = computed(() => {
|
||||
return helpShow.value
|
||||
})
|
||||
|
||||
// 模块切换展示
|
||||
const rankingShow = ref(true)
|
||||
const rewardsShow = ref(false)
|
||||
|
||||
const handleBt = (type) => {
|
||||
rankingShow.value = type === 'rank'
|
||||
rewardsShow.value = type === 'reward'
|
||||
}
|
||||
|
||||
const RankingShow = computed(() => rankingShow.value)
|
||||
|
||||
// 排行榜数据
|
||||
const PageNo = ref(1)
|
||||
const Ranking = ref([])
|
||||
const addRanking = ref([])
|
||||
const myRanking = ref({})
|
||||
const rankingRewards = ref([])
|
||||
const RankingLoadmore = ref(null)
|
||||
const rankingLoading = ref(false)
|
||||
const rankingHasMore = ref(true)
|
||||
|
||||
const rankingTotal = computed(() => {
|
||||
return [...Ranking.value, ...addRanking.value]
|
||||
})
|
||||
|
||||
const RankingHasTop3 = computed(() => {
|
||||
const top3 = rankingTotal.value.filter((_, index) => index < 3)
|
||||
if (top3.length < 3) {
|
||||
top3.push(
|
||||
...Array.from({ length: 3 - top3.length }, () => ({
|
||||
userAvatar: '',
|
||||
userName: '',
|
||||
account: '',
|
||||
specialAccount: '',
|
||||
amount: '',
|
||||
})),
|
||||
)
|
||||
}
|
||||
return top3
|
||||
})
|
||||
|
||||
const showRanking = computed(() => {
|
||||
return rankingTotal.value.filter((_, index) => index >= 3)
|
||||
})
|
||||
|
||||
// 倒计时
|
||||
const Days = ref(0)
|
||||
const Hours = ref('00')
|
||||
const Minutes = ref('00')
|
||||
const Seconds = ref('00')
|
||||
let countdownTimer = null
|
||||
|
||||
const ACTIVITY_END_TIME = new Date(2026, 4, 31, 5, 0, 0).getTime()
|
||||
|
||||
const formatTime = (value) => String(value).padStart(2, '0')
|
||||
|
||||
const calculateCountdown = () => {
|
||||
const diff = ACTIVITY_END_TIME - Date.now()
|
||||
|
||||
if (diff <= 0) {
|
||||
Days.value = 0
|
||||
Hours.value = '00'
|
||||
Minutes.value = '00'
|
||||
Seconds.value = '00'
|
||||
|
||||
if (countdownTimer) {
|
||||
clearInterval(countdownTimer)
|
||||
countdownTimer = null
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
const totalSeconds = Math.floor(diff / 1000)
|
||||
Days.value = Math.floor(totalSeconds / (24 * 3600))
|
||||
Hours.value = formatTime(Math.floor((totalSeconds % (24 * 3600)) / 3600))
|
||||
Minutes.value = formatTime(Math.floor((totalSeconds % 3600) / 60))
|
||||
Seconds.value = formatTime(totalSeconds % 60)
|
||||
}
|
||||
|
||||
const startCountdown = () => {
|
||||
calculateCountdown()
|
||||
countdownTimer = setInterval(calculateCountdown, 1000)
|
||||
}
|
||||
|
||||
// 打开弹窗
|
||||
const openPopup = (type) => {
|
||||
closedPopup() // 先关闭所有弹窗
|
||||
switch (type) {
|
||||
case 'help':
|
||||
helpShow.value = true
|
||||
break
|
||||
default:
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
// 关闭弹窗
|
||||
const closedPopup = () => {
|
||||
helpShow.value = false
|
||||
}
|
||||
|
||||
// 获取排行榜
|
||||
const getRanking = async () => {
|
||||
if (rankingLoading.value || !rankingHasMore.value) {
|
||||
return
|
||||
}
|
||||
|
||||
if (PageNo.value == 0) {
|
||||
PageNo.value = 1
|
||||
}
|
||||
rankingLoading.value = true
|
||||
|
||||
try {
|
||||
let data = {
|
||||
activityId: '2007771533988204877',
|
||||
pageNo: PageNo.value,
|
||||
pageSize: 20,
|
||||
}
|
||||
const resRanking = await ranklist(data)
|
||||
if (resRanking.status && resRanking.body) {
|
||||
myRanking.value = resRanking.body?.currentUser // 我的排名
|
||||
|
||||
const topList = resRanking.body.topList || []
|
||||
if (topList.length > 0) {
|
||||
if (topList.length == 20) {
|
||||
addRanking.value = []
|
||||
Ranking.value.push(...topList)
|
||||
PageNo.value++
|
||||
} else {
|
||||
addRanking.value = topList
|
||||
rankingHasMore.value = false
|
||||
}
|
||||
} else {
|
||||
rankingHasMore.value = false
|
||||
}
|
||||
} else {
|
||||
myRanking.value = {}
|
||||
Ranking.value = []
|
||||
addRanking.value = []
|
||||
PageNo.value = 1
|
||||
rankingHasMore.value = true
|
||||
}
|
||||
} finally {
|
||||
rankingLoading.value = false
|
||||
}
|
||||
}
|
||||
|
||||
// 获取奖励列表
|
||||
const getGameReward = async () => {
|
||||
let data = {
|
||||
sysOrigin: 'LIKEI',
|
||||
activityType: 'LUCKY_GIFT_REWARD',
|
||||
}
|
||||
const resActivityReward = await apigetRewardConfigs(data)
|
||||
if (resActivityReward.status && resActivityReward.body) {
|
||||
rankingRewards.value = resActivityReward.body
|
||||
.filter((rewards) => rewards.rule.sort <= 3)
|
||||
.slice(0, 3)
|
||||
.map((rewards) => {
|
||||
let addReward = []
|
||||
if (rewards.rule.sort == 1) {
|
||||
addReward = [
|
||||
{
|
||||
type: 'DOLLARS',
|
||||
cover: '',
|
||||
content: 100,
|
||||
},
|
||||
]
|
||||
} else if (rewards.rule.sort == 2) {
|
||||
addReward = [
|
||||
{
|
||||
type: 'DOLLARS',
|
||||
cover: '',
|
||||
content: 50,
|
||||
},
|
||||
]
|
||||
} else if (rewards.rule.sort == 3) {
|
||||
addReward = [
|
||||
{
|
||||
type: 'DOLLARS',
|
||||
cover: '',
|
||||
content: 30,
|
||||
},
|
||||
]
|
||||
} else if (rewards.rule.sort == 4) {
|
||||
addReward = [
|
||||
{
|
||||
type: 'DOLLARS',
|
||||
cover: '',
|
||||
content: 10,
|
||||
},
|
||||
]
|
||||
}
|
||||
|
||||
rewards.propsGroup.activityRewardProps.push(...addReward) // 补充奖励项
|
||||
|
||||
rewards = {
|
||||
...rewards,
|
||||
rewards: rewards.propsGroup.activityRewardProps,
|
||||
}
|
||||
return rewards
|
||||
})
|
||||
} else {
|
||||
rankingRewards.value = []
|
||||
}
|
||||
}
|
||||
|
||||
// 刷新页面数据
|
||||
const initData = async () => {
|
||||
await Promise.all([getRanking(), getGameReward()])
|
||||
}
|
||||
|
||||
// 预加载关键图片
|
||||
const preloadCriticalImages = async () => {
|
||||
const criticalImages = [
|
||||
imageUrl('bg'),
|
||||
imageUrl('helpBt'),
|
||||
imageUrl('timeBg'),
|
||||
imageUrl(getImgName('rankBtActive')),
|
||||
imageUrl(getImgName('rankBt')),
|
||||
imageUrl(getImgName('rewardBtActive')),
|
||||
imageUrl(getImgName('rewardBt')),
|
||||
imageUrl('topBg'),
|
||||
imageUrl('frameTop1'),
|
||||
imageUrl('frameTop2'),
|
||||
imageUrl('frameTop3'),
|
||||
imageUrl('rankingItmeBg'),
|
||||
imageUrl('rewardTitleTop1'),
|
||||
imageUrl('rewardTitleTop2'),
|
||||
imageUrl('rewardTitleTop3'),
|
||||
imageUrl('giftItemBg'),
|
||||
imageUrl('myRankingBg'),
|
||||
imageUrl(getImgName('helpInfo')),
|
||||
]
|
||||
|
||||
await preloadImages(criticalImages)
|
||||
}
|
||||
|
||||
// 完成预加载
|
||||
const completePreloading = async () => {
|
||||
try {
|
||||
// 执行所有初始化操作
|
||||
await Promise.all([initData(), preloadCriticalImages()])
|
||||
} catch (error) {
|
||||
console.error('预加载过程中发生错误:', error)
|
||||
} finally {
|
||||
// 无论成功或失败都结束加载状态
|
||||
isLoading.value = false
|
||||
|
||||
await nextTick()
|
||||
if (RankingLoadmore.value) {
|
||||
observer.observe(RankingLoadmore.value)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 使用工具函数连接 APP
|
||||
const connectToAppHandler = async () => {
|
||||
await connectToApp(() => {
|
||||
// 连接成功回调
|
||||
completePreloading() // 完成预加载
|
||||
})
|
||||
}
|
||||
|
||||
const debouceGetRanking = useThrottle(() => {
|
||||
getRanking()
|
||||
}, 1000)
|
||||
|
||||
const observer = new IntersectionObserver((entries) => {
|
||||
entries.forEach((entry) => {
|
||||
if (entry.intersectionRatio > 0 && rankingTotal.value.length !== 0) {
|
||||
debouceGetRanking()
|
||||
}
|
||||
})
|
||||
})
|
||||
|
||||
onMounted(async () => {
|
||||
handleBt('rank')
|
||||
startCountdown()
|
||||
await connectToAppHandler()
|
||||
})
|
||||
|
||||
onUnmounted(() => {
|
||||
if (countdownTimer) {
|
||||
clearInterval(countdownTimer)
|
||||
countdownTimer = null
|
||||
}
|
||||
|
||||
observer.disconnect()
|
||||
})
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
* {
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
.fullPage {
|
||||
position: relative;
|
||||
background: #000000;
|
||||
}
|
||||
|
||||
.bg {
|
||||
width: 100vw;
|
||||
min-height: 100vh;
|
||||
position: relative;
|
||||
z-index: 1;
|
||||
}
|
||||
|
||||
.background-overlay {
|
||||
position: absolute;
|
||||
inset: 236vw 0 0;
|
||||
background-image: var(--bg2-url);
|
||||
background-repeat: repeat-y;
|
||||
background-position: top center;
|
||||
background-size: 100% auto;
|
||||
z-index: 0;
|
||||
}
|
||||
|
||||
/* 加载动画样式 */
|
||||
.loading-container {
|
||||
width: 100vw;
|
||||
height: 100vh;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
color: #666;
|
||||
}
|
||||
|
||||
.scrollY::-webkit-scrollbar {
|
||||
display: none;
|
||||
}
|
||||
|
||||
[dir='rtl'] .flipImg {
|
||||
transform: scaleX(-1);
|
||||
}
|
||||
|
||||
.helpBt {
|
||||
display: block;
|
||||
width: 10vw;
|
||||
object-fit: cover;
|
||||
position: absolute;
|
||||
z-index: 3;
|
||||
top: 0;
|
||||
right: 3vw;
|
||||
}
|
||||
|
||||
[dir='rtl'] .helpBt {
|
||||
right: unset;
|
||||
left: 3vw;
|
||||
}
|
||||
|
||||
.timeBoxItem {
|
||||
flex: 1;
|
||||
min-width: 0;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.timeText {
|
||||
color: #fff;
|
||||
font-weight: 590;
|
||||
font-size: 1.8em;
|
||||
font-style: italic;
|
||||
}
|
||||
|
||||
@media screen and (max-width: 360px) {
|
||||
* {
|
||||
font-size: 10px;
|
||||
}
|
||||
}
|
||||
|
||||
@media screen and (min-width: 360px) {
|
||||
* {
|
||||
font-size: 12px;
|
||||
}
|
||||
}
|
||||
|
||||
@media screen and (min-width: 768px) {
|
||||
* {
|
||||
font-size: 24px;
|
||||
}
|
||||
}
|
||||
|
||||
@media screen and (min-width: 1024px) {
|
||||
* {
|
||||
font-size: 32px;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
Loading…
x
Reference in New Issue
Block a user