feat(admin): 数据源页转 RAG 运维台 + 支付/模型菜单重组 + 概览升级为仪表盘
后端: - 新增 POST /admin/kb/search:管理端跨租户检索,支持 mode 指定单路 (vector/fulltext/graph/hybrid),不走 scopedKB(否则会被强制锁到调用者 自己的 space,跨租户排障就没法做了) - KB 清单补 space_id(检索键是 <space_id>/<name>,缺它前端拼不出 key) 前端: - 数据源&RAG 页补「检索试验台」:同一 query 并排跑生产链路 + 四路诊断, 召回不准时能直接定位是向量/分词/图谱哪一环挂了 - 支付拆成「配置 / 订单与对账」两个子页,挂到运维 > 支付 下; 导航支持二级菜单(NavParent 命中子路由自动展开) - SettingsPage → ModelConfigPage「模型配置」,模型参数与计费规则合一 - 概览 → 仪表盘:并入计费与用量(UsagePage → UsageSection), 去掉系统健康拓扑(与服务状态页重复,同一份 /admin/status 数据) - 全局隐藏滚动条(保留滚动) Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -5,8 +5,7 @@ import { lazy, type ReactNode } from "react";
|
||||
|
||||
|
||||
const DashboardPage = lazy(() => import("./pages/DashboardPage").then((m) => ({ default: m.DashboardPage })));
|
||||
const UsagePage = lazy(() => import("./pages/UsagePage").then((m) => ({ default: m.UsagePage })));
|
||||
const SettingsPage = lazy(() => import("./pages/SettingsPage").then((m) => ({ default: m.SettingsPage })));
|
||||
const ModelConfigPage = lazy(() => import("./pages/ModelConfigPage").then((m) => ({ default: m.ModelConfigPage })));
|
||||
const DatasourcesPage = lazy(() => import("./pages/DatasourcesPage").then((m) => ({ default: m.DatasourcesPage })));
|
||||
const StatusPage = lazy(() => import("./pages/StatusPage").then((m) => ({ default: m.StatusPage })));
|
||||
const TasksPage = lazy(() => import("./pages/TasksPage").then((m) => ({ default: m.TasksPage })));
|
||||
@@ -15,52 +14,33 @@ const TenantsPage = lazy(() => import("./pages/TenantsPage").then((m) => ({ defa
|
||||
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 })));
|
||||
const PaymentConfigPage = lazy(() => import("./pages/PaymentConfigPage").then((m) => ({ default: m.PaymentConfigPage })));
|
||||
const PaymentOrdersPage = lazy(() => import("./pages/PaymentOrdersPage").then((m) => ({ default: m.PaymentOrdersPage })));
|
||||
const SpacesPage = lazy(() => import("./pages/SpacesPage").then((m) => ({ default: m.SpacesPage })));
|
||||
|
||||
export interface RouteDef {
|
||||
path: string;
|
||||
label: string;
|
||||
group: string;
|
||||
/** 二级菜单名。同 group 内 parent 相同的若干条会聚成一个可展开子菜单(如 运维 > 支付 > 配置/订单与对账)。 */
|
||||
parent?: string;
|
||||
ready?: boolean;
|
||||
element: ReactNode;
|
||||
}
|
||||
|
||||
/** 侧栏一个节点:要么是单条路由,要么是带子项的可展开二级菜单。 */
|
||||
export type NavNode =
|
||||
| { kind: "item"; route: RouteDef }
|
||||
| { kind: "parent"; label: string; items: RouteDef[] };
|
||||
|
||||
export const routes: RouteDef[] = [
|
||||
{
|
||||
path: "dashboard",
|
||||
label: "概览",
|
||||
label: "仪表盘",
|
||||
group: "分析",
|
||||
ready: true,
|
||||
element: <DashboardPage />,
|
||||
},
|
||||
{
|
||||
path: "usage",
|
||||
label: "计费 & 用量",
|
||||
group: "分析",
|
||||
ready: true,
|
||||
element: <UsagePage />,
|
||||
},
|
||||
{
|
||||
path: "settings",
|
||||
label: "系统配置",
|
||||
group: "配置",
|
||||
ready: true,
|
||||
element: <SettingsPage />,
|
||||
},
|
||||
{
|
||||
path: "datasources",
|
||||
label: "数据源 & RAG",
|
||||
group: "配置",
|
||||
ready: true,
|
||||
element: <DatasourcesPage />,
|
||||
},
|
||||
{
|
||||
path: "prompts",
|
||||
label: "提示词",
|
||||
group: "配置",
|
||||
ready: true,
|
||||
element: <PromptsPage />,
|
||||
},
|
||||
{
|
||||
path: "status",
|
||||
label: "服务状态",
|
||||
@@ -89,6 +69,43 @@ export const routes: RouteDef[] = [
|
||||
ready: true,
|
||||
element: <AuditPage />,
|
||||
},
|
||||
{
|
||||
path: "models",
|
||||
label: "模型配置",
|
||||
group: "运维",
|
||||
ready: true,
|
||||
element: <ModelConfigPage />,
|
||||
},
|
||||
{
|
||||
path: "datasources",
|
||||
label: "数据源 & RAG",
|
||||
group: "运维",
|
||||
ready: true,
|
||||
element: <DatasourcesPage />,
|
||||
},
|
||||
{
|
||||
path: "prompts",
|
||||
label: "提示词",
|
||||
group: "运维",
|
||||
ready: true,
|
||||
element: <PromptsPage />,
|
||||
},
|
||||
{
|
||||
path: "payment/config",
|
||||
label: "配置",
|
||||
group: "运维",
|
||||
parent: "支付",
|
||||
ready: true,
|
||||
element: <PaymentConfigPage />,
|
||||
},
|
||||
{
|
||||
path: "payment/orders",
|
||||
label: "订单与对账",
|
||||
group: "运维",
|
||||
parent: "支付",
|
||||
ready: true,
|
||||
element: <PaymentOrdersPage />,
|
||||
},
|
||||
{
|
||||
path: "tenants",
|
||||
label: "租户 & 用户",
|
||||
@@ -115,15 +132,22 @@ export const routes: RouteDef[] = [
|
||||
export const defaultPath = "dashboard";
|
||||
|
||||
// 派生分组导航(保持注册顺序)。
|
||||
export function navGroups(): Array<{ group: string; items: RouteDef[] }> {
|
||||
const out: Array<{ group: string; items: RouteDef[] }> = [];
|
||||
export function navGroups(): Array<{ group: string; nodes: NavNode[] }> {
|
||||
const out: Array<{ group: string; nodes: NavNode[] }> = [];
|
||||
for (const r of routes) {
|
||||
let g = out.find((x) => x.group === r.group);
|
||||
if (!g) {
|
||||
g = { group: r.group, items: [] };
|
||||
g = { group: r.group, nodes: [] };
|
||||
out.push(g);
|
||||
}
|
||||
g.items.push(r);
|
||||
if (!r.parent) {
|
||||
g.nodes.push({ kind: "item", route: r });
|
||||
continue;
|
||||
}
|
||||
// 同 parent 的归到同一个可展开节点(首次出现决定它在组内的位置)。
|
||||
const exist = g.nodes.find((n): n is Extract<NavNode, { kind: "parent" }> => n.kind === "parent" && n.label === r.parent);
|
||||
if (exist) exist.items.push(r);
|
||||
else g.nodes.push({ kind: "parent", label: r.parent, items: [r] });
|
||||
}
|
||||
return out;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user