aslan-h5/src/views/Ranking/Ranking.vue
2025-09-29 20:56:10 +08:00

517 lines
14 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.

<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>