import { ref, computed } from 'vue' import { dashboardApi } from '@/utils/api' import * as mock from '@/utils/mock/dashboard' // USE_MOCK_API 是写死常量——直接内联短路逻辑,绕开 api.js 的 dashboardRequest 链路 // 原因:标准基座 + Vite HMR 在 App 端会让 api.js 内部的 async 包装 + signal 透传 // 导致 useDashboardData.loadSection 里的 await 永远不 resolve。 // 直接调 mock 工厂,200-600ms 内必定 resolve。 const MOCK_FACTORIES = { today: mock.mockTodayOverview, curve: mock.mock7DayIncomeCurve, exhibition: mock.mockExhibitionSummary, likeIncome: mock.mockLikeIncomeByLevel, topAssets: mock.mockTopAssets, levels: mock.mockLevelDistribution, upgrades: mock.mockUpgradeProgress, } // section 名 → fetcher 映射,模块级单例,loadAll 与 refresh 复用 const SECTION_FETCHERS = { today: dashboardApi.getTodayOverview, curve: dashboardApi.get7DayIncomeCurve, exhibition: dashboardApi.getExhibitionSummary, likeIncome: dashboardApi.getLikeIncomeByLevel, topAssets: dashboardApi.getTopAssets, levels: dashboardApi.getLevelDistribution, upgrades: dashboardApi.getUpgradeProgress, } /** * 数据看板聚合 composable * - 7 个接口并发(Promise.allSettled,单失败不阻塞) * - 按 section 维度暴露 loading/error/data * - 30 分钟内 refresh 不重发请求(lastFetched 缓存) * * @param {object} options * @param {number|null} options.starId 顶粉星城 ID */ export function useDashboardData({ starId = null } = {}) { 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 const isReady = computed(() => Object.values(data.value).every((v) => v !== null && v !== undefined) ) // 内部辅助:单 section 加载 async function loadSection(section, fetcher) { loading.value[section] = true error.value[section] = null try { // USE_MOCK_API 走 mock:直接调本地工厂,绕开 api.js 的 dashboardRequest 包装 // 解决标准基座 + Vite HMR 下 await fetcher(starId, { signal }) 永远不 resolve 的问题 const mockFactory = MOCK_FACTORIES[section] const result = mockFactory ? await mockFactory({ star_id: starId }) : await fetcher(starId) data.value[section] = result?.data ?? 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 { await Promise.allSettled( Object.entries(SECTION_FETCHERS).map(([section, fetcher]) => loadSection(section, fetcher) ) ) lastFetched.value = Date.now() } finally { loading.value.overall = false } } // 局部刷新:refresh('curve') 只刷一个;refresh() 全量 cache-aware;refresh(null, true) 全量强制 async function refresh(section, force = false) { if (section) { const fetcher = SECTION_FETCHERS[section] if (!fetcher) return return loadSection(section, fetcher) } return loadAll(force) } // 初次加载 loadAll() return { loading, error, data, refresh, isReady, lastFetched, } }