feat(cp榜单页): 对接接口数据

This commit is contained in:
hzj 2026-01-19 18:38:05 +08:00
parent b67c593b39
commit 7bd2299faf
5 changed files with 956 additions and 267 deletions

View File

@ -2,6 +2,13 @@ import { defineStore } from 'pinia'
export const useCoupleStore = defineStore('couple', {
state: () => ({
helpShow: false, //帮助展示
topRewardShow: false, // 前三名奖励展示
topReward: {}, //前三名奖励项
myRanking: {}, // 我的排名
TipsShow: false, // 金币不足提示
needCheckInTipsShow: false, // 需要补签提示
signInSuccessShow: false, // 签到成功提示

View File

@ -12,27 +12,59 @@
gap: 2vw;
"
>
<img
v-smart-img
:src="imageUrl(getImgName('gameLotteryBt'))"
alt=""
style="flex: 1; min-width: 0; display: block; object-fit: cover"
@click="changeTag('lottery')"
/>
<img
v-smart-img
:src="imageUrl(getImgName('gameSignInBt'))"
alt=""
style="flex: 1; min-width: 0; display: block; object-fit: cover"
@click="changeTag('signIn')"
/>
<img
v-smart-img
:src="imageUrl(getImgName('gameExchangeBt'))"
alt=""
style="flex: 1; min-width: 0; display: block; object-fit: cover"
@click="changeTag('exchange')"
/>
<!-- 抽奖栏目按钮 -->
<div style="flex: 1; min-width: 0" @click="changeTag('lottery')">
<img
v-show="lotteryShow"
v-smart-img
:src="imageUrl(getImgName('gameLotteryBtActive'))"
alt=""
style="width: 100%; display: block; object-fit: cover"
/>
<img
v-show="!lotteryShow"
v-smart-img
:src="imageUrl(getImgName('gameLotteryBt'))"
alt=""
style="width: 100%; display: block; object-fit: cover"
/>
</div>
<!-- 签到按钮 -->
<div style="flex: 1; min-width: 0" @click="changeTag('signIn')">
<img
v-show="signInShow"
v-smart-img
:src="imageUrl(getImgName('gameSignInBtActive'))"
alt=""
style="width: 100%; display: block; object-fit: cover"
/>
<img
v-show="!signInShow"
v-smart-img
:src="imageUrl(getImgName('gameSignInBt'))"
alt=""
style="width: 100%; display: block; object-fit: cover"
/>
</div>
<!-- 兑换按钮 -->
<div style="flex: 1; min-width: 0" @click="changeTag('exchange')">
<img
v-show="exchangeShow"
v-smart-img
:src="imageUrl(getImgName('gameExchangeBtActive'))"
alt=""
style="width: 100%; display: block; object-fit: cover"
/>
<img
v-show="!exchangeShow"
v-smart-img
:src="imageUrl(getImgName('gameExchangeBt'))"
alt=""
style="width: 100%; display: block; object-fit: cover"
/>
</div>
</div>
<div style="position: relative">

View File

@ -12,27 +12,159 @@
gap: 10vw;
"
>
<img
v-smart-img
:src="imageUrl(getImgName('rankingWeeklyBt'))"
alt=""
style="flex: 1; min-width: 0; display: block; object-fit: cover"
@click="changeTag('weekly')"
/>
<img
v-smart-img
:src="imageUrl(getImgName('rankingSeasonBt'))"
alt=""
style="flex: 1; min-width: 0; display: block; object-fit: cover"
@click="changeTag('season')"
/>
<!-- 周榜按钮 -->
<div style="flex: 1; min-width: 0" @click="changeTag('weekly')">
<img
v-show="weeklyShow"
v-smart-img
:src="imageUrl(getImgName('rankingWeeklyBtActive'))"
alt=""
style="width: 100%; display: block; object-fit: cover"
/>
<img
v-show="!weeklyShow"
v-smart-img
:src="imageUrl(getImgName('rankingWeeklyBt'))"
alt=""
style="width: 100%; display: block; object-fit: cover"
/>
</div>
<!-- 季榜按钮 -->
<div style="flex: 1; min-width: 0" @click="changeTag('season')">
<img
v-show="seasonShow"
v-smart-img
:src="imageUrl(getImgName('rankingSeasonBtActive'))"
alt=""
style="width: 100%; display: block; object-fit: cover"
/>
<img
v-show="!seasonShow"
v-smart-img
:src="imageUrl(getImgName('rankingSeasonBt'))"
alt=""
style="width: 100%; display: block; object-fit: cover"
/>
</div>
</div>
<!-- 排行榜 -->
<div>
<div style="position: relative; display: flex; flex-direction: column; gap: 2vw">
<!-- 取消按钮 -->
<img
:src="imageUrl('helpBt')"
alt=""
style="
display: block;
width: 8vw;
object-fit: cover;
position: absolute;
z-index: 3;
top: 0;
right: 3vw;
"
@click="helpShow = true"
/>
<!-- 前三名 -->
<TopUser
v-for="(value, index) in 3"
:ranking="index + 1 + ''"
:rankingItem="RankingHasTop3[index] || {}"
:rewards="rewardsTop3[index] || {}"
/>
<!-- 从第4名开始 -->
<itemCenter
v-for="item in showRanking"
:imgUrl="imageUrl(`rankingItemBg`)"
:contentStyle="`padding: 0 5%;justify-content: flex-start;`"
>
<!-- 排名 -->
<div style="width: 10vw; display: flex; justify-content: center; align-items: center">
<img
v-for="(digit, index) in getRankDigits(item.rank)"
:src="getDigitImageUrl(digit)"
style="display: block; width: 3vw; object-fit: cover"
/>
</div>
<!-- 头像 -->
<div style="width: 36vw">
<div
style="
width: 100%;
padding: 3vw 5vw;
height: 80%;
display: flex;
justify-content: space-between;
align-items: center;
"
>
<!-- 情侣1头像 -->
<img
:src="item.userAvatar"
alt=""
style="
display: block;
min-width: 0;
width: 12vw;
object-fit: cover;
aspect-ratio: 1/1;
border-radius: 50%;
border: 1px solid #fefa9f;
"
@error="handleAvatarImageError"
/>
<!-- 情侣2头像 -->
<img
:src="item.cpUserAvatar"
alt=""
style="
display: block;
min-width: 0;
width: 12vw;
object-fit: cover;
aspect-ratio: 1/1;
border-radius: 50%;
border: 1px solid #fefa9f;
"
@error="handleAvatarImageError"
/>
</div>
</div>
<!-- 名称 -->
<div
style="
flex: 1;
min-width: 0;
height: 100%;
display: flex;
flex-direction: column;
justify-content: center;
gap: 1vw;
"
>
<div style="width: 100%; overflow: hidden; text-overflow: ellipsis; white-space: nowrap">
{{ item.userNickname || '' }}
</div>
<div style="width: 100%; overflow: hidden; text-overflow: ellipsis; white-space: nowrap">
{{ item.cpUserNickname || '' }}
</div>
</div>
<!-- 贡献 -->
<div style="width: auto; min-width: 0; display: flex; align-items: center">
<img
:src="imageUrl('distribution')"
alt=""
style="width: 10vw; display: block; object-fit: cover"
/>
<div style="font-weight: 700">{{ item.total || '0' }}</div>
</div>
</itemCenter>
</div>
</div>
</template>
@ -50,11 +182,13 @@ import { preloadImages } from '@/utils/imagePreloader.js'
import { formatUTCCustom } from '@/utils/utcFormat.js'
import { showError, showSuccess } from '@/utils/toast.js'
import { handleAvatarImageError, handleRewardImageError } from '@/utils/imageHandler.js'
import { useCoupleStore } from '@/stores/couple'
import { useThrottle } from '@/utils/useDebounce.js'
import { storeToRefs } from 'pinia'
import {
apiGetCpRanking, // cp
apiGetCpRewardPool, // cp()
} from '@/api/couple.js'
import TopUser from './components/topUser.vue'
@ -64,6 +198,7 @@ import maskLayer from '@/components/MaskLayer.vue'
const { t } = useI18n()
const router = useRouter()
const route = useRoute()
const coupleStore = useCoupleStore()
const langStore = useLangStore()
//
@ -81,6 +216,11 @@ const getImgName = (filename) => {
return currentLangType.value === 'ar' ? `${filename}_ar` : filename
}
const {
helpShow, //
myRanking, //
} = storeToRefs(coupleStore)
const weeklyShow = ref(true) //
const seasonShow = ref(false) //
@ -91,16 +231,20 @@ const changeTag = (module) => {
weeklyShow.value = true
seasonShow.value = false
apiGetRanking() //
apiGetTop3Rewards() //
break
case 'season':
if (seasonShow.value) return
weeklyShow.value = false
seasonShow.value = true
apiGetRanking() //
apiGetTop3Rewards() //
break
}
}
const rewardsTop3 = ref([]) //
const RankingLoadmore = ref(null) //
const rankingPageNo = ref(1) //
const Ranking = ref([]) //
@ -109,16 +253,26 @@ const rankingAdd = ref([]) //加载排行榜项
const rankingTotal = computed(() => {
return [...Ranking.value, ...rankingAdd.value]
})
const myRanking = ref({}) //
// 3
const RankingHasTop3 = computed(() => {
let RankingShow = [...Ranking.value]
if (Ranking.value.length < 3) {
let addNullUser = Array.from({ length: 3 - Ranking.value.length }, () => ({
avatar: '',
nickname: '',
quantity: '',
rank: '',
userAvatar: '',
userId: '',
userNickname: '',
userSex: '',
cpUserAvatar: '',
cpUserId: '',
cpUserNickname: '',
cpUserSex: '',
total: '0',
totalNumber: '0',
}))
RankingShow.push(...addNullUser)
}
@ -127,10 +281,24 @@ const RankingHasTop3 = computed(() => {
//
const showRanking = computed(() => {
return Ranking.value.filter((_, index) => index >= 10)
return Ranking.value.filter((_, index) => index >= 3)
// return []
})
//
const getRankDigits = (rank) => {
return String(rank).split('')
}
// URL
const getDigitImageUrl = (digit) => {
if (/^[0-9]$/.test(digit)) {
return imageUrl(`num${digit}`) //
}
return ''
}
//
const apiGetRanking = async () => {
let type = weeklyShow.value ? 'WEEK' : 'SEASON'
try {
@ -144,6 +312,19 @@ const apiGetRanking = async () => {
}
}
//
const apiGetTop3Rewards = async () => {
let type = weeklyShow.value ? 'WEEK_CP_GIFT' : 'SEASON_CP_GIFT'
try {
const response = await apiGetCpRewardPool(type)
if (response.status && response.body) {
rewardsTop3.value = response.body || [] //
}
} catch (error) {
showError(error.message)
}
}
//
const debouceGetRanking = useThrottle(() => {
// getRanking() //
@ -162,6 +343,7 @@ const observer = new IntersectionObserver((entries) => {
//
onMounted(() => {
apiGetRanking() //
apiGetTop3Rewards() //
//
if (RankingLoadmore.value) {

View File

@ -1,123 +1,346 @@
<template>
<div style="width: 100%">
<div style="position: relative">
<img
v-smart-img
:src="BorderImgUrl"
alt=""
width="100%"
style="display: block; position: relative; z-index: 2"
/>
<!-- 头像 -->
<div
style="
position: absolute;
top: 0;
left: 0;
right: 0;
z-index: 1;
display: flex;
justify-content: center;
align-items: center;
"
:style="{ height: avatarContainerHeight }"
>
<div :style="{ width: avatarWidth }">
<img
v-smart-img
:src="avatarUrl || ''"
alt=""
width="100%"
style="display: block; border-radius: 50%; aspect-ratio: 1/1; object-fit: cover"
@error="handleAvatarImageError"
/>
<itemCenter :imgUrl="BorderImgUrl" :contentStyle="`display:block;`">
<!-- 第一名 -->
<div v-if="isTopOne" style="width: 98vw; height: 80%; position: relative">
<!-- 查看个人信息按钮 -->
<div style="position: absolute; z-index: 2; inset: 0; display: flex">
<!-- 情侣1 -->
<div
style="flex: 1; min-width: 0; align-self: stretch"
@click="viewUserInfo(rankingItem.userId)"
></div>
<!-- 情侣1 -->
<div
style="flex: 1; min-width: 0; align-self: stretch"
@click="viewUserInfo(rankingItem.cpUserId)"
></div>
</div>
</div>
<!-- 用户名和贡献值 -->
<div
style="
position: absolute;
bottom: 0;
left: 0;
right: 0;
z-index: 3;
display: flex;
flex-direction: column;
align-items: center;
gap: 2px;
"
:style="{ height: bottomSectionHeight }"
>
<div
style="display: flex; justify-content: center; align-items: center"
:style="{
width: nameValueWidth,
height: nameValueHeight,
}"
>
<div style="font-weight: 700" class="showText" :class="isTopOne ? 'top1Text' : 'text'">
{{ name }}
</div>
</div>
<!-- 头像 -->
<div
style="
padding: 1px 2px;
position: absolute;
z-index: -1;
top: 17vw;
left: 0;
width: 100%;
height: 25vw;
display: flex;
justify-content: center;
align-items: center;
gap: 14vw;
"
>
<!-- 情侣1头像 -->
<img
:src="rankingItem.userAvatar"
alt=""
style="
display: block;
width: 25vw;
object-fit: cover;
aspect-ratio: 1/1;
border-radius: 50%;
"
@error="handleAvatarImageError"
@click="viewUserInfo(rankingItem.userId)"
/>
<!-- 情侣2头像 -->
<img
:src="rankingItem.cpUserAvatar"
alt=""
style="
display: block;
width: 25vw;
object-fit: cover;
aspect-ratio: 1/1;
border-radius: 50%;
"
@error="handleAvatarImageError"
@click="viewUserInfo(rankingItem.cpUserId)"
/>
</div>
<!-- 顶部距离 -->
<div style="height: 27vw"></div>
<!-- 贡献值 -->
<div style="height: 17vw; display: flex; justify-content: center; align-items: center">
<div style="color: #fff; font-weight: 700">
{{ rankingItem.total }}
</div>
</div>
<!-- 名称 -->
<div
style="
margin-top: -1vw;
height: 5vw;
display: flex;
justify-content: center;
align-items: center;
"
:style="{
width: distributionValueWidth,
height: distributionValueHeight,
}"
>
<div
style="font-weight: 590"
class="showText"
:class="isTopOne ? 'top1Text' : 'text'"
:style="{
color: ranking == '1' ? `#FFE601` : '#FFF',
}"
>
${{ distributionValue }}
<div style="width: 76vw; display: flex; align-items: center; gap: 2vw">
<!-- 情侣1名称 -->
<div
style="
flex: 1;
min-width: 0;
display: flex;
justify-content: center;
align-items: center;
"
>
<div
style="
padding: 0 20%;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
font-size: 0.9em;
font-weight: 600;
"
>
{{ rankingItem.userNickname || '' }}
</div>
</div>
<!-- 情侣2名称 -->
<div
style="
flex: 1;
min-width: 0;
display: flex;
justify-content: center;
align-items: center;
"
>
<div
style="
padding: 0 20%;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
font-size: 0.9em;
font-weight: 600;
"
>
{{ rankingItem.cpUserNickname || '' }}
</div>
</div>
</div>
</div>
</div>
</div>
<!-- 第二三名 -->
<div v-else style="height: 65%; padding: 0 7%; display: flex; align-items: center">
<!-- 排名 -->
<img :src="imageUrl(`top${ranking}Ranking`)" alt="" style="width: 8vw" />
<!-- 头像 -->
<div style="width: 36vw; position: relative">
<!-- 查看个人信息按钮 -->
<div style="position: absolute; z-index: 2; inset: 0; display: flex">
<!-- 情侣1 -->
<div
style="flex: 1; min-width: 0; align-self: stretch"
@click="viewUserInfo(rankingItem.userId)"
></div>
<!-- 情侣1 -->
<div
style="flex: 1; min-width: 0; align-self: stretch"
@click="viewUserInfo(rankingItem.cpUserId)"
></div>
</div>
<!-- 内容 -->
<itemCenter
style="width: 100%; z-index: 1"
:imgUrl="imageUrl(`top${ranking}Frame`)"
:contentStyle="`z-index: -1;`"
>
<div
style="
width: 100%;
padding: 3vw 5vw;
height: 80%;
display: flex;
justify-content: space-between;
align-items: center;
"
>
<!-- 情侣1头像 -->
<img
:src="rankingItem.userAvatar"
alt=""
style="
display: block;
min-width: 0;
width: 10.5vw;
object-fit: cover;
aspect-ratio: 1/1;
border-radius: 50%;
"
@error="handleAvatarImageError"
/>
<!-- 情侣2头像 -->
<img
:src="rankingItem.cpUserAvatar"
alt=""
style="
display: block;
min-width: 0;
width: 10.5vw;
object-fit: cover;
aspect-ratio: 1/1;
border-radius: 50%;
"
@error="handleAvatarImageError"
/>
</div>
</itemCenter>
</div>
<!-- 名称 -->
<div
style="
flex: 1;
min-width: 0;
height: 100%;
display: flex;
flex-direction: column;
justify-content: center;
gap: 1vw;
"
>
<div style="width: 100%; overflow: hidden; text-overflow: ellipsis; white-space: nowrap">
{{ rankingItem.userNickname || '' }}
</div>
<div style="width: 100%; overflow: hidden; text-overflow: ellipsis; white-space: nowrap">
{{ rankingItem.cpUserNickname || '' }}
</div>
</div>
<!-- 贡献 -->
<div style="width: auto; min-width: 0; display: flex; align-items: center">
<img
:src="imageUrl('distribution')"
alt=""
style="width: 10vw; display: block; object-fit: cover"
/>
<div style="font-weight: 700">{{ rankingItem.total || '0' }}</div>
</div>
</div>
<!-- 奖励 -->
<div style="position: absolute; bottom: 0; left: 15vw; right: 4vw; height: 13.5vw">
<div style="height: 100%; display: flex; align-items: center; overflow: hidden">
<!-- 滚动容器 -->
<div class="scroll-wrapper">
<!-- 第一套内容 -->
<div class="scroll-content" v-for="value in 2">
<div
v-for="(item, index) in rewards.activityRewardProps"
:key="index"
style="display: flex; align-items: center; gap: 1vw"
@click="clickReward(item)"
>
<!-- 预览图 -->
<img
:src="item.cover"
alt=""
style="display: block; width: 10vw; object-fit: cover"
/>
<div style="color: #fff">
{{ formatRewardDisplay(item.type, item) }}
</div>
</div>
</div>
</div>
</div>
</div>
</itemCenter>
</div>
</template>
<script setup>
import { handleAvatarImageError } from '@/utils/imageHandler.js'
import { computed } from 'vue'
import { computed, onMounted, onUnmounted, ref } from 'vue'
import { useRoute, useRouter } from 'vue-router'
import { useLangStore } from '@/stores/lang'
import { useI18n } from 'vue-i18n'
import { getPngUrl } from '@/config/imagePaths.js'
import { setUserInfo } from '@/utils/userStore.js'
import { viewUserInfo } from '@/utils/appBridge.js'
import { connectToApp } from '@/utils/appConnector.js'
import { preloadImages } from '@/utils/imagePreloader.js'
import { formatUTCCustom } from '@/utils/utcFormat.js'
import { showError, showSuccess } from '@/utils/toast.js'
import { handleAvatarImageError, handleRewardImageError } from '@/utils/imageHandler.js'
import { useThrottle } from '@/utils/useDebounce.js'
import { useCoupleStore } from '@/stores/couple'
import { storeToRefs } from 'pinia'
import { formatRewardDisplay } from '@/utils/rewardFormatter.js'
import itemCenter from '@/components/itemCenter.vue'
const { t } = useI18n()
const router = useRouter()
const route = useRoute()
const coupleStore = useCoupleStore()
const langStore = useLangStore()
//
const currentLangType = computed(() => {
return langStore.selectedLang?.type || 'en'
})
// OSSURL
const imageUrl = (filename) => getPngUrl('Ranking/Couple/', filename)
//
const getImgName = (filename) => {
return currentLangType.value === 'ar' ? `${filename}_ar` : filename
}
const {
topRewardShow, //
topReward, //
} = storeToRefs(coupleStore)
const props = defineProps({
ranking: {
type: String,
default: '',
default: '0',
},
avatarUrl: {
type: String,
default: '',
rankingItem: {
type: Object,
default: () => ({}),
},
BorderImgUrl: {
type: String,
required: true,
rewards: {
type: Object,
default: () => ({}),
},
})
name: {
type: String,
default: '',
},
distributionValue: {
type: String,
default: '',
},
const BorderImgUrl = computed(() => {
switch (props.ranking) {
case '1':
return imageUrl('top1')
case '2':
return imageUrl('top2-3')
case '3':
return imageUrl('top2-3')
default:
return imageUrl('top2-3')
}
})
//
@ -125,122 +348,16 @@ const isTopOne = computed(() => {
return props.ranking === '1'
})
//
const avatarContainerHeight = computed(() => {
switch (props.ranking) {
case '1':
return '70%'
case '2':
return '60%'
case '3':
return '60%'
default:
return '60%'
}
})
//
const avatarWidth = computed(() => {
switch (props.ranking) {
case '1':
return '43%'
case '2':
return '77%'
case '3':
return '77%'
default:
return '77%'
}
})
//
const bottomSectionHeight = computed(() => {
switch (props.ranking) {
case '1':
return '35%'
case '2':
return '39%'
case '3':
return '40%'
default:
return '39%'
}
})
//
const nameValueWidth = computed(() => {
switch (props.ranking) {
case '1':
return '30%'
case '2':
return '60%'
case '3':
return '60%'
default:
return '60%'
}
})
//
const nameValueHeight = computed(() => {
switch (props.ranking) {
case '1':
return '25%'
case '2':
return '25%'
case '3':
return '25%'
default:
return '25%'
}
})
//
const distributionValueWidth = computed(() => {
switch (props.ranking) {
case '1':
return '25%'
case '2':
return '40%'
case '3':
return '40%'
default:
return '40%'
}
})
//
const distributionValueHeight = computed(() => {
switch (props.ranking) {
case '1':
return '35%'
case '2':
return '20%'
case '3':
return '20%'
default:
return '20%'
}
})
//
const coinWidth = computed(() => {
switch (props.ranking) {
case '1':
return '1em'
case '2':
return '0.9em'
case '3':
return '0.9em'
default:
return '0.9em'
}
})
//
const clickReward = (item) => {
topReward.value = item //
topRewardShow.value = true //
}
</script>
<style lang="scss" scoped>
* {
color: black;
color: #fff;
}
.showText {
@ -259,6 +376,54 @@ const coinWidth = computed(() => {
font-size: 0.6em;
}
.scroll-wrapper {
display: flex;
width: fit-content;
}
.scroll-content {
display: flex;
align-items: center;
gap: 2vw;
}
@keyframes scroll-left {
0% {
transform: translateX(0);
}
100% {
transform: translateX(-50%);
}
}
@keyframes scroll-right {
0% {
transform: translateX(0);
}
100% {
transform: translateX(50%);
}
}
[dir='ltr'] .scroll-content {
margin-left: 4vw;
}
[dir='rtl'] .scroll-content {
margin-left: auto;
margin-right: 4vw;
}
[dir='ltr'] .scroll-wrapper {
animation: scroll-left 8s linear infinite;
will-change: transform;
}
[dir='rtl'] .scroll-wrapper {
animation: scroll-right 8s linear infinite;
will-change: transform;
}
@media screen and (max-width: 360px) {
* {
font-size: 10px;

View File

@ -34,19 +34,80 @@
</div>
<!-- 历史榜首 -->
<!-- <itemCenter
<itemCenter
:imgUrl="imageUrl(getImgName('historyTopBg'))"
:contentStyle="`inset: 30vw 9vw 27vw;`"
>
<div style="width: 100%; height: 100%; overflow-x: auto">
<div style="padding: 0 16vw; display: flex; gap: 4vw">
<div v-for="value in 4">
<img
v-smart-img :src="imageUrl('topCoupleFrame')" alt="" style="width: 50vw" />
</div>
<div
style="width: 100%; display: flex; justify-content: space-between; align-items: center"
>
<!-- 上一个奖品 -->
<div
style="
width: 8vw;
align-self: stretch;
display: flex;
justify-content: center;
align-items: center;
"
>
<img
v-smart-img
v-show="result.length > 1 && resultShowIndex > 0"
:src="imageUrl('resultBack')"
alt=""
style="display: block; width: 80%; object-fit: cover"
@click="resultShowIndex--"
/>
</div>
<!-- 奖品 -->
<img
:src="result[resultShowIndex]?.prize?.prizeImage"
alt=""
width="50%"
style="display: block"
/>
<!-- 下一个奖品 -->
<div
style="
width: 8vw;
align-self: stretch;
display: flex;
justify-content: center;
align-items: center;
"
>
<img
v-smart-img
v-show="result.length > 1 && resultShowIndex + 1 < result.length"
:src="imageUrl('resultNext')"
alt=""
style="display: block; width: 80%; object-fit: cover"
@click="resultShowIndex++"
/>
</div>
</div>
</itemCenter> -->
<!-- 奖品说明 -->
<div
style="
width: 100%;
height: 5vw;
text-align: center;
font-size: 0.9em;
font-weight: 590;
background: linear-gradient(0deg, #f3b700 0%, #ffeec6 100%);
background-clip: text;
-webkit-background-clip: text;
-webkit-text-fill-color: transparent;
position: relative;
"
>
You Get {{ result[resultShowIndex]?.prize?.prizeName }}
</div>
</itemCenter>
<!-- 栏目按钮 -->
<div
@ -60,20 +121,41 @@
gap: 10vw;
"
>
<img
v-smart-img
:src="imageUrl(getImgName('rankingBt'))"
alt=""
style="flex: 1; min-width: 0; display: block; object-fit: cover"
@click="changeTag('ranking')"
/>
<img
v-smart-img
:src="imageUrl(getImgName('gamePlayBt'))"
alt=""
style="flex: 1; min-width: 0; display: block; object-fit: cover"
@click="changeTag('gamePlay')"
/>
<!-- 排行榜栏目按钮 -->
<div style="flex: 1; min-width: 0" @click="changeTag('ranking')">
<img
v-show="RankingShow"
v-smart-img
:src="imageUrl(getImgName('rankingBtActive'))"
alt=""
style="width: 100%; display: block; object-fit: cover"
/>
<img
v-show="!RankingShow"
v-smart-img
:src="imageUrl(getImgName('rankingBt'))"
alt=""
style="width: 100%; display: block; object-fit: cover"
/>
</div>
<!-- 游戏栏目按钮 -->
<div style="flex: 1; min-width: 0" @click="changeTag('gamePlay')">
<img
v-show="GamePlayShow"
v-smart-img
:src="imageUrl(getImgName('gamePlayBtActive'))"
alt=""
style="width: 100%; display: block; object-fit: cover"
/>
<img
v-show="!GamePlayShow"
v-smart-img
:src="imageUrl(getImgName('gamePlayBt'))"
alt=""
style="width: 100%; display: block; object-fit: cover"
/>
</div>
</div>
<!-- 排行榜标签页 -->
@ -83,13 +165,132 @@
<GamePlay v-show="GamePlayShow" />
</div>
<!-- 我的排名占位 -->
<div v-show="RankingShow" style="height: 25vw"></div>
<!-- 我的排名 -->
<div v-show="RankingShow" style="position: fixed; bottom: 0; z-index: 2">
<!-- 有伴侣和排名 -->
<itemCenter
v-if="myRanking?.cpUserId != ''"
style="position: fixed; bottom: 0; z-index: 2"
:imgUrl="imageUrl('myRankingBg')"
:contentStyle="`padding: 0 5%;justify-content: flex-start;`"
>
<!-- 排名 -->
<div style="width: 10vw; display: flex; justify-content: center; align-items: center">
<img
v-for="(digit, index) in getRankDigits(myRanking.rank)"
:src="getDigitImageUrl(digit)"
style="display: block; width: 3vw; object-fit: cover"
/>
</div>
<!-- 头像 -->
<div style="width: 36vw">
<div
style="
width: 100%;
padding: 3vw 5vw;
height: 80%;
display: flex;
justify-content: space-between;
align-items: center;
"
>
<!-- 情侣1头像 -->
<img
:src="myRanking.userAvatar"
alt=""
style="
display: block;
min-width: 0;
width: 12vw;
object-fit: cover;
aspect-ratio: 1/1;
border-radius: 50%;
border: 1px solid #fefa9f;
"
@error="handleAvatarImageError"
/>
<!-- 情侣2头像 -->
<img
:src="myRanking.cpUserAvatar"
alt=""
style="
display: block;
min-width: 0;
width: 12vw;
object-fit: cover;
aspect-ratio: 1/1;
border-radius: 50%;
border: 1px solid #fefa9f;
"
@error="handleAvatarImageError"
/>
</div>
</div>
<!-- 名称 -->
<div
style="
flex: 1;
min-width: 0;
height: 100%;
display: flex;
flex-direction: column;
justify-content: center;
gap: 1vw;
"
>
<div
style="width: 100%; overflow: hidden; text-overflow: ellipsis; white-space: nowrap"
>
{{ myRanking.userNickname || '' }}
</div>
<div
style="width: 100%; overflow: hidden; text-overflow: ellipsis; white-space: nowrap"
>
{{ myRanking.cpUserNickname || '' }}
</div>
</div>
<!-- 贡献 -->
<div style="width: auto; min-width: 0; display: flex; align-items: center">
<img
:src="imageUrl('distribution')"
alt=""
style="width: 10vw; display: block; object-fit: cover"
/>
<div style="font-weight: 700">{{ myRanking.total || '0' }}</div>
</div>
</itemCenter>
<!-- 无伴侣 -->
<div
v-else
style="
border-radius: 16px 16px 0 0;
background:
radial-gradient(
171.8% 117.34% at 34.23% 104.78%,
rgba(255, 188, 221, 0) 0%,
#ffbcdd 100%
),
linear-gradient(303deg, rgba(255, 255, 255, 0) 0%, #ff4656 91.85%),
linear-gradient(292deg, #ff3bc4 0%, rgba(255, 255, 255, 0) 100.5%), #fff;
padding: 24px 12px 0 12px;
"
></div>
</div>
<!-- 弹窗遮罩层 -->
<maskLayer :maskLayerShow="maskLayerShow" @click="closedPopup">
<!-- 我的伴侣弹窗 -->
<div v-show="myCoupleShow" style="position: fixed; bottom: 0; width: 100%">
<div
style="
border-radius: 16px 16px 0 0;
border-radius: 16px 16px 0 0;
background:
radial-gradient(
@ -877,6 +1078,83 @@
</div>
</div>
</div>
<!-- 前三名奖励展示 -->
<div
v-show="topRewardShow"
style="
position: relative;
margin: 10vh 4vw 5vh;
border-radius: 12px;
background:
radial-gradient(
171.8% 117.34% at 34.23% 104.78%,
rgba(255, 188, 221, 0) 0%,
#ffbcdd 100%
),
linear-gradient(303deg, rgba(255, 255, 255, 0) 0%, #ff4656 91.85%),
linear-gradient(292deg, #ff3bc4 0%, rgba(255, 255, 255, 0) 100.5%), #fff;
padding: 20px;
display: flex;
flex-direction: column;
align-items: center;
gap: 2vw;
"
@click.stop
>
<!-- 关闭按钮 -->
<div
style="position: absolute; top: 2vw; right: 5vw; color: #ff4668; font-size: 3em"
@click="closedPopup"
>
x
</div>
<!-- 奖品展示 -->
<img
:src="topReward.cover"
alt=""
style="width: 60%; display: block; object-fit: cover"
/>
<!-- 时长或数量 -->
<div
style="
font-size: 2em;
font-weight: 700;
background: linear-gradient(
180deg,
#ffdf6b 31.61%,
#ffe562 36.73%,
#fff 52.37%,
#ffe562 58.67%,
#ffa615 72.2%
);
background-clip: text;
-webkit-background-clip: text;
-webkit-text-fill-color: transparent;
"
>
{{ formatRewardDisplay(topReward.type, topReward) }}
</div>
</div>
<!-- help弹窗 -->
<div v-show="helpShow" style="margin: 10vh 0 5vh" @click.stop>
<itemCenter
:imgUrl="imageUrl(getImgName('helpBg'))"
:contentStyle="`inset: 30vw 18vw 28vw;overflow-y: auto;display:block;`"
>
<img
v-smart-img
:src="imageUrl(getImgName('helpInfo'))"
alt=""
style="display: block; width: 100%; object-fit: cover; margin-bottom: 6vw"
/>
</itemCenter>
</div>
</maskLayer>
</div>
</div>
@ -897,6 +1175,7 @@ import { showError, showSuccess } from '@/utils/toast.js'
import { handleAvatarImageError, handleRewardImageError } from '@/utils/imageHandler.js'
import { useCoupleStore } from '@/stores/couple'
import { storeToRefs } from 'pinia'
import { formatRewardDisplay } from '@/utils/rewardFormatter.js'
import {
apiGetMyLotteryTicketCount, //
@ -922,6 +1201,13 @@ const route = useRoute()
const coupleStore = useCoupleStore()
const {
helpShow, //
topRewardShow, //
topReward, //
myRanking, //
crystal, //
debris, //
@ -958,6 +1244,19 @@ const getImgName = (filename) => {
return currentLangType.value === 'ar' ? `${filename}_ar` : filename
}
//
const getRankDigits = (rank) => {
return String(rank).split('')
}
// URL
const getDigitImageUrl = (digit) => {
if (/^[0-9]$/.test(digit)) {
return imageUrl(`num${digit}`) //
}
return ''
}
const barrageList = ref([]) //
const RankingShow = ref(true) //
@ -1019,6 +1318,8 @@ const isPublic = ref(false) // 是否公开
const confirmShow = ref(false) //
const maskLayerShow = computed(() => {
return (
helpShow.value ||
topRewardShow.value ||
lastLetterShow.value ||
myCoupleShow.value ||
writeLetterShow.value ||
@ -1063,6 +1364,8 @@ const showPopup = (type) => {
//
const closedPopup = () => {
helpShow.value = false
topRewardShow.value = false
lastLetterShow.value = false
myCoupleShow.value = false
writeLetterShow.value = false