feat: refine mobile track workspace
This commit is contained in:
@@ -276,6 +276,10 @@ test('uses a Semi bottom SideSheet for mobile track criteria and evidence', asyn
|
|||||||
expect(screen.queryByRole('textbox', { name: '搜索轨迹车辆' })).not.toBeInTheDocument();
|
expect(screen.queryByRole('textbox', { name: '搜索轨迹车辆' })).not.toBeInTheDocument();
|
||||||
expect(screen.getByText('07-15 00:00 – 07-15 23:59 · 自动来源')).toBeInTheDocument();
|
expect(screen.getByText('07-15 00:00 – 07-15 23:59 · 自动来源')).toBeInTheDocument();
|
||||||
expect(screen.getByText('8 km · 1 个停留 · 3 个事件')).toBeInTheDocument();
|
expect(screen.getByText('8 km · 1 个停留 · 3 个事件')).toBeInTheDocument();
|
||||||
|
expect(sheet).toHaveStyle({ height: 'min(82dvh, 326px)' });
|
||||||
|
fireEvent.click(screen.getByRole('tab', { name: /事件点/ }));
|
||||||
|
await waitFor(() => expect(sheet).toHaveStyle({ height: 'min(82dvh, 450px)' }));
|
||||||
|
fireEvent.click(screen.getByRole('tab', { name: /停留点/ }));
|
||||||
fireEvent.click(screen.getByRole('button', { name: /停留 00:05:00/ }));
|
fireEvent.click(screen.getByRole('button', { name: /停留 00:05:00/ }));
|
||||||
expect(view.container.querySelector('.v2-track-page')).toHaveClass('is-rail-collapsed');
|
expect(view.container.querySelector('.v2-track-page')).toHaveClass('is-rail-collapsed');
|
||||||
fireEvent.click(screen.getByRole('button', { name: '打开轨迹详情' }));
|
fireEvent.click(screen.getByRole('button', { name: '打开轨迹详情' }));
|
||||||
|
|||||||
@@ -39,6 +39,9 @@ const TRACK_SOURCE_OPTIONS = [
|
|||||||
const numberFormatters = new Map<number, Intl.NumberFormat>();
|
const numberFormatters = new Map<number, Intl.NumberFormat>();
|
||||||
const timeFormatter = new Intl.DateTimeFormat('zh-CN', { timeZone: 'Asia/Shanghai', hour12: false, hour: '2-digit', minute: '2-digit', second: '2-digit' });
|
const timeFormatter = new Intl.DateTimeFormat('zh-CN', { timeZone: 'Asia/Shanghai', hour12: false, hour: '2-digit', minute: '2-digit', second: '2-digit' });
|
||||||
const dateTimeFormatter = new Intl.DateTimeFormat('zh-CN', { timeZone: 'Asia/Shanghai', hour12: false, month: '2-digit', day: '2-digit', hour: '2-digit', minute: '2-digit', second: '2-digit' });
|
const dateTimeFormatter = new Intl.DateTimeFormat('zh-CN', { timeZone: 'Asia/Shanghai', hour12: false, month: '2-digit', day: '2-digit', hour: '2-digit', minute: '2-digit', second: '2-digit' });
|
||||||
|
const TRACK_SHEET_MAX_HEIGHT_PX = 720;
|
||||||
|
const TRACK_SHEET_CHROME_HEIGHT_PX = 264;
|
||||||
|
const TRACK_SHEET_ROW_HEIGHT_PX = 62;
|
||||||
|
|
||||||
function number(value: number, digits = 1) {
|
function number(value: number, digits = 1) {
|
||||||
let formatter = numberFormatters.get(digits);
|
let formatter = numberFormatters.get(digits);
|
||||||
@@ -116,6 +119,14 @@ function eventTone(type: string) {
|
|||||||
return 'info';
|
return 'info';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function mobileTrackSheetHeight(track: TrackPlaybackResponse | undefined, queryCollapsed: boolean, tab: PanelTab) {
|
||||||
|
if (!track || !queryCollapsed || tab === 'overview') return `min(82dvh, ${TRACK_SHEET_MAX_HEIGHT_PX}px)`;
|
||||||
|
const itemCount = tab === 'stops' ? track.stops.length : track.events.length;
|
||||||
|
const visibleRows = itemCount ? Math.min(itemCount, 7) : 2;
|
||||||
|
const contentHeight = Math.min(TRACK_SHEET_MAX_HEIGHT_PX, TRACK_SHEET_CHROME_HEIGHT_PX + visibleRows * TRACK_SHEET_ROW_HEIGHT_PX);
|
||||||
|
return `min(82dvh, ${contentHeight}px)`;
|
||||||
|
}
|
||||||
|
|
||||||
function VehiclePicker({ value, onChange, onSelect }: { value: string; onChange: (value: string) => void; onSelect: (vehicle: VehicleRow) => void }) {
|
function VehiclePicker({ value, onChange, onSelect }: { value: string; onChange: (value: string) => void; onSelect: (vehicle: VehicleRow) => void }) {
|
||||||
const [open, setOpen] = useState(false);
|
const [open, setOpen] = useState(false);
|
||||||
const [debounced, setDebounced] = useState(value.trim());
|
const [debounced, setDebounced] = useState(value.trim());
|
||||||
@@ -390,7 +401,7 @@ export default function TrackPage() {
|
|||||||
className="v2-track-detail-sidesheet"
|
className="v2-track-detail-sidesheet"
|
||||||
visible={!railCollapsed}
|
visible={!railCollapsed}
|
||||||
placement="bottom"
|
placement="bottom"
|
||||||
height="min(82dvh, 720px)"
|
height={mobileTrackSheetHeight(track, queryCollapsed, panelTab)}
|
||||||
aria-label="轨迹查询与明细"
|
aria-label="轨迹查询与明细"
|
||||||
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>}
|
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}
|
footer={null}
|
||||||
|
|||||||
@@ -3217,6 +3217,88 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@media (max-width: 700px) and (max-height: 500px) and (orientation: landscape) {
|
||||||
|
.v2-track-current-card.semi-card,
|
||||||
|
.v2-track-page.is-rail-collapsed .v2-track-current-card.semi-card {
|
||||||
|
top: 6px;
|
||||||
|
right: auto;
|
||||||
|
bottom: auto;
|
||||||
|
left: 8px;
|
||||||
|
width: min(318px, 48vw);
|
||||||
|
min-width: 0;
|
||||||
|
border-radius: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.v2-track-current-card .v2-workspace-panel-header {
|
||||||
|
min-height: 42px;
|
||||||
|
padding: 5px 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.v2-track-current-card .v2-workspace-panel-meta .semi-tag {
|
||||||
|
min-width: 40px;
|
||||||
|
padding-inline: 6px;
|
||||||
|
font-size: 9px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.v2-track-current-card > .semi-card-body > .v2-track-current-metrics > span {
|
||||||
|
padding: 5px 6px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.v2-track-current-card > .semi-card-body > .v2-track-current-metrics small {
|
||||||
|
font-size: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.v2-track-current-card > .semi-card-body > .v2-track-current-metrics strong {
|
||||||
|
margin-top: 2px;
|
||||||
|
font-size: 12px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.v2-track-current-card > .semi-card-body > .v2-track-current-evidence {
|
||||||
|
min-height: 32px;
|
||||||
|
gap: 5px;
|
||||||
|
padding: 4px 7px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.v2-track-current-evidence > em {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.v2-track-stage-tools {
|
||||||
|
top: 6px;
|
||||||
|
right: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.v2-track-page.is-mobile-layout .v2-track-playback-dock.semi-card,
|
||||||
|
.v2-track-page.is-mobile-layout .v2-track-playback-dock > .semi-card-body {
|
||||||
|
min-height: 88px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.v2-track-page.is-mobile-layout .v2-track-playback-dock > .semi-card-body {
|
||||||
|
gap: 6px;
|
||||||
|
padding: 6px 8px !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.v2-track-page.is-mobile-layout .v2-track-progress-slider.semi-slider-wrapper {
|
||||||
|
margin-block: 6px 2px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.v2-track-stage:has(.v2-track-current-card) .v2-track-rail-expand {
|
||||||
|
top: 52px;
|
||||||
|
right: 8px;
|
||||||
|
bottom: auto;
|
||||||
|
left: auto;
|
||||||
|
min-width: 126px;
|
||||||
|
min-height: 38px;
|
||||||
|
padding: 5px 8px;
|
||||||
|
transform: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.v2-track-rail-expand.semi-button .semi-icon {
|
||||||
|
width: 26px;
|
||||||
|
height: 26px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/* Mileage workspace refinement: expose the active source strategy and keep the
|
/* Mileage workspace refinement: expose the active source strategy and keep the
|
||||||
* mobile matrix dense without sacrificing the fixed plate/period columns. */
|
* mobile matrix dense without sacrificing the fixed plate/period columns. */
|
||||||
.v2-mileage-source-trigger .semi-button-content {
|
.v2-mileage-source-trigger .semi-button-content {
|
||||||
|
|||||||
Reference in New Issue
Block a user