517 lines
14 KiB
Vue
517 lines
14 KiB
Vue
<template>
|
||
<div class="fullPage" :style="{ backgroundColor, backgroundImage }">
|
||
<!-- 哨兵元素 -->
|
||
<div ref="sentinel"></div>
|
||
|
||
<!-- 粘性布局贴顶部 -->
|
||
<div
|
||
ref="stickyHeader"
|
||
style="
|
||
position: sticky;
|
||
top: 0;
|
||
z-index: 999;
|
||
padding-bottom: 10px;
|
||
border-radius: 0 0 12px 12px;
|
||
transition: all 0.3s;
|
||
"
|
||
>
|
||
<!-- 状态栏占位区域(仅在APP中显示) -->
|
||
<div v-if="isInAppEnvironment" style="height: 30px"></div>
|
||
|
||
<!-- 顶部导航栏 -->
|
||
<div style="padding: 4px 8%">
|
||
<div style="display: flex; justify-content: space-around; position: relative">
|
||
<div
|
||
class="tab"
|
||
@click="rankingType = 'Wealth'"
|
||
style=""
|
||
:style="{ color: rankingType == 'Wealth' ? '#FFB760' : '#fff' }"
|
||
>
|
||
Wealth
|
||
</div>
|
||
<div
|
||
class="tab"
|
||
@click="rankingType = 'Charm'"
|
||
style=""
|
||
:style="{ color: rankingType == 'Charm' ? '#60FF83' : '#fff' }"
|
||
>
|
||
Charm
|
||
</div>
|
||
<div
|
||
class="tab"
|
||
@click="rankingType = 'Room'"
|
||
style=""
|
||
:style="{ color: rankingType == 'Room' ? '#9B60FF' : '#fff' }"
|
||
>
|
||
Room
|
||
</div>
|
||
|
||
<!-- 推出键 -->
|
||
<img
|
||
src="../../assets/icon/arrowBack.png"
|
||
alt=""
|
||
style="position: absolute; left: 0; top: 50%; transform: translateY(-50%)"
|
||
width="6%"
|
||
@click="closePage"
|
||
/>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- 排行榜时间选择 -->
|
||
<div
|
||
style="
|
||
margin: 10px 4% 0;
|
||
padding: 4px 16px;
|
||
border-radius: 32px;
|
||
backdrop-filter: blur(32px);
|
||
|
||
display: flex;
|
||
justify-content: space-between;
|
||
"
|
||
:style="{ border, background }"
|
||
>
|
||
<div
|
||
class="tag"
|
||
:class="{ tagActive: selectedTimeTab == 'Hourly' }"
|
||
@click="selectedTimeTab = 'Hourly'"
|
||
>
|
||
Hourly
|
||
</div>
|
||
<div
|
||
class="tag"
|
||
:class="{ tagActive: selectedTimeTab == 'Daily' }"
|
||
@click="selectedTimeTab = 'Daily'"
|
||
>
|
||
Daily
|
||
</div>
|
||
<div
|
||
class="tag"
|
||
:class="{ tagActive: selectedTimeTab == 'Weekly' }"
|
||
@click="selectedTimeTab = 'Weekly'"
|
||
>
|
||
Weekly
|
||
</div>
|
||
<div
|
||
class="tag"
|
||
:class="{ tagActive: selectedTimeTab == 'Monthly' }"
|
||
@click="selectedTimeTab = 'Monthly'"
|
||
>
|
||
Monthly
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- 排行榜模块 -->
|
||
<div style="padding: 10px 0">
|
||
<!-- 前3名 -->
|
||
<div style="margin: 3% 10px 0">
|
||
<!-- 第一 -->
|
||
<div style="display: flex; justify-content: center">
|
||
<TopUser
|
||
:isRoom="rankingType === 'Room'"
|
||
:isTopOne="true"
|
||
:BorderImgUrl="topBorderBg"
|
||
:avatarUrl="RankingTop3[0].avatar || ''"
|
||
:name="RankingTop3[0].nickname || ''"
|
||
:distributionValue="RankingTop3[0].quantityFormat || ''"
|
||
style="width: 40%"
|
||
/>
|
||
</div>
|
||
<!-- 第二三 -->
|
||
<div style="display: flex; justify-content: space-between; margin-top: -20%">
|
||
<TopUser
|
||
:isRoom="rankingType === 'Room'"
|
||
:BorderImgUrl="top2BorderBg"
|
||
:avatarUrl="RankingTop3[1].avatar || ''"
|
||
:name="RankingTop3[1].nickname || ''"
|
||
:distributionValue="RankingTop3[1].quantityFormat || ''"
|
||
style="width: 30%"
|
||
/>
|
||
<TopUser
|
||
:isRoom="rankingType === 'Room'"
|
||
:BorderImgUrl="top3BorderBg"
|
||
:avatarUrl="RankingTop3[2].avatar || ''"
|
||
:name="RankingTop3[2].nickname || ''"
|
||
:distributionValue="RankingTop3[2].quantityFormat || ''"
|
||
style="width: 30%"
|
||
/>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- 第四名开始 -->
|
||
<div style="margin: 3% 10px 0">
|
||
<div
|
||
v-for="(userInfo, index) in RankingFromTop4"
|
||
:key="userInfo.id"
|
||
style="
|
||
border-radius: 12px;
|
||
backdrop-filter: blur(32px);
|
||
margin: 5px 0;
|
||
padding: 12px;
|
||
display: flex;
|
||
justify-content: space-between;
|
||
align-items: center;
|
||
"
|
||
:style="{ background: rankItemBackground, border: '1px ' + rankItemBorder }"
|
||
>
|
||
<div style="display: flex; align-items: center; width: 70%">
|
||
<div style="font-weight: 700">{{ index + 4 }}</div>
|
||
<img
|
||
:src="userInfo.avatar"
|
||
alt=""
|
||
width="20%"
|
||
style="
|
||
display: block;
|
||
border-radius: 50%;
|
||
object-fit: cover;
|
||
aspect-ratio: 1/1;
|
||
margin: 0 4px;
|
||
"
|
||
:style="{ borderRadius: rankingType === 'Room' ? '0' : '50%' }"
|
||
@error="defaultAvatarUrl"
|
||
/>
|
||
<div
|
||
style="
|
||
font-weight: 700;
|
||
overflow: hidden;
|
||
white-space: nowrap;
|
||
text-overflow: ellipsis;
|
||
"
|
||
>
|
||
{{ userInfo.nickname }}
|
||
</div>
|
||
</div>
|
||
<div style="display: flex; align-items: center; width: 30%">
|
||
<img
|
||
src="/src/assets/icon/coin.png"
|
||
alt=""
|
||
width="20%"
|
||
style="display: block; aspect-ratio: 1/1"
|
||
/>
|
||
<div
|
||
style="
|
||
font-weight: 500;
|
||
color: rgba(248, 182, 45, 1);
|
||
overflow: hidden;
|
||
white-space: nowrap;
|
||
text-overflow: ellipsis;
|
||
"
|
||
>
|
||
{{ userInfo.quantityFormat }}
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- 我的排名 -->
|
||
<div
|
||
style="position: sticky; bottom: 0; z-index: 999; padding: 0 1px; width: 100%"
|
||
:style="{ backgroundColor }"
|
||
>
|
||
<div
|
||
style="
|
||
border-radius: 12px 12px 0 0;
|
||
border-top: 2px solid #ffa166;
|
||
border-right: 1px solid #ffa166;
|
||
border-left: 1px solid #ffa166;
|
||
backdrop-filter: blur(32px);
|
||
padding: 12px;
|
||
display: flex;
|
||
justify-content: space-between;
|
||
align-items: center;
|
||
"
|
||
:style="{
|
||
background: rankItemBackground,
|
||
borderTop: '2px ' + rankItemBorder,
|
||
borderLeft: '1px ' + rankItemBorder,
|
||
borderRight: '1px ' + rankItemBorder,
|
||
}"
|
||
>
|
||
<div style="display: flex; align-items: center; width: 70%">
|
||
<div style="font-weight: 700">999</div>
|
||
<img
|
||
src=""
|
||
alt=""
|
||
width="20%"
|
||
style="display: block; aspect-ratio: 1/1; margin: 0 4px"
|
||
@error="defaultAvatarUrl"
|
||
/>
|
||
<div
|
||
style="font-weight: 700; overflow: hidden; white-space: nowrap; text-overflow: ellipsis"
|
||
>
|
||
aaaaaaaaaaaa
|
||
</div>
|
||
</div>
|
||
<div style="display: flex; align-items: center; width: 30%">
|
||
<img
|
||
src="/src/assets/icon/coin.png"
|
||
alt=""
|
||
width="20%"
|
||
style="display: block; aspect-ratio: 1/1"
|
||
/>
|
||
<div
|
||
style="
|
||
font-weight: 500;
|
||
color: rgba(248, 182, 45, 1);
|
||
overflow: hidden;
|
||
white-space: nowrap;
|
||
text-overflow: ellipsis;
|
||
"
|
||
>
|
||
123456789
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</template>
|
||
|
||
<script setup>
|
||
import { isInApp, closePage } from '@/utils/appBridge.js'
|
||
import { computed, onMounted, ref, watch, watchEffect } from 'vue'
|
||
import TopUser from '@/components/Ranking/topUser.vue'
|
||
import { getWealthRanking, getCharmRanking, getRoomRanking } from '@/api/ranking'
|
||
|
||
//vite动态批量导入图片
|
||
const imageModules = import.meta.glob('@/assets/images/Ranking/*.{png,jpg,svg}', { eager: true })
|
||
// 生成文件名映射对象(自动移除路径和扩展名)
|
||
const images = Object.fromEntries(
|
||
Object.entries(imageModules).map(([path, module]) => {
|
||
const fileName = path.split('/').pop().split('.')[0]
|
||
return [fileName, module.default]
|
||
})
|
||
)
|
||
|
||
const isInAppEnvironment = ref(false) // 检测是否在APP环境中
|
||
const rankingType = ref('Wealth') //榜单类型
|
||
const selectedTimeTab = ref('Hourly') //时间标签
|
||
|
||
const totalRanking = ref({})
|
||
const showRanking = ref([])
|
||
|
||
const updateTimeRanking = () => {
|
||
console.log('updateTimeRanking()')
|
||
|
||
if (selectedTimeTab.value == 'Hourly' && totalRanking.value.hourly) {
|
||
showRanking.value = totalRanking.value.hourly
|
||
console.log('showRanking.value:', showRanking.value)
|
||
} else if (selectedTimeTab.value == 'Daily' && totalRanking.value.daily) {
|
||
showRanking.value = totalRanking.value.daily
|
||
} else if (selectedTimeTab.value == 'Weekly' && totalRanking.value.weekly) {
|
||
showRanking.value = totalRanking.value.weekly
|
||
} else if (selectedTimeTab.value == 'Monthly' && totalRanking.value.monthly) {
|
||
showRanking.value = totalRanking.value.monthly
|
||
}
|
||
}
|
||
|
||
watch(
|
||
() => rankingType.value,
|
||
() => {
|
||
console.log('监听到rankingType.value变化')
|
||
totalRanking.value = {}
|
||
showRanking.value = []
|
||
// 获取榜单
|
||
getRanking()
|
||
}
|
||
)
|
||
|
||
watch(
|
||
() => selectedTimeTab.value,
|
||
() => {
|
||
console.log('监听到selectedTimeTab.value变化')
|
||
showRanking.value = []
|
||
updateTimeRanking() //刷新展示榜单
|
||
}
|
||
)
|
||
|
||
// 前三名榜单
|
||
const RankingTop3 = computed(() => {
|
||
console.log('刷新RankingTop3')
|
||
|
||
let RankingShow = [...showRanking.value]
|
||
if (showRanking.value.length < 3) {
|
||
let addNullUser = Array.from({ length: 3 - showRanking.value.length }, () => ({
|
||
avatar: '',
|
||
nickname: '',
|
||
quantityFormat: '',
|
||
}))
|
||
RankingShow.push(...addNullUser)
|
||
}
|
||
return RankingShow
|
||
})
|
||
|
||
// 从第四名开始榜单
|
||
const RankingFromTop4 = computed(() => {
|
||
return showRanking.value.filter((_, index) => index >= 3)
|
||
})
|
||
|
||
// 动态样式
|
||
const backgroundColor = ref('') //页面
|
||
const backgroundImage = ref('') //页面
|
||
const topBorderBg = ref('') //top1头像框
|
||
const top2BorderBg = ref('') //top2头像框
|
||
const top3BorderBg = ref('') //top3头像框
|
||
const border = ref('') //时间标签
|
||
const background = ref('') //时间标签
|
||
const rankItemBackground = ref('') //排名项背景
|
||
const rankItemBorder = ref('') //排名项边框
|
||
const sentinel = ref(null)
|
||
const stickyHeader = ref(null)
|
||
|
||
watchEffect(() => {
|
||
if (rankingType.value == 'Wealth') {
|
||
backgroundColor.value = '#230F0A'
|
||
backgroundImage.value = `url(${images.bgWealth})`
|
||
topBorderBg.value = images['top1-wealth']
|
||
top2BorderBg.value = images['top2']
|
||
top3BorderBg.value = images['top3']
|
||
border.value = '1px solid rgba(221, 114, 0, 0.39)'
|
||
background.value = 'rgba(252, 194, 147, 0.20)'
|
||
rankItemBackground.value =
|
||
'linear-gradient(97deg, rgba(50, 34, 21, 0.60) 12.03%, rgba(99, 52, 33, 0.60) 100.37%)'
|
||
rankItemBorder.value = 'solid #FFA166'
|
||
}
|
||
if (rankingType.value == 'Charm') {
|
||
backgroundColor.value = '#112C1E'
|
||
backgroundImage.value = `url(${images.bgCharm})`
|
||
topBorderBg.value = images['top1-charm']
|
||
top2BorderBg.value = images['top2']
|
||
top3BorderBg.value = images['top3']
|
||
border.value = '1px solid rgba(0, 221, 74, 0.39)'
|
||
background.value = 'rgba(147, 252, 166, 0.20)'
|
||
rankItemBackground.value =
|
||
'linear-gradient(97deg, rgba(21, 50, 22, 0.60) 12.03%, rgba(33, 99, 40, 0.60) 100.37%)'
|
||
rankItemBorder.value = 'solid #66FF82'
|
||
}
|
||
if (rankingType.value == 'Room') {
|
||
backgroundColor.value = '#211031'
|
||
backgroundImage.value = `url(${images.bgRoom})`
|
||
topBorderBg.value = images['top1-room']
|
||
top2BorderBg.value = images['top2-room']
|
||
top3BorderBg.value = images['top3-room']
|
||
border.value = '1px solid rgba(155, 0, 221, 0.39)'
|
||
background.value = 'rgba(219, 147, 252, 0.20)'
|
||
rankItemBackground.value =
|
||
'linear-gradient(97deg, rgba(33, 21, 50, 0.60) 12.03%, rgba(60, 33, 99, 0.60) 100.37%)'
|
||
rankItemBorder.value = 'solid #A366FF'
|
||
}
|
||
})
|
||
|
||
const defaultAvatarUrl = (e) => {
|
||
console.log('头像资源出错')
|
||
e.target.onerror = null //防止循环
|
||
e.target.src = new URL('/src/assets/images/WeeklyStar/defaultAvatar.png', import.meta.url).href
|
||
}
|
||
|
||
const observer = new IntersectionObserver((entries) => {
|
||
entries.forEach((entry) => {
|
||
// 哨兵元素离开视口
|
||
if (!entry.isIntersecting) {
|
||
console.log('哨兵元素离开视口')
|
||
if (stickyHeader.value) {
|
||
stickyHeader.value.classList.add('stuck')
|
||
console.log('添加stuck类')
|
||
} else {
|
||
console.log('元素未找到!')
|
||
}
|
||
} else {
|
||
stickyHeader.value.classList.remove('stuck')
|
||
}
|
||
})
|
||
})
|
||
|
||
const getRanking = async () => {
|
||
console.log('获取榜单')
|
||
|
||
let resRanking = {}
|
||
if (rankingType.value == 'Wealth') {
|
||
resRanking = await getWealthRanking()
|
||
} else if (rankingType.value == 'Charm') {
|
||
resRanking = await getCharmRanking()
|
||
} else if (rankingType.value == 'Room') {
|
||
resRanking = await getRoomRanking()
|
||
}
|
||
if (resRanking.status && resRanking.body) {
|
||
totalRanking.value = resRanking.body
|
||
updateTimeRanking()
|
||
}
|
||
}
|
||
|
||
onMounted(() => {
|
||
isInAppEnvironment.value = isInApp()
|
||
|
||
observer.observe(sentinel.value)
|
||
|
||
// 获取榜单
|
||
getRanking()
|
||
})
|
||
</script>
|
||
|
||
<style lang="scss" scoped>
|
||
* {
|
||
color: #fff;
|
||
font-family: 'SF Pro';
|
||
}
|
||
|
||
.fullPage {
|
||
width: 100vw;
|
||
min-height: 100vh;
|
||
background-size: 100% auto;
|
||
background-repeat: no-repeat;
|
||
position: relative;
|
||
}
|
||
|
||
.tab {
|
||
font-weight: 700;
|
||
font-size: 1em;
|
||
}
|
||
|
||
.tag {
|
||
font-weight: 510;
|
||
padding: 4px 4%;
|
||
}
|
||
|
||
.tagActive {
|
||
color: black;
|
||
border-radius: 32px;
|
||
border: 1px solid #f5f54c;
|
||
box-sizing: border-box;
|
||
background: #fcfc93;
|
||
box-shadow: 0 0 8.6px 3px #fc8b02 inset;
|
||
|
||
display: flex;
|
||
justify-content: center;
|
||
align-items: center;
|
||
}
|
||
|
||
.stuck {
|
||
background-color: rgba(255, 255, 255, 0.1);
|
||
backdrop-filter: blur(32px);
|
||
}
|
||
|
||
@media screen and (max-width: 360px) {
|
||
* {
|
||
font-size: 10px;
|
||
}
|
||
}
|
||
|
||
@media screen and (min-width: 360px) {
|
||
* {
|
||
font-size: 16px;
|
||
}
|
||
}
|
||
|
||
@media screen and (min-width: 768px) {
|
||
* {
|
||
font-size: 24px;
|
||
}
|
||
}
|
||
|
||
@media screen and (min-width: 1024px) {
|
||
* {
|
||
font-size: 32px;
|
||
}
|
||
}
|
||
</style>
|