feat(平台端): 园区列表支持 状态显示/禁用启用/级联删除

园区管理每园显示状态(启用/已禁用);可【禁用】(不删,禁止园区端登录/data)【启用】【删除】;
删除为联级:后端 delete_tenant 一并删 企业/知识库/屏幕 + 引擎缓存;禁用后 /park/auth/login 返回 403。
npm run build 通过。
This commit is contained in:
Pine
2026-08-24 18:54:24 +08:00
parent 75ce902448
commit 08336c7d76
2 changed files with 13 additions and 1 deletions
+2
View File
@@ -20,6 +20,8 @@ export const parkApi = {
api.put<{ ok: boolean; tenant: Tenant }>(`/park/tenants/${tid}`, body),
bindAdmin: (tid: string, username: string) => api.post<{ ok: boolean }>(`/park/tenants/${tid}/bind`, { username }),
unbindAdmin: (tid: string) => api.post<{ ok: boolean }>(`/park/tenants/${tid}/unbind`),
deleteTenant: (tid: string) => api.del<{ ok: boolean }>(`/park/tenants/${tid}`),
setTenantStatus: (tid: string, status: "active" | "disabled") => api.post<{ ok: boolean }>(`/park/tenants/${tid}/status`, { status }),
// ---- 大屏控制台 ----
displayState: (tenantId: string) => api.get<DisplayState>("/park/api/display/state", { tenant_id: tenantId }),
+11 -1
View File
@@ -47,6 +47,13 @@ export function TenantTab() {
if (!window.confirm(`解除「${t.name}」的管理员绑定?`)) return;
await parkApi.unbindAdmin(t.id); reload();
};
const toggle = async (t: Tenant) => {
await parkApi.setTenantStatus(t.id, t.status === "disabled" ? "active" : "disabled"); reload();
};
const remove = async (t: Tenant) => {
if (!window.confirm(`确认删除园区「${t.name}」?将一并删除其全部资源(屏幕/企业/知识库/智能体/大屏数据)且不可恢复!`)) return;
await parkApi.deleteTenant(t.id); reload();
};
return (
<div className="space-y-4">
@@ -70,13 +77,16 @@ export function TenantTab() {
<div>
<span className="font-medium">{t.name}</span>
<span className="ml-2 text-xs text-muted-foreground">{t.id}</span>
<span className={`ml-2 text-xs ${t.status === "disabled" ? "text-destructive" : "text-emerald-600"}`}>{t.status === "disabled" ? "已禁用" : "启用"}</span>
<div className="text-xs text-muted-foreground">{t.admin_username || "(未绑定)"}</div>
<div className="text-xs text-muted-foreground">{(t.intro ?? []).join(" · ")}</div>
</div>
<div className="flex gap-2">
{t.admin_username
? <Button size="sm" variant="ghost" className="text-destructive" onClick={() => unbind(t)}></Button>
? <Button size="sm" variant="ghost" onClick={() => unbind(t)}></Button>
: <Button size="sm" variant="outline" onClick={() => openBind(t)}></Button>}
<Button size="sm" variant="outline" onClick={() => toggle(t)}>{t.status === "disabled" ? "启用" : "禁用"}</Button>
<Button size="sm" variant="ghost" className="text-destructive" onClick={() => remove(t)}></Button>
</div>
</div>
))}