背景:A2 开卡前完成 H1 权限门,确认权限点目录、特殊权限、SoD 三对、六类数据范围与超管隐式全集口径(ADR-001)。 改动: - shared:30 权限点 / 10 模块目录与元数据、特殊权限 6 编码、SoD 三对、角色与权限树契约;/me 改 dataScopes 多角色并集。 - prisma:expand 迁移新增 permissions / data_scopes;种子按原型口径重写角色权限、数据范围与 8 项特殊授予。 - api:角色 CRUD / 复制 / 停用 / 删除、权限点启动同步、SoD 40304、系统角色保护、六类范围 ScopeContext 与 CustomerDirectory 端口、变更审计与 outbox。 - web:方案 B 主从分栏三角色页面(列表 / 编辑器 / 新建),权限树、范围选择、特殊权限二次确认、保存影响、SoD 冲突、只读态与模板预填。 - 测试:角色集成 13 条 + A2 e2e 1 条;A1 全量回归。 验证:format:check / lint / typecheck / test(306)/ test:int(27)/ A2 e2e(1,复跑通过)/ openapi:export 全绿。
98 lines
3.7 KiB
TypeScript
98 lines
3.7 KiB
TypeScript
/* eslint-disable max-lines-per-function -- 单组件按原型区块展开,拆分反而降低可读性 */
|
|
import { Button, SideSheet } from '@douyinfe/semi-ui-19';
|
|
import type { PermissionTree, RoleDetail } from '@lingxi/shared';
|
|
import { IconCheck, IconStar } from '@lingxi/ui/icons';
|
|
import type { ReactNode } from 'react';
|
|
|
|
import { scopeLabelOf } from './role-display';
|
|
|
|
export interface PermissionCatalogDrawerProps {
|
|
visible: boolean;
|
|
tree: PermissionTree | undefined;
|
|
role: RoleDetail | null;
|
|
onClose: () => void;
|
|
}
|
|
|
|
/** 权限点目录抽屉:只读展示 30 个权限点与当前角色的持有情况 */
|
|
export function PermissionCatalogDrawer({
|
|
visible,
|
|
tree,
|
|
role,
|
|
onClose,
|
|
}: PermissionCatalogDrawerProps): ReactNode {
|
|
const granted = new Set<string>([
|
|
...(role?.permissions ?? []),
|
|
...(role?.specialPermissions ?? []),
|
|
]);
|
|
return (
|
|
<SideSheet
|
|
visible={visible}
|
|
title="权限点目录"
|
|
width={480}
|
|
placement="right"
|
|
onCancel={onClose}
|
|
footer={
|
|
<div className="lx-role-catalog__foot">
|
|
<span>权限点变更即时对绑定用户生效</span>
|
|
<Button onClick={onClose}>关闭</Button>
|
|
</div>
|
|
}
|
|
>
|
|
<div className="lx-role-catalog">
|
|
<p className="lx-role-catalog__sub">
|
|
{tree?.totalPermissions ?? 30} 个权限点 · {tree?.specialPermissions.length ?? 6}{' '}
|
|
个特殊权限;★ 默认关闭、需单独授予。
|
|
{role === null
|
|
? ' 当前角色尚未加载。'
|
|
: ` 当前勾选为「${role.name}」持有(${String(role.permissionCount + role.specialPermissionCount)} 个)。`}
|
|
</p>
|
|
{tree === undefined ? (
|
|
<p className="lx-role-catalog__empty">权限点目录加载中…</p>
|
|
) : (
|
|
tree.modules.map((module) => (
|
|
<section key={module.module} className="lx-role-catalog__module">
|
|
<header className="lx-role-catalog__module-head">
|
|
<b>{module.moduleLabel}</b>
|
|
<span>
|
|
{module.permissions.filter((permission) => granted.has(permission.code)).length} /{' '}
|
|
{module.permissions.length}
|
|
</span>
|
|
</header>
|
|
{module.permissions.map((permission) => {
|
|
const owned = granted.has(permission.code);
|
|
return (
|
|
<div
|
|
key={permission.code}
|
|
className={owned ? 'lx-role-catalog__row is-granted' : 'lx-role-catalog__row'}
|
|
>
|
|
<span className="lx-role-catalog__check" aria-hidden="true">
|
|
{owned ? <IconCheck size={12} /> : null}
|
|
</span>
|
|
<span className="lx-role-catalog__copy">
|
|
<span className="lx-role-catalog__name">
|
|
{permission.name}
|
|
{permission.isSpecial ? (
|
|
<span className="lx-role-tag lx-role-tag--special">★ 特殊</span>
|
|
) : null}
|
|
</span>
|
|
<span className="lx-role-catalog__desc">{permission.description}</span>
|
|
</span>
|
|
<code className="lx-role-catalog__code">{permission.code}</code>
|
|
</div>
|
|
);
|
|
})}
|
|
</section>
|
|
))
|
|
)}
|
|
<div className="lx-role-catalog__note">
|
|
<IconStar size={14} />
|
|
<span>
|
|
特殊权限 6 项与普通权限分开授予,不随角色复制带入;当前角色数据范围为「
|
|
{role === null ? '—' : scopeLabelOf(role.dataScope.type)}」。
|
|
</span>
|
|
</div>
|
|
</div>
|
|
</SideSheet>
|
|
);
|
|
}
|