背景:角色详情页把「编辑角色」放在右下角页脚,与页头的复制/停用分离,主操作位置突兀;页脚只剩一句备注也浪费垂直空间。 改动: - 页头操作顺序调整为:复制 → 停用/启用(系统角色为改名)→ 编辑角色(实心主按钮); - 只读用户显示「查看角色」,系统角色也显示「查看角色」(仅名称可改,避免编辑语义过强); - 移除详情页脚及编辑按钮,权限摘要与绑定用户获得更多高度; - 更新只读态单测断言。 验证:format:check / lint / typecheck / test 全绿;5173 实测页头操作与主按钮位置正确。
This commit is contained in:
1 parent
f5a8afe835
commit
f4da1f3c02
3 files changed
+31
-39
No files matched your search
@@ -160,7 +160,8 @@ describe('角色详情面板', () => {
|
|||||||
renderPanel({ canManage: false });
|
renderPanel({ canManage: false });
|
||||||
expect(screen.queryByText('复制')).toBeNull();
|
expect(screen.queryByText('复制')).toBeNull();
|
||||||
expect(screen.queryByText('停用')).toBeNull();
|
expect(screen.queryByText('停用')).toBeNull();
|
||||||
expect(screen.getByText('编辑角色')).toBeTruthy();
|
expect(screen.queryByText('编辑角色')).toBeNull();
|
||||||
|
expect(screen.getByText('查看角色')).toBeTruthy();
|
||||||
});
|
});
|
||||||
|
|
||||||
it('未选择角色时展示空态', () => {
|
it('未选择角色时展示空态', () => {
|
||||||
|
|||||||
@@ -2,7 +2,15 @@
|
|||||||
import { Button } from '@douyinfe/semi-ui-19';
|
import { Button } from '@douyinfe/semi-ui-19';
|
||||||
import type { PermissionTree, RoleDetail } from '@lingxi/shared';
|
import type { PermissionTree, RoleDetail } from '@lingxi/shared';
|
||||||
import { ErrorState, PageEmpty, PageSkeleton } from '@lingxi/ui';
|
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 type { ReactNode } from 'react';
|
||||||
import { useState } from 'react';
|
import { useState } from 'react';
|
||||||
|
|
||||||
@@ -47,11 +55,15 @@ function RoleAvatar({ status }: { status: 'active' | 'disabled' }): ReactNode {
|
|||||||
function DetailHead({
|
function DetailHead({
|
||||||
role,
|
role,
|
||||||
canManage,
|
canManage,
|
||||||
|
onEdit,
|
||||||
onCopy,
|
onCopy,
|
||||||
onRename,
|
onRename,
|
||||||
onDisable,
|
onDisable,
|
||||||
onEnable,
|
onEnable,
|
||||||
}: Pick<RoleDetailPanelProps, 'canManage' | 'onCopy' | 'onRename' | 'onDisable' | 'onEnable'> & {
|
}: Pick<
|
||||||
|
RoleDetailPanelProps,
|
||||||
|
'canManage' | 'onEdit' | 'onCopy' | 'onRename' | 'onDisable' | 'onEnable'
|
||||||
|
> & {
|
||||||
role: RoleDetail;
|
role: RoleDetail;
|
||||||
}): ReactNode {
|
}): ReactNode {
|
||||||
return (
|
return (
|
||||||
@@ -65,8 +77,9 @@ function DetailHead({
|
|||||||
</div>
|
</div>
|
||||||
<p className="lx-role-detail__desc">{role.description}</p>
|
<p className="lx-role-detail__desc">{role.description}</p>
|
||||||
</div>
|
</div>
|
||||||
{canManage ? (
|
|
||||||
<div className="lx-role-detail__actions">
|
<div className="lx-role-detail__actions">
|
||||||
|
{canManage ? (
|
||||||
|
<>
|
||||||
<Button
|
<Button
|
||||||
icon={<IconCopy size={14} />}
|
icon={<IconCopy size={14} />}
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
@@ -104,8 +117,19 @@ function DetailHead({
|
|||||||
启用
|
启用
|
||||||
</Button>
|
</Button>
|
||||||
)}
|
)}
|
||||||
</div>
|
</>
|
||||||
) : null}
|
) : 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>
|
</header>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@@ -300,6 +324,7 @@ export function RoleDetailPanel(props: RoleDetailPanelProps): ReactNode {
|
|||||||
<DetailHead
|
<DetailHead
|
||||||
role={detail}
|
role={detail}
|
||||||
canManage={props.canManage}
|
canManage={props.canManage}
|
||||||
|
onEdit={props.onEdit}
|
||||||
onCopy={props.onCopy}
|
onCopy={props.onCopy}
|
||||||
onRename={props.onRename}
|
onRename={props.onRename}
|
||||||
onDisable={props.onDisable}
|
onDisable={props.onDisable}
|
||||||
@@ -324,21 +349,6 @@ export function RoleDetailPanel(props: RoleDetailPanelProps): ReactNode {
|
|||||||
<ScopeAndSpecial role={detail} tree={props.tree} />
|
<ScopeAndSpecial role={detail} tree={props.tree} />
|
||||||
<BoundUsers role={detail} />
|
<BoundUsers role={detail} />
|
||||||
</div>
|
</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">
|
<div className="lx-role-detail__empty">
|
||||||
|
|||||||
@@ -711,25 +711,6 @@
|
|||||||
white-space: nowrap;
|
white-space: nowrap;
|
||||||
}
|
}
|
||||||
|
|
||||||
.lx-role-detail__foot {
|
|
||||||
display: flex;
|
|
||||||
height: 56px;
|
|
||||||
flex: 0 0 56px;
|
|
||||||
align-items: center;
|
|
||||||
justify-content: space-between;
|
|
||||||
gap: var(--lx-space-3);
|
|
||||||
padding: 0 18px;
|
|
||||||
border-top: 1px solid var(--lx-color-border);
|
|
||||||
}
|
|
||||||
|
|
||||||
.lx-role-detail__foot-note {
|
|
||||||
overflow: hidden;
|
|
||||||
font-size: var(--lx-font-size-xs);
|
|
||||||
color: var(--lx-color-text-tertiary);
|
|
||||||
text-overflow: ellipsis;
|
|
||||||
white-space: nowrap;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* ---------- 角色模板(新建页左列) ---------- */
|
/* ---------- 角色模板(新建页左列) ---------- */
|
||||||
|
|
||||||
.lx-role-templates {
|
.lx-role-templates {
|
||||||
|
|||||||
Reference in new issue
Block a user