11d321f218
按"配置和观测放一个页面"收口:删掉重复的「计价 & 预算」页(其预算区块是 mock), 把真·计价配置并入「计费 & 用量」,形成 规则→扣费→观测 一页闭环。 - 新组件 BillingRules:全局 token→积分汇率 + 每模型 单价/币种/积分权重(读写 /admin/pricing + /admin/billing-config),改完对后续任务实时生效。 - UsagePage:顶部「计费规则」(配置端) + 下方「用量观测」(按规则折算后的实际消耗)。 - 删 PricingPage + /pricing 路由;api 加 credit_weight / getBillingConfig / saveBillingConfig。 live 验证(preview):改汇率保存→持久化→新任务按新汇率扣→用量观测反映; tsc + 41 vitest 全过;无 PricingPage 残引。 Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
115 lines
3.1 KiB
TypeScript
115 lines
3.1 KiB
TypeScript
import { lazy, type ReactNode } from "react";
|
|
import { Soon } from "./components/Soon";
|
|
|
|
// 路由注册表 —— 控制台的单一事实源:导航 + 内容都从这里派生。
|
|
// 新增页面 = 在此加一条;real 页面用 lazy 懒加载(代码分割)。
|
|
|
|
|
|
const DashboardPage = lazy(() => import("./pages/DashboardPage").then((m) => ({ default: m.DashboardPage })));
|
|
const UsagePage = lazy(() => import("./pages/UsagePage").then((m) => ({ default: m.UsagePage })));
|
|
const ModelsPage = lazy(() => import("./pages/ModelsPage").then((m) => ({ default: m.ModelsPage })));
|
|
const DatasourcesPage = lazy(() => import("./pages/DatasourcesPage").then((m) => ({ default: m.DatasourcesPage })));
|
|
const StatusPage = lazy(() => import("./pages/StatusPage").then((m) => ({ default: m.StatusPage })));
|
|
const EvalsPage = lazy(() => import("./pages/EvalsPage").then((m) => ({ default: m.EvalsPage })));
|
|
const TenantsPage = lazy(() => import("./pages/TenantsPage").then((m) => ({ default: m.TenantsPage })));
|
|
const GuardrailsPage = lazy(() => import("./pages/GuardrailsPage").then((m) => ({ default: m.GuardrailsPage })));
|
|
const PromptsPage = lazy(() => import("./pages/PromptsPage").then((m) => ({ default: m.PromptsPage })));
|
|
const AuditPage = lazy(() => import("./pages/AuditPage").then((m) => ({ default: m.AuditPage })));
|
|
|
|
export interface RouteDef {
|
|
path: string;
|
|
label: string;
|
|
group: string;
|
|
ready?: boolean;
|
|
element: ReactNode;
|
|
}
|
|
|
|
export const routes: RouteDef[] = [
|
|
{
|
|
path: "/dashboard",
|
|
label: "概览",
|
|
group: "分析",
|
|
ready: true,
|
|
element: <DashboardPage />,
|
|
},
|
|
{
|
|
path: "/usage",
|
|
label: "计费 & 用量",
|
|
group: "分析",
|
|
ready: true,
|
|
element: <UsagePage />,
|
|
},
|
|
{
|
|
path: "/models",
|
|
label: "模型",
|
|
group: "配置",
|
|
ready: true,
|
|
element: <ModelsPage />,
|
|
},
|
|
{
|
|
path: "/datasources",
|
|
label: "数据源 & RAG",
|
|
group: "配置",
|
|
ready: true,
|
|
element: <DatasourcesPage />,
|
|
},
|
|
{
|
|
path: "/prompts",
|
|
label: "提示词",
|
|
group: "配置",
|
|
ready: true,
|
|
element: <PromptsPage />,
|
|
},
|
|
{
|
|
path: "/status",
|
|
label: "服务状态",
|
|
group: "运维",
|
|
ready: true,
|
|
element: <StatusPage />,
|
|
},
|
|
{
|
|
path: "/evals",
|
|
label: "自动评测",
|
|
group: "运维",
|
|
ready: true,
|
|
element: <EvalsPage />,
|
|
},
|
|
{
|
|
path: "/audit",
|
|
label: "审计 & 安全",
|
|
group: "运维",
|
|
ready: true,
|
|
element: <AuditPage />,
|
|
},
|
|
{
|
|
path: "/tenants",
|
|
label: "租户 & 用户",
|
|
group: "平台",
|
|
ready: true,
|
|
element: <TenantsPage />,
|
|
},
|
|
{
|
|
path: "/guardrails",
|
|
label: "安全护栏",
|
|
group: "平台",
|
|
ready: true,
|
|
element: <GuardrailsPage />,
|
|
},
|
|
];
|
|
|
|
export const defaultPath = "/dashboard";
|
|
|
|
// 派生分组导航(保持注册顺序)。
|
|
export function navGroups(): Array<{ group: string; items: RouteDef[] }> {
|
|
const out: Array<{ group: string; items: RouteDef[] }> = [];
|
|
for (const r of routes) {
|
|
let g = out.find((x) => x.group === r.group);
|
|
if (!g) {
|
|
g = { group: r.group, items: [] };
|
|
out.push(g);
|
|
}
|
|
g.items.push(r);
|
|
}
|
|
return out;
|
|
}
|