refine Semi UI account workspace
This commit is contained in:
@@ -47,12 +47,17 @@ test('deduplicates vehicle candidates by VIN and renders granted vehicles plate
|
||||
expect(screen.getByLabelText('账号管理操作')).toHaveTextContent('客户访问治理');
|
||||
expect(screen.getByLabelText('账号管理操作')).toHaveTextContent('变更最多 30 秒生效');
|
||||
expect(view.container.querySelector('.v2-page-heading')).not.toBeInTheDocument();
|
||||
expect(view.container.querySelector('.v2-user-discovery-shell')).toContainElement(screen.getByLabelText('账号管理操作'));
|
||||
expect(screen.getByRole('heading', { name: '账号范围', level: 5 })).toBeInTheDocument();
|
||||
expect(screen.getByText('按客户名称、登录账号、客户标识与启停状态查找')).toBeInTheDocument();
|
||||
expect(screen.getAllByText('1 / 1 个账号')).toHaveLength(2);
|
||||
expect(view.container.querySelector('.v2-user-list.semi-card')).toBeInTheDocument();
|
||||
expect(view.container.querySelector('.v2-user-filter-panel.v2-workspace-filter-panel.semi-card')).toBeInTheDocument();
|
||||
expect(view.container.querySelector('.v2-customer-list.semi-list')).toBeInTheDocument();
|
||||
expect(screen.getByRole('row', { name: '客户账号目录列' })).toHaveTextContent('客户账号菜单权限车辆权限最近登录状态');
|
||||
expect(screen.getByText('启用 1')).toBeInTheDocument();
|
||||
expect(screen.getByText('车辆授权 1')).toBeInTheDocument();
|
||||
expect(view.container.querySelector('.v2-user-directory-metrics')).not.toBeInTheDocument();
|
||||
expect(customer).toHaveClass('semi-button', 'v2-user-list-item');
|
||||
expect(customer.closest('.semi-list-item')).toHaveClass('v2-user-list-row');
|
||||
expect(customer).toHaveAttribute('aria-pressed', 'false');
|
||||
@@ -235,7 +240,8 @@ test('filters the customer directory by status and exposes the active result sco
|
||||
await waitFor(() => expect(screen.queryByRole('button', { name: /选择客户 华东客户/ })).not.toBeInTheDocument());
|
||||
expect(screen.getByRole('button', { name: /选择客户 西部客户/ })).toBeInTheDocument();
|
||||
expect(screen.getAllByText('1 / 2 个账号')).toHaveLength(2);
|
||||
expect(screen.getByText('启用账号').nextSibling).toHaveTextContent('0');
|
||||
expect(screen.getByText('启用 0')).toBeInTheDocument();
|
||||
expect(screen.getByText('车辆授权 0')).toBeInTheDocument();
|
||||
|
||||
fireEvent.click(screen.getByRole('button', { name: '清空账号筛选' }));
|
||||
await waitFor(() => expect(screen.getByRole('button', { name: /选择客户 华东客户/ })).toBeInTheDocument());
|
||||
|
||||
@@ -256,75 +256,83 @@ export default function UsersPage() {
|
||||
};
|
||||
|
||||
return <div className="v2-user-admin">
|
||||
<WorkspaceCommandBar
|
||||
className="v2-user-command-bar"
|
||||
ariaLabel="账号管理操作"
|
||||
title="客户访问治理"
|
||||
description="菜单与车辆按最小权限开放,变更最多 30 秒生效"
|
||||
status={`${customers.length} 个客户账号`}
|
||||
meta={<Typography.Text type="tertiary">本地账号 · 可扩展外部身份源</Typography.Text>}
|
||||
actions={<Button theme="solid" icon={<IconPlus />} aria-label="新建客户账号" onClick={startCreate}>新建客户账号</Button>}
|
||||
/>
|
||||
<WorkspaceFilterPanel
|
||||
className="v2-user-filter-panel"
|
||||
title="账号范围"
|
||||
description="按客户名称、登录账号、客户标识与启停状态查找"
|
||||
mobileSummary={`${customerStatus === 'all' ? '全部状态' : customerStatus === 'enabled' ? '启用账号' : '停用账号'}${customerKeyword.trim() ? ` · ${customerKeyword.trim()}` : ''}`}
|
||||
expanded={!filtersCollapsed}
|
||||
status={users.isPending ? '正在读取账号' : `${visibleCustomers.length} / ${customers.length} 个账号`}
|
||||
statusColor={customerKeyword.trim() || customerStatus !== 'all' ? 'blue' : 'grey'}
|
||||
collapsedLabel="修改"
|
||||
onToggle={() => setFiltersCollapsed((value) => !value)}
|
||||
>
|
||||
<div className={`v2-user-filter-form${filtersCollapsed ? ' is-mobile-collapsed' : ''}`}>
|
||||
<label className="v2-user-filter-keyword">
|
||||
<span>客户账号</span>
|
||||
<Input className="v2-user-list-search" aria-label="搜索客户账号" prefix={<IconSearch />} showClear value={customerKeyword} onChange={setCustomerKeyword} placeholder="搜索名称、账号或客户标识" />
|
||||
</label>
|
||||
<label>
|
||||
<span id="v2-user-status-filter-label">账号状态</span>
|
||||
<Select
|
||||
aria-labelledby="v2-user-status-filter-label"
|
||||
value={customerStatus}
|
||||
onChange={(value) => setCustomerStatus(String(value) as typeof customerStatus)}
|
||||
optionList={[
|
||||
{ value: 'all', label: '全部状态' },
|
||||
{ value: 'enabled', label: '启用账号' },
|
||||
{ value: 'disabled', label: '停用账号' }
|
||||
]}
|
||||
/>
|
||||
</label>
|
||||
<Button
|
||||
className="v2-user-filter-reset"
|
||||
theme="light"
|
||||
type="tertiary"
|
||||
icon={<IconRefresh />}
|
||||
aria-label="清空账号筛选"
|
||||
disabled={!customerKeyword.trim() && customerStatus === 'all'}
|
||||
onClick={() => {
|
||||
setCustomerKeyword('');
|
||||
setCustomerStatus('all');
|
||||
}}
|
||||
>
|
||||
清空
|
||||
</Button>
|
||||
</div>
|
||||
</WorkspaceFilterPanel>
|
||||
<div className="v2-user-discovery-shell">
|
||||
<WorkspaceCommandBar
|
||||
className="v2-user-command-bar"
|
||||
ariaLabel="账号管理操作"
|
||||
title="客户访问治理"
|
||||
description="菜单与车辆按最小权限开放,变更最多 30 秒生效"
|
||||
status={`${customers.length} 个客户账号`}
|
||||
meta={<Typography.Text type="tertiary">本地账号 · 可扩展外部身份源</Typography.Text>}
|
||||
actions={<Button theme="solid" icon={<IconPlus />} aria-label="新建客户账号" onClick={startCreate}>新建客户账号</Button>}
|
||||
/>
|
||||
<WorkspaceFilterPanel
|
||||
className="v2-user-filter-panel"
|
||||
title="账号范围"
|
||||
description="按客户名称、登录账号、客户标识与启停状态查找"
|
||||
mobileSummary={`${customerStatus === 'all' ? '全部状态' : customerStatus === 'enabled' ? '启用账号' : '停用账号'}${customerKeyword.trim() ? ` · ${customerKeyword.trim()}` : ''}`}
|
||||
expanded={!filtersCollapsed}
|
||||
status={users.isPending ? '正在读取账号' : `${visibleCustomers.length} / ${customers.length} 个账号`}
|
||||
statusColor={customerKeyword.trim() || customerStatus !== 'all' ? 'blue' : 'grey'}
|
||||
collapsedLabel="修改"
|
||||
onToggle={() => setFiltersCollapsed((value) => !value)}
|
||||
>
|
||||
<div className={`v2-user-filter-form${filtersCollapsed ? ' is-mobile-collapsed' : ''}`}>
|
||||
<label className="v2-user-filter-keyword">
|
||||
<span>客户账号</span>
|
||||
<Input className="v2-user-list-search" aria-label="搜索客户账号" prefix={<IconSearch />} showClear value={customerKeyword} onChange={setCustomerKeyword} placeholder="搜索名称、账号或客户标识" />
|
||||
</label>
|
||||
<label>
|
||||
<span id="v2-user-status-filter-label">账号状态</span>
|
||||
<Select
|
||||
aria-labelledby="v2-user-status-filter-label"
|
||||
value={customerStatus}
|
||||
onChange={(value) => setCustomerStatus(String(value) as typeof customerStatus)}
|
||||
optionList={[
|
||||
{ value: 'all', label: '全部状态' },
|
||||
{ value: 'enabled', label: '启用账号' },
|
||||
{ value: 'disabled', label: '停用账号' }
|
||||
]}
|
||||
/>
|
||||
</label>
|
||||
<Button
|
||||
className="v2-user-filter-reset"
|
||||
theme="light"
|
||||
type="tertiary"
|
||||
icon={<IconRefresh />}
|
||||
aria-label="清空账号筛选"
|
||||
disabled={!customerKeyword.trim() && customerStatus === 'all'}
|
||||
onClick={() => {
|
||||
setCustomerKeyword('');
|
||||
setCustomerStatus('all');
|
||||
}}
|
||||
>
|
||||
清空
|
||||
</Button>
|
||||
</div>
|
||||
</WorkspaceFilterPanel>
|
||||
</div>
|
||||
<div className={`v2-user-admin-grid${creating || selected ? ' is-editor-open' : ''}`}>
|
||||
<Card className="v2-user-list" bodyStyle={{ padding: 0 }}>
|
||||
<WorkspacePanelHeader
|
||||
className="v2-user-directory-header"
|
||||
title="客户权限目录"
|
||||
description="当前范围内的账号、菜单与车辆授权摘要"
|
||||
meta={<Tag color={visibleCustomers.length === customers.length ? 'grey' : 'blue'} type="light" size="small">{visibleCustomers.length} / {customers.length} 个账号</Tag>}
|
||||
meta={<span className="v2-user-directory-summary">
|
||||
<Tag color={visibleCustomers.length === customers.length ? 'grey' : 'blue'} type="light" size="small">{visibleCustomers.length} / {customers.length} 个账号</Tag>
|
||||
<Typography.Text type="tertiary">启用 {enabledCustomers}</Typography.Text>
|
||||
<Typography.Text type="tertiary">车辆授权 {grantedVehicles}</Typography.Text>
|
||||
</span>}
|
||||
/>
|
||||
<div className="v2-user-directory-body">
|
||||
<div className="v2-user-directory-metrics" aria-label="客户账号概览">
|
||||
<span className="is-primary"><small>启用账号</small><strong>{enabledCustomers}</strong></span>
|
||||
<span><small>停用账号</small><strong>{customers.length - enabledCustomers}</strong></span>
|
||||
<span><small>车辆授权</small><strong>{grantedVehicles}</strong></span>
|
||||
</div>
|
||||
{users.isPending ? <PanelLoading className="v2-user-list-loading" title="正在加载客户账号" description="账号目录和授权摘要就绪后会自动显示。" /> : customers.length === 0 ? <PanelEmpty className="v2-user-list-empty" title="还没有客户账号" description="创建客户账号后,可在这里配置菜单和车辆范围。" action={<Button theme="solid" onClick={startCreate}>创建第一个客户账号</Button>} /> : <>
|
||||
{visibleCustomers.length ? <div className="v2-user-directory-columns" role="row" aria-label="客户账号目录列">
|
||||
<span>客户账号</span>
|
||||
<span>菜单权限</span>
|
||||
<span>车辆权限</span>
|
||||
<span>最近登录</span>
|
||||
<span>状态</span>
|
||||
</div> : null}
|
||||
<div className="v2-customer-list-scroll" role="region" aria-label="客户账号目录,可上下滚动" tabIndex={0}>
|
||||
<List
|
||||
className="v2-customer-list"
|
||||
@@ -341,9 +349,9 @@ export default function UsersPage() {
|
||||
onClick={() => selectCustomer(user)}
|
||||
>
|
||||
<Avatar className="v2-user-avatar" color={user.status === 'enabled' ? 'light-blue' : 'grey'} shape="square" size="small">{user.displayName.slice(0, 1)}</Avatar>
|
||||
<span className="v2-user-list-identity"><b>{user.displayName}</b><small>@{user.username}</small></span>
|
||||
<span className="v2-user-list-facts"><small>菜单权限</small><b>{user.menuKeys.length} 项</b></span>
|
||||
<span className="v2-user-list-facts"><small>车辆权限</small><b>{user.vehicles.length} 辆</b></span>
|
||||
<span className="v2-user-list-identity"><b>{user.displayName}</b><small>@{user.username}{user.customerRef ? ` · ${user.customerRef}` : ''}</small></span>
|
||||
<span className="v2-user-list-facts is-menu"><small>菜单权限</small><b>{user.menuKeys.length} 项</b></span>
|
||||
<span className="v2-user-list-facts is-vehicle"><small>车辆权限</small><b>{user.vehicles.length} 辆</b></span>
|
||||
<span className="v2-user-list-facts is-login"><small>最近登录</small><b>{formatTime(user.lastLoginAt)}</b></span>
|
||||
<span className="v2-user-list-trailing"><Tag className={`v2-user-status-tag is-${user.status}`} color={user.status === 'enabled' ? 'green' : 'grey'} type="light" size="small">{user.status === 'enabled' ? '启用' : '停用'}</Tag><IconChevronRight /></span>
|
||||
</Button>
|
||||
|
||||
Reference in New Issue
Block a user