topfans/frontend/pages/dashboard/dashboard.vue

211 lines
5.5 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>
<scroll-view
scroll-y
class="dashboard-page-bg dashboard-scroll"
:refresher-enabled="true"
:refresher-triggered="loading.overall"
@refresherrefresh="handlePullDownRefresh"
:show-scrollbar="false"
>
<view class="dashboard-container">
<view class="nav-back" @tap="goBack">
<text class="nav-back-icon"></text>
</view>
<DashboardHeader
:active-tab="activeTab"
@update:active-tab="handleTabChange"
/>
<view class="dashboard-box">
<!-- Tab 1: 水晶相关 -->
<view v-if="activeTab === 'crystal'" class="dashboard-content">
<CrystalOverview
:data="data.today"
:loading="loading.today"
:error="error.today"
@retry="refresh('today')"
/>
<IncomeCurve
:points="data.curve?.points || []"
:loading="loading.curve"
:error="error.curve"
@retry="refresh('curve')"
/>
<ExhibitionCenter
:data="data.exhibition"
:loading="loading.exhibition"
:error="error.exhibition"
@retry="refresh('exhibition')"
/>
<LikeIncomeBoard
:stats="
data.likeIncome
? {
total_like_count: data.likeIncome.total_like_count,
total_income: data.likeIncome.total_income,
}
: null
"
:levels="data.likeIncome?.levels || []"
:loading="loading.likeIncome"
:error="error.likeIncome"
@retry="refresh('likeIncome')"
/>
<CollectionMatrix
:top-five="data.topAssets?.items || []"
:levels="data.levels?.items || []"
:upgrades="data.upgrades || { upcoming: [], recent: [] }"
/>
</view>
<!-- Tab 2: 赛季总览(占位) -->
<view v-else class="dashboard-content">
<view class="season-placeholder">
<text class="placeholder-icon">🏆</text>
<text class="placeholder-title">赛季总览 · 即将上线</text>
<text class="placeholder-sub">历史赛季数据正在筹备中</text>
</view>
</view>
</view>
</view>
</scroll-view>
</template>
<script setup>
import { ref } from "vue";
import { onShow } from "@dcloudio/uni-app";
import DashboardHeader from "./components/DashboardHeader.vue";
import CrystalOverview from "./components/CrystalOverview.vue";
import IncomeCurve from "./components/IncomeCurve.vue";
import ExhibitionCenter from "./components/ExhibitionCenter.vue";
import LikeIncomeBoard from "./components/LikeIncomeBoard.vue";
import CollectionMatrix from "./components/CollectionMatrix.vue";
import { useDashboardData } from "@/composables/useDashboardData";
const activeTab = ref("crystal");
const starId = ref(uni.getStorageSync("star_id") || null);
const isFirstShow = ref(true);
const { loading, error, data, refresh } = useDashboardData({
starId: starId.value,
});
// 返回上一页
const goBack = () => {
const pages = getCurrentPages();
if (pages.length > 1) {
uni.navigateBack();
} else {
uni.reLaunch({
url: "/pages/square/square",
});
}
};
// Tab 切换30 分钟内复用缓存;切回水晶 Tab 时 cache-aware 刷新
function handleTabChange(tab) {
activeTab.value = tab;
if (tab === "crystal") {
// refresh() 默认走 30 分钟缓存(不闪骨架屏);需要强刷用 refresh(null, true)
refresh();
}
}
// 下拉刷新
async function handlePullDownRefresh() {
await refresh(null, true); // force=true 绕缓存
uni.stopPullDownRefresh();
}
// 从其他页面返回时强制刷新(绕 30 分钟缓存spec §4.1
// 首次 onShow 跳过composable 内部已主动 loadAll(),避免冷启动 14 个并发请求
onShow(() => {
if (isFirstShow.value) {
isFirstShow.value = false;
return;
}
refresh(null, true);
});
</script>
<style lang="scss" scoped>
@import "@/uni.scss";
.dashboard-page-bg {
min-height: 100vh;
// 多层背景:第一层 $d-page-bg 渐变作为蒙层(在最上),第二层是 bj.png 图片
background:
$d-page-bg,
url("/static/dashboard/bj.png") center / cover no-repeat;
}
.dashboard-scroll {
height: 100vh;
}
.dashboard-container {
min-height: 100vh;
}
.nav-back {
position: fixed;
top: 96rpx;
left: 32rpx;
width: 80rpx;
height: 80rpx;
display: flex;
align-items: center;
justify-content: center;
/* background: rgba(255,255,255,0.5);
border-radius: 50%; */
z-index: 4;
}
.nav-back-icon {
font-size: 48rpx;
font-weight: bold;
color: #fff;
}
.dashboard-box {
min-height: 100vh;
position: absolute;
left: 6px;
right: 6px;
z-index: 3;
}
.dashboard-content {
padding: 8px 9px 0;
background: linear-gradient(
174.13deg,
rgba(255, 149, 151, 0.57) -6.18%,
rgba(128, 223, 255, 0.57) 34.5%,
rgba(184, 184, 184, 0.57) 73.48%,
rgba(217, 217, 217, 0.57) 111.34%
);
border: 1px solid rgba(255, 255, 255, 0.6);
border-radius: 22px;
}
.season-placeholder {
background: rgba(255, 255, 255, 0.15);
backdrop-filter: blur(10px);
border-radius: 22rpx;
padding: 120rpx 32rpx;
display: flex;
flex-direction: column;
align-items: center;
}
.placeholder-icon {
font-size: 96rpx;
margin-bottom: 24rpx;
}
.placeholder-title {
color: #ffffff;
font-size: 36rpx;
font-weight: 700;
margin-bottom: 16rpx;
}
.placeholder-sub {
color: rgba(255, 255, 255, 0.7);
font-size: 26rpx;
}
</style>