feat: unify account directory metrics with semi ui
This commit is contained in:
@@ -51,15 +51,18 @@ test('deduplicates vehicle candidates by VIN and renders granted vehicles plate
|
||||
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(screen.getByText('1 / 1 个账号')).toBeInTheDocument();
|
||||
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(screen.getByText('权限就绪')).toBeInTheDocument();
|
||||
expect(view.container.querySelector('.v2-user-directory-metrics')).not.toBeInTheDocument();
|
||||
const directoryMetrics = screen.getByRole('list', { name: '客户权限目录统计' });
|
||||
expect(directoryMetrics.closest('.v2-user-directory-metric-rail')).toHaveClass('v2-workspace-metric-rail', 'is-queue');
|
||||
expect(directoryMetrics.querySelectorAll(':scope > [role="listitem"]')).toHaveLength(4);
|
||||
expect(directoryMetrics).toHaveTextContent('当前账号1 / 1全部客户账号');
|
||||
expect(directoryMetrics).toHaveTextContent('待完善0缺少菜单或车辆');
|
||||
expect(directoryMetrics).toHaveTextContent('权限就绪1启用且权限完整');
|
||||
expect(directoryMetrics).toHaveTextContent('车辆授权1当前范围合计');
|
||||
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');
|
||||
@@ -228,6 +231,7 @@ test('keeps the customer directory visible on mobile and opens details on demand
|
||||
const view = render(<QueryClientProvider client={client}><UsersPage /></QueryClientProvider>);
|
||||
|
||||
expect(await screen.findByText('客户权限目录')).toBeInTheDocument();
|
||||
expect(screen.getByRole('list', { name: '客户权限目录统计' }).querySelectorAll(':scope > [role="listitem"].is-primary')).toHaveLength(2);
|
||||
const customer = await screen.findByRole('button', { name: /选择客户 华东客户/ });
|
||||
expect(view.container.querySelector('.v2-user-mobile-picker')).not.toBeInTheDocument();
|
||||
expect(view.container.querySelector('.v2-customer-list.semi-list')).toBeInTheDocument();
|
||||
@@ -366,9 +370,10 @@ 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('权限就绪 0')).toBeInTheDocument();
|
||||
expect(screen.getByText('车辆授权 0')).toBeInTheDocument();
|
||||
expect(screen.getByText('1 / 2 个账号')).toBeInTheDocument();
|
||||
expect(screen.getByRole('list', { name: '客户权限目录统计' })).toHaveTextContent('当前账号1 / 2筛选结果 / 全部客户');
|
||||
expect(screen.getByRole('list', { name: '客户权限目录统计' })).toHaveTextContent('权限就绪0启用且权限完整');
|
||||
expect(screen.getByRole('list', { name: '客户权限目录统计' })).toHaveTextContent('车辆授权0当前范围合计');
|
||||
|
||||
fireEvent.click(screen.getByRole('button', { name: '清空账号筛选' }));
|
||||
await waitFor(() => expect(screen.getByRole('button', { name: /选择客户 华东客户/ })).toBeInTheDocument());
|
||||
@@ -395,7 +400,7 @@ 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.getByText('待分配车辆')).toBeInTheDocument();
|
||||
expect(screen.getByText('待完善 1')).toBeInTheDocument();
|
||||
expect(screen.getByRole('list', { name: '客户权限目录统计' })).toHaveTextContent('待完善1缺少菜单或车辆');
|
||||
});
|
||||
|
||||
test('uses a standard Semi empty state when no customer account exists', async () => {
|
||||
|
||||
@@ -12,6 +12,7 @@ import { VehicleCandidateList } from '../shared/VehicleCandidateList';
|
||||
import { WorkspaceFilterPanel } from '../shared/WorkspaceFilterPanel';
|
||||
import { WorkspaceCommandBar } from '../shared/WorkspaceCommandBar';
|
||||
import { WorkspaceConfirmDialog } from '../shared/WorkspaceDialog';
|
||||
import { WorkspaceMetricRail, type WorkspaceQueueMetricRailItem } from '../shared/WorkspaceMetricRail';
|
||||
import { WorkspacePanelHeader } from '../shared/WorkspacePanelHeader';
|
||||
import { WorkspaceSideSheet, type WorkspaceSideSheetBadgeColor } from '../shared/WorkspaceSideSheet';
|
||||
import { mergeVehicleCandidates } from '../shared/vehicleCandidates';
|
||||
@@ -153,6 +154,36 @@ export default function UsersPage() {
|
||||
const readyCustomers = useMemo(() => visibleCustomers.filter((user) => customerAccessState(user.status, user.menuKeys.length, user.vehicles.length).key === 'ready').length, [visibleCustomers]);
|
||||
const attentionCustomers = useMemo(() => visibleCustomers.filter((user) => customerAccessState(user.status, user.menuKeys.length, user.vehicles.length).attention).length, [visibleCustomers]);
|
||||
const grantedVehicles = useMemo(() => visibleCustomers.reduce((total, user) => total + user.vehicles.length, 0), [visibleCustomers]);
|
||||
const directoryMetrics: WorkspaceQueueMetricRailItem[] = [
|
||||
{
|
||||
label: '当前账号',
|
||||
value: <>{visibleCustomers.length}<i className="v2-user-directory-metric-unit"> / {customers.length}</i></>,
|
||||
note: customerKeyword.trim() || customerStatus !== 'all' ? '筛选结果 / 全部客户' : '全部客户账号',
|
||||
tone: 'primary',
|
||||
emphasis: 'primary'
|
||||
},
|
||||
{
|
||||
label: '待完善',
|
||||
value: attentionCustomers,
|
||||
note: '缺少菜单或车辆',
|
||||
tone: attentionCustomers ? 'warning' : 'success',
|
||||
emphasis: 'primary'
|
||||
},
|
||||
{
|
||||
label: '权限就绪',
|
||||
value: readyCustomers,
|
||||
note: '启用且权限完整',
|
||||
tone: 'success',
|
||||
emphasis: 'secondary'
|
||||
},
|
||||
{
|
||||
label: '车辆授权',
|
||||
value: grantedVehicles,
|
||||
note: '当前范围合计',
|
||||
tone: 'neutral',
|
||||
emphasis: 'secondary'
|
||||
}
|
||||
];
|
||||
const editingGrant = useMemo(() => draft.vehicleGrants.find((grant) => grant.vin === editingGrantVIN), [draft.vehicleGrants, editingGrantVIN]);
|
||||
const editingGrantPlate = editingGrant ? vehicleLabels[editingGrant.vin] : '';
|
||||
const grantWindowInvalid = Boolean(editingGrant && (!editingGrant.validFrom || (editingGrant.validTo && editingGrant.validTo <= editingGrant.validFrom)));
|
||||
@@ -469,12 +500,12 @@ export default function UsersPage() {
|
||||
className="v2-user-directory-header"
|
||||
title="客户权限目录"
|
||||
description="当前范围内的账号、菜单与车辆授权摘要"
|
||||
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 className="is-ready" type="tertiary">权限就绪 {readyCustomers}</Typography.Text>
|
||||
{attentionCustomers ? <Tag className="is-attention" color="amber" type="light" size="small">待完善 {attentionCustomers}</Tag> : null}
|
||||
<Typography.Text type="tertiary">车辆授权 {grantedVehicles}</Typography.Text>
|
||||
</span>}
|
||||
/>
|
||||
<WorkspaceMetricRail
|
||||
variant="queue"
|
||||
className="v2-user-directory-metric-rail"
|
||||
ariaLabel="客户权限目录统计"
|
||||
items={directoryMetrics}
|
||||
/>
|
||||
<div className="v2-user-directory-body">
|
||||
{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>} /> : <>
|
||||
|
||||
@@ -304,7 +304,10 @@ describe('V2 production entry', () => {
|
||||
expect(corePageSources.UsersPage).toContain('className="v2-user-command-bar"');
|
||||
expect(corePageSources.UsersPage).toContain('className="v2-user-discovery-shell"');
|
||||
expect(corePageSources.UsersPage).toContain('className="v2-user-directory-columns"');
|
||||
expect(corePageSources.UsersPage).not.toContain('v2-user-directory-metrics');
|
||||
expect(corePageSources.UsersPage).toContain("from '../shared/WorkspaceMetricRail'");
|
||||
expect(corePageSources.UsersPage).toContain('className="v2-user-directory-metric-rail"');
|
||||
expect(corePageSources.UsersPage).toContain('ariaLabel="客户权限目录统计"');
|
||||
expect(corePageSources.UsersPage).not.toContain('v2-user-directory-summary');
|
||||
expect(corePageSources.UsersPage).toContain('aria-expanded={selectedID === user.id && !creating}');
|
||||
expect(corePageSources.UsersPage).toContain('<Tabs className="v2-user-editor-tabs"');
|
||||
expect(corePageSources.UsersPage).toContain('<Tabs.TabPane');
|
||||
|
||||
@@ -23325,3 +23325,41 @@
|
||||
grid-template-columns: repeat(2, minmax(0, 1fr));
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Account governance uses the same compact evidence rail as the operational
|
||||
* workspaces. It replaces small header metadata and keeps the directory itself
|
||||
* as the dominant surface.
|
||||
*/
|
||||
.v2-user-directory-metric-rail.v2-workspace-metric-rail.is-queue.semi-card {
|
||||
flex: 0 0 auto;
|
||||
}
|
||||
|
||||
.v2-user-directory-metric-rail .v2-workspace-metric-list {
|
||||
grid-template-columns: 1.15fr 1fr .95fr .95fr;
|
||||
}
|
||||
|
||||
.v2-user-directory-metric-unit {
|
||||
margin-left: 2px;
|
||||
color: #6f90bb;
|
||||
font-size: 10px;
|
||||
font-style: normal;
|
||||
font-weight: 600;
|
||||
letter-spacing: 0;
|
||||
}
|
||||
|
||||
.v2-user-directory-metric-rail .v2-workspace-metric-list > span.is-primary.is-warning {
|
||||
background: linear-gradient(145deg, #fff7ea 0%, #fffdf8 100%);
|
||||
box-shadow: inset 0 -3px #e59a2f;
|
||||
}
|
||||
|
||||
.v2-user-directory-metric-rail .v2-workspace-metric-list > span.is-primary.is-success {
|
||||
background: linear-gradient(145deg, #eefaf5 0%, #fbfefd 100%);
|
||||
box-shadow: inset 0 -3px #20a678;
|
||||
}
|
||||
|
||||
@media (max-width: 680px) {
|
||||
.v2-user-directory-metric-rail .v2-workspace-metric-list {
|
||||
grid-template-columns: repeat(2, minmax(0, 1fr));
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user