unify Semi UI workspace command hierarchy
This commit is contained in:
@@ -50,6 +50,8 @@ test('removes old access rows immediately when the vehicle filter scope changes'
|
||||
const view = render(<QueryClientProvider client={client}><MemoryRouter future={ROUTER_FUTURE} initialEntries={['/access?keyword=OLDVIN']}><AccessPage /></MemoryRouter></QueryClientProvider>);
|
||||
|
||||
expect(await screen.findByText('旧接入车牌')).toBeInTheDocument();
|
||||
expect(screen.getByRole('heading', { name: '真实来源与接入健康', level: 5 })).toBeInTheDocument();
|
||||
expect(screen.getByLabelText('接入管理操作')).toHaveClass('v2-workspace-command-bar', 'v2-access-command-bar');
|
||||
for (const className of ['v2-access-filter-card-v3', 'v2-access-kpis-card-v3', 'v2-access-table-v3']) {
|
||||
expect(view.container.querySelector(`.${className}.semi-card`)).toBeInTheDocument();
|
||||
}
|
||||
|
||||
@@ -8,8 +8,8 @@ import type { AccessProtocolStatus, AccessQuery, AccessSummary, AccessThresholdC
|
||||
import { accessRowsToCSV, formatAccessTime, formatSeconds, updateProtocolThreshold } from '../domain/access';
|
||||
import { InlineError, PanelEmpty, PanelLoading } from '../shared/AsyncState';
|
||||
import { MetricActionButton } from '../shared/MetricActionButton';
|
||||
import { PageHeader } from '../shared/PageHeader';
|
||||
import { TablePagination } from '../shared/TablePagination';
|
||||
import { WorkspaceCommandBar } from '../shared/WorkspaceCommandBar';
|
||||
import { WorkspaceFilterPanel } from '../shared/WorkspaceFilterPanel';
|
||||
import { WorkspacePanelHeader } from '../shared/WorkspacePanelHeader';
|
||||
import { detailTriggerRow } from '../shared/detailTriggerRow';
|
||||
@@ -201,8 +201,10 @@ export default function AccessPage() {
|
||||
const refresh = () => Promise.all([summaryQuery.refetch(), vehiclesQuery.refetch(), ...(editable ? [unresolvedQuery.refetch(), thresholdQuery.refetch()] : [])]);
|
||||
|
||||
return <div className="v2-access-page v2-access-page-v3">
|
||||
<PageHeader
|
||||
title="车辆接入管理"
|
||||
<WorkspaceCommandBar
|
||||
className="v2-access-command-bar"
|
||||
ariaLabel="接入管理操作"
|
||||
title="真实来源与接入健康"
|
||||
description="核对车辆真实存在的数据来源、在线健康和待维护资料,不对尚未接入的业务协议作推断。"
|
||||
status={summary ? `${summary.totalVehicles.toLocaleString('zh-CN')} 辆主车辆` : '正在读取车辆'}
|
||||
meta={<Typography.Text type="tertiary">数据时间 {summary?.asOf ? formatAccessTime(summary.asOf) : '—'}</Typography.Text>}
|
||||
|
||||
@@ -59,6 +59,8 @@ test('renders reconciliation queue, loads evidence on demand and records review
|
||||
render(<QueryClientProvider client={client}><OperationsPage /></QueryClientProvider>);
|
||||
|
||||
expect(await screen.findByText('数据差异中心')).toBeInTheDocument();
|
||||
expect(screen.getByRole('heading', { name: '运行与数据质量', level: 5 })).toBeInTheDocument();
|
||||
expect(screen.getByLabelText('运维质量操作')).toHaveClass('v2-workspace-command-bar', 'v2-ops-command-bar');
|
||||
expect(screen.getByRole('tab', { name: '差异处置', selected: true })).toHaveClass('semi-button');
|
||||
expect(screen.getByRole('tabpanel', { name: '差异处置' })).toBeInTheDocument();
|
||||
expect(screen.queryByText('先选择一辆车')).not.toBeInTheDocument();
|
||||
|
||||
@@ -5,9 +5,9 @@ import { FormEvent, useDeferredValue, useEffect, useState } from 'react';
|
||||
import { api } from '../../api/client';
|
||||
import type { VehicleCoverageRow, VehicleLocationSourceEvidence, VehicleSourceDiagnostic } from '../../api/types';
|
||||
import { InlineError } from '../shared/AsyncState';
|
||||
import { PageHeader } from '../shared/PageHeader';
|
||||
import { SegmentedTabs } from '../shared/SegmentedTabs';
|
||||
import { VehicleCandidateList } from '../shared/VehicleCandidateList';
|
||||
import { WorkspaceCommandBar } from '../shared/WorkspaceCommandBar';
|
||||
import { WorkspaceFilterPanel } from '../shared/WorkspaceFilterPanel';
|
||||
import { WorkspacePanelHeader } from '../shared/WorkspacePanelHeader';
|
||||
import { LIVE_QUERY_POLICY, QUERY_MEMORY } from '../queryPolicy';
|
||||
@@ -340,8 +340,10 @@ export default function OperationsPage() {
|
||||
const healthIssueCount = linkIssueCount + runtimeIssueCount + capacityIssueCount + Number((data?.kafkaLag ?? 0) > 0);
|
||||
const overallStatus = !data ? 'unknown' : healthIssueCount > 0 ? 'warning' : 'ok';
|
||||
return <div className="v2-ops-page">
|
||||
<PageHeader
|
||||
title="运维质量"
|
||||
<WorkspaceCommandBar
|
||||
className="v2-ops-command-bar"
|
||||
ariaLabel="运维质量操作"
|
||||
title="运行与数据质量"
|
||||
description="先定位单车多来源问题,再核对服务和协议全局健康;所有结论来自服务端证据。"
|
||||
status={data?.runtime.dataMode === 'production' ? '生产模式' : '状态待确认'}
|
||||
statusColor={data?.runtime.dataMode === 'production' ? 'green' : 'orange'}
|
||||
|
||||
@@ -9,6 +9,7 @@ import { useSideSheetA11y } from '../hooks/useSideSheetA11y';
|
||||
import { TablePagination } from '../shared/TablePagination';
|
||||
import { VehicleCandidateList } from '../shared/VehicleCandidateList';
|
||||
import { WorkspaceFilterPanel } from '../shared/WorkspaceFilterPanel';
|
||||
import { WorkspaceCommandBar } from '../shared/WorkspaceCommandBar';
|
||||
import { WorkspacePanelHeader } from '../shared/WorkspacePanelHeader';
|
||||
import { mergeVehicleCandidates } from '../shared/vehicleCandidates';
|
||||
import { PanelEmpty, PanelLoading } from '../shared/AsyncState';
|
||||
@@ -244,17 +245,15 @@ export default function UsersPage() {
|
||||
};
|
||||
|
||||
return <div className="v2-user-admin">
|
||||
<div className="v2-user-command-bar" aria-label="账号管理操作">
|
||||
<div className="v2-user-command-copy">
|
||||
<strong>客户访问治理</strong>
|
||||
<span>菜单与车辆按最小权限开放,变更最多 30 秒生效</span>
|
||||
</div>
|
||||
<div className="v2-user-command-meta">
|
||||
<Tag color="blue" type="light" size="small">{customers.length} 个客户账号</Tag>
|
||||
<Typography.Text type="tertiary">本地账号 · 可扩展外部身份源</Typography.Text>
|
||||
<Button theme="solid" icon={<IconPlus />} aria-label="新建客户账号" onClick={startCreate}>新建客户账号</Button>
|
||||
</div>
|
||||
</div>
|
||||
<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="账号范围"
|
||||
|
||||
@@ -151,7 +151,8 @@ test('shows deduplicated Semi vehicle candidates and opens the selected VIN', as
|
||||
|
||||
const view = render(<QueryClientProvider client={client}><MemoryRouter initialEntries={['/vehicles']} future={ROUTER_FUTURE}><Routes><Route path="/vehicles" element={<VehiclePage />} /><Route path="/vehicles/:vin" element={<VehiclePage />} /></Routes></MemoryRouter></QueryClientProvider>);
|
||||
|
||||
expect(screen.getByRole('heading', { name: '车辆查询', level: 2 })).toBeInTheDocument();
|
||||
expect(screen.getByRole('heading', { name: '车辆快速定位', level: 5 })).toBeInTheDocument();
|
||||
expect(screen.getByLabelText('查车操作')).toHaveClass('v2-workspace-command-bar', 'v2-vehicle-command-bar');
|
||||
expect(screen.getByRole('heading', { name: '车辆定位', level: 5 }).closest('.semi-card')).toHaveClass('v2-vehicle-query-panel', 'v2-workspace-filter-panel');
|
||||
const input = screen.getByRole('textbox', { name: '搜索车辆' });
|
||||
expect(input).toHaveAttribute('aria-expanded', 'false');
|
||||
|
||||
@@ -17,11 +17,11 @@ import { isValidAMapCoordinate } from '../../integrations/amap';
|
||||
import { FleetMap } from '../map/FleetMap';
|
||||
import { InlineError, PageLoading } from '../shared/AsyncState';
|
||||
import { MonitorReturnBar } from '../shared/MonitorReturnBar';
|
||||
import { PageHeader } from '../shared/PageHeader';
|
||||
import { SegmentedTabs } from '../shared/SegmentedTabs';
|
||||
import { VehicleCandidateList } from '../shared/VehicleCandidateList';
|
||||
import { mergeVehicleCandidates } from '../shared/vehicleCandidates';
|
||||
import { VehicleSourceEvidencePanel } from '../shared/VehicleSourceEvidencePanel';
|
||||
import { WorkspaceCommandBar } from '../shared/WorkspaceCommandBar';
|
||||
import { WorkspaceFilterPanel } from '../shared/WorkspaceFilterPanel';
|
||||
import { WorkspacePanelHeader } from '../shared/WorkspacePanelHeader';
|
||||
import { monitorReturnFromParams, withMonitorReturn } from '../routing/monitorContext';
|
||||
@@ -105,9 +105,11 @@ function VehicleSearch() {
|
||||
};
|
||||
const vehicleScope = candidates.data ? `${candidates.data.total.toLocaleString('zh-CN')} 辆授权` : '授权范围';
|
||||
return <section className={`v2-vehicle-search-page${syncOpen ? ' has-sync-panel' : ''}${candidatesOpen ? ' has-candidates' : ''}`}>
|
||||
<PageHeader
|
||||
title="车辆查询"
|
||||
description="通过车牌、VIN 或终端手机号定位车辆,进入统一数字档案"
|
||||
<WorkspaceCommandBar
|
||||
className="v2-vehicle-command-bar"
|
||||
ariaLabel="查车操作"
|
||||
title="车辆快速定位"
|
||||
description="车牌、VIN 或手机号快速查车"
|
||||
status={candidates.isPending ? '正在读取授权范围' : vehicleScope}
|
||||
statusColor={candidates.isError ? 'red' : 'blue'}
|
||||
actions={canAdminister(session) ? <Button theme="light" onClick={() => setSyncOpen((value) => !value)}>{syncOpen ? '收起主档同步' : '批量同步主档'}</Button> : null}
|
||||
@@ -115,7 +117,7 @@ function VehicleSearch() {
|
||||
<WorkspaceFilterPanel
|
||||
className="v2-vehicle-query-panel"
|
||||
title="车辆定位"
|
||||
description="车牌优先、VIN 辅助,支持终端手机号精确定位"
|
||||
description="车牌优先,VIN 辅助"
|
||||
mobileSummary={deferredKeyword ? `正在查找 ${deferredKeyword}` : candidates.data ? `${candidates.data.total.toLocaleString('zh-CN')} 辆授权车辆` : '正在读取授权范围'}
|
||||
expanded={!filtersCollapsed}
|
||||
status={deferredKeyword ? candidates.isFetching ? '正在查找' : `${options.length} 辆匹配` : vehicleScope}
|
||||
|
||||
Reference in New Issue
Block a user