feat(frontend/admin): M10 后台管理面板(/admin hash 路由 + 15 大模块 + RBAC(owner/admin/viewer) + 审计 + Miuix);Docs/api-client.md v0.3 契约补全;tsc+vite build 通过
This commit is contained in:
1 parent
4c48e94659
commit
f43eb17030
26 files changed
+2253
-4
No files matched your search
@@ -0,0 +1,80 @@
|
||||
import { useState } from "react";
|
||||
import { useAsyncData } from "../../lib/useAsyncData";
|
||||
import { adminGetLlm, isNotImplemented } from "../../lib/api";
|
||||
import { useAuditStore } from "../audit";
|
||||
import { Notice, PageHeader } from "../components";
|
||||
import { Button, Surface, Switch, Tag } from "../../components/primitives";
|
||||
|
||||
interface ProviderRow {
|
||||
id: string;
|
||||
name: string;
|
||||
baseUrl: string;
|
||||
model: string;
|
||||
multimodal: boolean;
|
||||
enabled: boolean;
|
||||
}
|
||||
|
||||
const sample: ProviderRow[] = [
|
||||
{ id: "p1", name: "主模型", baseUrl: "https://api.example.com/v1", model: "example-ultra", multimodal: true, enabled: true },
|
||||
{ id: "p2", name: "备选模型", baseUrl: "https://api.backup.example.com/v1", model: "example-fast", multimodal: false, enabled: true },
|
||||
];
|
||||
|
||||
export function LlmModule(): React.ReactNode {
|
||||
const { data, error } = useAsyncData(adminGetLlm, []);
|
||||
const log = useAuditStore((s) => s.log);
|
||||
const [rows, setRows] = useState<ProviderRow[]>(sample);
|
||||
const backend = !isNotImplemented(error);
|
||||
|
||||
return (
|
||||
<div className="space-y-5">
|
||||
<PageHeader
|
||||
title="LLM 模型"
|
||||
subtitle="供应商 + 模型配置(base/key/多模态检测) + 测试 + fallback + 按角色路由 + 用量"
|
||||
actions={
|
||||
<Button
|
||||
variant="primary"
|
||||
onClick={() => {
|
||||
log("llm.addProvider", "ok");
|
||||
alert("新增模型(占位):后端 M10+ 提供真实持久化。");
|
||||
}}
|
||||
>
|
||||
新增模型
|
||||
</Button>
|
||||
}
|
||||
/>
|
||||
|
||||
{backend && !error ? <Notice tone="ok">后端已返回 LLM 配置(数据以接口为准)。</Notice> : null}
|
||||
{error && !backend ? <Notice tone="warn">后端未提供 /api/v1/admin/llm,以下为示例壳(M10 提供真实数据)。</Notice> : null}
|
||||
{error && backend ? <Notice tone="err">{error}</Notice> : null}
|
||||
|
||||
<Surface className="space-y-3">
|
||||
<h3 className="text-sm font-semibold">供应商 / 模型</h3>
|
||||
{rows.map((r) => (
|
||||
<div key={r.id} className="flex flex-wrap items-center gap-3 rounded-[var(--mu-radius)] bg-[var(--mu-surface-2)] px-3 py-2.5">
|
||||
<span className="font-medium">{r.name}</span>
|
||||
<Tag tone="neutral">{r.model}</Tag>
|
||||
<span className="font-mono text-xs text-[var(--mu-text-lo)]">{r.baseUrl}</span>
|
||||
<span className="ml-auto flex items-center gap-4">
|
||||
<Switch checked={r.enabled} onChange={(v) => setRows((rs) => rs.map((x) => (x.id === r.id ? { ...x, enabled: v } : x)))} label="启用" />
|
||||
<Switch checked={r.multimodal} onChange={(v) => setRows((rs) => rs.map((x) => (x.id === r.id ? { ...x, multimodal: v } : x)))} label="多模态" />
|
||||
<Button size="sm" variant="ghost" onClick={() => { log("llm.test", "ok"); alert(`测试 ${r.name}(占位)`); }}>测试</Button>
|
||||
</span>
|
||||
</div>
|
||||
))}
|
||||
</Surface>
|
||||
|
||||
<Surface className="space-y-3">
|
||||
<h3 className="text-sm font-semibold">按角色路由 + 降级链</h3>
|
||||
{["owner", "admin", "viewer"].map((role) => (
|
||||
<div key={role} className="flex items-center gap-3 text-sm">
|
||||
<Tag tone="neutral">{role}</Tag>
|
||||
<span className="text-[var(--mu-text-mid)]">主 → 备 → 本地</span>
|
||||
<span className="ml-auto text-xs text-[var(--mu-text-lo)]">接口报错自动降级</span>
|
||||
</div>
|
||||
))}
|
||||
</Surface>
|
||||
|
||||
<Notice>配置写入 / 多模态检测 / 用量费用统计为后端 M10+ 提供;per-role 路由 + 降级逻辑由后端 loop 实现。</Notice>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
Reference in new issue
Block a user