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:
Aska committed 2026-09-06 07:55:04 +08:00
1 parent 4c48e94659
commit f43eb17030
26 files changed
+2253 -4

No files matched your search

+84
View File
@@ -0,0 +1,84 @@
import { useAsyncData } from "../../lib/useAsyncData";
import { adminListIoT } from "../../lib/api";
import type { AdminIoTDevice } 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<AdminIoTDevice>[] = [
{ key: "name", title: "设备", render: (d) => <span className="font-medium">{d.name}</span> },
{ key: "type", title: "类型", render: (d) => <Tag tone="accent">{d.type}</Tag> },
{ key: "id", title: "ID", render: (d) => <span className="font-mono text-xs text-[var(--mu-text-lo)]">{d.id}</span> },
{
key: "online",
title: "状态",
render: (d) => (
<Tag tone={d.online ? "ok" : "err"}>
<span className={`mr-1 inline-block h-1.5 w-1.5 rounded-full ${d.online ? "bg-[var(--mu-ok)]" : "bg-[var(--mu-err)]"}`} />
{d.online ? "在线" : "离线"}
</Tag>
),
},
{
key: "state",
title: "实时状态",
render: (d) => (
<span className="block max-w-md font-mono text-xs text-[var(--mu-text-mid)]">
{Object.entries(d.state)
.map(([k, v]) => `${k}=${typeof v === "string" ? v : JSON.stringify(v)}`)
.join(" · ") || "—"}
</span>
),
},
];
export function IoTModule(): React.ReactNode {
const { data, loading, error, reload } = useAsyncData(adminListIoT, []);
useAutoReload(reload, 8000);
const log = useAuditStore((s) => s.log);
return (
<div className="space-y-5">
<PageHeader
title="IoT 设备"
subtitle="GET /api/v1/admin/iot · 设备列表/实时状态 + 控制台 + 场景 + 自动化规则"
actions={
<>
<Button
variant="soft"
onClick={() => {
log("iot.scene", "ok");
alert("场景 / 自动化规则 CRUD(占位):后端 M10+ 提供。");
}}
>
场景与规则
</Button>
<Button
variant="primary"
onClick={() => {
log("iot.control", "ok");
alert("设备控制(占位):后端 M10+ 提供指令下发。");
}}
>
设备控制台
</Button>
</>
}
/>
<Surface className="space-y-2">
<AdminTable
columns={columns}
rows={loading ? null : data}
loading={loading}
error={error}
emptyTitle="无 IoT 设备"
rowKey={(d) => d.id}
/>
</Surface>
<Notice>实时上/下线经 WS device_update 事件或轮询刷新;控制/场景/规则写入需后端 M10+ 提供。</Notice>
</div>
);
}