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,97 @@
|
||||
import { useState } from "react";
|
||||
import { useAsyncData } from "../../lib/useAsyncData";
|
||||
import { adminListChannels } from "../../lib/api";
|
||||
import type { AdminChannelRecord } from "../../lib/types";
|
||||
import { useAutoReload } from "../hooks";
|
||||
import { useAuditStore } from "../audit";
|
||||
import { AdminTable, Notice, PageHeader, type AdminColumn } from "../components";
|
||||
import { Button, Surface, Tag } from "../../components/primitives";
|
||||
|
||||
const columns: AdminColumn<AdminChannelRecord>[] = [
|
||||
{ key: "name", title: "渠道", render: (c) => <span className="font-medium">{c.name}</span> },
|
||||
{
|
||||
key: "connected",
|
||||
title: "状态",
|
||||
render: (c) => (
|
||||
<Tag tone={c.connected ? "ok" : "err"}>
|
||||
<span className={`mr-1 inline-block h-1.5 w-1.5 rounded-full ${c.connected ? "bg-[var(--mu-ok)]" : "bg-[var(--mu-err)]"}`} />
|
||||
{c.connected ? "已连接" : "未连接"}
|
||||
</Tag>
|
||||
),
|
||||
},
|
||||
];
|
||||
|
||||
export function ChannelsModule(): React.ReactNode {
|
||||
const { data, loading, error, reload } = useAsyncData(adminListChannels, []);
|
||||
useAutoReload(reload, 10000);
|
||||
const log = useAuditStore((s) => s.log);
|
||||
const [ownerId, setOwnerId] = useState("");
|
||||
const [group, setGroup] = useState(false);
|
||||
const [verdict, setVerdict] = useState<string | null>(null);
|
||||
|
||||
return (
|
||||
<div className="space-y-5">
|
||||
<PageHeader
|
||||
title="渠道管理"
|
||||
subtitle="GET /api/v1/admin/channels · 类型/账号/状态/健康 · 身份识别基于 actor 元数据"
|
||||
actions={
|
||||
<Button
|
||||
variant="soft"
|
||||
onClick={() => {
|
||||
log("channel.reconnect", "ok");
|
||||
alert("热重载/重连(占位):后端 M10+ 提供。");
|
||||
}}
|
||||
>
|
||||
热重载
|
||||
</Button>
|
||||
}
|
||||
/>
|
||||
|
||||
<Surface className="space-y-2">
|
||||
<AdminTable
|
||||
columns={columns}
|
||||
rows={loading ? null : data}
|
||||
loading={loading}
|
||||
error={error}
|
||||
emptyTitle="无渠道数据"
|
||||
rowKey={(c) => c.name}
|
||||
/>
|
||||
</Surface>
|
||||
|
||||
<Surface className="space-y-3">
|
||||
<h3 className="text-sm font-semibold">身份识别测试(占位 · 策略引擎 M10 接入)</h3>
|
||||
<p className="text-xs text-[var(--mu-text-lo)]">
|
||||
输入渠道确认的发送者账号 id,模拟 channel 层 actor 元数据 → 策略引擎判定;判定绝不依赖提示词 / LLM 自我判断。
|
||||
</p>
|
||||
<div className="flex flex-wrap items-center gap-2">
|
||||
<input
|
||||
value={ownerId}
|
||||
onChange={(e) => setOwnerId(e.target.value)}
|
||||
placeholder="渠道账号 id(如 weixin-ilink 的 id)"
|
||||
className="min-w-0 flex-1 rounded-[var(--mu-radius)] bg-[var(--mu-surface-2)] px-3 py-2 text-sm outline-none focus:ring-1 focus:ring-[var(--mu-accent)]"
|
||||
/>
|
||||
<label className="flex items-center gap-1.5 text-xs text-[var(--mu-text-mid)]">
|
||||
<input type="checkbox" checked={group} onChange={(e) => setGroup(e.target.checked)} className="accent-[var(--mu-accent)]" />
|
||||
群聊
|
||||
</label>
|
||||
<Button
|
||||
variant="primary"
|
||||
onClick={() => {
|
||||
log("channel.identity-test", "ok");
|
||||
setVerdict(
|
||||
group
|
||||
? "群聊上下文 → 隔离;记忆只读不写;仅可调用标记允许的工具"
|
||||
: "匹配主人白名单 → owner;若未命中则按陌生人处理(default deny)"
|
||||
);
|
||||
}}
|
||||
>
|
||||
测试
|
||||
</Button>
|
||||
</div>
|
||||
{verdict ? <div className="rounded-[var(--mu-radius)] bg-[var(--mu-surface-2)] px-3 py-2 text-xs text-[var(--mu-text-mid)]">判定:{verdict}</div> : null}
|
||||
</Surface>
|
||||
|
||||
<Notice>账号配置(扫码/token)、消息流预览、推送测试为后端 M10+ 提供;此页当前展示渠道注册与连接健康。</Notice>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
Reference in new issue
Block a user