1398 lines
41 KiB
Vue
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<!-- src/views/Ranking/WeeklyStar/WeeklyStar.vue -->
<template>
<div class="fullPage">
<!-- 预加载状态 -->
<div v-if="isLoading" class="loading-container">
<!-- 页面背景 -->
<BackgroundLayer :backgroundImages="[imageUrl('bg')]" />
<!-- 加载动画 -->
<div class="loading-spinner"></div>
<p>{{ $t('loading') }}...</p>
</div>
<!-- 主要内容 -->
<div v-else style="width: 100vw; min-height: 100vh; overflow: hidden; position: relative">
<!-- 页面背景 -->
<BackgroundLayer :backgroundImages="[imageUrl('bg')]" />
<div style="width: 100vw; position: relative">
<!-- 状态栏占位区域 -->
<div style="height: 30px"></div>
<!-- 帮助按钮 -->
<img
v-smart-img
src="../../../assets/icon/helpAct.png"
alt=""
style="width: 10%; position: absolute; top: 48px; right: 8px; z-index: 4"
@click="helpInfoShow = true"
/>
<!-- history按钮 -->
<img
v-smart-img
:src="imageUrl('history')"
alt=""
style="min-height: 20.66vw; width: 20%; margin-top: 20px"
@click="historyShow = true"
/>
<!-- gifts -->
<div
style="min-height: 20.66vw; display: flex; justify-content: flex-end; width: 100%"
:style="{
marginTop: 'calc(65vw - 30px - 10vw - 10vw - 20px)',
}"
>
<img
v-smart-img
:src="imageUrl('gifts')"
alt=""
style="width: 20%"
@click="giftsShow = true"
/>
</div>
<!-- 倒计时 -->
<div style="min-height: 20.37vw; display: flex; justify-content: center; margin-top: 0%">
<div style="width: 20%">
<itemCenter style="min-height: 20.37vw" :imgUrl="imageUrl('dayBg')">
<div class="timeText">{{ Days }}D</div>
</itemCenter>
</div>
<div class="timeText timeGap" style="width: 6px"></div>
<div style="width: 20%">
<itemCenter style="min-height: 20.37vw" :imgUrl="imageUrl('hourAndMinBg')">
<div class="timeText">{{ Hours }}</div>
</itemCenter>
</div>
<div class="timeText timeGap">:</div>
<div style="width: 20%">
<itemCenter style="min-height: 20.37vw" :imgUrl="imageUrl('hourAndMinBg')">
<div class="timeText">{{ Minutes }}</div>
</itemCenter>
</div>
<div class="timeText timeGap">:</div>
<div style="width: 20%">
<itemCenter style="min-height: 20.37vw" :imgUrl="imageUrl('secBg')">
<div class="timeText">{{ Second }}</div>
</itemCenter>
</div>
</div>
<!-- 切换按钮 -->
<div
style="
min-height: 15.96vw;
margin: 10% 0 8% 0;
display: flex;
justify-content: space-around;
"
>
<div style="width: 30%" @click="handleBt('Ranking')">
<itemCenter :imgUrl="rankingShow ? imageUrl('btActive') : imageUrl('btNoActive')">
<div class="btTitle">Ranking</div>
</itemCenter>
</div>
<div style="width: 30%" @click="handleBt('Rewards')">
<itemCenter :imgUrl="rewardShow ? imageUrl('btActive') : imageUrl('btNoActive')">
<div class="btTitle">Rewards</div>
</itemCenter>
</div>
<div style="width: 30%" @click="handleBt('Exchange')">
<itemCenter :imgUrl="exchargeShow ? imageUrl('btActive') : imageUrl('btNoActive')">
<div class="btTitle">Exchange</div>
</itemCenter>
</div>
</div>
<!-- 展示内容 -->
<div>
<!-- Ranking -->
<div
v-show="rankingShow"
style="position: relative; width: 100%; left: 50%; transform: translateX(-50%)"
>
<!-- 背景板 -->
<div>
<!-- 头部背景 -->
<img
v-smart-img
:src="imageUrl('RankingMain')"
alt=""
width="100%"
style="min-height: 149.66vw; display: block"
/>
<!-- 用户项 -->
<img
v-smart-img
:src="imageUrl('RankingItem')"
alt=""
width="100%"
style="min-height: 25.33vw; display: block; margin-top: -2px"
v-for="value in showRanking.length - 1"
v-if="showRanking.length > 1"
/>
<!-- 底框 -->
<img
v-smart-img
:src="imageUrl('RankingBottomBorder')"
alt=""
width="100%"
style="min-height: 20.66vw; display: block; margin-top: -9%"
/>
</div>
<!-- 主体内容 -->
<div
style="position: absolute; top: 0; bottom: 0; left: 0; right: 0; padding: 30% 8% 13%"
>
<!-- 前3名 -->
<div style="margin-top: 3%">
<!-- 第一 -->
<div style="display: flex; justify-content: center">
<TopUser
:BorderImgUrl="imageUrl('topOne')"
:avatarUrl="RankingHasTop3[0].userAvatar"
:name="RankingHasTop3[0].userNickname"
:distributionValue="RankingHasTop3[0].quantity"
style="width: 70%"
@click="viewUserInfo(RankingHasTop3[0].userId)"
/>
</div>
<!-- 第二三 -->
<div
style="
position: relative;
z-index: 3;
display: flex;
justify-content: space-between;
margin-top: -20%;
pointer-events: none;
"
>
<div
style="pointer-events: auto; width: 35%"
@click.stop="viewUserInfo(RankingHasTop3[1].userId)"
>
<TopUser
:isTopOne="false"
:BorderImgUrl="imageUrl('topTwo')"
:avatarUrl="RankingHasTop3[1].userAvatar"
:name="RankingHasTop3[1].userNickname"
:distributionValue="RankingHasTop3[1].quantity"
style="width: 100%"
/>
</div>
<div
style="pointer-events: auto; width: 35%"
@click.stop="viewUserInfo(RankingHasTop3[2].userId)"
>
<TopUser
:isTopOne="false"
:BorderImgUrl="imageUrl('topThree')"
:avatarUrl="RankingHasTop3[2].userAvatar"
:name="RankingHasTop3[2].userNickname"
:distributionValue="RankingHasTop3[2].quantity"
style="width: 100%"
/>
</div>
</div>
</div>
<!-- 第四名开始 -->
<itemCenter
v-for="listItem in showRanking"
style="min-height: 21.41vw; margin-top: 3.3vw; position: relative"
@click="viewUserInfo(listItem.userId)"
:imgUrl="imageUrl('itemUser')"
:contentStyle="`justify-content: flex-start;padding: 0 3%;gap: 8px;`"
>
<!-- 基本信息 -->
<div
style="
flex: 1;
min-width: 0;
align-self: stretch;
display: flex;
justify-content: space-around;
align-items: center;
gap: 4px;
"
>
<!-- 排名 -->
<div
style="
width: auto;
min-width: 0;
align-self: stretch;
display: flex;
justify-content: center;
align-items: center;
"
>
<div style="color: #edb247; font-weight: 700">{{ listItem.rank }}</div>
</div>
<!-- 头像 -->
<img
:src="listItem.userAvatar || ''"
alt=""
style="
display: block;
width: 3em;
aspect-ratio: 1/1;
border-radius: 50%;
object-fit: cover;
"
@error="handleAvatarImageError"
/>
<!-- 名称id -->
<div
style="
flex: 1;
min-width: 0;
padding: 0 2px;
display: flex;
flex-direction: column;
"
>
<div
style="
color: #fff;
font-weight: 860;
overflow: hidden;
white-space: nowrap;
text-overflow: ellipsis;
"
>
{{ listItem.userNickname }}
</div>
<div style="color: #fff; font-weight: 590">
{{ $t('user_id_prefix') }} {{ listItem.account }}
</div>
</div>
</div>
<!-- 贡献值 -->
<div
style="
width: auto;
min-width: 0;
align-self: stretch;
display: flex;
align-items: center;
"
>
<img
v-smart-img
src="/src/assets/icon/coin.png"
alt=""
style="display: block; width: 1.3em; aspect-ratio: 1/1"
/>
<div style="color: #edb247; font-weight: 700">
{{ listItem.quantity }}
</div>
</div>
</itemCenter>
<!-- 触发加载功能 -->
<div ref="RankingLoadmore" v-show="showRankingLoading && rankingShow"></div>
</div>
</div>
<!-- Rewards -->
<div
v-show="rewardShow"
style="display: flex; flex-direction: column; align-items: center; gap: 20px"
>
<!-- top模块 -->
<div
v-for="(topItem, topIndex) in rewardsList"
style="min-height: 158.66vw; position: relative; width: 100%"
>
<!-- top背景图 -->
<img
v-smart-img
:src="imageUrl('topBg')"
alt=""
style="width: 100%; display: block"
/>
<!-- 标题 -->
<div
style="
position: absolute;
top: 7%;
left: 25%;
right: 25%;
height: 8.5%;
display: flex;
justify-content: center;
align-items: center;
color: #fff;
font-weight: 860;
"
class="topTitle"
>
TOP{{ topItem.rule.sort }}
</div>
<!-- 主体内容 -->
<div
style="
position: absolute;
inset: 0;
padding: 30% 6% 13%;
display: grid;
grid-template-columns: repeat(2, 1fr);
"
>
<div
v-for="gift in topItem.propsGroup.activityRewardProps"
style="display: flex; flex-direction: column; align-items: center"
>
<itemCenter
:imgUrl="imageUrl('giftItemBg')"
style="min-height: 39.6vw; width: 90%; margin-bottom: 3px"
>
<!-- gift图 -->
<img
:src="gift.cover || ''"
alt=""
style="width: 50%; display: block"
@error="(e) => handleRewardImageError(e, gift.type)"
/>
</itemCenter>
<itemCenter :imgUrl="imageUrl('btBg')" style="min-height: 12.58vw; width: 75%">
<div style="font-weight: 700">
{{ showDetail(gift.type, gift.quantity) }}
</div>
</itemCenter>
</div>
</div>
</div>
</div>
<!-- Exchange -->
<div v-show="exchargeShow" style="display: flex; justify-content: center">
<div style="min-height: 153.66vw; position: relative; width: 100%">
<!-- 背景图 -->
<img
v-smart-img
:src="imageUrl('longBg')"
alt=""
style="width: 100%; display: block"
/>
<!-- 标题 -->
<div
style="
position: absolute;
top: 6%;
left: 25%;
right: 25%;
height: 11%;
display: flex;
justify-content: center;
align-items: center;
color: #fff;
font-weight: 860;
"
class="topTitle"
>
Props
</div>
<!-- 内容 -->
<div
style="
position: absolute;
top: 0;
bottom: 0;
left: 0;
right: 0;
padding: 30% 7% 13%;
display: flex;
flex-direction: column;
justify-content: space-around;
"
>
<!-- 碎片 -->
<div style="min-height: 12.89vw; display: flex; align-items: center">
<img
v-smart-img
:src="imageUrl('debris')"
alt=""
style="width: 15%; display: block; margin-right: 5px"
/>
<div style="font-weight: 590; font-size: 1.3em">
:{{ myFragment.quantity || 0 }}
</div>
</div>
<!-- 礼品 -->
<div style="display: grid; grid-template-columns: repeat(3, 1fr); gap: 10px">
<div
v-for="(goods, goodsIndex) in exchangeGoodsList"
:key="goodsIndex"
style="display: flex; flex-direction: column; align-items: center"
>
<itemCenter style="min-height: 26.44vw" :imgUrl="imageUrl('giftItemBg')">
<!-- gift图 -->
<img
:src="goods.propsGroup.activityRewardProps[0].cover"
alt=""
style="width: 68%; display: block; margin-bottom: 7%"
/>
</itemCenter>
<div class="needDebris">
Permanent/{{ JSON.parse(goods.rule.jsonData).need_fragments }} Star
</div>
<itemCenter
style="min-height: 7.55vw; width: 75%"
@click="selectGood(goods)"
:imgUrl="imageUrl('btBg')"
>
<div style="font-weight: 700" class="needDebris">Redeem</div>
</itemCenter>
</div>
</div>
</div>
</div>
</div>
</div>
<!-- 我的排名占位 -->
<div style="height: 25vw"></div>
<!-- 我的排名 -->
<itemCenter
style="
min-height: 23.66vw;
width: 100vw;
background-color: #311300;
margin-top: 10px;
position: fixed;
bottom: 0;
z-index: 999;
"
:imgUrl="imageUrl('myRankingBg')"
:contentStyle="`padding: 4vw;gap: 8px;`"
>
<!-- 基本信息 -->
<div
style="
flex: 1;
min-width: 0;
align-self: stretch;
display: flex;
justify-content: space-around;
align-items: center;
gap: 8px;
"
>
<!-- 排名 -->
<div
style="
width: auto;
min-width: 0;
align-self: stretch;
display: flex;
justify-content: center;
align-items: center;
"
>
<div style="color: #edb247; font-size: 1.1em; font-weight: 700">
{{ myRanking.rank || 999 }}
</div>
</div>
<!-- 头像 -->
<img
:src="myRanking.userAvatar || ''"
alt=""
style="
display: block;
width: 20%;
aspect-ratio: 1/1;
border-radius: 50%;
object-fit: cover;
"
@error="handleAvatarImageError"
/>
<!-- 名称 -->
<div
style="
flex: 1;
min-width: 0;
color: #fff;
font-size: 1.1em;
font-weight: 860;
padding: 0 4px;
overflow: hidden;
white-space: nowrap;
text-overflow: ellipsis;
"
>
{{ myRanking.userNickname || '' }}
</div>
</div>
<!-- 贡献值 -->
<div
style="
width: auto;
min-width: 0;
align-self: stretch;
display: flex;
align-items: center;
"
>
<img
v-smart-img
src="/src/assets/icon/coin.png"
alt=""
style="display: block; width: 1.5em; aspect-ratio: 1/1"
/>
<div style="color: #edb247; font-size: 1.1em; font-weight: 700">
{{ myRanking.quantity || 0 }}
</div>
</div>
</itemCenter>
<!-- 弹窗遮罩层 -->
<maskLayer :maskLayerShow="maskLayerShow" @click="closedPopup">
<!-- help弹窗 -->
<div v-show="helpInfoShow" style="margin: 20% 0" @click.stop>
<img v-smart-img :src="imageUrl(getImgName('helpInfo'))" alt="" style="width: 100%" />
</div>
<!-- history弹窗 -->
<div
v-show="historyShow"
style="min-height: 107.97vw; position: relative; margin: 20% 0"
@click.stop
>
<!-- 背景图 -->
<img
v-smart-img
:src="imageUrl('mediumBg')"
alt=""
style="width: 100%; display: block"
/>
<!-- 标题 -->
<div
style="
position: absolute;
top: 10%;
left: 25%;
right: 25%;
height: 13%;
display: flex;
justify-content: center;
align-items: center;
color: #fff;
font-weight: 860;
"
class="topTitle"
>
History
</div>
<!-- 内容 -->
<div
style="
position: absolute;
top: 0;
bottom: 0;
left: 0;
right: 0;
padding: 31% 7% 15%;
display: flex;
flex-direction: column;
justify-content: space-between;
align-items: center;
"
>
<!-- 头像 -->
<div style="min-height: 43.93vw; position: relative; width: 75%">
<!-- 头像框 -->
<img
v-smart-img
:src="imageUrl('topOneHistory')"
alt=""
style="width: 100%; display: block; position: relative; z-index: 1"
/>
<div style="position: absolute; top: 0; bottom: 0; left: 0; right: 0">
<div
style="
height: 100%;
display: flex;
justify-content: center;
align-items: center;
"
>
<img
:src="showTopOne.userAvatar || ''"
alt=""
style="
width: 50%;
display: block;
border-radius: 50%;
aspect-ratio: 1/1;
object-fit: cover;
"
@error="handleAvatarImageError"
/>
</div>
</div>
</div>
<!-- 名字 -->
<div style="min-height: 7.16vw; position: relative; width: 25%">
<!-- 背景图 -->
<img
v-smart-img
:src="imageUrl('nameBg')"
alt=""
style="width: 100%; display: block"
/>
<div
style="
position: absolute;
top: 0;
bottom: 0;
left: 0;
right: 0;
padding-bottom: 1%;
display: flex;
justify-content: center;
align-items: center;
"
>
<div
style="
width: 90%;
overflow: hidden;
white-space: nowrap;
text-overflow: ellipsis;
text-align: center;
"
class="history-name"
>
{{ showTopOne.userNickname }}
</div>
</div>
</div>
<!-- 历史时间 -->
<div
style="
width: 50%;
display: flex;
justify-content: space-between;
align-items: center;
"
>
<img
v-smart-img
:src="imageUrl('timeBack')"
alt=""
width="13%"
@click="changeHistoryIndex(1)"
/>
<div
style="
flex: 1;
text-align: center;
font-weight: 590;
background-image: -webkit-linear-gradient(
top,
rgba(243, 183, 0, 1),
rgba(255, 238, 198, 1)
);
background-clip: text;
-webkit-background-clip: text;
-webkit-text-fill-color: transparent;
"
class="history-time"
>
{{ showTopOne.group }}
</div>
<img
v-smart-img
:src="imageUrl('timeNext')"
alt=""
width="13%"
@click="changeHistoryIndex(-1)"
/>
</div>
</div>
</div>
<!-- gifts弹窗 -->
<itemCenter
v-show="giftsShow"
style="min-height: 76.66vw; margin: 20% 0"
@click.stop
:imgUrl="imageUrl('giftsBg')"
:contentStyle="`padding: 30% 5% 12%;display: flex;justify-content: space-between;`"
>
<!-- 礼物单元 -->
<div style="width: 30%" v-for="gift in gifts" :key="gift.id">
<!-- 礼物背景 -->
<itemCenter style="min-height: 27vw" :imgUrl="imageUrl('giftItemBg')">
<!-- 礼物 -->
<img
:src="gift.giftPhoto"
alt=""
style="width: 50%; aspect-ratio: 1/1; border-radius: 50%; object-fit: cover"
/>
</itemCenter>
<!-- 礼物价值 -->
<div style="display: flex; justify-content: center; align-items: center; gap: 2px">
<img
v-smart-img
src="/src/assets/icon/coin.png"
alt=""
style="display: block; width: 1.3em; aspect-ratio: 1/1; object-fit: cover"
/>
<div style="color: #fff; font-weight: 590">
{{ gift.giftCandy }}
</div>
</div>
</div>
</itemCenter>
<!-- 交易商品弹窗 -->
<div
v-if="exchargeGoodsShow"
style="min-height: 108vw; position: relative; margin: 20% 0"
@click.stop
>
<!-- 背景图 -->
<img
v-smart-img
:src="imageUrl('mediumBg')"
alt=""
style="width: 100%; display: block"
/>
<!-- 标题 -->
<div
style="
position: absolute;
top: 10%;
left: 25%;
right: 25%;
height: 13%;
display: flex;
justify-content: center;
align-items: center;
color: #fff;
font-weight: 860;
"
class="topTitle"
>
Exchange
</div>
<!-- 内容 -->
<div
style="
position: absolute;
top: 0;
bottom: 0;
left: 0;
right: 0;
padding: 30% 7% 15%;
display: flex;
flex-direction: column;
justify-content: space-between;
align-items: center;
"
>
<img
:src="selectedGood.propsGroup.activityRewardProps[0].cover"
alt=""
style="width: 50%; display: block"
/>
<div>Permanent/{{ JSON.parse(selectedGood.rule.jsonData).need_fragments }} Star</div>
<img
v-smart-img
:src="imageUrl('ConfirmBt')"
alt=""
style="width: 30%; display: block"
@click="exchangeSelectedGood(selectedGood.rule.id)"
/>
</div>
</div>
</maskLayer>
</div>
</div>
</div>
</template>
<script setup>
import { computed, onMounted, onUnmounted, ref } from 'vue'
import { viewUserInfo } from '@/utils/appBridge.js'
import { useLangStore } from '@/stores/lang'
import { connectToApp } from '@/utils/appConnector.js'
import { useThrottle } from '@/utils/useDebounce'
import { showWarning } from '@/utils/toast.js'
import { getPngUrl } from '@/config/imagePaths.js'
import { preloadImages } from '@/utils/imagePreloader.js'
import { handleAvatarImageError, handleRewardImageError } from '@/utils/imageHandler.js'
import { getMemberProfile } from '@/api/wallet'
import {
getWeekStarRanking,
getWeekRewards,
getWeekGifts,
getHistoryTopOne,
getMyWeekStarRanking,
getMyFragment,
getExchangeGoods,
exchangeGood,
} from '@/api/weeklyStar.js'
import BackgroundLayer from '@/components/BackgroundLayer.vue'
import itemCenter from '@/components/itemCenter.vue'
import maskLayer from '@/components/MaskLayer.vue'
import TopUser from './components/topUser.vue'
const langStore = useLangStore()
// 当前语言类型
const currentLangType = computed(() => {
return langStore.selectedLang?.type || 'en'
})
// 预加载状态
const isLoading = ref(true)
// 获取OSS图片URL的函数
const imageUrl = (filename) => getPngUrl('Ranking/WeeklyStar/', filename)
// 根据语言获取图片名
const getImgName = (filename) => {
return currentLangType.value === 'ar' ? `${filename}_ar` : filename
}
// 使用工具函数连接APP
const connectToAppHandler = async () => {
await connectToApp(() => {
// 连接成功回调
completePreloading() // 完成预加载
})
}
const userInfo = ref({})
const helpInfoShow = ref(false) //帮助模块
const historyShow = ref(false) //历史模块
const giftsShow = ref(false) //礼物模块
const exchargeGoodsShow = ref(false) //交易弹窗
// 展示遮罩层
const maskLayerShow = computed(() => {
return helpInfoShow.value || historyShow.value || giftsShow.value || exchargeGoodsShow.value
})
// 倒计时
const Days = ref(0)
const Hours = ref(0)
const Minutes = ref(0)
const Second = ref(0)
let timer = null
// 模块切换展示
const rankingShow = ref(true) //排行榜
const rewardShow = ref(false) //奖励
const exchargeShow = ref(false) //交易
const current = ref(1) //排行榜第几页
const RankingLoadmore = ref(null) //触底模块
const showRankingLoading = ref(true) //触底加载功能
const Ranking = ref([]) // 排行榜
const RankingLoadParams = computed(() => {
return {
type: 'THIS_WEEK',
current: current.value,
size: 10,
}
})
const myRanking = ref([]) // 我的排名
const rewardsList = ref([]) // 奖励列表
const gifts = ref([]) // 本周礼物
const historyRankingTop1 = ref([]) // 历史榜首
const historyIndex = ref(0)
const showTopOne = computed(() => {
return historyRankingTop1.value[historyIndex.value] || {}
})
const exchangeGoodsList = ref([])
const myFragment = ref({})
const selectedGood = ref({})
const changeHistoryIndex = (num) => {
let targetIndex = historyIndex.value + num
let length = historyRankingTop1.value.length
if (targetIndex >= 0 && targetIndex < length) {
historyIndex.value = targetIndex
}
}
// 处理排行榜小于3人时的情况
const RankingHasTop3 = computed(() => {
let RankingShow = [...Ranking.value]
if (Ranking.value.length < 3) {
let addNullUser = Array.from({ length: 3 - Ranking.value.length }, () => ({
userAvatar: '',
userNickname: '',
quantity: '',
}))
RankingShow.push(...addNullUser)
}
return RankingShow
})
// 展示用的榜单
const showRanking = computed(() => {
return Ranking.value.filter((_, index) => index >= 3)
// return []
})
// 选中要兑换的商品
const selectGood = (good) => {
exchargeGoodsShow.value = true
selectedGood.value = good
}
const exchangeSelectedGood = async (propsGroupId) => {
try {
let data = {
propsGroupId,
fragmentsId: '1968948330210508801',
}
const resExchangeGood = await exchangeGood(data)
if (resExchangeGood.status) {
getThisWeekExchangeGoods() //刷新我的碎片
}
} catch (error) {
console.log('error:', error)
// 信息提示id有误
showWarning(error.response.errorMsg)
}
}
// 关闭弹窗
const closedPopup = () => {
helpInfoShow.value = false
historyShow.value = false
giftsShow.value = false
exchargeGoodsShow.value = false
selectedGood.value = {} //选中商品置空
}
// 点击展示主体内容
const handleBt = (type) => {
rankingShow.value = type == 'Ranking' ? true : false
rewardShow.value = type == 'Rewards' ? true : false
exchargeShow.value = type == 'Exchange' ? true : false
}
// 获取对应的时间戳
const getNextMondayInBeijing = () => {
const now = new Date()
const targetDate = new Date(now.getTime()) // 创建一个基于当前时间的新日期对象
// 转换为北京时间来判断星期几
const utcTime = now.getTime() + now.getTimezoneOffset() * 60000
const beijingTime = new Date(utcTime + 8 * 3600000) // UTC+8
const beijingDay = beijingTime.getDay() // 0=周日, 1=周一, ..., 6=周六
const beijingHour = beijingTime.getHours()
// 转换为 0=周一, 1=周二, ..., 6=周日
const normalizedDay = beijingDay === 0 ? 6 : beijingDay - 1
let daysToAdd
if (normalizedDay === 0) {
// 今天是周一
if (beijingHour >= 5) {
daysToAdd = 7 // 超过5点计算到下周一的所需时间
} else {
daysToAdd = 0 // 今天
}
} else {
daysToAdd = 7 - normalizedDay // 到下周一的天数
}
// 正确地添加天数
targetDate.setDate(targetDate.getDate() + daysToAdd) // 添加天数到下周一的当前时间
targetDate.setHours(5, 0, 0, 0) // 将时间重新设置为早上5点作为目标时间戳
return targetDate.getTime()
}
const targetTime = ref(getNextMondayInBeijing()) // 闭包保存目标时间 (核心修正)
// 格式化时间
const formatTime = (value) => {
return value < 10 ? `0${value}` : value
}
// 倒计时
const getCountdown = () => {
const now = Date.now()
let diff = targetTime.value - now
if (diff <= 0) {
// 初始化数据
getUserInfo()
resetPage()
targetTime.value += 7 * 86400000 // +7天 (更新闭包变量)
diff = targetTime.value - now
}
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))
Second.value = formatTime(Math.floor(totalSeconds % 60))
}
// 重置数据
const resetPage = () => {
current.value = 1 //排行榜第几页
showRankingLoading.value = true //触底加载功能
Ranking.value = [] // 排行榜
historyIndex.value = 0 //历史榜首重置展示当前
updatePageData() // 刷新页面数据
}
// 格式化奖励的展示信息
const showDetail = (type, quantity) => {
let showamount = ['GOLD', 'DIAMOND', 'GIFT', 'FRAGMENTS']
if (showamount.includes(type)) {
return quantity
} else {
return `${quantity}Days`
}
}
//获取用户信息
const getUserInfo = async () => {
if (Object.keys(userInfo.value).length === 0) {
const resUserInfo = await getMemberProfile()
if (resUserInfo.status && resUserInfo.body) {
userInfo.value = resUserInfo.body
}
}
getMyThisWeekRanking() // 获取本周我的排名
getThisWeekMyFragment() // 获取本周我的碎片
}
// 获取本周排名
const getThisWeekRanking = async () => {
const resWeekStarRanking = await getWeekStarRanking(RankingLoadParams.value)
console.log('resWeekStarRanking:', resWeekStarRanking)
if (
resWeekStarRanking.status &&
resWeekStarRanking.body &&
resWeekStarRanking.body.rankUsers?.records
) {
let rankUsers = resWeekStarRanking.body.rankUsers
Ranking.value.push(...rankUsers.records)
if (current.value * rankUsers.size >= rankUsers.total) {
showRankingLoading.value = false
}
}
}
// 获取历史榜首
const getHistoryRankingTop1 = async () => {
const resHistoryTopOne = await getHistoryTopOne()
console.log('resHistoryTopOne:', resHistoryTopOne)
if (resHistoryTopOne.status && resHistoryTopOne.body) {
historyRankingTop1.value = resHistoryTopOne.body
}
}
// 获取本周我的排名
const getMyThisWeekRanking = async () => {
let data = {
userId: userInfo.value.id,
type: 'THIS_WEEK',
}
const resMyWeekStarRanking = await getMyWeekStarRanking(data)
console.log('resMyWeekStarRanking:', resMyWeekStarRanking)
if (resMyWeekStarRanking.status && resMyWeekStarRanking.body) {
myRanking.value = resMyWeekStarRanking.body
}
}
// 获取本周礼物
const getThisWeekGifts = async () => {
const resThisWeekGifts = await getWeekGifts('THIS_WEEK')
console.log('resThisWeekGifts:', resThisWeekGifts)
if (resThisWeekGifts.status && resThisWeekGifts.body) {
gifts.value = resThisWeekGifts.body
}
}
// 获取本周奖励
const getThisWeekRewards = async () => {
const resThisWeekRewards = await getWeekRewards()
console.log('resThisWeekRewards:', resThisWeekRewards)
if (resThisWeekRewards.status && resThisWeekRewards.body) {
rewardsList.value = resThisWeekRewards.body
}
}
// 获取本周可兑换商品
const getThisWeekExchangeGoods = async () => {
let data = {
sysOrigin: 'LIKEI',
activityType: 'STAR_EXCHANGE',
}
const resExchangeGoods = await getExchangeGoods(data)
console.log('resExchangeGoods:', resExchangeGoods)
if (resExchangeGoods.status && resExchangeGoods.body) {
exchangeGoodsList.value = resExchangeGoods.body
}
}
// 获取本周我拥有的碎片
const getThisWeekMyFragment = async () => {
let data = {
userId: userInfo.value.id,
fragmentsIds: ['1968948330210508801'],
}
const resMyFragment = await getMyFragment(data)
console.log('resMyFragment:', resMyFragment)
if (resMyFragment.status && resMyFragment.body) {
myFragment.value =
resMyFragment.body.find((item) => item.fragmentsId == '1968948330210508801') || {}
}
}
// 刷新页面数据
const updatePageData = async () => {
await Promise.all([
getThisWeekRanking(), // 获取本周排名
getHistoryRankingTop1(), // 获取历史top1
getThisWeekRewards(), // 获取本周奖励
getThisWeekGifts(), // 获取本周礼物
getThisWeekExchangeGoods(), // 获取本周可兑换商品
])
}
// 预加载关键图片
const preloadCriticalImages = async () => {
const criticalImages = [
imageUrl('bg'),
imageUrl('history'),
imageUrl('gifts'),
imageUrl('dayBg'),
imageUrl('hourAndMinBg'),
imageUrl('secBg'),
imageUrl('btActive'),
imageUrl('btNoActive'),
imageUrl('RankingMain'),
imageUrl('RankingItem'),
imageUrl('RankingBottomBorder'),
imageUrl('topOne'),
imageUrl('topTwo'),
imageUrl('topThree'),
imageUrl('itemUser'),
imageUrl('topBg'),
imageUrl('giftItemBg'),
imageUrl('btBg'),
imageUrl('longBg'),
imageUrl('debris'),
imageUrl('myRankingBg'),
imageUrl(getImgName('helpInfo')),
imageUrl('mediumBg'),
imageUrl('topOneHistory'),
imageUrl('nameBg'),
imageUrl('giftsBg'),
imageUrl('ConfirmBt'),
imageUrl('timeBack'),
imageUrl('timeNext'),
]
await preloadImages(criticalImages)
}
const debouceGetThisWeekRanking = useThrottle(() => {
getThisWeekRanking()
}, 1000)
// IntersectionObserver配置
const observer = new IntersectionObserver((entries) => {
entries.forEach((entry) => {
if (entry.intersectionRatio > 0 && Ranking.value.length != 0) {
console.log('触发RankingLoadmore加载')
current.value++
debouceGetThisWeekRanking()
}
})
})
// 完成预加载
const completePreloading = async () => {
try {
// 执行所有初始化操作
await Promise.all([getUserInfo(), updatePageData(), preloadCriticalImages()])
} catch (error) {
console.error('预加载过程中发生错误:', error)
} finally {
// 无论成功或失败都结束加载状态
isLoading.value = false
}
}
// 组件挂载时检测环境
onMounted(() => {
connectToAppHandler()
getCountdown()
timer = setInterval(getCountdown, 1000) //每秒更新
// 监听加载模块
if (RankingLoadmore.value) {
observer.observe(RankingLoadmore.value)
}
})
onUnmounted(() => {
clearInterval(timer) //摧毁计时器
})
</script>
<style lang="scss" scoped>
* {
color: #fff;
}
.fullPage {
width: 100vw;
min-height: 100vh;
background-color: #311300;
position: relative;
direction: ltr;
}
.timeText {
color: #fff;
font-size: 1.3em;
font-weight: 860;
}
.timeGap {
display: flex;
justify-content: center;
align-items: center;
}
.btTitle {
color: #fff;
font-weight: 700;
}
.topTitle {
font-size: 1.5em;
}
.needDebris {
font-size: 0.6em;
}
.history-name {
font-size: 0.8em;
}
.history-time {
font-size: 0.8em;
}
/* 添加加载动画样式 */
.loading-container {
width: 100vw;
height: 100vh;
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
color: #666;
overflow: hidden;
position: relative;
}
.loading-spinner {
width: 32px;
height: 32px;
border: 3px solid #f3f3f3;
border-top: 3px solid #f59e0b;
border-radius: 50%;
animation: spin 1s linear infinite;
margin-bottom: 12px;
}
@keyframes spin {
0% {
transform: rotate(0deg);
}
100% {
transform: rotate(360deg);
}
}
@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>