From 3dc46460fefd89a4731f31259d510dd329da360f Mon Sep 17 00:00:00 2001 From: Jason Date: Sat, 3 Oct 2026 20:51:52 +0800 Subject: [PATCH] fix(usage): keep previous stats while filters, range or page change None of the usage queries had placeholder data, so every new filter, time range or page cleared the dashboard: hero metrics flashed to an ellipsis, the trend chart spun, and the request log table collapsed to its skeleton (scrolling back up and possibly toggling the scrollbar). Use keepPreviousData on the summary, trend, provider/model stats and request log queries so the old numbers stay until the new ones land. --- src/lib/query/usage.ts | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/src/lib/query/usage.ts b/src/lib/query/usage.ts index b07cd69e4..fd4e41cb0 100644 --- a/src/lib/query/usage.ts +++ b/src/lib/query/usage.ts @@ -1,4 +1,9 @@ -import { useQuery, useMutation, useQueryClient } from "@tanstack/react-query"; +import { + keepPreviousData, + useQuery, + useMutation, + useQueryClient, +} from "@tanstack/react-query"; import { usageApi } from "@/lib/api/usage"; import { resolveUsageRange } from "@/lib/usageRange"; import type { @@ -161,6 +166,8 @@ function normalizeScopeFilters(filters?: UsageScopeFilters): UsageScopeFilters { } // Hooks +// 统计类查询都带 keepPreviousData:换筛选、时间范围、翻页时先留着上一份数据, +// 新数据到了再换,不让指标闪成「…」、请求日志表塌成骨架。 export function useUsageSummary( range: UsageRangeSelection, filters?: UsageScopeFilters, @@ -185,6 +192,7 @@ export function useUsageSummary( effective.model, ); }, + placeholderData: keepPreviousData, refetchInterval: options?.refetchInterval ?? DEFAULT_REFETCH_INTERVAL_MS, refetchIntervalInBackground: options?.refetchIntervalInBackground ?? false, }); @@ -212,6 +220,7 @@ export function useUsageSummaryByApp( filters?.model, ); }, + placeholderData: keepPreviousData, refetchInterval: options?.refetchInterval ?? DEFAULT_REFETCH_INTERVAL_MS, refetchIntervalInBackground: options?.refetchIntervalInBackground ?? false, }); @@ -241,6 +250,7 @@ export function useUsageTrends( effective.model, ); }, + placeholderData: keepPreviousData, refetchInterval: options?.refetchInterval ?? DEFAULT_REFETCH_INTERVAL_MS, refetchIntervalInBackground: options?.refetchIntervalInBackground ?? false, }); @@ -270,6 +280,7 @@ export function useProviderStats( effective.model, ); }, + placeholderData: keepPreviousData, refetchInterval: options?.refetchInterval ?? DEFAULT_REFETCH_INTERVAL_MS, refetchIntervalInBackground: options?.refetchIntervalInBackground ?? false, }); @@ -299,6 +310,7 @@ export function useModelStats( effective.model, ); }, + placeholderData: keepPreviousData, refetchInterval: options?.refetchInterval ?? DEFAULT_REFETCH_INTERVAL_MS, refetchIntervalInBackground: options?.refetchIntervalInBackground ?? false, }); @@ -328,6 +340,7 @@ export function useRequestLogs({ const effectiveFilters = { ...filters, ...resolveUsageRange(range) }; return usageApi.getRequestLogs(effectiveFilters, page, pageSize); }, + placeholderData: keepPreviousData, refetchInterval: options?.refetchInterval ?? DEFAULT_REFETCH_INTERVAL_MS, // 每30秒自动刷新 refetchIntervalInBackground: options?.refetchIntervalInBackground ?? false, });