refactor(web): 收敛角色页权限点目录入口
ci / 质量门禁 (push) Canceled after 0s

背景:列表页页头「权限点目录」与详情面板「查看权限点目录」和编辑器权限点 Tab 信息重复,抽屉只读、无搜索、无操作,且页头出现两个并列动作。

改动:
- 移除列表页页头目录按钮与 PermissionCatalogDrawer 组件及对应样式;
- 详情面板删除「查看权限点目录」入口,保留「已选 n / 30」信息;
- 权限查看统一走编辑器「权限点」Tab(只读用户也可进入查看);
- 同步 A2 实施报告与设计说明。

验证:pnpm format:check / lint / typecheck / test 全绿;5173 实测页头仅保留「新建角色」,详情面板无目录入口。
This commit is contained in:
Stev_Wang committed 2026-11-03 13:56:35 +08:00
1 parent fe70a2bcee
commit a3419cf5da
6 files changed
+3 -263

No files matched your search

@@ -1,97 +0,0 @@
/* 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>
);
}
@@ -125,7 +125,6 @@ function renderPanel(overrides: Partial<Parameters<typeof RoleDetailPanel>[0]> =
onRename={vi.fn()}
onDisable={vi.fn()}
onEnable={vi.fn()}
onOpenCatalog={vi.fn()}
{...overrides}
/>,
);
@@ -29,7 +29,6 @@ export interface RoleDetailPanelProps {
onRename: (role: RoleDetail) => void;
onDisable: (role: RoleDetail) => void;
onEnable: (role: RoleDetail) => void;
onOpenCatalog: (role: RoleDetail) => void;
}
function RoleAvatar({ status }: { status: 'active' | 'disabled' }): ReactNode {
@@ -114,11 +113,9 @@ function DetailHead({
function PermissionSummary({
role,
tree,
onOpenCatalog,
}: {
role: RoleDetail;
tree: PermissionTree | undefined;
onOpenCatalog: () => void;
}): ReactNode {
const selected = [...role.permissions, ...role.specialPermissions];
const modules = tree === undefined ? [] : summarizePermissionModules(tree, selected);
@@ -130,9 +127,6 @@ function PermissionSummary({
已选 {role.permissionCount + role.specialPermissionCount} /{' '}
{tree?.totalPermissions ?? '—'}
</span>
<Button theme="borderless" size="small" onClick={onOpenCatalog}>
查看权限点目录
</Button>
</div>
{modules.length === 0 ? (
<p className="lx-role-detail__muted">权限点目录加载中…</p>
@@ -326,13 +320,7 @@ export function RoleDetailPanel(props: RoleDetailPanelProps): ReactNode {
{formatRoleDateTime(detail.updatedAt)}
</span>
</div>
<PermissionSummary
role={detail}
tree={props.tree}
onOpenCatalog={() => {
props.onOpenCatalog(detail);
}}
/>
<PermissionSummary role={detail} tree={props.tree} />
<ScopeAndSpecial role={detail} tree={props.tree} />
<BoundUsers role={detail} />
</div>
+1 -22
View File
@@ -2,7 +2,7 @@
import { Button, Input, Switch } from '@douyinfe/semi-ui-19';
import { Permission, type RoleDetail, type RoleListItem } from '@lingxi/shared';
import { notifySuccess } from '@lingxi/ui';
import { IconAdd, IconSearch, IconShield } from '@lingxi/ui/icons';
import { IconAdd, IconSearch } from '@lingxi/ui/icons';
import type { ReactNode } from 'react';
import { useMemo, useState } from 'react';
import { useNavigate } from 'react-router';
@@ -19,7 +19,6 @@ import {
import { usePermission } from '@/hooks/use-permission';
import { roleDetailPath, ROUTES } from '@/routes';
import { PermissionCatalogDrawer } from './PermissionCatalogDrawer';
import {
CopyRoleDialog,
DeleteRoleDialog,
@@ -129,7 +128,6 @@ export function RolesPage(): ReactNode {
const [filters, setFilters] = useState<RoleListFilters>(DEFAULT_ROLE_LIST_FILTERS);
const [selectedId, setSelectedId] = useState<string | null>(null);
const [dialog, setDialog] = useState<DialogState>(null);
const [catalogOpen, setCatalogOpen] = useState(false);
const filteredRoles = useMemo(() => filterRoles(roles, filters), [roles, filters]);
const stats = useMemo(() => summarizeRoles(roles), [roles]);
const effectiveSelectedId = filteredRoles.some((role) => role.id === selectedId)
@@ -176,14 +174,6 @@ export function RolesPage(): ReactNode {
<p>配置功能权限、数据范围与特殊权限,变更即时生效并留痕</p>
</div>
<div className="lx-roles__header-actions">
<Button
icon={<IconShield size={14} />}
onClick={() => {
setCatalogOpen(true);
}}
>
权限点目录
</Button>
<Can permission={Permission.ROLE_MANAGE}>
<Button
theme="solid"
@@ -265,9 +255,6 @@ export function RolesPage(): ReactNode {
},
);
}}
onOpenCatalog={() => {
setCatalogOpen(true);
}}
/>
</div>
@@ -383,14 +370,6 @@ export function RolesPage(): ReactNode {
);
}}
/>
<PermissionCatalogDrawer
visible={catalogOpen}
tree={tree.data}
role={selectedDetail ?? null}
onClose={() => {
setCatalogOpen(false);
}}
/>
</div>
);
}