refactor(web): 编辑角色主操作移入详情页头并移除页脚
ci / 质量门禁 (push) Canceled after 0s

背景:角色详情页把「编辑角色」放在右下角页脚,与页头的复制/停用分离,主操作位置突兀;页脚只剩一句备注也浪费垂直空间。

改动:
- 页头操作顺序调整为:复制 → 停用/启用(系统角色为改名)→ 编辑角色(实心主按钮);
- 只读用户显示「查看角色」,系统角色也显示「查看角色」(仅名称可改,避免编辑语义过强);
- 移除详情页脚及编辑按钮,权限摘要与绑定用户获得更多高度;
- 更新只读态单测断言。

验证:format:check / lint / typecheck / test 全绿;5173 实测页头操作与主按钮位置正确。
This commit is contained in:
Stev_Wang committed 2026-11-03 14:16:39 +08:00
1 parent f5a8afe835
commit f4da1f3c02
3 files changed
+65 -73

No files matched your search

@@ -160,7 +160,8 @@ describe('角色详情面板', () => {
renderPanel({ canManage: false });
expect(screen.queryByText('复制')).toBeNull();
expect(screen.queryByText('停用')).toBeNull();
expect(screen.getByText('编辑角色')).toBeTruthy();
expect(screen.queryByText('编辑角色')).toBeNull();
expect(screen.getByText('查看角色')).toBeTruthy();
});
it('未选择角色时展示空态', () => {
@@ -2,7 +2,15 @@
import { Button } from '@douyinfe/semi-ui-19';
import type { PermissionTree, RoleDetail } from '@lingxi/shared';
import { ErrorState, PageEmpty, PageSkeleton } from '@lingxi/ui';
import { IconCopy, IconEdit, IconPause, IconPlay, IconShield, IconStar } from '@lingxi/ui/icons';
import {
IconCopy,
IconEdit,
IconPause,
IconPlay,
IconShield,
IconStar,
IconView,
} from '@lingxi/ui/icons';
import type { ReactNode } from 'react';
import { useState } from 'react';
@@ -47,11 +55,15 @@ function RoleAvatar({ status }: { status: 'active' | 'disabled' }): ReactNode {
function DetailHead({
role,
canManage,
onEdit,
onCopy,
onRename,
onDisable,
onEnable,
}: Pick<RoleDetailPanelProps, 'canManage' | 'onCopy' | 'onRename' | 'onDisable' | 'onEnable'> & {
}: Pick<
RoleDetailPanelProps,
'canManage' | 'onEdit' | 'onCopy' | 'onRename' | 'onDisable' | 'onEnable'
> & {
role: RoleDetail;
}): ReactNode {
return (
@@ -65,47 +77,59 @@ function DetailHead({
</div>
<p className="lx-role-detail__desc">{role.description}</p>
</div>
{canManage ? (
<div className="lx-role-detail__actions">
<Button
icon={<IconCopy size={14} />}
onClick={() => {
onCopy(role);
}}
>
复制
</Button>
{role.isSystem ? (
<div className="lx-role-detail__actions">
{canManage ? (
<>
<Button
icon={<IconEdit size={14} />}
icon={<IconCopy size={14} />}
onClick={() => {
onRename(role);
onCopy(role);
}}
>
改名
复制
</Button>
) : role.status === 'active' ? (
<Button
type="danger"
icon={<IconPause size={14} />}
onClick={() => {
onDisable(role);
}}
>
停用
</Button>
) : (
<Button
icon={<IconPlay size={14} />}
onClick={() => {
onEnable(role);
}}
>
启用
</Button>
)}
</div>
) : null}
{role.isSystem ? (
<Button
icon={<IconEdit size={14} />}
onClick={() => {
onRename(role);
}}
>
改名
</Button>
) : role.status === 'active' ? (
<Button
type="danger"
icon={<IconPause size={14} />}
onClick={() => {
onDisable(role);
}}
>
停用
</Button>
) : (
<Button
icon={<IconPlay size={14} />}
onClick={() => {
onEnable(role);
}}
>
启用
</Button>
)}
</>
) : null}
<Button
theme="solid"
type="primary"
icon={canManage && !role.isSystem ? <IconEdit size={14} /> : <IconView size={14} />}
onClick={() => {
onEdit(role.id);
}}
>
{canManage && !role.isSystem ? '编辑角色' : '查看角色'}
</Button>
</div>
</header>
);
}
@@ -300,6 +324,7 @@ export function RoleDetailPanel(props: RoleDetailPanelProps): ReactNode {
<DetailHead
role={detail}
canManage={props.canManage}
onEdit={props.onEdit}
onCopy={props.onCopy}
onRename={props.onRename}
onDisable={props.onDisable}
@@ -324,21 +349,6 @@ export function RoleDetailPanel(props: RoleDetailPanelProps): ReactNode {
<ScopeAndSpecial role={detail} tree={props.tree} />
<BoundUsers role={detail} />
</div>
<footer className="lx-role-detail__foot">
<span className="lx-role-detail__foot-note">
{detail.isSystem ? '系统内置角色,不可停用或删除' : '自定义角色,可复制、停用或删除'}
</span>
<Button
theme="solid"
type="primary"
icon={<IconEdit size={14} />}
onClick={() => {
props.onEdit(detail.id);
}}
>
编辑角色
</Button>
</footer>
</>
) : (
<div className="lx-role-detail__empty">