polish Semi UI query workspaces
This commit is contained in:
@@ -46,6 +46,9 @@ test('renders only the desktop matrix with dates as columns and a period total',
|
||||
expect(screen.getAllByText('7/13').length).toBeGreaterThan(0);
|
||||
expect(screen.getAllByText('7/14').length).toBeGreaterThan(0);
|
||||
expect(screen.getAllByText('粤A12345').length).toBeGreaterThan(0);
|
||||
expect(screen.getByRole('heading', { name: '里程范围', level: 5 })).toBeInTheDocument();
|
||||
expect(screen.getByText('车牌、自然日与里程数据源策略')).toBeInTheDocument();
|
||||
expect(screen.getByText('1 辆 · 2 天').closest('.semi-tag')).toBeInTheDocument();
|
||||
expect(screen.queryByText('当日起始里程')).not.toBeInTheDocument();
|
||||
expect(screen.queryByText('数据来源')).not.toBeInTheDocument();
|
||||
expect(screen.queryByRole('img', { name: '每日行驶里程趋势图' })).not.toBeInTheDocument();
|
||||
|
||||
@@ -9,10 +9,10 @@ import { createMileageExportStream, type MileageExportStream } from '../domain/m
|
||||
import { formatZhNumber } from '../domain/formatters';
|
||||
import { InlineError, PanelEmpty, PanelLoading } from '../shared/AsyncState';
|
||||
import { MonitorReturnBar } from '../shared/MonitorReturnBar';
|
||||
import { MobileFilterToggle } from '../shared/MobileFilterToggle';
|
||||
import { TablePagination } from '../shared/TablePagination';
|
||||
import { VehicleCandidateList } from '../shared/VehicleCandidateList';
|
||||
import { mergeVehicleCandidates } from '../shared/vehicleCandidates';
|
||||
import { WorkspaceFilterPanel } from '../shared/WorkspaceFilterPanel';
|
||||
import { WorkspacePanelHeader } from '../shared/WorkspacePanelHeader';
|
||||
import { QUERY_MEMORY, retainPreviousPageWithinScope } from '../queryPolicy';
|
||||
import { monitorReturnFromParams, preserveMonitorReturn } from '../routing/monitorContext';
|
||||
@@ -486,8 +486,17 @@ export default function StatisticsPage() {
|
||||
|
||||
return <div className="v2-mileage-page" ref={pageRef}>
|
||||
<MonitorReturnBar />
|
||||
<Card className="v2-mileage-query-panel" bodyStyle={{ padding: 0 }}>
|
||||
<MobileFilterToggle title="查询条件" summary={criteria.vehicles.length ? `已选 ${criteria.vehicles.length} 辆 · ${inclusiveDays(criteria.dateFrom, criteria.dateTo)} 天` : `全部车辆 · ${inclusiveDays(criteria.dateFrom, criteria.dateTo)} 天`} expanded={!filtersCollapsed} collapsedLabel="修改" onToggle={() => setFiltersCollapsed((value) => !value)} />
|
||||
<WorkspaceFilterPanel
|
||||
className="v2-mileage-query-panel"
|
||||
title="里程范围"
|
||||
description="车牌、自然日与里程数据源策略"
|
||||
mobileSummary={criteria.vehicles.length ? `已选 ${criteria.vehicles.length} 辆 · ${inclusiveDays(criteria.dateFrom, criteria.dateTo)} 天` : `全部车辆 · ${inclusiveDays(criteria.dateFrom, criteria.dateTo)} 天`}
|
||||
expanded={!filtersCollapsed}
|
||||
status={`${criteria.vehicles.length ? `${criteria.vehicles.length} 辆` : '全部授权车辆'} · ${inclusiveDays(criteria.dateFrom, criteria.dateTo)} 天`}
|
||||
statusColor={criteria.vehicles.length ? 'blue' : 'grey'}
|
||||
collapsedLabel="修改"
|
||||
onToggle={() => setFiltersCollapsed((value) => !value)}
|
||||
>
|
||||
<form className={`v2-mileage-filter${filtersCollapsed ? ' is-mobile-collapsed' : ''}`} onSubmit={submit}>
|
||||
<VehicleMultiSelect value={draft.vehicles} onChange={(vehicles) => setDraft((current) => ({ ...current, vehicles }))} />
|
||||
<label><span>开始日期</span><Input aria-label="开始日期" type="date" value={draft.dateFrom} max={draft.dateTo} onChange={(value) => setDraft((current) => ({ ...current, dateFrom: value }))} /></label>
|
||||
@@ -498,13 +507,13 @@ export default function StatisticsPage() {
|
||||
</form>
|
||||
{validationError || criteriaError ? <p className="v2-mileage-validation" role="alert">{validationError || criteriaError}</p> : null}
|
||||
<SummaryRail data={statistics.data} criteria={criteria} fleetTotal={fleetVehicles.data?.total} loading={statistics.isLoading} />
|
||||
</Card>
|
||||
</WorkspaceFilterPanel>
|
||||
{statistics.isError || mileage.isError || fleetVehicles.isError ? <InlineError message={(statistics.error ?? mileage.error ?? fleetVehicles.error) instanceof Error ? (statistics.error ?? mileage.error ?? fleetVehicles.error as Error).message : '里程数据加载失败'} onRetry={() => { statistics.refetch(); mileage.refetch(); if (!hasVehicles) fleetVehicles.refetch(); }} /> : null}
|
||||
<Card className="v2-mileage-results" bodyStyle={{ padding: 0 }}>
|
||||
<WorkspacePanelHeader
|
||||
title="车辆每日里程"
|
||||
description={`${criteria.dateFrom} 至 ${criteria.dateTo}`}
|
||||
meta={`${hasVehicles ? `${totalVehicles} 辆车` : `当前 ${displayVehicles.length} 辆 / 共 ${totalVehicles} 辆`} · ${dates.length} 个自然日`}
|
||||
meta={<span className="v2-workspace-result-meta"><Tag color="blue" type="light" size="small">{hasVehicles ? `${totalVehicles} 辆` : `${displayVehicles.length} / ${totalVehicles} 辆`}</Tag><span>{dates.length} 个自然日</span></span>}
|
||||
actionsClassName="v2-mileage-result-actions"
|
||||
actions={<><Button className="is-refresh" theme="borderless" aria-label="刷新里程数据" icon={<IconRefresh />} onClick={() => { statistics.refetch(); mileage.refetch(); if (!hasVehicles) fleetVehicles.refetch(); }} disabled={refreshing}>{refreshing ? '更新中' : '刷新'}</Button><Button theme="light" aria-label={isExporting ? '取消导出' : '导出 Excel'} icon={isExporting ? <IconClose /> : <IconDownload />} onClick={isExporting ? cancelExport : exportExcel} disabled={!totalVehicles || Boolean(criteriaError)}>{isExporting ? '取消导出' : '导出 Excel'}</Button></>}
|
||||
/>
|
||||
|
||||
@@ -234,7 +234,7 @@ const TrackRail = memo(function TrackRail({ draft, track, loading, activeStopInd
|
||||
className="v2-track-result-header"
|
||||
title={track ? track.plate || track.vin : '轨迹明细'}
|
||||
description={track ? track.vin : '停留、事件与行程证据'}
|
||||
meta={track ? `${number(track.summary.distanceKm)} km` : undefined}
|
||||
meta={<Tag color={track ? 'blue' : 'grey'} type="light" size="small">{track ? `${number(track.summary.distanceKm)} km` : '等待查询'}</Tag>}
|
||||
/>
|
||||
<SegmentedTabs className="v2-track-rail-tabs" ariaLabel="轨迹明细分类" value={tab} onChange={onTab} items={[{ key: 'stops', label: '停留点', count: track?.stops.length ?? 0 }, { key: 'events', label: '事件点', count: track?.events.length ?? 0 }, { key: 'overview', label: '概览' }]} />
|
||||
<div className="v2-track-rail-scroll">
|
||||
|
||||
@@ -146,7 +146,7 @@ function VehicleSearch() {
|
||||
<WorkspacePanelHeader
|
||||
title={deferredKeyword ? '匹配车辆快捷入口' : '最近上报车辆'}
|
||||
description={deferredKeyword ? '按当前搜索条件展示,可直接进入车辆数字档案' : '按照最新上报时间排列,无需搜索即可继续查看'}
|
||||
meta={candidates.data ? `当前显示 ${formatZhNumber(quickVehicles.length)} 辆` : '授权范围'}
|
||||
meta={<Tag color={deferredKeyword ? 'blue' : 'grey'} type="light" size="small">{candidates.data ? `当前显示 ${formatZhNumber(quickVehicles.length)} 辆` : '授权范围'}</Tag>}
|
||||
/>
|
||||
{candidates.isFetching && !candidates.data
|
||||
? <div className="v2-vehicle-recent-state" role="status"><span className="v2-spinner" /><span>正在读取授权车辆…</span></div>
|
||||
|
||||
@@ -310,7 +310,7 @@ describe('V2 production entry', () => {
|
||||
expect(corePageSources.TrackPage).not.toContain('<input');
|
||||
expect(corePageSources.TrackPage).not.toContain('<button');
|
||||
expect(corePageSources.StatisticsPage).toContain('<Input');
|
||||
expect(corePageSources.StatisticsPage).toContain('<Card className="v2-mileage-query-panel"');
|
||||
expect(corePageSources.StatisticsPage).toContain('className="v2-mileage-query-panel"');
|
||||
expect(corePageSources.StatisticsPage).toContain('<Card className="v2-mileage-summary"');
|
||||
expect(corePageSources.StatisticsPage).toContain('<CardGroup className="v2-mileage-summary-secondary"');
|
||||
expect(corePageSources.StatisticsPage).toContain('<Card className="v2-mileage-summary-card');
|
||||
@@ -440,15 +440,10 @@ describe('V2 production entry', () => {
|
||||
expect(v2Styles).toContain('.v2-track-current-card.semi-card { top: auto; right: 8px; bottom: 126px; left: 8px; width: auto; height: auto;');
|
||||
expect(workspaceFilterPanelSource).toContain("from './MobileFilterToggle'");
|
||||
expect(workspaceFilterPanelSource).toContain('<MobileFilterToggle');
|
||||
for (const name of ['HistoryPage', 'AlertsPage', 'AccessPage']) {
|
||||
for (const name of ['HistoryPage', 'AlertsPage', 'AccessPage', 'StatisticsPage']) {
|
||||
expect(corePageSources[name]).toContain("from '../shared/WorkspaceFilterPanel'");
|
||||
expect(corePageSources[name]).toContain('<WorkspaceFilterPanel');
|
||||
}
|
||||
for (const name of ['StatisticsPage']) {
|
||||
expect(corePageSources[name]).toContain("from '../shared/MobileFilterToggle'");
|
||||
expect(corePageSources[name]).toContain('<MobileFilterToggle');
|
||||
expect(corePageSources[name]).not.toContain('<button type="button" className="v2-mobile-filter-toggle"');
|
||||
}
|
||||
expect(tablePaginationSource).toContain('<Pagination');
|
||||
});
|
||||
|
||||
|
||||
@@ -21,6 +21,7 @@ describe('WorkspaceFilterPanel', () => {
|
||||
</WorkspaceFilterPanel>
|
||||
);
|
||||
|
||||
expect(screen.getByRole('heading', { name: '事件筛选', level: 5 })).toBeInTheDocument();
|
||||
expect(screen.getByText('按关键词与状态收窄范围')).toBeInTheDocument();
|
||||
expect(screen.getByText('2 项已启用')).toBeInTheDocument();
|
||||
expect(screen.getByRole('button', { name: '修改事件筛选:已启用 2 项' })).toHaveAttribute('aria-expanded', 'false');
|
||||
|
||||
@@ -3,7 +3,7 @@ import { Card, Tag, Typography } from '@douyinfe/semi-ui';
|
||||
import type { ComponentProps, ReactNode } from 'react';
|
||||
import { MobileFilterToggle } from './MobileFilterToggle';
|
||||
|
||||
const { Text } = Typography;
|
||||
const { Text, Title } = Typography;
|
||||
|
||||
type WorkspaceFilterPanelProps = {
|
||||
title?: string;
|
||||
@@ -48,7 +48,7 @@ export function WorkspaceFilterPanel({
|
||||
<header className="v2-workspace-filter-heading">
|
||||
<span className="v2-workspace-filter-icon" aria-hidden="true"><IconFilter /></span>
|
||||
<span className="v2-workspace-filter-copy">
|
||||
<Text strong>{title}</Text>
|
||||
<Title heading={5}>{title}</Title>
|
||||
<Text type="tertiary" size="small">{description}</Text>
|
||||
</span>
|
||||
<Tag color={statusColor} type="light" size="small">{status}</Tag>
|
||||
|
||||
@@ -435,6 +435,30 @@
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.v2-workspace-panel-meta > .semi-tag,
|
||||
.v2-workspace-result-meta > .semi-tag {
|
||||
min-height: 24px;
|
||||
margin: 0;
|
||||
border-radius: 999px;
|
||||
padding-inline: 8px;
|
||||
font-size: 10px;
|
||||
font-variant-numeric: tabular-nums;
|
||||
font-weight: 650;
|
||||
}
|
||||
|
||||
.v2-workspace-result-meta {
|
||||
display: inline-flex;
|
||||
min-width: 0;
|
||||
align-items: center;
|
||||
gap: 7px;
|
||||
}
|
||||
|
||||
.v2-workspace-result-meta > span {
|
||||
color: #748398;
|
||||
font-size: 10px;
|
||||
font-variant-numeric: tabular-nums;
|
||||
}
|
||||
|
||||
.v2-workspace-panel-actions > .semi-button {
|
||||
min-height: 34px;
|
||||
border-radius: 8px;
|
||||
@@ -521,6 +545,10 @@
|
||||
padding: 7px 10px 6px;
|
||||
}
|
||||
.v2-workspace-panel-header.is-inverted > .v2-workspace-panel-copy > h5.semi-typography { font-size: 13px; }
|
||||
|
||||
.v2-workspace-result-meta > span {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -3721,9 +3749,11 @@
|
||||
|
||||
.v2-workspace-filter-copy > .semi-typography:first-child {
|
||||
flex: 0 0 auto;
|
||||
margin: 0;
|
||||
color: #30445d;
|
||||
font-size: 12px;
|
||||
font-weight: 700;
|
||||
line-height: 1.3;
|
||||
}
|
||||
|
||||
.v2-workspace-filter-copy > .semi-typography:last-child {
|
||||
@@ -3744,6 +3774,51 @@
|
||||
font-weight: 650;
|
||||
}
|
||||
|
||||
.v2-mileage-query-panel.v2-workspace-filter-panel.semi-card {
|
||||
border-radius: 12px;
|
||||
}
|
||||
|
||||
.v2-mileage-query-panel .v2-workspace-filter-heading {
|
||||
min-height: 42px;
|
||||
}
|
||||
|
||||
@media (min-width: 681px) and (max-width: 1050px) {
|
||||
.v2-mileage-query-panel .v2-mileage-filter {
|
||||
min-height: 0;
|
||||
grid-template-columns: minmax(200px, 1.35fr) repeat(2, minmax(112px, .72fr)) auto;
|
||||
grid-template-areas:
|
||||
"vehicle start end submit"
|
||||
"source ranges ranges ranges";
|
||||
align-items: end;
|
||||
}
|
||||
|
||||
.v2-mileage-query-panel .v2-mileage-vehicle-field {
|
||||
grid-area: vehicle;
|
||||
}
|
||||
|
||||
.v2-mileage-query-panel .v2-mileage-filter > label:nth-of-type(2) {
|
||||
grid-area: start;
|
||||
}
|
||||
|
||||
.v2-mileage-query-panel .v2-mileage-filter > label:nth-of-type(3) {
|
||||
grid-area: end;
|
||||
}
|
||||
|
||||
.v2-mileage-query-panel .v2-mileage-filter > .v2-primary-button {
|
||||
grid-area: submit;
|
||||
width: auto;
|
||||
margin-top: 0;
|
||||
}
|
||||
|
||||
.v2-mileage-query-panel .v2-mileage-source-strategy {
|
||||
grid-area: source;
|
||||
}
|
||||
|
||||
.v2-mileage-query-panel .v2-mileage-ranges {
|
||||
grid-area: ranges;
|
||||
}
|
||||
}
|
||||
|
||||
.v2-table-pagination.semi-page {
|
||||
min-width: 0;
|
||||
color: #607086;
|
||||
@@ -3802,6 +3877,10 @@
|
||||
border-radius: 11px;
|
||||
}
|
||||
|
||||
.v2-mileage-query-panel.v2-workspace-filter-panel.semi-card {
|
||||
border-radius: 11px;
|
||||
}
|
||||
|
||||
.v2-table-pagination-controls {
|
||||
justify-content: space-between;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user