211 lines
5.5 KiB
Vue
211 lines
5.5 KiB
Vue
<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>
|