- 范围新增 yesterday:本地时区昨日 0 点 ~ 今日 0 点,range_end 引入上界过滤 - RangeStats 新增 modelsByName:按裸模型名聚合,同一模型跨供应商合并, 子代理记录归入 __secondary__ 单独成行,按 Token 降序 - 趋势选项卡第 4 个 tab「模型用量统计」:新增 ModelTotalsChart 横向条形图, 悬停显示请求数/Token/缓存命中/费用 - 概览卡网格扩展为 5 张;i18n 中英同步 - 顺带:pricing 测试基准从已下架的 moonshotai/kimi-k2.5 切换到 kimi-k3
65 lines
1.9 KiB
TypeScript
65 lines
1.9 KiB
TypeScript
import { useCallback, useEffect, useState } from "react";
|
|
import { invoke } from "@tauri-apps/api/core";
|
|
import type { SummaryResult } from "../types/dashboard";
|
|
|
|
export type DashboardRange = "today" | "yesterday" | "7d" | "30d" | "all";
|
|
|
|
const RANGE_STORAGE_KEY = "kimi-switch-dashboard-range";
|
|
|
|
export function useDashboard() {
|
|
const [range, setRange] = useState<DashboardRange>(() => {
|
|
try {
|
|
const stored = localStorage.getItem(RANGE_STORAGE_KEY) as DashboardRange | null;
|
|
if (stored === "today" || stored === "yesterday" || stored === "7d" || stored === "30d" || stored === "all") {
|
|
return stored;
|
|
}
|
|
} catch {
|
|
// ignore
|
|
}
|
|
return "30d";
|
|
});
|
|
|
|
const [data, setData] = useState<SummaryResult | null>(null);
|
|
const [loading, setLoading] = useState(false);
|
|
const [error, setError] = useState<string | null>(null);
|
|
/** Round-trip timing for the last get_summary call (user-perceived lag). */
|
|
const [loadStats, setLoadStats] = useState<{ ms: number; kb: number } | null>(null);
|
|
|
|
const refresh = useCallback(async (force = false) => {
|
|
setLoading(true);
|
|
setError(null);
|
|
const start = performance.now();
|
|
try {
|
|
const result = await invoke<SummaryResult>("get_summary", {
|
|
range,
|
|
refresh: force,
|
|
});
|
|
setData(result);
|
|
setLoadStats({
|
|
ms: Math.round(performance.now() - start),
|
|
kb: Math.round(JSON.stringify(result).length / 1024),
|
|
});
|
|
} catch (err) {
|
|
const msg = err instanceof Error ? err.message : String(err);
|
|
setError(msg);
|
|
} finally {
|
|
setLoading(false);
|
|
}
|
|
}, [range]);
|
|
|
|
useEffect(() => {
|
|
refresh();
|
|
}, [refresh]);
|
|
|
|
const changeRange = useCallback((next: DashboardRange) => {
|
|
setRange(next);
|
|
try {
|
|
localStorage.setItem(RANGE_STORAGE_KEY, next);
|
|
} catch {
|
|
// ignore
|
|
}
|
|
}, []);
|
|
|
|
return { range, changeRange, data, loading, error, refresh, loadStats };
|
|
}
|