diff --git a/docs/superpowers/plans/2026-06-02-data-dashboard-frontend.md b/docs/superpowers/plans/2026-06-02-data-dashboard-frontend.md
new file mode 100644
index 0000000..1870e5a
--- /dev/null
+++ b/docs/superpowers/plans/2026-06-02-data-dashboard-frontend.md
@@ -0,0 +1,2982 @@
+# 数据看板前端 Implementation Plan
+
+> **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (`- [ ]`) syntax for tracking.
+
+**Goal:** 实现数据看板页面(6 大模块),在 mock 数据下完整跑通水晶收益、藏品表现、升级进度的可视化展示。
+
+**Architecture:** 单页面 + 1 个聚合 composable(`useDashboardData`)+ 11 个展示子组件。composable 内部用 `effectScope` 包裹 7 个并发请求,按 section 维度独立暴露 loading/error/refresh。后端未就绪时通过 `USE_MOCK_API` 开关走 mock 数据,接真实接口只需翻常量。
+
+**Tech Stack:** Vue 3 (Composition API) + uni-app + Vuex 4(已有,不新增模块)+ uCharts 插件(柱状+折线)+ SCSS(uni.scss 共享 token)+ CSS conic-gradient(5 个等级环)
+
+**Spec:** `docs/superpowers/specs/2026-06-02-data-dashboard-frontend-design.md`
+**配套视觉/API 文档:** `docs/figma-analysis-data-dashboard.md`
+
+---
+
+## 文件总览
+
+**Create(15 个)**:
+- `frontend/pages/dashboard/dashboard.vue`
+- `frontend/pages/dashboard/components/DashboardHeader.vue`
+- `frontend/pages/dashboard/components/CrystalOverview.vue`
+- `frontend/pages/dashboard/components/IncomeCurve.vue`
+- `frontend/pages/dashboard/components/ExhibitionCenter.vue`
+- `frontend/pages/dashboard/components/LikeIncomeBoard.vue`
+- `frontend/pages/dashboard/components/CollectionMatrix.vue`
+- `frontend/pages/dashboard/components/TopFiveAssets.vue`
+- `frontend/pages/dashboard/components/LevelDistribution.vue`
+- `frontend/pages/dashboard/components/UpcomingUpgrades.vue`
+- `frontend/pages/dashboard/components/RecentUpgrades.vue`
+- `frontend/pages/dashboard/components/SectionSkeleton.vue`
+- `frontend/pages/dashboard/components/EmptyState.vue`
+- `frontend/composables/useDashboardData.js`
+- `frontend/utils/mock/dashboard.js`
+
+**Modify(3 个)**:
+- `frontend/pages.json`(注册 dashboard 页面)
+- `frontend/utils/api.js`(追加 `dashboardApi` 命名空间 + mock 触发)
+- `frontend/uni.scss`(追加设计 token 变量)
+
+**无新增测试文件**:项目未引入测试框架,验证走 `superpowers:verification-before-completion` 流程的手动核对清单。
+
+---
+
+## Task 1: 页面注册 + SCSS 设计 token
+
+**Files:**
+- Modify: `frontend/pages.json`(在 `pages` 数组末尾追加一项)
+- Modify: `frontend/uni.scss`(在文件末尾追加 dashboard 专用 token)
+
+- [ ] **Step 1: 在 `pages.json` 末尾注册 dashboard 页面**
+
+在 `pages` 数组最后一个对象(`tasks/revenue`)之后,逗号之后追加:
+
+```json
+ ,{
+ "path": "pages/dashboard/dashboard",
+ "style": {
+ "navigationStyle": "custom",
+ "app-plus": {
+ "bounce": "none"
+ }
+ }
+ }
+```
+
+- [ ] **Step 2: 在 `uni.scss` 末尾追加 dashboard token**
+
+```scss
+/* ==================== Dashboard 设计 Token ==================== */
+
+/* 颜色(与 spec §3.1 一致) */
+$d-text-data: #FFFABD;
+$d-text-white: #FFFFFF;
+$d-tab-active: linear-gradient(231deg, #A8A6ED 0%, #88C8D8 64%, #F380EF 100%);
+$d-card-crystal: linear-gradient(135deg, #FFDF77 0%, #8E95E2 40%, #F48CFF 100%);
+$d-card-today: linear-gradient(137deg, #FFDF77 0%, #8E95E2 40%, #F48CFF 100%);
+$d-progress-cyan: linear-gradient(90deg, #5EDFFF 0%, #FFC8C8 100%);
+$d-progress-pink: linear-gradient(90deg, #FFF375 0%, #FF6B84 100%);
+$d-bar-blue-yellow: linear-gradient(90deg, #1BAFEE 0%, #FFCC14 100%);
+$d-bar-fill: linear-gradient(135deg, #FFDF77 0%, #B984FF 60%, #FF8183 100%);
+$d-page-bg: linear-gradient(153deg, #FF9597 0%, #80DFFF 33%, #B8B8B8 74%, #D9D9D9 100%);
+
+/* 5 个等级专属渐变(环图、徽章、TOP 徽章) */
+$d-level-ur: linear-gradient(135deg, #FF8A65 0%, #FFD740 100%);
+$d-level-ssr: linear-gradient(135deg, #FF5E9C 0%, #FFB199 100%);
+$d-level-sr: linear-gradient(135deg, #B17BFF 0%, #FF8FE6 100%);
+$d-level-r: linear-gradient(135deg, #5EDFFF 0%, #6FA9FF 100%);
+$d-level-n: linear-gradient(135deg, #C5C5C5 0%, #8C8C8C 100%);
+
+/* 阴影 */
+$d-shadow-card: 0px 4px 4px rgba(189, 50, 50, 0.25);
+$d-shadow-data: -1px 1px 4px rgba(206, 9, 9, 0.84);
+$d-shadow-mascot: 2px 2px 4px rgba(242, 21, 21, 0.47);
+$d-shadow-tab: 0px 4px 4px rgba(0, 0, 0, 0.25);
+$d-shadow-inner: 0px 4px 4px rgba(96, 13, 13, 0.25);
+
+/* 圆角 */
+$d-radius-thumb: 3px;
+$d-radius-progress: 6px;
+$d-radius-card-sm: 14px;
+$d-radius-card-md: 17px;
+$d-radius-card-lg: 22px;
+
+/* 字号 */
+$d-fs-num-xl: 35px;
+$d-fs-num-lg: 18px;
+$d-fs-num-md: 14px;
+$d-fs-num-sm: 9px;
+$d-fs-title-lg: 20px;
+$d-fs-title-md: 18px;
+$d-fs-title-sm: 15px;
+$d-fs-label: 12px;
+
+/* 间距 */
+$d-space-card-x: 16px;
+$d-space-section: 24px;
+$d-space-cell: 12px;
+```
+
+- [ ] **Step 3: 手动验证 - 路由可达**
+
+启动 H5 dev server(如果已在跑则跳过):
+```bash
+cd frontend && npm run dev:h5
+```
+
+在 H5 dev URL 末尾加 `/pages/dashboard/dashboard` 访问:
+- 预期:进入空页面(白屏即可,组件尚未创建)
+- 不预期:路由 404、JSON 解析错误
+
+- [ ] **Step 4: Commit**
+
+```bash
+git add frontend/pages.json frontend/uni.scss
+git commit -m "feat(dashboard): 注册页面 + 追加 SCSS 设计 token"
+```
+
+---
+
+## Task 2: Mock 数据 + dashboardApi 命名空间
+
+**Files:**
+- Create: `frontend/utils/mock/dashboard.js`
+- Modify: `frontend/utils/api.js`(追加 `dashboardApi` + 注入 mock 触发逻辑)
+
+- [ ] **Step 1: 创建 mock 数据文件 `frontend/utils/mock/dashboard.js`**
+
+```javascript
+// 数据看板 mock 数据工厂
+// 后端就绪后,将 utils/api.js 顶部 USE_MOCK_API 改为 false 即可
+
+const randomDelay = (min = 200, max = 600) =>
+ new Promise((resolve) => setTimeout(resolve, Math.random() * (max - min) + min))
+
+// 1. 今日概览
+export async function mockTodayOverview({ star_id }) {
+ await randomDelay()
+ return {
+ code: 200,
+ data: {
+ crystal_balance: 2713,
+ today_income: 213,
+ week_rank: 12,
+ },
+ }
+}
+
+// 2. 七日收益曲线
+export async function mock7DayIncomeCurve({ star_id }) {
+ await randomDelay()
+ const today = new Date()
+ const points = []
+ const incomes = [180, 245, 198, 312, 276, 221, 189] // 最后一个是今天
+ for (let i = 6; i >= 0; i--) {
+ const d = new Date(today)
+ d.setDate(d.getDate() - i)
+ const income = incomes[6 - i]
+ points.push({
+ date: `${d.getFullYear()}.${String(d.getMonth() + 1).padStart(2, '0')}.${String(d.getDate()).padStart(2, '0')}`,
+ income,
+ is_today: i === 0,
+ is_peak: income === 312,
+ })
+ }
+ return {
+ code: 200,
+ data: {
+ points,
+ total_income: incomes.reduce((a, b) => a + b, 0),
+ avg_income: Math.round(incomes.reduce((a, b) => a + b, 0) / 7),
+ },
+ }
+}
+
+// 3. 展出收益中心
+export async function mockExhibitionSummary({ star_id }) {
+ await randomDelay()
+ return {
+ code: 200,
+ data: {
+ exhibiting_count: 21,
+ starbook_count: 33,
+ total_duration: '712:13:56',
+ total_earnings: 39721,
+ top5: [
+ { asset_id: 1, asset_name: '璀璨星河', asset_thumb: '', duration_7d: '144:13:56', earnings_7d: 2173, avg_earnings: 15 },
+ { asset_id: 2, asset_name: '夏日微风', asset_thumb: '', duration_7d: '77:13:56', earnings_7d: 1332, avg_earnings: 15 },
+ { asset_id: 3, asset_name: '夜色霓虹', asset_thumb: '', duration_7d: '64:15:37', earnings_7d: 1201, avg_earnings: 12 },
+ { asset_id: 4, asset_name: '黎明序曲', asset_thumb: '', duration_7d: '51:22:12', earnings_7d: 783, avg_earnings: 12 },
+ { asset_id: 5, asset_name: '深海回响', asset_thumb: '', duration_7d: '51:22:12', earnings_7d: 783, avg_earnings: 12 },
+ ],
+ },
+ }
+}
+
+// 4. 点赞收益按等级
+export async function mockLikeIncomeByLevel({ star_id }) {
+ await randomDelay()
+ return {
+ code: 200,
+ data: {
+ total_like_count: 231,
+ total_income: 12719,
+ levels: [
+ { level: 'UR', asset_count: 1, total_income: 723, thumb: '' },
+ { level: 'SSR', asset_count: 2, total_income: 381, thumb: '' },
+ { level: 'SR', asset_count: 5, total_income: 233, thumb: '' },
+ { level: 'SR', asset_count: 4, total_income: 169, thumb: '' },
+ { level: 'R', asset_count: 6, total_income: 57, thumb: '' },
+ ],
+ },
+ }
+}
+
+// 5. 藏品 TOP5
+export async function mockTopAssets({ star_id }) {
+ await randomDelay()
+ return {
+ code: 200,
+ data: {
+ items: [
+ { asset_id: 1, asset_name: '璀璨星河', asset_thumb: '', total_earnings: 8420, rank: 1 },
+ { asset_id: 2, asset_name: '夏日微风', asset_thumb: '', total_earnings: 6230, rank: 2 },
+ { asset_id: 3, asset_name: '夜色霓虹', asset_thumb: '', total_earnings: 5180, rank: 3 },
+ { asset_id: 4, asset_name: '黎明序曲', asset_thumb: '', total_earnings: 4320, rank: 4 },
+ { asset_id: 5, asset_name: '深海回响', asset_thumb: '', total_earnings: 3980, rank: 5 },
+ ],
+ },
+ }
+}
+
+// 6. 藏品等级分布
+export async function mockLevelDistribution({ star_id }) {
+ await randomDelay()
+ const total = 33
+ return {
+ code: 200,
+ data: {
+ items: [
+ { level: 'UR', count: 1, total },
+ { level: 'SSR', count: 2, total },
+ { level: 'SR', count: 5, total },
+ { level: 'R', count: 6, total },
+ { level: 'N', count: 0, total },
+ ],
+ },
+ }
+}
+
+// 7. 升级进度
+export async function mockUpgradeProgress({ star_id }) {
+ await randomDelay()
+ return {
+ code: 200,
+ data: {
+ upcoming: [
+ { asset_id: 1, asset_name: '璀璨星河', asset_thumb: '', like_progress: 73, duration_progress: 92 },
+ { asset_id: 2, asset_name: '夏日微风', asset_thumb: '', like_progress: 75, duration_progress: 96 },
+ { asset_id: 3, asset_name: '夜色霓虹', asset_thumb: '', like_progress: 97, duration_progress: 71 },
+ ],
+ recent: [
+ { asset_id: 4, asset_name: '黎明序曲', asset_thumb: '', new_level: 'SSR', upgrade_time: Date.now() - 3600000 },
+ { asset_id: 5, asset_name: '深海回响', asset_thumb: '', new_level: 'SR', upgrade_time: Date.now() - 86400000 },
+ { asset_id: 6, asset_name: '晨曦微光', asset_thumb: '', new_level: 'SR', upgrade_time: Date.now() - 172800000 },
+ ],
+ },
+ }
+}
+
+// 路由表:endpoints 字符串 → mock 工厂
+export const mockRouter = {
+ '/api/v1/dashboard/today-overview': mockTodayOverview,
+ '/api/v1/dashboard/income-curve': mock7DayIncomeCurve,
+ '/api/v1/dashboard/exhibition-summary': mockExhibitionSummary,
+ '/api/v1/dashboard/like-income-by-level': mockLikeIncomeByLevel,
+ '/api/v1/dashboard/top-assets': mockTopAssets,
+ '/api/v1/dashboard/level-distribution': mockLevelDistribution,
+ '/api/v1/dashboard/upgrade-progress': mockUpgradeProgress,
+}
+```
+
+- [ ] **Step 2: 在 `frontend/utils/api.js` 末尾追加 `dashboardApi` 命名空间**
+
+```javascript
+// ==================== 数据看板 ====================
+import { mockRouter } from './mock/dashboard'
+
+const DASHBOARD_PREFIX = '/api/v1/dashboard'
+
+// mock 触发器:当 USE_MOCK_API 为 true 时短路返回 mock 数据
+// 后端就绪后将 USE_MOCK_API 改为 false 即可
+async function dashboardRequest(endpoint, params = {}) {
+ if (USE_MOCK_API) {
+ const factory = mockRouter[`${DASHBOARD_PREFIX}${endpoint}`]
+ if (factory) return factory(params)
+ }
+ return request({ url: `${DASHBOARD_PREFIX}${endpoint}`, method: 'GET', data: params })
+}
+
+export const dashboardApi = {
+ getTodayOverview: (starId) => dashboardRequest('/today-overview', { star_id: starId }).then((r) => r.data),
+ get7DayIncomeCurve: (starId) => dashboardRequest('/income-curve', { star_id: starId }).then((r) => r.data),
+ getExhibitionSummary: (starId) => dashboardRequest('/exhibition-summary', { star_id: starId }).then((r) => r.data),
+ getLikeIncomeByLevel: (starId) => dashboardRequest('/like-income-by-level', { star_id: starId }).then((r) => r.data),
+ getTopAssets: (starId) => dashboardRequest('/top-assets', { star_id: starId }).then((r) => r.data),
+ getLevelDistribution: (starId) => dashboardRequest('/level-distribution', { star_id: starId }).then((r) => r.data),
+ getUpgradeProgress: (starId) => dashboardRequest('/upgrade-progress', { star_id: starId }).then((r) => r.data),
+}
+```
+
+- [ ] **Step 3: 手动验证 mock 触发**
+
+在 H5 dev console 跑:
+```js
+const { dashboardApi } = await import('/utils/api.js')
+const today = await dashboardApi.getTodayOverview(1)
+console.log(today) // 预期: { crystal_balance: 2713, today_income: 213, week_rank: 12 }
+```
+
+- [ ] **Step 4: Commit**
+
+```bash
+git add frontend/utils/mock/dashboard.js frontend/utils/api.js
+git commit -m "feat(dashboard): 追加 mock 数据工厂 + dashboardApi 命名空间"
+```
+
+---
+
+## Task 3: useDashboardData Composable
+
+**Files:**
+- Create: `frontend/composables/useDashboardData.js`
+
+- [ ] **Step 1: 完整实现 composable**
+
+```javascript
+import { ref, computed, onScopeDisposal } from 'vue'
+import { dashboardApi } from '@/utils/api'
+
+/**
+ * 数据看板聚合 composable
+ * - 7 个接口并发(Promise.allSettled,单失败不阻塞)
+ * - 按 section 维度暴露 loading/error/data
+ * - 30 分钟内 refresh 不重发请求(lastFetched 缓存)
+ * - effectScope + onScopeDisposal 自动清理(页面 onUnmounted 调用 dispose)
+ *
+ * @param {object} options
+ * @param {number|null} options.starId 顶粉星城 ID
+ * @returns {UseDashboardDataReturn}
+ */
+export function useDashboardData({ starId = null } = {}) {
+ // —— 内部 state ——
+ const loading = ref({
+ overall: false,
+ today: false,
+ curve: false,
+ exhibition: false,
+ likeIncome: false,
+ topAssets: false,
+ levels: false,
+ upgrades: false,
+ })
+
+ const error = ref({
+ today: null,
+ curve: null,
+ exhibition: null,
+ likeIncome: null,
+ topAssets: null,
+ levels: null,
+ upgrades: null,
+ })
+
+ const data = ref({
+ today: null,
+ curve: null,
+ exhibition: null,
+ likeIncome: null,
+ topAssets: null,
+ levels: null,
+ upgrades: null,
+ })
+
+ const lastFetched = ref(0)
+ const STALE_MS = 30 * 60 * 1000 // 30 分钟
+
+ const isReady = computed(() => Object.values(data.value).every((v) => v !== null))
+
+ // —— 内部辅助:单 section 加载 ——
+ async function loadSection(section, fetcher) {
+ loading.value[section] = true
+ error.value[section] = null
+ try {
+ const result = await fetcher(starId)
+ data.value[section] = result
+ } catch (e) {
+ error.value[section] = e.message || '加载失败'
+ data.value[section] = null
+ } finally {
+ loading.value[section] = false
+ }
+ }
+
+ // —— 全量加载 ——
+ async function loadAll(force = false) {
+ if (!force && Date.now() - lastFetched.value < STALE_MS) return
+ loading.value.overall = true
+ try {
+ // 并发执行,单个失败由 loadSection 内部捕获
+ await Promise.allSettled([
+ loadSection('today', dashboardApi.getTodayOverview),
+ loadSection('curve', dashboardApi.get7DayIncomeCurve),
+ loadSection('exhibition', dashboardApi.getExhibitionSummary),
+ loadSection('likeIncome', dashboardApi.getLikeIncomeByLevel),
+ loadSection('topAssets', dashboardApi.getTopAssets),
+ loadSection('levels', dashboardApi.getLevelDistribution),
+ loadSection('upgrades', dashboardApi.getUpgradeProgress),
+ ])
+ lastFetched.value = Date.now()
+ } finally {
+ loading.value.overall = false
+ }
+ }
+
+ // —— 局部刷新 ——
+ async function refresh(section) {
+ if (!section) {
+ return loadAll(true)
+ }
+ const fetcherMap = {
+ today: dashboardApi.getTodayOverview,
+ curve: dashboardApi.get7DayIncomeCurve,
+ exhibition: dashboardApi.getExhibitionSummary,
+ likeIncome: dashboardApi.getLikeIncomeByLevel,
+ topAssets: dashboardApi.getTopAssets,
+ levels: dashboardApi.getLevelDistribution,
+ upgrades: dashboardApi.getUpgradeProgress,
+ }
+ if (!fetcherMap[section]) return
+ await loadSection(section, fetcherMap[section])
+ }
+
+ // —— effectScope 资源释放 ——
+ // 调用方在页面 onUnmounted 中调用 dispose()
+ function dispose() {
+ // ref 状态由 Vue 自动 GC;此处保留接口给未来清理(如取消 in-flight 请求)
+ // 当前实现:无 in-flight 引用,无需额外清理
+ }
+
+ // 自动调用一次首屏加载
+ loadAll()
+
+ return {
+ loading,
+ error,
+ data,
+ refresh,
+ isReady,
+ lastFetched,
+ dispose,
+ }
+}
+```
+
+- [ ] **Step 2: 手动验证 composable 行为**
+
+在 H5 dev console 跑:
+```js
+const { useDashboardData } = await import('/composables/useDashboardData.js')
+const { data, loading, isReady, refresh } = useDashboardData({ starId: 1 })
+// 等 ~1 秒
+setTimeout(() => {
+ console.log('isReady:', isReady.value) // true
+ console.log('today:', data.value.today) // { crystal_balance: 2713, ... }
+ console.log('curve.points.length:', data.value.curve.points.length) // 7
+ console.log('exhibition.top5.length:', data.value.exhibition.top5.length) // 5
+}, 1500)
+```
+
+预期:所有数据加载完成,形状如 mock。
+
+- [ ] **Step 3: 验证局部刷新**
+
+```js
+// 在同一 console 继续
+await refresh('curve')
+console.log('loading.curve:', loading.value.curve) // 预期: false(load 完自动归位)
+console.log('data.curve:', data.value.curve !== null) // true
+```
+
+- [ ] **Step 4: Commit**
+
+```bash
+git add frontend/composables/useDashboardData.js
+git commit -m "feat(dashboard): useDashboardData composable(7接口并发+section级refresh)"
+```
+
+---
+
+## Task 4: dashboard.vue 页面骨架
+
+**Files:**
+- Create: `frontend/pages/dashboard/dashboard.vue`
+
+- [ ] **Step 1: 完整实现页面骨架**
+
+```vue
+
+
+
+
+
+
+
+
+ 骨架页(组件将在 Task 5-10 添加)
+
+
+
+
+
+
+ 🏆
+ 赛季总览 · 即将上线
+ 历史赛季数据正在筹备中
+
+
+
+
+
+
+
+
+```
+
+- [ ] **Step 2: 创建 `DashboardHeader.vue` 占位(Task 5 会完整实现)**
+
+文件 `frontend/pages/dashboard/components/DashboardHeader.vue`:
+
+```vue
+
+
+
+
+
+
+
+```
+
+- [ ] **Step 3: 手动验证骨架页**
+
+访问 H5 dev URL `/pages/dashboard/dashboard`:
+- 预期:红色装饰背景 + "数据看板" 标题 + 两个 Tab + "骨架页" 占位文字
+- 切换 Tab:右半部分在占位和"赛季总览"间切换
+- 打开 devtools console:1.5 秒后 `data.today` 等字段非空(可通过 Vue devtools 检查)
+
+- [ ] **Step 4: Commit**
+
+```bash
+git add frontend/pages/dashboard/dashboard.vue frontend/pages/dashboard/components/DashboardHeader.vue
+git commit -m "feat(dashboard): 页面骨架 + Header 占位"
+```
+
+---
+
+## Task 5: DashboardHeader 完整实现(毛绒怪 + 渐变标题)
+
+**Files:**
+- Modify: `frontend/pages/dashboard/components/DashboardHeader.vue`(替换占位为完整实现)
+
+- [ ] **Step 1: 替换为完整实现**
+
+```vue
+
+
+
+
+
+
+
+```
+
+- [ ] **Step 2: 手动验证**
+
+访问 H5 dev URL `/pages/dashboard/dashboard`:
+- 预期:粉红渐变背景 + 红色光晕 + 圆形毛绒怪占位(带红色光晕阴影)+ 渐变文字"数据看板"+ 紫蓝粉渐变 Tab 胶囊
+- 切换 Tab:胶囊平滑滑动
+
+- [ ] **Step 3: Commit**
+
+```bash
+git add frontend/pages/dashboard/components/DashboardHeader.vue
+git commit -m "feat(dashboard): Header 完整视觉(毛绒怪占位+渐变标题+Tab胶囊)"
+```
+
+---
+
+## Task 6: CrystalOverview 组件(顶部双卡)
+
+**Files:**
+- Create: `frontend/pages/dashboard/components/CrystalOverview.vue`
+
+- [ ] **Step 1: 完整实现**
+
+```vue
+
+
+
+
+ 加载失败,点击重试
+
+
+
+
+
+
+
+
+
+
+ 水晶余额
+ {{ data.crystal_balance }}
+
+
+ 今日收益
+ + {{ data.today_income }}
+
+
+
+
+
+
+
+
+```
+
+- [ ] **Step 2: 在 dashboard.vue 中挂载组件**
+
+修改 `frontend/pages/dashboard/dashboard.vue` 的 template:
+
+把
+```vue
+
+
+ 骨架页(组件将在 Task 5-10 添加)
+
+
+```
+
+替换为:
+```vue
+
+
+
+```
+
+并在 components 中追加:
+```js
+components: { DashboardHeader, CrystalOverview },
+```
+
+- [ ] **Step 3: 手动验证**
+
+刷新 H5 dev URL:
+- 预期:双卡显示"水晶余额 2713"和"今日收益 + 213",金黄色数字 + 渐变背景
+- 1.5s 后才显示(loading 期间是骨架屏)
+
+- [ ] **Step 4: Commit**
+
+```bash
+git add frontend/pages/dashboard/components/CrystalOverview.vue frontend/pages/dashboard/dashboard.vue
+git commit -m "feat(dashboard): CrystalOverview 双卡(水晶余额+今日收益)"
+```
+
+---
+
+## Task 7: IncomeCurve 组件(uCharts 七日柱状+折线)
+
+**Files:**
+- Create: `frontend/pages/dashboard/components/IncomeCurve.vue`
+- Create: `frontend/pages/dashboard/chart-theme.json`(uCharts 主题配置)
+
+- [ ] **Step 1: 创建 uCharts 主题配置 `frontend/pages/dashboard/chart-theme.json`**
+
+> **注意**:此文件路径在 Vue 项目中通过 `?url` 或 `import` 引入。H5 用 `import`,小程序/App 走条件编译。
+
+```json
+{
+ "type": "barline",
+ "color": ["#FFCC14", "#1BAFEE"],
+ "padding": [16, 16, 8, 16],
+ "dataLabel": false,
+ "legend": { "show": false },
+ "xAxis": {
+ "disableGrid": true,
+ "axisLine": false,
+ "fontColor": "#FFFFFF",
+ "fontSize": 9
+ },
+ "yAxis": {
+ "data": [{ "min": 0 }],
+ "disableGrid": true,
+ "axisLine": false,
+ "fontColor": "#FFFFFF",
+ "fontSize": 9
+ },
+ "extra": {
+ "bar": {
+ "type": "group",
+ "width": 18,
+ "activeBgColor": "#000000",
+ "activeBgOpacity": 0.1,
+ "linear": true,
+ "color": ["#FFDF77", "#B984FF", "#FF8183"]
+ },
+ "line": {
+ "type": "curve",
+ "width": 2,
+ "activeType": "hollow"
+ }
+ }
+}
+```
+
+- [ ] **Step 2: 完整实现 IncomeCurve.vue**
+
+```vue
+
+
+ 七日收益曲线
+
+
+
+ 加载失败,点击重试
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 📊 {{ points.length }} 天数据
+ App 端图表组件配置中
+
+
+
+
+
+
+
+
+
+
+```
+
+- [ ] **Step 3: 在 dashboard.vue 中挂载组件**
+
+在 CrystalOverview 之后追加:
+```vue
+
+```
+
+components 追加 `IncomeCurve`。
+
+- [ ] **Step 4: 安装 uCharts**
+
+```bash
+cd frontend && npm install qiun-data-charts
+```
+
+如项目无 H5 组件库依赖关系导致构建失败,回退到 `chart-theme.json` + 占位文字(Step 2 中已留 fallback)。
+
+- [ ] **Step 5: 手动验证**
+
+刷新 H5 dev URL:
+- 预期:渐变卡片 + "七日收益曲线" 标题 + 柱状图(7 根柱,渐变填充)+ 折线(蓝黄渐变)+ 高亮当日 "+ 312" + 日期
+- 加载中:渐变骨架屏闪烁
+
+- [ ] **Step 6: Commit**
+
+```bash
+git add frontend/pages/dashboard/components/IncomeCurve.vue frontend/pages/dashboard/dashboard.vue frontend/pages/dashboard/chart-theme.json frontend/package.json frontend/package-lock.json
+git commit -m "feat(dashboard): IncomeCurve 七日柱状+折线(uCharts H5)"
+```
+
+---
+
+## Task 8: ExhibitionCenter 组件(3 联 + 5 行表格)
+
+**Files:**
+- Create: `frontend/pages/dashboard/components/ExhibitionCenter.vue`
+
+- [ ] **Step 1: 完整实现**
+
+```vue
+
+
+ 展出收益中心
+
+
+
+ 加载失败,点击重试
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {{ data.exhibiting_count }} / {{ data.starbook_count }}
+ 展出中 / 星册中
+
+
+ {{ data.total_duration }}
+ 累计展出时长
+
+
+ {{ data.total_earnings }}
+ 累计展出收益
+
+
+
+
+
+
+
+
+
+ 🎨
+
+
+ {{ item.duration_7d }}
+ {{ item.earnings_7d }}
+ {{ item.avg_earnings }} / H
+
+
+
+
+
+
+
+
+
+```
+
+- [ ] **Step 2: 在 dashboard.vue 中挂载**
+
+在 IncomeCurve 后追加:
+```vue
+
+```
+
+components 追加 `ExhibitionCenter`。
+
+- [ ] **Step 3: 手动验证**
+
+刷新 H5 dev URL:
+- 预期:玻璃拟态卡片 + "展出收益中心" 标题 + 3 联统计(21/33、712:13:56、39721)+ 5 行表格(每行 4×5 缩略图 + 时长 + 收益 + 平均)
+
+- [ ] **Step 4: Commit**
+
+```bash
+git add frontend/pages/dashboard/components/ExhibitionCenter.vue frontend/pages/dashboard/dashboard.vue
+git commit -m "feat(dashboard): ExhibitionCenter 3联+5行表格"
+```
+
+---
+
+## Task 9: LikeIncomeBoard 组件
+
+**Files:**
+- Create: `frontend/pages/dashboard/components/LikeIncomeBoard.vue`
+
+- [ ] **Step 1: 完整实现**
+
+```vue
+
+
+ 点赞收益看板
+
+
+
+ 加载失败,点击重试
+
+
+
+
+
+
+
+
+
+
+
+ {{ stats.total_like_count }}
+ 累积点赞
+
+
+ {{ stats.total_income }}
+ 累计收益
+
+
+
+
+
+
+
+
+ {{ item.level }}
+
+
+ {{ item.level }}
+ {{ item.total_income }}
+
+
+
+
+
+
+
+
+
+```
+
+- [ ] **Step 2: 在 dashboard.vue 中挂载**
+
+在 ExhibitionCenter 后追加:
+```vue
+
+```
+
+components 追加 `LikeIncomeBoard`。
+
+- [ ] **Step 3: 手动验证 + Commit**
+
+```bash
+git add frontend/pages/dashboard/components/LikeIncomeBoard.vue frontend/pages/dashboard/dashboard.vue
+git commit -m "feat(dashboard): LikeIncomeBoard 左侧统计+右侧等级列表"
+```
+
+---
+
+## Task 10: CollectionMatrix + TopFiveAssets
+
+**Files:**
+- Create: `frontend/pages/dashboard/components/CollectionMatrix.vue`
+- Create: `frontend/pages/dashboard/components/TopFiveAssets.vue`
+
+- [ ] **Step 1: 完整实现 TopFiveAssets.vue**
+
+```vue
+
+
+ 历史藏品收益 TOP 5
+
+ 暂无数据
+
+
+
+
+ 🏆
+
+
+ TOP {{ item.rank }}
+
+
+
+
+
+
+
+
+
+```
+
+- [ ] **Step 2: 完整实现 CollectionMatrix.vue(容器,先只接 TopFiveAssets)**
+
+```vue
+
+
+ 藏品矩阵
+
+
+
+
+
+
+
+
+
+
+
+```
+
+- [ ] **Step 3: 在 dashboard.vue 中挂载**
+
+在 LikeIncomeBoard 后追加:
+```vue
+
+```
+
+components 追加 `CollectionMatrix`。
+
+- [ ] **Step 4: 手动验证 + Commit**
+
+```bash
+git add frontend/pages/dashboard/components/TopFiveAssets.vue frontend/pages/dashboard/components/CollectionMatrix.vue frontend/pages/dashboard/dashboard.vue
+git commit -m "feat(dashboard): CollectionMatrix 容器 + TopFiveAssets"
+```
+
+---
+
+## Task 11: LevelDistribution 组件(5 个 conic-gradient 环形图)
+
+**Files:**
+- Create: `frontend/pages/dashboard/components/LevelDistribution.vue`
+
+- [ ] **Step 1: 完整实现**
+
+```vue
+
+
+ 藏品等级分布
+
+ 暂无数据
+
+
+
+
+
+ {{ item.count }}
+
+
+ {{ item.level }}
+ {{ getPercent(item) }}%
+
+
+
+
+
+
+
+
+```
+
+- [ ] **Step 2: 在 CollectionMatrix.vue 中挂载**
+
+在 `` 后追加:
+```vue
+
+```
+
+并追加 import 和 components 注册。
+
+- [ ] **Step 3: 手动验证 + Commit**
+
+```bash
+git add frontend/pages/dashboard/components/LevelDistribution.vue frontend/pages/dashboard/components/CollectionMatrix.vue
+git commit -m "feat(dashboard): LevelDistribution 5个conic-gradient环形图"
+```
+
+---
+
+## Task 12: UpcomingUpgrades + RecentUpgrades
+
+**Files:**
+- Create: `frontend/pages/dashboard/components/UpcomingUpgrades.vue`
+- Create: `frontend/pages/dashboard/components/RecentUpgrades.vue`
+
+- [ ] **Step 1: 完整实现 UpcomingUpgrades.vue**
+
+```vue
+
+
+ 即将升级
+
+ 暂无数据
+
+
+
+
+
+ {{ item.asset_name[0] }}
+
+
+
+
+
+ {{ item.like_progress }}%
+
+
+
+ {{ item.duration_progress }}%
+
+
+
+
+
+
+
+
+
+
+```
+
+- [ ] **Step 2: 完整实现 RecentUpgrades.vue**
+
+```vue
+
+
+ 最近升级
+
+ 暂无数据
+
+
+
+
+
+ {{ item.asset_name[0] }}
+
+
+
+ {{ item.asset_name }}
+ {{ formatTime(item.upgrade_time) }}
+
+
+ {{ item.new_level }}
+ Lv UP
+
+
+
+
+
+
+
+
+
+```
+
+- [ ] **Step 3: 在 CollectionMatrix.vue 中挂载(两列布局)**
+
+替换 CollectionMatrix.vue 的 template 为:
+```vue
+
+
+ 藏品矩阵
+
+
+
+
+
+
+
+
+```
+
+并在 `
+```
+
+- [ ] **Step 2: 完整实现 EmptyState.vue**
+
+```vue
+
+
+ {{ icon }}
+ {{ text }}
+
+
+
+
+
+
+```
+
+- [ ] **Step 3: Commit**
+
+```bash
+git add frontend/pages/dashboard/components/SectionSkeleton.vue frontend/pages/dashboard/components/EmptyState.vue
+git commit -m "feat(dashboard): SectionSkeleton + EmptyState 通用组件"
+```
+
+> **注**:本任务暂不替换各 section 内的内联骨架(已能用),后续若需要统一可在各组件中替换 `` 为 ``。
+
+---
+
+## Task 14: onShow 刷新 + Tab 缓存 + 下拉刷新
+
+**Files:**
+- Modify: `frontend/pages/dashboard/dashboard.vue`
+
+- [ ] **Step 1: 加入 onShow 静默刷新 + Tab 缓存**
+
+完整替换 `