refine Semi UI track replay details
This commit is contained in:
@@ -106,7 +106,7 @@ test('keeps committed track criteria authoritative to same-route navigation and
|
||||
expect((await screen.findAllByText('粤A12345')).length).toBeGreaterThan(0);
|
||||
expect(screen.getByTestId('track-map')).toHaveAttribute('data-point-count', '3');
|
||||
expect(screen.queryByRole('textbox', { name: '搜索轨迹车辆' })).not.toBeInTheDocument();
|
||||
expect(screen.getByText(/07-15 00:00/)).toBeInTheDocument();
|
||||
expect(screen.getAllByText(/07-15 00:00/).length).toBeGreaterThan(0);
|
||||
fireEvent.click(screen.getByRole('button', { name: '修改条件' }));
|
||||
expect(screen.getByRole('textbox', { name: '搜索轨迹车辆' })).toHaveValue('LTEST000000000001');
|
||||
|
||||
@@ -225,8 +225,8 @@ test('renders a map-first replay workspace and connects stop, event, and panel i
|
||||
expect(screen.queryByRole('textbox', { name: '搜索轨迹车辆' })).not.toBeInTheDocument();
|
||||
|
||||
fireEvent.click(screen.getByRole('button', { name: '隐藏查询与明细' }));
|
||||
expect(screen.getByRole('button', { name: '展开查询与明细' })).toBeInTheDocument();
|
||||
fireEvent.click(screen.getByRole('button', { name: '展开查询与明细' }));
|
||||
expect(screen.getByRole('button', { name: '打开轨迹详情' })).toBeInTheDocument();
|
||||
fireEvent.click(screen.getByRole('button', { name: '打开轨迹详情' }));
|
||||
expect(screen.getByRole('button', { name: '隐藏查询与明细' })).toBeInTheDocument();
|
||||
|
||||
await waitFor(() => expect(mocks.trackPlayback).toHaveBeenCalledTimes(1));
|
||||
@@ -246,7 +246,7 @@ test('uses a Semi bottom SideSheet for mobile track criteria and evidence', asyn
|
||||
expect((await screen.findAllByText('粤A12345')).length).toBeGreaterThan(0);
|
||||
expect(view.container.querySelector('.v2-track-page')).toHaveClass('is-mobile-layout', 'is-rail-collapsed');
|
||||
expect(view.container.querySelector('.v2-track-page > .v2-track-stage')).toBeInTheDocument();
|
||||
fireEvent.click(screen.getByRole('button', { name: '展开查询与明细' }));
|
||||
fireEvent.click(screen.getByRole('button', { name: '打开轨迹详情' }));
|
||||
|
||||
const sheet = await waitFor(() => {
|
||||
const element = document.querySelector('.v2-track-detail-sidesheet .semi-sidesheet-inner');
|
||||
@@ -255,9 +255,24 @@ test('uses a Semi bottom SideSheet for mobile track criteria and evidence', asyn
|
||||
});
|
||||
expect(sheet).toHaveClass('semi-sidesheet-inner');
|
||||
expect(document.querySelector('.v2-track-detail-sidesheet .v2-track-rail')).toBeInTheDocument();
|
||||
expect(document.querySelector('.v2-track-detail-sidesheet .v2-track-query-summary')).not.toBeInTheDocument();
|
||||
expect(screen.getByText('07-15 00:00 – 07-15 23:59 · 自动来源')).toBeInTheDocument();
|
||||
expect(screen.getByText('8 km · 1 个停留 · 3 个事件')).toBeInTheDocument();
|
||||
expect(screen.getByRole('button', { name: '关闭轨迹查询与明细' })).toBeInTheDocument();
|
||||
});
|
||||
|
||||
test('keeps one clear mobile query entry before a vehicle is selected', async () => {
|
||||
Object.defineProperty(window, 'matchMedia', { configurable: true, value: vi.fn(() => ({ matches: true, addEventListener: vi.fn(), removeEventListener: vi.fn() })) });
|
||||
const client = new QueryClient({ defaultOptions: { queries: { retry: false } } });
|
||||
render(<QueryClientProvider client={client}><MemoryRouter future={ROUTER_FUTURE} initialEntries={['/tracks']}><TrackPage /></MemoryRouter></QueryClientProvider>);
|
||||
|
||||
expect(await screen.findByRole('button', { name: /选择车辆/ })).toBeInTheDocument();
|
||||
expect(screen.queryByRole('button', { name: '打开轨迹详情' })).not.toBeInTheDocument();
|
||||
fireEvent.click(screen.getByRole('button', { name: /选择车辆/ }));
|
||||
expect(await screen.findByRole('dialog', { name: '轨迹查询与明细' })).toBeInTheDocument();
|
||||
expect(screen.getByRole('textbox', { name: '搜索轨迹车辆' })).toBeInTheDocument();
|
||||
});
|
||||
|
||||
test('coalesces rapid track scrubbing into one address lookup for the final point', async () => {
|
||||
const client = new QueryClient({ defaultOptions: { queries: { retry: false } } });
|
||||
render(<QueryClientProvider client={client}><MemoryRouter future={ROUTER_FUTURE} initialEntries={['/tracks?keyword=LTEST000000000001&dateFrom=2026-07-15T00:00&dateTo=2026-07-15T23:59']}><TrackPage /></MemoryRouter></QueryClientProvider>);
|
||||
|
||||
@@ -175,7 +175,7 @@ function OverviewPanel({ track }: { track: TrackPlaybackResponse }) {
|
||||
</div>;
|
||||
}
|
||||
|
||||
const TrackRail = memo(function TrackRail({ draft, track, loading, activeStopIndexes, activeEventIndexes, tab, queryCollapsed, onDraft, onSubmit, onTab, onSelectIndex, onToggleQuery, onCollapse }: {
|
||||
const TrackRail = memo(function TrackRail({ draft, track, loading, activeStopIndexes, activeEventIndexes, tab, queryCollapsed, mobileLayout, onDraft, onSubmit, onTab, onSelectIndex, onToggleQuery, onCollapse }: {
|
||||
draft: Draft;
|
||||
track?: TrackPlaybackResponse;
|
||||
loading: boolean;
|
||||
@@ -183,6 +183,7 @@ const TrackRail = memo(function TrackRail({ draft, track, loading, activeStopInd
|
||||
activeEventIndexes: readonly number[];
|
||||
tab: PanelTab;
|
||||
queryCollapsed: boolean;
|
||||
mobileLayout: boolean;
|
||||
onDraft: (draft: Draft) => void;
|
||||
onSubmit: (event: FormEvent) => void;
|
||||
onTab: (tab: PanelTab) => void;
|
||||
@@ -192,13 +193,15 @@ const TrackRail = memo(function TrackRail({ draft, track, loading, activeStopInd
|
||||
}) {
|
||||
const activePreset = trackPreset(draft);
|
||||
const choosePreset = (daysBack: number) => onDraft({ ...draft, ...trackWindow(daysBack, daysBack > 0) });
|
||||
const appliedWindow = `${draft.dateFrom.replace('T', ' ').slice(5)} – ${draft.dateTo.replace('T', ' ').slice(5)}`;
|
||||
const appliedSource = draft.protocol || '自动来源';
|
||||
return <aside className="v2-track-rail">
|
||||
<Card className="v2-track-rail-shell" bodyStyle={{ padding: 0 }}>
|
||||
<section className={`v2-track-query-panel${queryCollapsed ? ' is-collapsed' : ''}`}>
|
||||
<WorkspacePanelHeader
|
||||
className="v2-track-query-header"
|
||||
title="轨迹查询"
|
||||
description={queryCollapsed ? '条件已应用,可随时修改' : '最长支持连续 7 天'}
|
||||
description={queryCollapsed ? `${appliedWindow} · ${appliedSource}` : '最长支持连续 7 天'}
|
||||
actions={<>
|
||||
<Button
|
||||
className="v2-track-query-toggle"
|
||||
@@ -213,7 +216,7 @@ const TrackRail = memo(function TrackRail({ draft, track, loading, activeStopInd
|
||||
<Button className="v2-track-rail-hide" theme="borderless" aria-label="隐藏查询与明细" icon={<IconChevronLeft />} onClick={onCollapse}><span className="v2-track-rail-hide-label">地图</span></Button>
|
||||
</>}
|
||||
/>
|
||||
{queryCollapsed ? <div className="v2-track-query-summary">
|
||||
{queryCollapsed && !mobileLayout ? <div className="v2-track-query-summary">
|
||||
<span><strong>{draft.keyword || '尚未选择车辆'}</strong><small>{draft.dateFrom.replace('T', ' ').slice(5)} – {draft.dateTo.replace('T', ' ').slice(5)}</small></span>
|
||||
<Tag color={draft.protocol ? 'blue' : 'grey'} type="light" size="small">{draft.protocol || '自动来源'}</Tag>
|
||||
</div> : <form className="v2-track-query" onSubmit={onSubmit}>
|
||||
@@ -366,7 +369,7 @@ export default function TrackPage() {
|
||||
setPlaying((value) => !value);
|
||||
};
|
||||
const progress = points.length > 1 ? boundedIndex / (points.length - 1) * 100 : 0;
|
||||
const trackRail = <TrackRail draft={draft} track={track} loading={query.isFetching} activeStopIndexes={activeStopIndexes} activeEventIndexes={activeEventIndexes} tab={panelTab} queryCollapsed={queryCollapsed} onDraft={setDraft} onSubmit={submit} onTab={setPanelTab} onSelectIndex={selectIndex} onToggleQuery={() => setQueryCollapsed((value) => !value)} onCollapse={collapseRail} />;
|
||||
const trackRail = <TrackRail draft={draft} track={track} loading={query.isFetching} activeStopIndexes={activeStopIndexes} activeEventIndexes={activeEventIndexes} tab={panelTab} queryCollapsed={queryCollapsed} mobileLayout={mobileLayout} onDraft={setDraft} onSubmit={submit} onTab={setPanelTab} onSelectIndex={selectIndex} onToggleQuery={() => setQueryCollapsed((value) => !value)} onCollapse={collapseRail} />;
|
||||
|
||||
return <div className={`v2-track-page${mobileLayout ? ' is-mobile-layout' : ''}${railCollapsed ? ' is-rail-collapsed' : ''}${monitorReturn ? ' has-monitor-return' : ''}`}>
|
||||
<MonitorReturnBar />
|
||||
@@ -376,14 +379,14 @@ export default function TrackPage() {
|
||||
placement="bottom"
|
||||
height="min(82dvh, 720px)"
|
||||
aria-label="轨迹查询与明细"
|
||||
title={<div className="v2-track-detail-sheet-title"><strong>轨迹查询与明细</strong><span>{track ? `${track.plate || track.vin} · ${number(track.summary.distanceKm)} km` : '选择车辆和时间范围'}</span></div>}
|
||||
title={<div className="v2-track-detail-sheet-title"><strong>轨迹查询与明细</strong><span>{track ? `${number(track.summary.distanceKm)} km · ${track.stops.length} 个停留 · ${track.events.length} 个事件` : '选择车辆和时间范围'}</span></div>}
|
||||
footer={null}
|
||||
onCancel={collapseRail}
|
||||
>{trackRail}</SideSheet> : trackRail}
|
||||
<section className="v2-track-stage">
|
||||
<TrackMap points={points} stops={track?.stops ?? []} activeIndex={boundedIndex} showStops={showStops} follow={follow} followDurationMs={playing ? Math.max(40, trackPlaybackInterval(playbackSpeed) - 10) : 180} onSelectIndex={selectIndex} onFollowChange={setFollow} />
|
||||
|
||||
{railCollapsed ? <Button className="v2-track-rail-expand" theme="light" aria-label="展开查询与明细" icon={<IconList />} onClick={() => setRailCollapsed(false)}>查询与明细</Button> : null}
|
||||
{railCollapsed && track?.points.length ? <Button className="v2-track-rail-expand" theme="light" aria-label="打开轨迹详情" icon={<IconList />} onClick={() => setRailCollapsed(false)}>轨迹详情</Button> : null}
|
||||
<div className="v2-track-stage-tools" role="toolbar" aria-label="地图轨迹工具">
|
||||
<Button theme="borderless" aria-label={follow ? '关闭车辆跟随' : '开启车辆跟随'} className={follow ? 'is-active' : ''} icon={<IconMapPin />} disabled={!points.length} onClick={() => setFollow((value) => !value)}>{follow ? '跟随车辆' : '自由浏览'}</Button>
|
||||
<Button theme="borderless" aria-label={showStops ? '隐藏停留点' : '显示停留点'} className={showStops ? 'is-active' : ''} icon={showStops ? <IconEyeOpened /> : <IconEyeClosed />} disabled={!track?.stops.length} onClick={() => setShowStops((value) => !value)}>停留点</Button>
|
||||
|
||||
Reference in New Issue
Block a user