aslan-h5/src/views/Ranking/Ranking.vue

562 lines
16 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="changeType('Wealth')"
style=""
:style="{ color: rankingType == 'Wealth' ? '#FFB760' : '#fff' }"
>
Wealth
</div>
<div
class="tab"
@click="changeType('Charm')"
style=""
:style="{ color: rankingType == 'Charm' ? '#60FF83' : '#fff' }"
>
Charm
</div>
<div
class="tab"
@click="changeType('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="changeDateType('Hourly')"
>
Hourly
</div>
<div
class="tag"
:class="{ tagActive: selectedTimeTab == 'Daily' }"
@click="changeDateType('Daily')"
>
Daily
</div>
<div
class="tag"
:class="{ tagActive: selectedTimeTab == 'Weekly' }"
@click="changeDateType('Weekly')"
>
Weekly
</div>
<div
class="tag"
:class="{ tagActive: selectedTimeTab == 'Monthly' }"
@click="changeDateType('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">{{ myRanking.rank || 0 }}</div>
<img
:src="myRanking.avatar || ''"
alt=""
width="20%"
style="display: block; 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"
>
{{ myRanking.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;
"
>
{{ myRanking.quantityFormat || 0 }}
</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, getMyRanking } from '@/api/ranking'
import { useRoute } from 'vue-router'
const route = useRoute()
//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(route.query.first || 'Wealth') //榜单类型
const selectedTimeTab = ref('Hourly') //时间标签
// 接口参数
const params_type = ref('GIFTS_SEND') //排行榜类型
const params_dateType = ref('HOUR') //时间类型
const totalRanking = ref({})
const showRanking = ref([])
const myRanking = 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
}
}
// 改变排行榜类型
const changeType = (type) => {
if (rankingType.value != type) {
console.log('改变rankingType')
rankingType.value = type
selectedTimeTab.value = 'Hourly'
params_dateType.value = 'HOUR'
if (type == 'Wealth') {
params_type.value = 'GIFTS_SEND'
} else if (type == 'Charm') {
params_type.value = 'GIFTS_RECEIVED'
} else if (type == 'Room') {
params_type.value = 'ROOM_GIFTS'
}
totalRanking.value = {}
showRanking.value = []
myRanking.value = {}
getRanking() // 获取榜单
getMyRankingInfo() //获取我的排名
}
}
// 改变时间类型
const changeDateType = (dateType) => {
if (selectedTimeTab.value != dateType) {
console.log('改变rankingType')
selectedTimeTab.value = dateType
if (dateType == 'Hourly') {
params_dateType.value = 'HOUR'
} else if (dateType == 'Daily') {
params_dateType.value = 'DAY'
} else if (dateType == 'Weekly') {
params_dateType.value = 'WEEK'
} else if (dateType == 'Monthly') {
params_dateType.value = 'MONTH'
}
showRanking.value = []
myRanking.value = {}
updateTimeRanking() //刷新展示榜单
getMyRankingInfo() //获取我的排名
}
}
// 前三名榜单
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 border = ref('') //时间标签样式
const background = ref('') //时间标签样式
const rankItemBackground = ref('') //排名项背景
const rankItemBorder = ref('') //排名项边框
const topBorderBg = ref('') //top1头像框
const top2BorderBg = ref('') //top2头像框
const top3BorderBg = ref('') //top3头像框
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()
}
}
//获取我的排名
const getMyRankingInfo = async () => {
console.log('获取我的排名')
const resMyRanking = await getMyRanking(params_type.value, params_dateType.value)
if (resMyRanking.status && resMyRanking.body) {
myRanking.value = resMyRanking.body
} else {
myRanking.value = {}
}
}
onMounted(() => {
isInAppEnvironment.value = isInApp()
observer.observe(sentinel.value)
getRanking() // 获取榜单
getMyRankingInfo() //获取我的排名
})
</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>