Files
model-platform/frontend/app/features/admin/PermissionCheckboxList.tsx
T
2026-09-02 10:10:41 +08:00

97 lines
3.0 KiB
TypeScript

import type { PlatformPermission } from "../../services/api";
import { Checkbox } from "~/components/ui/checkbox";
import {
adminEmptyClass,
permissionGroupClass,
permissionGroupTitleClass,
permissionGroupsClass,
permissionItemClassName,
} from "./adminUi";
/** 模块编码 → 展示名(用于权限分组标题)。 */
const MODULE_LABELS: Record<string, string> = {
dashboard: "工作台",
script: "构建脚本",
schedule: "调度配置",
system: "系统管理",
};
/** 勾选框样式:复刻原生 checkbox 外观(白底、灰边、蓝色勾选),并对齐 permission item 的 3px 顶部偏移。 */
const CHECKBOX_CLASS =
"mt-[3px] size-[16px] rounded-[3px] border-[#cdd9e4] bg-white disabled:opacity-100 data-checked:border-[#1277d3] data-checked:bg-[#1277d3] data-checked:text-white";
/**
* 权限勾选列表:按 module_code 分组渲染。
* - targetRoleCode === "admin" 时 system:view 锁定勾选(后端守卫);
* - 非 admin 角色 system:* 全部锁定(后端守卫)。
*/
export function PermissionCheckboxList({
permissions,
selected,
targetRoleCode,
onToggle,
}: {
permissions: PlatformPermission[];
selected: string[];
targetRoleCode: string;
onToggle: (code: string) => void;
}) {
const groups = permissions.reduce<Record<string, PlatformPermission[]>>(
(acc, permission) => {
const bucket = acc[permission.module_code] ?? [];
bucket.push(permission);
acc[permission.module_code] = bucket;
return acc;
},
{},
);
if (permissions.length === 0) {
return <p className={adminEmptyClass}>暂无可用权限</p>;
}
const isAdmin = targetRoleCode === "admin";
return (
<div className={permissionGroupsClass}>
{Object.entries(groups).map(([module, items]) => (
<div className={permissionGroupClass} key={module}>
<div className={permissionGroupTitleClass}>
{MODULE_LABELS[module] ?? module}
</div>
{items.map((permission) => {
const code = permission.permission_code;
const locked = isAdmin
? code === "system:view"
: code.startsWith("system:");
const checked = selected.includes(code);
return (
<label
className={permissionItemClassName(locked)}
key={code}
>
<Checkbox
checked={checked}
disabled={locked}
onCheckedChange={() => onToggle(code)}
className={CHECKBOX_CLASS}
/>
<span>
<strong>{permission.permission_name}</strong>
<small>
{code}
{permission.description
? ` · ${permission.description}`
: ""}
</small>
</span>
</label>
);
})}
</div>
))}
</div>
);
}