feat(identity): 完成 A2 角色权限与数据范围全栈落地
背景: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 全绿。
This commit is contained in:
1 parent
f97f97b0cd
commit
61894df401
80 files changed
+14380
-237
No files matched your search
@@ -0,0 +1,97 @@
|
||||
/* 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>
|
||||
);
|
||||
}
|
||||
Reference in new issue
Block a user