729 lines
19 KiB
Vue
729 lines
19 KiB
Vue
<script setup>
|
||
import { computed, nextTick, onMounted, ref } from "vue";
|
||
import { onLoad } from "@dcloudio/uni-app";
|
||
import Avatar from "@/components/Avatar.vue";
|
||
import {
|
||
getMvpRankList,
|
||
getMyMvpRank,
|
||
getMyScoreRank,
|
||
getMyTenRingRank,
|
||
getScoreRankList,
|
||
getTenRingRankList,
|
||
} from "@/apis";
|
||
import { capsuleHeight } from "@/util";
|
||
import useStore from "@/store";
|
||
import { storeToRefs } from "pinia";
|
||
|
||
const PAGE_SIZE = 10;
|
||
|
||
const store = useStore();
|
||
const { user } = storeToRefs(store);
|
||
const { getLvlName } = store;
|
||
|
||
const createRankState = () => ({
|
||
list: [],
|
||
page: 0,
|
||
pageSize: PAGE_SIZE,
|
||
loading: false,
|
||
noMore: false,
|
||
loaded: false,
|
||
scrollTop: 0,
|
||
myData: null,
|
||
myDataLoaded: false,
|
||
});
|
||
|
||
const rankTabs = [
|
||
{
|
||
key: "score",
|
||
title: "积分榜",
|
||
subTitle: "排位赛积分",
|
||
listApi: getScoreRankList,
|
||
myApi: getMyScoreRank,
|
||
},
|
||
{
|
||
key: "mvp",
|
||
title: "MVP榜",
|
||
subTitle: "MVP次数",
|
||
listApi: getMvpRankList,
|
||
myApi: getMyMvpRank,
|
||
},
|
||
{
|
||
key: "tenRing",
|
||
title: "十环榜",
|
||
subTitle: "十环次数",
|
||
listApi: getTenRingRankList,
|
||
myApi: getMyTenRingRank,
|
||
},
|
||
];
|
||
|
||
// 解析 ranking 页面传入的榜单参数,进入页面时默认选中对应 tab。
|
||
const getTabIndexByRouteParam = (tab) => {
|
||
if (tab === "mvp") return 1;
|
||
if (tab === "tenRing") return 2;
|
||
return 0;
|
||
};
|
||
|
||
const rankStates = ref({
|
||
score: createRankState(),
|
||
mvp: createRankState(),
|
||
tenRing: createRankState(),
|
||
});
|
||
|
||
const selectedIndex = ref(0);
|
||
const initialTabIndex = ref(0);
|
||
const pageMounted = ref(false);
|
||
const initializedFromRoute = ref(false);
|
||
const addBg = ref(false);
|
||
const currentScrollTop = ref(0);
|
||
const restoreScrollTop = ref(0);
|
||
const tabSwitchAnimating = ref(false);
|
||
const suppressScrollSync = ref(false);
|
||
const suppressLoadMore = ref(false);
|
||
const stickyTabsTop = capsuleHeight + 50;
|
||
const stickyTabsActive = ref(false);
|
||
const tabsStickyThreshold = ref(0);
|
||
const tabsStickyReady = ref(false);
|
||
const tabsHeight = ref(0);
|
||
|
||
const getTabConfig = (index = selectedIndex.value) => rankTabs[index];
|
||
const getTabKey = (index = selectedIndex.value) => getTabConfig(index).key;
|
||
|
||
// 统一提取榜单接口返回的列表数据,兼容数组和对象两种返回格式。
|
||
const getRankListFromResponse = (result) => {
|
||
if (Array.isArray(result)) return result;
|
||
if (Array.isArray(result?.list)) return result.list;
|
||
if (Array.isArray(result?.items)) return result.items;
|
||
return [];
|
||
};
|
||
|
||
// 为当前登录用户构造默认的个人榜单信息,避免接口未返回时底部区域缺数据。
|
||
const buildDefaultMyData = () => ({
|
||
rank: null,
|
||
userId: user.value.id,
|
||
name: user.value.nickName,
|
||
avatar: user.value.avatar,
|
||
totalScore: 0,
|
||
mvpCount: 0,
|
||
tenRings: 0,
|
||
totalGames: 0,
|
||
totalCount: 0,
|
||
rankName: user.value.lvlName,
|
||
rankLvl: user.value.rankLvl,
|
||
});
|
||
|
||
const currentTabKey = computed(() => getTabKey(selectedIndex.value));
|
||
const currentState = computed(() => rankStates.value[currentTabKey.value]);
|
||
const currentList = computed(() => currentState.value.list);
|
||
const currentSubTitle = computed(() => getTabConfig(selectedIndex.value).subTitle);
|
||
const currentMyData = computed(() => {
|
||
if (!user.value.id) return null;
|
||
return currentState.value.myData || buildDefaultMyData();
|
||
});
|
||
|
||
// 统一格式化段位和场次文案,兼容不同接口的字段命名。
|
||
const formatLevelText = (item = {}) => {
|
||
const levelName = item.rankName || getLvlName(item.rankLvl) || "暂无段位";
|
||
const totalGames = item.totalGames ?? item.TotalGames ?? 0;
|
||
return `${levelName},${totalGames}场`;
|
||
};
|
||
|
||
// 统一读取榜单项的排名字段,没有后端 rank 时回退到前端序号。
|
||
const getDisplayRank = (item = {}, index = 0) => {
|
||
return item.rank ?? index + 1;
|
||
};
|
||
|
||
// 底部个人排名在未上榜时展示占位符,而不是空白。
|
||
const getDisplayMyRank = (item = {}) => {
|
||
return item.rank ?? "-";
|
||
};
|
||
|
||
const getScoreValue = (item = {}) => item.totalScore ?? 0;
|
||
const getMvpValue = (item = {}) => item.mvpCount ?? item.totalScore ?? 0;
|
||
const getTenRingValue = (item = {}) =>
|
||
item.tenRings ?? item.TenRings ?? item.totalScore ?? 0;
|
||
|
||
// 根据当前选中的榜单类型,读取对应的展示值。
|
||
const getRankValue = (item = {}, index = selectedIndex.value) => {
|
||
if (index === 0) return getScoreValue(item);
|
||
if (index === 1) return getMvpValue(item);
|
||
return getTenRingValue(item);
|
||
};
|
||
|
||
const getRankUnit = (index = selectedIndex.value) => {
|
||
if (index === 0) return "分";
|
||
return "次";
|
||
};
|
||
|
||
// 统一设置页面当前的视觉滚动状态,避免吸顶和顶部背景不同步。
|
||
const syncScrollVisualState = (scrollTop = 0) => {
|
||
currentScrollTop.value = scrollTop;
|
||
addBg.value = scrollTop > 100;
|
||
if (!tabsStickyReady.value) {
|
||
stickyTabsActive.value = false;
|
||
return;
|
||
}
|
||
stickyTabsActive.value = scrollTop >= tabsStickyThreshold.value;
|
||
};
|
||
|
||
// 只保留一条滚动恢复链路:从当前滚动位置平滑滚到目标位置,避免多套控制同时生效造成闪烁。
|
||
const applyScrollPosition = async (
|
||
fromScrollTop = currentScrollTop.value,
|
||
toScrollTop = 0,
|
||
withAnimation = false
|
||
) => {
|
||
tabSwitchAnimating.value = withAnimation;
|
||
restoreScrollTop.value = fromScrollTop;
|
||
await nextTick();
|
||
restoreScrollTop.value = toScrollTop;
|
||
syncScrollVisualState(toScrollTop);
|
||
};
|
||
|
||
// 请求指定榜单的某一页数据,只有当前榜单会追加分页,不影响其他榜单的浏览状态。
|
||
const loadRankPage = async (tabKey, { reset = false } = {}) => {
|
||
const state = rankStates.value[tabKey];
|
||
const config = rankTabs.find((item) => item.key === tabKey);
|
||
if (!config || state.loading) return;
|
||
if (!reset && state.noMore) return;
|
||
|
||
const nextPage = reset ? 1 : state.page + 1;
|
||
state.loading = true;
|
||
if (reset) state.noMore = false;
|
||
|
||
try {
|
||
const result = await config.listApi(undefined, nextPage, PAGE_SIZE);
|
||
const list = getRankListFromResponse(result);
|
||
state.list = reset ? list : state.list.concat(list);
|
||
state.page = nextPage;
|
||
state.loaded = true;
|
||
state.noMore = list.length < PAGE_SIZE;
|
||
} catch (error) {
|
||
if (reset) {
|
||
state.list = [];
|
||
state.page = 0;
|
||
state.loaded = false;
|
||
state.noMore = false;
|
||
}
|
||
uni.showToast({
|
||
title: "排行榜加载失败",
|
||
icon: "none",
|
||
});
|
||
console.error("load rank page error", error);
|
||
} finally {
|
||
state.loading = false;
|
||
}
|
||
};
|
||
|
||
// 每个榜单独立请求一次个人排名信息,切回该榜单时直接复用,避免打断浏览上下文。
|
||
const loadMyRankData = async (tabKey) => {
|
||
if (!user.value.id) return;
|
||
|
||
const state = rankStates.value[tabKey];
|
||
const config = rankTabs.find((item) => item.key === tabKey);
|
||
if (!config || state.myDataLoaded) return;
|
||
|
||
try {
|
||
const result = await config.myApi();
|
||
state.myData = {
|
||
...buildDefaultMyData(),
|
||
...(result || {}),
|
||
};
|
||
} catch (error) {
|
||
state.myData = buildDefaultMyData();
|
||
console.error("load my rank data error", error);
|
||
} finally {
|
||
state.myDataLoaded = true;
|
||
}
|
||
};
|
||
|
||
// 首次进入或切换到未加载过的榜单时,初始化它的分页数据和个人横条数据。
|
||
const ensureTabReady = async (index = selectedIndex.value) => {
|
||
const tabKey = getTabKey(index);
|
||
const state = rankStates.value[tabKey];
|
||
|
||
if (!state.loaded) {
|
||
await loadRankPage(tabKey, { reset: true });
|
||
}
|
||
|
||
if (user.value.id && !state.myDataLoaded) {
|
||
await loadMyRankData(tabKey);
|
||
}
|
||
|
||
await nextTick();
|
||
return state.scrollTop || 0;
|
||
};
|
||
|
||
onLoad((options = {}) => {
|
||
initialTabIndex.value = getTabIndexByRouteParam(options.tab);
|
||
selectedIndex.value = initialTabIndex.value;
|
||
if (pageMounted.value && !initializedFromRoute.value) {
|
||
initializePage();
|
||
}
|
||
});
|
||
|
||
// 页面初始化同时兼容 onLoad 和 onMounted 的先后顺序,确保首屏一定落到路由指定的榜单。
|
||
const initializePage = async () => {
|
||
if (initializedFromRoute.value) return;
|
||
initializedFromRoute.value = true;
|
||
const nextScrollTop = await ensureTabReady(selectedIndex.value);
|
||
await applyScrollPosition(0, nextScrollTop, false);
|
||
setTimeout(() => {
|
||
measureTabsMetrics();
|
||
}, 0);
|
||
};
|
||
|
||
onMounted(async () => {
|
||
pageMounted.value = true;
|
||
await initializePage();
|
||
});
|
||
|
||
// 切换榜单时保留原榜单的列表和滚动位置,切回来后继续从之前的位置浏览。
|
||
const handleSelect = async (index) => {
|
||
if (index === selectedIndex.value) return;
|
||
|
||
const previousTabKey = currentTabKey.value;
|
||
rankStates.value[previousTabKey].scrollTop = currentScrollTop.value;
|
||
const previousScrollTop = currentScrollTop.value;
|
||
|
||
suppressScrollSync.value = true;
|
||
suppressLoadMore.value = true;
|
||
selectedIndex.value = index;
|
||
const nextScrollTop = await ensureTabReady(index);
|
||
await applyScrollPosition(previousScrollTop, nextScrollTop, false);
|
||
setTimeout(() => {
|
||
tabSwitchAnimating.value = false;
|
||
suppressScrollSync.value = false;
|
||
suppressLoadMore.value = false;
|
||
}, 220);
|
||
};
|
||
|
||
// 触底后只加载当前榜单的下一页数据,其他榜单的数据和页码保持不变。
|
||
const loadMore = async () => {
|
||
if (suppressLoadMore.value) return;
|
||
await loadRankPage(currentTabKey.value);
|
||
};
|
||
|
||
// 实时记录当前榜单的滚动位置,切换回来时恢复到上一次浏览位置。
|
||
const onScrollView = (e) => {
|
||
const scrollTop = e.detail.scrollTop || 0;
|
||
if (suppressScrollSync.value) return;
|
||
syncScrollVisualState(scrollTop);
|
||
rankStates.value[currentTabKey.value].scrollTop = scrollTop;
|
||
};
|
||
|
||
// 计算 tab 在滚动内容中的真实位置和高度,作为吸顶切换的唯一依据。
|
||
const measureTabsMetrics = () => {
|
||
const query = uni.createSelectorQuery();
|
||
query
|
||
.select("#rank-list-content-start")
|
||
.boundingClientRect()
|
||
.select(".rank-tabs-anchor")
|
||
.boundingClientRect()
|
||
.exec((res = []) => {
|
||
const [startRect, rect] = res;
|
||
if (!startRect || !rect) return;
|
||
const tabOffset = rect.top - startRect.top;
|
||
tabsStickyThreshold.value = Math.max(0, tabOffset - 92);
|
||
tabsHeight.value = rect.height || 0;
|
||
tabsStickyReady.value = true;
|
||
syncScrollVisualState(currentScrollTop.value);
|
||
});
|
||
};
|
||
</script>
|
||
|
||
<template>
|
||
<view class="container">
|
||
<view
|
||
class="header"
|
||
:style="{
|
||
paddingTop: capsuleHeight + 'px',
|
||
}"
|
||
>
|
||
<image
|
||
:style="{ opacity: addBg ? 1 : 0 }"
|
||
src="https://static.shelingxingqiu.com/shootmini/static/app-bg.png"
|
||
mode="widthFix"
|
||
/>
|
||
<navigator open-type="navigateBack">
|
||
<image class="header-back" src="../static/back.png" mode="widthFix" />
|
||
</navigator>
|
||
<text :style="{ opacity: addBg ? 1 : 0 }">本赛季排行榜</text>
|
||
</view>
|
||
<scroll-view
|
||
scroll-y
|
||
:scroll-with-animation="tabSwitchAnimating"
|
||
:scroll-top="restoreScrollTop"
|
||
@scroll="onScrollView"
|
||
@scrolltolower="loadMore"
|
||
:style="{ height: user.id ? '90vh' : '100vh' }"
|
||
>
|
||
<view id="rank-list-content-start" class="content-start-anchor"></view>
|
||
<image
|
||
src="https://static.shelingxingqiu.com/attachment/2025-09-25/dd1p9b3wcrwnlnghiq.png"
|
||
mode="widthFix"
|
||
class="header-bg"
|
||
@load="measureTabsMetrics"
|
||
/>
|
||
<view
|
||
v-if="stickyTabsActive"
|
||
class="rank-tabs-placeholder"
|
||
:style="{ height: `${tabsHeight}px` }"
|
||
/>
|
||
<view
|
||
class="rank-tabs rank-tabs-anchor"
|
||
:class="{ 'rank-tabs-anchor-fixed': stickyTabsActive }"
|
||
:style="stickyTabsActive ? { top: `${stickyTabsTop}px` } : {}"
|
||
>
|
||
<view
|
||
v-for="(rankType, index) in rankTabs"
|
||
:key="rankType.key"
|
||
:style="{
|
||
fontSize: index === selectedIndex ? '16px' : '14px',
|
||
color: index === selectedIndex ? '#000' : '#fff',
|
||
backgroundColor: index === selectedIndex ? '#FFD947' : '#383737',
|
||
}"
|
||
@tap="handleSelect(index)"
|
||
>
|
||
{{ rankType.title }}
|
||
</view>
|
||
</view>
|
||
<view class="rank-list">
|
||
<view class="rank-list-header">
|
||
<text>排名</text>
|
||
<text>用户ID</text>
|
||
<text>{{ currentSubTitle }}</text>
|
||
</view>
|
||
<view
|
||
v-for="(item, index) in currentList"
|
||
:key="`${currentTabKey}-${index}-${item.userId || item.name}`"
|
||
class="rank-list-item"
|
||
:style="{
|
||
backgroundColor: index % 2 === 0 ? '#9898981f' : 'transparent',
|
||
}"
|
||
>
|
||
<image
|
||
v-if="index === 0"
|
||
class="player-bg"
|
||
src="../static/melee-player-bg1.png"
|
||
mode="aspectFill"
|
||
/>
|
||
<image
|
||
v-if="index === 1"
|
||
class="player-bg"
|
||
src="../static/melee-player-bg2.png"
|
||
mode="aspectFill"
|
||
/>
|
||
<image
|
||
v-if="index === 2"
|
||
class="player-bg"
|
||
src="../static/melee-player-bg3.png"
|
||
mode="aspectFill"
|
||
/>
|
||
<image
|
||
v-if="index === 0"
|
||
class="player-crown"
|
||
src="../static/champ1.png"
|
||
mode="widthFix"
|
||
/>
|
||
<image
|
||
v-if="index === 1"
|
||
class="player-crown"
|
||
src="../static/champ2.png"
|
||
mode="widthFix"
|
||
/>
|
||
<image
|
||
v-if="index === 2"
|
||
class="player-crown"
|
||
src="../static/champ3.png"
|
||
mode="widthFix"
|
||
/>
|
||
<view v-if="index > 2" class="view-crown">
|
||
{{ getDisplayRank(item, index) }}
|
||
</view>
|
||
<Avatar :src="item.avatar" />
|
||
<view class="rank-item-content">
|
||
<text class="truncate">{{ item.name }}</text>
|
||
<text>{{ formatLevelText(item) }}</text>
|
||
</view>
|
||
<text class="rank-item-integral">
|
||
<text
|
||
:style="{ fontSize: '14px', color: '#fff', marginRight: '5px' }"
|
||
>
|
||
{{ getRankValue(item) }}
|
||
</text>
|
||
{{ getRankUnit() }}
|
||
</text>
|
||
</view>
|
||
<view
|
||
v-if="currentState.loading && !currentList.length"
|
||
class="no-data"
|
||
>
|
||
<text>加载中...</text>
|
||
</view>
|
||
<view
|
||
v-else-if="!currentState.loading && !currentList.length"
|
||
class="no-data"
|
||
>
|
||
<text>暂无数据</text>
|
||
</view>
|
||
<view v-else class="list-tip">
|
||
<text v-if="currentState.loading">加载中...</text>
|
||
<text v-else-if="currentState.noMore">没有更多了</text>
|
||
</view>
|
||
</view>
|
||
</scroll-view>
|
||
<view class="my-rank-data" v-if="currentMyData">
|
||
<image
|
||
src="https://static.shelingxingqiu.com/attachment/2025-08-05/dbuaf19pf7qd8ps0uh.png"
|
||
mode="widthFix"
|
||
/>
|
||
<text>{{ getDisplayMyRank(currentMyData) }}</text>
|
||
<Avatar :src="currentMyData.avatar || user.avatar" />
|
||
<view class="rank-item-content">
|
||
<text class="truncate">{{ currentMyData.name || user.nickName }}</text>
|
||
<text>{{ formatLevelText(currentMyData) }}</text>
|
||
</view>
|
||
<text class="rank-item-integral">
|
||
<text
|
||
:style="{ fontSize: '14px', color: '#fff', marginRight: '5px' }"
|
||
>
|
||
{{ getRankValue(currentMyData) }}
|
||
</text>
|
||
{{ getRankUnit() }}
|
||
</text>
|
||
</view>
|
||
</view>
|
||
</template>
|
||
|
||
<style scoped>
|
||
.container {
|
||
width: 100%;
|
||
}
|
||
|
||
.content-start-anchor {
|
||
width: 100%;
|
||
height: 0;
|
||
}
|
||
|
||
.header-bg {
|
||
width: 100%;
|
||
}
|
||
|
||
.header {
|
||
width: 100%;
|
||
height: 50px;
|
||
display: flex;
|
||
align-items: center;
|
||
position: fixed;
|
||
top: 0;
|
||
z-index: 10;
|
||
overflow: hidden;
|
||
}
|
||
|
||
.header-back {
|
||
width: 22px;
|
||
height: 22px;
|
||
margin: 0px 15px;
|
||
margin-top: 5px;
|
||
position: relative;
|
||
}
|
||
|
||
.header > image:first-child {
|
||
width: 100vw;
|
||
height: 100vh;
|
||
position: absolute;
|
||
top: 0;
|
||
left: 0;
|
||
transition: all 0.5s ease;
|
||
}
|
||
|
||
.header > text {
|
||
color: #fff;
|
||
font-weight: bold;
|
||
transition: all 0.5s ease;
|
||
position: relative;
|
||
}
|
||
|
||
.rank-tabs {
|
||
width: calc(100% - 20px);
|
||
display: flex;
|
||
justify-content: space-around;
|
||
padding: 20rpx 10px;
|
||
}
|
||
|
||
.rank-tabs > view {
|
||
width: 25%;
|
||
padding: 10px;
|
||
text-align: center;
|
||
border-radius: 20px;
|
||
}
|
||
|
||
.rank-tabs-placeholder {
|
||
width: 100%;
|
||
}
|
||
|
||
.rank-tabs-anchor-fixed {
|
||
position: fixed;
|
||
left: 0;
|
||
z-index: 11;
|
||
background: #000000;
|
||
backdrop-filter: blur(8px);
|
||
}
|
||
|
||
.rank-list {
|
||
display: flex;
|
||
flex-direction: column;
|
||
align-items: center;
|
||
width: calc(100% - 20px);
|
||
color: #fff9;
|
||
font-size: 12px;
|
||
margin: 0 10px 10px 10px;
|
||
border: 1px solid rgb(255 217 71 / 0.2);
|
||
border-radius: 10px;
|
||
background-color: #313131;
|
||
}
|
||
|
||
.rank-list > view {
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: space-between;
|
||
width: 100%;
|
||
overflow: hidden;
|
||
position: relative;
|
||
}
|
||
|
||
.rank-list-header {
|
||
width: calc(100% - 20px) !important;
|
||
padding: 10px;
|
||
}
|
||
|
||
.rank-list-header > text:nth-child(2) {
|
||
width: 14%;
|
||
}
|
||
|
||
.rank-list-header > text:last-child {
|
||
width: 30%;
|
||
text-align: right;
|
||
}
|
||
|
||
.rank-list-item {
|
||
padding: 10px 0;
|
||
}
|
||
|
||
.player-bg {
|
||
position: absolute;
|
||
width: 100%;
|
||
height: 100%;
|
||
top: 0;
|
||
left: 0;
|
||
}
|
||
|
||
.player-crown {
|
||
position: relative;
|
||
width: 27px;
|
||
height: 27px;
|
||
margin: 0 15px;
|
||
}
|
||
|
||
.view-crown {
|
||
width: 27px;
|
||
height: 27px;
|
||
line-height: 27px;
|
||
text-align: center;
|
||
border-radius: 50%;
|
||
margin: 0 15px;
|
||
color: #fff;
|
||
background-color: #676767;
|
||
position: relative;
|
||
}
|
||
|
||
.rank-item-content {
|
||
display: flex;
|
||
flex-direction: column;
|
||
align-items: flex-start;
|
||
justify-content: center;
|
||
width: 50%;
|
||
position: relative;
|
||
padding-left: 10px;
|
||
}
|
||
|
||
.rank-item-content > text:first-child {
|
||
color: #fff;
|
||
font-size: 14px;
|
||
margin-bottom: 3px;
|
||
width: 120px;
|
||
}
|
||
|
||
.rank-list-item > text:last-child {
|
||
margin-right: 10px;
|
||
width: 56px;
|
||
text-align: right;
|
||
}
|
||
|
||
.my-rank-data {
|
||
width: calc(100% - 30px);
|
||
padding: 15px;
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: space-between;
|
||
color: #fff9;
|
||
font-size: 12px;
|
||
height: calc(10vh - 30px);
|
||
position: relative;
|
||
overflow: hidden;
|
||
border-radius: 10px;
|
||
}
|
||
|
||
.my-rank-data > image:first-child {
|
||
position: absolute;
|
||
width: 100%;
|
||
left: 0;
|
||
top: -5px;
|
||
}
|
||
|
||
.my-rank-data > text:nth-child(2) {
|
||
background-color: #c1a434;
|
||
position: relative;
|
||
color: #fff;
|
||
padding: 3px 5px;
|
||
border-radius: 20px;
|
||
margin: 10px;
|
||
font-size: 14px;
|
||
min-width: 15px;
|
||
text-align: center;
|
||
}
|
||
|
||
.my-rank-data > text:last-child {
|
||
position: relative;
|
||
margin-right: 10px;
|
||
width: 65px;
|
||
text-align: right;
|
||
}
|
||
|
||
.my-rank-data > .rank-item-content > text:first-child {
|
||
color: #fed847;
|
||
}
|
||
|
||
.my-rank-data > .rank-item-integral {
|
||
color: #fff9;
|
||
font-size: 12px;
|
||
margin-right: 0 !important;
|
||
}
|
||
|
||
.no-data {
|
||
width: 100%;
|
||
height: 400px;
|
||
display: flex;
|
||
justify-content: center !important;
|
||
align-items: center;
|
||
color: #fff9;
|
||
font-size: 14px;
|
||
}
|
||
|
||
.list-tip {
|
||
justify-content: center !important;
|
||
color: #fff9;
|
||
font-size: 12px;
|
||
min-height: 60rpx;
|
||
}
|
||
</style>
|