feat(新页面): 排行榜
BIN
src/assets/icon/arrowBack.png
Normal file
|
After Width: | Height: | Size: 348 B |
BIN
src/assets/images/Ranking/bgCharm.png
Normal file
|
After Width: | Height: | Size: 4.1 MiB |
BIN
src/assets/images/Ranking/bgRoom.png
Normal file
|
After Width: | Height: | Size: 4.6 MiB |
BIN
src/assets/images/Ranking/bgWealth.png
Normal file
|
After Width: | Height: | Size: 4.4 MiB |
BIN
src/assets/images/Ranking/coinNumBg.png
Normal file
|
After Width: | Height: | Size: 20 KiB |
BIN
src/assets/images/Ranking/top1-charm.png
Normal file
|
After Width: | Height: | Size: 409 KiB |
BIN
src/assets/images/Ranking/top1-room.png
Normal file
|
After Width: | Height: | Size: 452 KiB |
BIN
src/assets/images/Ranking/top1-wealth.png
Normal file
|
After Width: | Height: | Size: 420 KiB |
BIN
src/assets/images/Ranking/top2-room.png
Normal file
|
After Width: | Height: | Size: 191 KiB |
BIN
src/assets/images/Ranking/top2.png
Normal file
|
After Width: | Height: | Size: 141 KiB |
BIN
src/assets/images/Ranking/top3-room.png
Normal file
|
After Width: | Height: | Size: 249 KiB |
BIN
src/assets/images/Ranking/top3.png
Normal file
|
After Width: | Height: | Size: 250 KiB |
164
src/components/Ranking/topUser.vue
Normal file
@ -0,0 +1,164 @@
|
||||
<template>
|
||||
<div style="width: 100%">
|
||||
<div style="position: relative">
|
||||
<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: isTopOne ? '55%' : '60%' }"
|
||||
>
|
||||
<div style="width: 75%">
|
||||
<img
|
||||
:src="avatarUrl || ''"
|
||||
alt=""
|
||||
width="100%"
|
||||
style="aspect-ratio: 1/1; object-fit: cover"
|
||||
:style="{ borderRadius: isRoom ? '0' : '50%' }"
|
||||
@error="defaultAvatarUrl"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<!-- 用户名和贡献值 -->
|
||||
<div
|
||||
style="
|
||||
position: absolute;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
height: 40%;
|
||||
z-index: 3;
|
||||
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
"
|
||||
>
|
||||
<div
|
||||
style="
|
||||
height: 28%;
|
||||
width: 60%;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
"
|
||||
>
|
||||
<div style="font-weight: 860" class="showText" :class="isTopOne ? 'top1Text' : 'text'">
|
||||
{{ name }}
|
||||
</div>
|
||||
</div>
|
||||
<div style="width: 60%; position: relative">
|
||||
<img
|
||||
src="/src/assets/images/Ranking/coinNumBg.png"
|
||||
alt=""
|
||||
width="100%"
|
||||
style="display: block"
|
||||
/>
|
||||
<div
|
||||
style="position: absolute; inset: 0; display: flex; align-items: center; padding: 3% 5%"
|
||||
>
|
||||
<img src="/src/assets/icon/coin.png" alt="" width="25%" style="display: block" />
|
||||
<div
|
||||
style="
|
||||
font-weight: 700;
|
||||
background: linear-gradient(180deg, #ffe656 0%, #fff 100%);
|
||||
background-clip: text;
|
||||
-webkit-background-clip: text;
|
||||
-webkit-text-fill-color: transparent;
|
||||
"
|
||||
class="showText"
|
||||
:class="isTopOne ? 'top1Text' : 'text'"
|
||||
>
|
||||
{{ distributionValue }}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { watch } from 'vue'
|
||||
|
||||
const props = defineProps({
|
||||
isRoom: {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
},
|
||||
|
||||
isTopOne: {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
},
|
||||
|
||||
avatarUrl: {
|
||||
type: String,
|
||||
default: '/src/assets/images/WeeklyStar/defaultAvatar.png',
|
||||
},
|
||||
|
||||
BorderImgUrl: {
|
||||
type: String,
|
||||
required: true,
|
||||
},
|
||||
|
||||
name: {
|
||||
type: String,
|
||||
default: '',
|
||||
},
|
||||
|
||||
distributionValue: {
|
||||
type: String,
|
||||
default: '',
|
||||
},
|
||||
})
|
||||
|
||||
watch(
|
||||
() => props.isRoom,
|
||||
(newVal, oldVal) => {
|
||||
console.log('isRoom 变化 =>', '新值:', newVal, '旧值:', oldVal)
|
||||
}
|
||||
)
|
||||
|
||||
const defaultAvatarUrl = (e) => {
|
||||
console.log('头像资源出错')
|
||||
e.target.onerror = null //防止循环
|
||||
e.target.src = new URL('/src/assets/images/WeeklyStar/defaultAvatar.png', import.meta.url).href
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
* {
|
||||
font-family: 'SF Pro';
|
||||
color: black;
|
||||
}
|
||||
|
||||
.showText {
|
||||
color: #fff;
|
||||
text-align: center;
|
||||
overflow: hidden;
|
||||
white-space: nowrap;
|
||||
text-overflow: ellipsis;
|
||||
}
|
||||
|
||||
.top1Text {
|
||||
font-size: 1em;
|
||||
}
|
||||
|
||||
.text {
|
||||
font-size: 0.9em;
|
||||
}
|
||||
</style>
|
||||
@ -209,6 +209,12 @@ const router = createRouter({
|
||||
component: () => import('../views/WeeklyStar/WeeklyStar.vue'),
|
||||
meta: { requiresAuth: true },
|
||||
},
|
||||
{
|
||||
path: '/ranking',
|
||||
name: 'ranking',
|
||||
component: () => import('../views/Ranking/Ranking.vue'),
|
||||
meta: { requiresAuth: true },
|
||||
},
|
||||
],
|
||||
})
|
||||
|
||||
|
||||
@ -112,6 +112,7 @@ export const ROLE_PERMISSIONS = {
|
||||
'/recharge-freight-agent',
|
||||
'/top-list', //排行榜
|
||||
'/weekly-star', //每周明星
|
||||
'/ranking', //总排行榜
|
||||
],
|
||||
|
||||
// 加载页面
|
||||
|
||||
@ -19,7 +19,7 @@ import { appConnectionManager, isAppConnected } from './appConnectionManager.js'
|
||||
* APP连接检查器
|
||||
*/
|
||||
class AppConnectionChecker {
|
||||
constructor () {
|
||||
constructor() {
|
||||
this.isConnecting = false
|
||||
}
|
||||
|
||||
@ -27,7 +27,7 @@ class AppConnectionChecker {
|
||||
* 确保APP连接
|
||||
* @returns {Promise<boolean>} 连接是否成功
|
||||
*/
|
||||
async ensureConnection () {
|
||||
async ensureConnection() {
|
||||
console.debug('🔗 Checking APP connection...')
|
||||
|
||||
// 如果不在APP环境中,直接返回成功
|
||||
@ -66,7 +66,7 @@ class AppConnectionChecker {
|
||||
* 执行APP连接
|
||||
* @returns {Promise<boolean>}
|
||||
*/
|
||||
async _performConnection () {
|
||||
async _performConnection() {
|
||||
try {
|
||||
console.debug('📱 Starting new APP connection...')
|
||||
|
||||
@ -120,7 +120,7 @@ class AppConnectionChecker {
|
||||
* APP连接成功后获取用户信息
|
||||
* @private
|
||||
*/
|
||||
async _fetchUserInfoAfterConnection () {
|
||||
async _fetchUserInfoAfterConnection() {
|
||||
try {
|
||||
console.debug('👤 Fetching user info after connection...')
|
||||
|
||||
@ -143,7 +143,7 @@ class AppConnectionChecker {
|
||||
/**
|
||||
* 重置连接状态
|
||||
*/
|
||||
reset () {
|
||||
reset() {
|
||||
appConnectionManager.reset()
|
||||
}
|
||||
}
|
||||
@ -152,7 +152,7 @@ class AppConnectionChecker {
|
||||
* 身份检查器
|
||||
*/
|
||||
class IdentityChecker {
|
||||
constructor () {
|
||||
constructor() {
|
||||
this.isChecking = false
|
||||
this.checkPromise = null
|
||||
}
|
||||
@ -162,7 +162,7 @@ class IdentityChecker {
|
||||
* @param {string} userId - 用户ID
|
||||
* @returns {Promise<string>} 返回主要身份
|
||||
*/
|
||||
async checkUserIdentity (userId) {
|
||||
async checkUserIdentity(userId) {
|
||||
if (!userId) {
|
||||
console.warn('⚠️ No user ID provided')
|
||||
permissionManager.setUserIdentity(null)
|
||||
@ -192,7 +192,7 @@ class IdentityChecker {
|
||||
* @param {string} userId - 用户ID
|
||||
* @returns {Promise<string>} 主要身份
|
||||
*/
|
||||
async _performIdentityCheck (userId) {
|
||||
async _performIdentityCheck(userId) {
|
||||
try {
|
||||
console.debug('🔍 Checking user identity for:', userId)
|
||||
|
||||
@ -221,7 +221,7 @@ class IdentityChecker {
|
||||
/**
|
||||
* 重置检查状态
|
||||
*/
|
||||
reset () {
|
||||
reset() {
|
||||
this.isChecking = false
|
||||
this.checkPromise = null
|
||||
}
|
||||
@ -238,7 +238,7 @@ class PageRedirector {
|
||||
* @param {string} currentPath - 当前页面路径
|
||||
* @returns {Promise<boolean>} 是否进行了重定向
|
||||
*/
|
||||
async redirectToCorrectPage (router, targetPath, currentPath) {
|
||||
async redirectToCorrectPage(router, targetPath, currentPath) {
|
||||
const primaryRole = permissionManager.getPrimaryRole()
|
||||
|
||||
// 检查目标页面权限
|
||||
@ -277,7 +277,7 @@ class PageRedirector {
|
||||
* @param {Object} router - Vue Router 实例
|
||||
* @param {string} attemptedPage - 尝试访问的页面
|
||||
*/
|
||||
async handleUnauthorizedAccess (router, attemptedPage) {
|
||||
async handleUnauthorizedAccess(router, attemptedPage) {
|
||||
const primaryRole = permissionManager.getPrimaryRole()
|
||||
const defaultPage = permissionManager.getDefaultPage()
|
||||
|
||||
@ -296,7 +296,7 @@ class PageRedirector {
|
||||
* 路由守卫类
|
||||
*/
|
||||
class RouteGuard {
|
||||
constructor () {
|
||||
constructor() {
|
||||
this.appConnectionChecker = new AppConnectionChecker()
|
||||
this.identityChecker = new IdentityChecker()
|
||||
this.pageRedirector = new PageRedirector()
|
||||
@ -307,7 +307,7 @@ class RouteGuard {
|
||||
* 安装路由守卫
|
||||
* @param {Object} router - Vue Router 实例
|
||||
*/
|
||||
install (router) {
|
||||
install(router) {
|
||||
router.beforeEach(async (to, from, next) => {
|
||||
await this.handleRouteChange(to, from, next)
|
||||
})
|
||||
@ -322,7 +322,7 @@ class RouteGuard {
|
||||
* @param {Object} from - 来源路由
|
||||
* @param {Function} next - 路由继续函数
|
||||
*/
|
||||
async handleRouteChange (to, from, next) {
|
||||
async handleRouteChange(to, from, next) {
|
||||
console.group('🛡️ Route Guard Check')
|
||||
console.debug('📍 Route change:', from.path, '→', to.path)
|
||||
|
||||
@ -394,7 +394,7 @@ class RouteGuard {
|
||||
* @param {string} path - 页面路径
|
||||
* @returns {boolean}
|
||||
*/
|
||||
isPublicPage (path) {
|
||||
isPublicPage(path) {
|
||||
const publicPages = [
|
||||
PAGES.APPLY, // 申请页面 - 重要:申请页面不需要权限检查
|
||||
PAGES.NOT_APP, // 错误页面 - APP连接失败时的页面
|
||||
@ -406,6 +406,7 @@ class RouteGuard {
|
||||
'/pay-result',
|
||||
'/top-list', //排行榜
|
||||
'/weekly-star', //每周明星
|
||||
'/ranking', //总排行榜
|
||||
]
|
||||
return publicPages.includes(path)
|
||||
}
|
||||
@ -415,7 +416,7 @@ class RouteGuard {
|
||||
* @param {Object} router - Vue Router 实例
|
||||
* @param {string} userId - 用户ID
|
||||
*/
|
||||
async checkAndRedirect (router, userId) {
|
||||
async checkAndRedirect(router, userId) {
|
||||
try {
|
||||
console.debug('🔄 Manual identity check and redirect')
|
||||
|
||||
@ -443,7 +444,7 @@ class RouteGuard {
|
||||
/**
|
||||
* 重置守卫状态
|
||||
*/
|
||||
reset () {
|
||||
reset() {
|
||||
this.appConnectionChecker.reset()
|
||||
this.identityChecker.reset()
|
||||
permissionManager.reset()
|
||||
|
||||
426
src/views/Ranking/Ranking.vue
Normal file
@ -0,0 +1,426 @@
|
||||
<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>
|
||||
<!-- 前3名 -->
|
||||
<div style="margin: 3% 10px 0">
|
||||
<!-- 第一 -->
|
||||
<div style="display: flex; justify-content: center">
|
||||
<TopUser
|
||||
:isRoom="rankingType === 'Room'"
|
||||
:isTopOne="true"
|
||||
:BorderImgUrl="topBorderBg"
|
||||
avatarUrl=""
|
||||
name="aaaa"
|
||||
distributionValue="11111"
|
||||
style="width: 40%"
|
||||
/>
|
||||
</div>
|
||||
<!-- 第二三 -->
|
||||
<div style="display: flex; justify-content: space-between; margin-top: -20%">
|
||||
<TopUser
|
||||
:isRoom="rankingType === 'Room'"
|
||||
:BorderImgUrl="top2BorderBg"
|
||||
avatarUrl=""
|
||||
name="aaaa"
|
||||
distributionValue="11111"
|
||||
style="width: 30%"
|
||||
/>
|
||||
<TopUser
|
||||
:isRoom="rankingType === 'Room'"
|
||||
:BorderImgUrl="top3BorderBg"
|
||||
avatarUrl=""
|
||||
name="aaaa"
|
||||
distributionValue="11111"
|
||||
style="width: 30%"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 第四名开始 -->
|
||||
<div style="margin: 3% 10px 0">
|
||||
<div
|
||||
v-for="value in 9"
|
||||
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">999</div>
|
||||
<img
|
||||
src=""
|
||||
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;
|
||||
"
|
||||
>
|
||||
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>
|
||||
</div>
|
||||
|
||||
<!-- 我的排名 -->
|
||||
<div style="position: sticky; bottom: 0; z-index: 999; padding: 0 1px; width: 100%">
|
||||
<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>
|
||||
</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'
|
||||
|
||||
//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 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')
|
||||
}
|
||||
})
|
||||
})
|
||||
|
||||
onMounted(() => {
|
||||
isInAppEnvironment.value = isInApp()
|
||||
|
||||
observer.observe(sentinel.value)
|
||||
})
|
||||
</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>
|
||||