diff --git a/vehicle-data-platform/apps/web/src/v2/pages/StatisticsPage.test.tsx b/vehicle-data-platform/apps/web/src/v2/pages/StatisticsPage.test.tsx index acd57dc8..d0b5693d 100644 --- a/vehicle-data-platform/apps/web/src/v2/pages/StatisticsPage.test.tsx +++ b/vehicle-data-platform/apps/web/src/v2/pages/StatisticsPage.test.tsx @@ -60,15 +60,18 @@ test('renders only the desktop matrix with dates as columns and a period total', expect(screen.queryByText('车辆里程排名')).not.toBeInTheDocument(); expect(screen.getByText('已绑定主车辆')).toBeInTheDocument(); expect(screen.getByText('已选择 1 辆')).toBeInTheDocument(); - expect(screen.getByText('已绑定主车辆').closest('.semi-card')).toHaveClass('v2-mileage-summary-card'); + expect(screen.getByText('已绑定主车辆').closest('[role="listitem"]')).toHaveClass('is-primary', 'is-success'); expect(screen.getByText('车辆每日里程').closest('.semi-card')).toHaveClass('v2-mileage-results'); expect(view.container.querySelector('.v2-mileage-query-panel.semi-card')).toBeInTheDocument(); expect(view.container.querySelector('.v2-mileage-discovery-shell > .v2-mileage-command-bar')).toBeInTheDocument(); expect(view.container.querySelector('.v2-mileage-page > .v2-page-heading')).not.toBeInTheDocument(); - expect(view.container.querySelector('.v2-mileage-summary.semi-card')).toHaveAttribute('aria-label', '里程查询统计信息'); - expect(view.container.querySelector('.v2-mileage-summary-secondary.semi-card-group')).toBeInTheDocument(); - expect(view.container.querySelectorAll('.v2-mileage-summary-card.semi-card')).toHaveLength(4); - expect(view.container.querySelector('.v2-mileage-summary-card.is-primary .v2-mileage-summary-value')).toHaveTextContent('193.3 km'); + expect(view.container.querySelector('.v2-mileage-summary')).toHaveClass('v2-workspace-metric-rail', 'is-queue'); + expect(view.container.querySelector('.v2-mileage-summary .v2-workspace-metric-list')).toHaveAttribute('aria-label', '里程查询统计信息'); + expect(view.container.querySelectorAll('.v2-mileage-summary .v2-workspace-metric-list > span')).toHaveLength(4); + expect(view.container.querySelector('.v2-mileage-summary .v2-workspace-metric-list > span.is-primary')).toHaveTextContent('区间总里程193.3 km2 条车辆日记录'); + expect(view.container.querySelector('.v2-mileage-summary .v2-workspace-metric-list')).toHaveTextContent('已绑定主车辆1 辆已选择 1 辆'); + expect(view.container.querySelector('.v2-mileage-summary .v2-workspace-metric-list')).toHaveTextContent('统计天数2 天2026/07/13 — 07/14'); + expect(view.container.querySelector('.v2-mileage-summary .v2-workspace-metric-list')).toHaveTextContent('日均里程96.7 km按有效车辆日平均'); expect(view.container.querySelector('.v2-mileage-table-wrap')).toBeInTheDocument(); expect(screen.getByRole('note', { name: '里程矩阵读表说明' })).toHaveTextContent('0 km已上报、无里程增量'); expect(screen.getByRole('note', { name: '里程矩阵读表说明' })).toHaveTextContent('—无可用里程'); diff --git a/vehicle-data-platform/apps/web/src/v2/pages/StatisticsPage.tsx b/vehicle-data-platform/apps/web/src/v2/pages/StatisticsPage.tsx index b64bd1b6..8bb7a3cf 100644 --- a/vehicle-data-platform/apps/web/src/v2/pages/StatisticsPage.tsx +++ b/vehicle-data-platform/apps/web/src/v2/pages/StatisticsPage.tsx @@ -16,7 +16,7 @@ import { VehicleCandidateList } from '../shared/VehicleCandidateList'; import { mergeVehicleCandidates } from '../shared/vehicleCandidates'; import { WorkspaceFilterPanel } from '../shared/WorkspaceFilterPanel'; import { WorkspaceCommandBar } from '../shared/WorkspaceCommandBar'; -import { WorkspaceMetricRail } from '../shared/WorkspaceMetricRail'; +import { WorkspaceMetricRail, type WorkspaceQueueMetricRailItem } from '../shared/WorkspaceMetricRail'; import { WorkspacePanelHeader } from '../shared/WorkspacePanelHeader'; import { WorkspaceSideSheet } from '../shared/WorkspaceSideSheet'; import { QUERY_MEMORY, retainPreviousPageWithinScope } from '../queryPolicy'; @@ -253,27 +253,42 @@ function SummaryRail({ data, criteria, fleetTotal, loading }: { data?: MileageSt const summaryRange = criteria.dateFrom.slice(0, 4) === criteria.dateTo.slice(0, 4) ? `${criteria.dateFrom.replace(/-/g, '/')} — ${criteria.dateTo.slice(5).replace('-', '/')}` : `${criteria.dateFrom.replace(/-/g, '/')} — ${criteria.dateTo.replace(/-/g, '/')}`; - const items = loading ? [ - ['已绑定主车辆', '—', '正在按新筛选范围查询'], - ['统计天数', `${days} 天`, summaryRange], - ['区间总里程', '—', '正在计算区间汇总'], - ['日均里程', '—', '正在计算有效车辆日'] - ] : [ - ['已绑定主车辆', `${vehicleCount} 辆`, criteria.vehicles.length ? `已选择 ${criteria.vehicles.length} 辆` : `档案口径 · ${data?.vehicleCount ?? 0} 辆有里程`], - ['统计天数', `${days} 天`, summaryRange], - ['区间总里程', `${formatKm(data?.periodMileageKm)} km`, `${data?.recordCount ?? 0} 条车辆日记录`], - ['日均里程', `${formatKm(data?.averageDailyMileageKm)} km`, '按有效车辆日平均'] + const withUnit = (value: string | number, unit: string) => <>{value} {unit}; + const items: WorkspaceQueueMetricRailItem[] = [ + { + label: '区间总里程', + value: loading ? '—' : withUnit(formatKm(data?.periodMileageKm), 'km'), + note: loading ? '正在计算区间汇总' : `${data?.recordCount ?? 0} 条车辆日记录`, + tone: 'primary', + emphasis: 'primary' + }, + { + label: '已绑定主车辆', + value: loading ? '—' : withUnit(vehicleCount, '辆'), + note: loading ? '正在按新筛选范围查询' : criteria.vehicles.length ? `已选择 ${criteria.vehicles.length} 辆` : `档案口径 · ${data?.vehicleCount ?? 0} 辆有里程`, + tone: 'success', + emphasis: 'primary' + }, + { + label: '统计天数', + value: withUnit(days, '天'), + note: summaryRange, + tone: 'neutral', + emphasis: 'secondary' + }, + { + label: '日均里程', + value: loading ? '—' : withUnit(formatKm(data?.averageDailyMileageKm), 'km'), + note: loading ? '正在计算有效车辆日' : '按有效车辆日平均', + tone: 'primary', + emphasis: 'secondary' + } ]; - const primary = items[2]; - const secondary = [items[0], items[1], items[3]]; return ({ label, value, note, className: `is-${['fleet', 'range', 'average'][index]}` }))} + items={items} />; } diff --git a/vehicle-data-platform/apps/web/src/v2/productionEntry.test.ts b/vehicle-data-platform/apps/web/src/v2/productionEntry.test.ts index 8a07ec3c..1d24d486 100644 --- a/vehicle-data-platform/apps/web/src/v2/productionEntry.test.ts +++ b/vehicle-data-platform/apps/web/src/v2/productionEntry.test.ts @@ -430,9 +430,11 @@ describe('V2 production entry', () => { expect(corePageSources.StatisticsPage).toContain(' { />); const rail = view.container.querySelector('.v2-workspace-metric-rail'); - expect(rail).toHaveClass('semi-card', 'is-queue', 'queue-metrics'); + expect(rail).toHaveClass('semi-card', 'is-queue', 'has-context', 'queue-metrics'); const list = screen.getByRole('list', { name: '处置队列概览' }); expect(within(list).getAllByRole('listitem')).toHaveLength(4); expect(within(list).getByText('待复核').closest('[role="listitem"]')).toHaveClass('is-primary'); @@ -70,6 +70,7 @@ describe('WorkspaceMetricRail', () => { />); const list = screen.getByRole('list', { name: '告警队列概览' }); + expect(list.closest('.v2-workspace-metric-rail')).not.toHaveClass('has-context'); const action = within(list).getByRole('button', { name: '筛选未处理,共 687 条' }); expect(action).toHaveClass('semi-button', 'v2-workspace-metric-action'); expect(action).toHaveAttribute('aria-pressed', 'true'); diff --git a/vehicle-data-platform/apps/web/src/v2/shared/WorkspaceMetricRail.tsx b/vehicle-data-platform/apps/web/src/v2/shared/WorkspaceMetricRail.tsx index cccc1c5a..1fbcb32d 100644 --- a/vehicle-data-platform/apps/web/src/v2/shared/WorkspaceMetricRail.tsx +++ b/vehicle-data-platform/apps/web/src/v2/shared/WorkspaceMetricRail.tsx @@ -39,7 +39,8 @@ type WorkspaceQueueMetricRailProps = { export function WorkspaceMetricRail(props: WorkspaceSummaryMetricRailProps | WorkspaceQueueMetricRailProps) { if ('items' in props) { - return + const hasContext = props.context != null; + return
{props.items.map((item, index) => { const toneClassName = `is-${item.tone ?? 'neutral'}`; @@ -72,7 +73,7 @@ export function WorkspaceMetricRail(props: WorkspaceSummaryMetricRailProps | Wor ; })}
- {props.context ?
{props.context}
: null} + {hasContext ?
{props.context}
: null}
; } diff --git a/vehicle-data-platform/apps/web/src/v2/styles/workspace.css b/vehicle-data-platform/apps/web/src/v2/styles/workspace.css index b54a8a73..1de3a180 100644 --- a/vehicle-data-platform/apps/web/src/v2/styles/workspace.css +++ b/vehicle-data-platform/apps/web/src/v2/styles/workspace.css @@ -15646,6 +15646,10 @@ .v2-workspace-metric-rail.is-queue > .semi-card-body { display: grid; min-height: 58px; + grid-template-columns: minmax(0, 1fr); +} + +.v2-workspace-metric-rail.is-queue.has-context > .semi-card-body { grid-template-columns: minmax(0, 1fr) 218px; } @@ -15942,7 +15946,7 @@ } @media (max-width: 900px) { - .v2-workspace-metric-rail.is-queue > .semi-card-body { + .v2-workspace-metric-rail.is-queue.has-context > .semi-card-body { grid-template-columns: minmax(0, 1fr) 180px; } @@ -23218,3 +23222,72 @@ display: none; } } + +/* + * Mileage analysis now uses the shared Semi UI metric rail. These late + * overrides retire the former hero-card dimensions without carrying their + * fixed heights into the compact queue layout. + */ +.v2-mileage-summary.v2-workspace-metric-rail.is-queue.semi-card { + display: block; + width: 100%; + height: auto; + min-height: 0; + gap: 0; + overflow: hidden; + border: 0; + border-top: 1px solid #e2e9f2; + border-radius: 0 0 11px 11px; + background: #fff; + box-shadow: none; +} + +.v2-mileage-summary.v2-workspace-metric-rail.is-queue > .semi-card-body { + display: grid; + height: auto; + min-height: 58px; + grid-template-columns: minmax(0, 1fr); + gap: 0; +} + +.v2-mileage-summary .v2-workspace-metric-list { + grid-template-columns: 1.3fr 1.1fr .9fr .9fr; +} + +.v2-mileage-summary .v2-workspace-metric-list > span { + display: grid; + min-height: 58px; +} + +.v2-mileage-summary-unit { + margin-left: 2px; + color: #8493a5; + font-size: 8px; + font-style: normal; + font-weight: 600; + letter-spacing: 0; +} + +.v2-mileage-summary .v2-workspace-metric-list > span.is-primary .v2-mileage-summary-unit { + color: #6f90bb; +} + +.v2-mileage-summary .v2-workspace-metric-list > span.is-success .v2-mileage-summary-unit { + color: #5b9c7f; +} + +@media (max-width: 680px) { + .v2-mileage-summary.v2-workspace-metric-rail.is-queue.semi-card, + .v2-mileage-summary.v2-workspace-metric-rail.is-queue > .semi-card-body { + height: auto; + min-height: 56px; + } + + .v2-mileage-summary .v2-workspace-metric-list { + grid-template-columns: repeat(2, minmax(0, 1fr)); + } + + .v2-mileage-summary .v2-workspace-metric-list > span { + min-height: 56px; + } +}