diff --git a/vehicle-data-platform/apps/web/src/v2/pages/AlertsPage.test.tsx b/vehicle-data-platform/apps/web/src/v2/pages/AlertsPage.test.tsx index e0087eb5..f3dcbf42 100644 --- a/vehicle-data-platform/apps/web/src/v2/pages/AlertsPage.test.tsx +++ b/vehicle-data-platform/apps/web/src/v2/pages/AlertsPage.test.tsx @@ -237,6 +237,8 @@ test('renders only selectable Semi alert cards on mobile', async () => { expect(action).toHaveAttribute('aria-pressed', 'true'); expect(action).toHaveAttribute('aria-expanded', 'true'); expect(await screen.findByRole('dialog', { name: '告警事件详情' })).toBeInTheDocument(); + expect(document.querySelector('.v2-alert-detail-sidesheet')).toHaveClass('semi-sidesheet-bottom'); + expect(document.querySelector('.v2-alert-detail-sidesheet .semi-sidesheet-inner')).toHaveStyle({ height: 'min(86dvh, 760px)' }); expect(await screen.findByText('mobile-event')).toBeInTheDocument(); fireEvent.click(screen.getByRole('button', { name: '关闭告警详情' })); expect(screen.queryByText('mobile-event')).not.toBeInTheDocument(); @@ -265,6 +267,23 @@ test('keeps the primary alert query compact and applies advanced filters from a expect(screen.getByRole('button', { name: /更多筛选 · 1/ })).toHaveAttribute('aria-expanded', 'false'); }); +test('uses the shared Semi bottom SideSheet pattern for mobile advanced filters', async () => { + layout.mobile = true; + mocks.alertSummaryV2.mockResolvedValue({ active: 0, unprocessed: 0, processing: 0, recovered: 0, closed: 0, ignored: 0, unreadNotifications: 0, asOf: '' }); + mocks.alertEventsV2.mockResolvedValue({ items: [], total: 0, limit: 20, offset: 0 }); + mocks.alertRulesV2.mockResolvedValue([alertRule()]); + mocks.alertNotificationsV2.mockResolvedValue({ items: [], total: 0, limit: 100, offset: 0 }); + const client = new QueryClient({ defaultOptions: { queries: { retry: false } } }); + render(); + + await screen.findByText('当前筛选条件没有告警事件'); + fireEvent.click(screen.getByRole('button', { name: /更多筛选/ })); + + expect(await screen.findByRole('dialog', { name: '告警高级筛选' })).toBeInTheDocument(); + expect(document.querySelector('.v2-alert-filter-sidesheet')).toHaveClass('semi-sidesheet-bottom'); + expect(document.querySelector('.v2-alert-filter-sidesheet .semi-sidesheet-inner')).toHaveStyle({ height: 'min(72dvh, 620px)' }); +}); + test('creates auditable drafts from simple offline and hydrogen rule templates', async () => { mocks.alertRulesV2.mockResolvedValue([alertRule()]); mocks.metricCatalog.mockResolvedValue({ diff --git a/vehicle-data-platform/apps/web/src/v2/pages/AlertsPage.tsx b/vehicle-data-platform/apps/web/src/v2/pages/AlertsPage.tsx index c8288e4f..eea84c32 100644 --- a/vehicle-data-platform/apps/web/src/v2/pages/AlertsPage.tsx +++ b/vehicle-data-platform/apps/web/src/v2/pages/AlertsPage.tsx @@ -268,7 +268,9 @@ function EventWorkspace({ filters, draft, setDraft, setFilters, rules, unread, e className="v2-alert-filter-sidesheet" visible={advancedOpen} aria-label="告警高级筛选" - width={430} + placement={mobileLayout ? 'bottom' : 'right'} + width={mobileLayout ? undefined : 430} + height={mobileLayout ? 'min(72dvh, 620px)' : undefined} title={
更多筛选按规则、协议和触发时间收窄告警范围
} onCancel={() => setAdvancedOpen(false)} footer={
} @@ -284,7 +286,8 @@ function EventWorkspace({ filters, draft, setDraft, setFilters, rules, unread, e className="v2-alert-detail-sidesheet" visible={mobileLayout && Boolean(selectedID)} aria-label="告警事件详情" - width="100%" + placement="bottom" + height="min(86dvh, 760px)" title={
告警详情{selectedEvent ? `${selectedEvent.plate || selectedEvent.vin} · ${selectedEvent.ruleName}` : '证据、状态与处置履历'}
} onCancel={closeDetail} > 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 edd2c177..592f751d 100644 --- a/vehicle-data-platform/apps/web/src/v2/styles/workspace.css +++ b/vehicle-data-platform/apps/web/src/v2/styles/workspace.css @@ -1826,6 +1826,30 @@ width: 100% !important; } + .v2-alert-filter-sidesheet.semi-sidesheet-bottom .semi-sidesheet-header, + .v2-alert-detail-sidesheet.semi-sidesheet-bottom .semi-sidesheet-header { + min-height: 72px; + padding: 15px 16px 12px; + } + + .v2-alert-filter-sidesheet.semi-sidesheet-bottom .semi-sidesheet-body { + padding: 12px 14px 16px; + } + + .v2-alert-filter-sidesheet.semi-sidesheet-bottom .semi-sidesheet-footer { + padding: 10px 14px calc(10px + env(safe-area-inset-bottom)); + } + + .v2-alert-detail-sidesheet.semi-sidesheet-bottom .semi-sidesheet-body { + overflow: auto; + overscroll-behavior: contain; + } + + .v2-alert-detail-sidesheet.semi-sidesheet-bottom .v2-alert-inspector.is-sheet.semi-card { + border-radius: 11px 11px 0 0; + background: transparent; + } + .v2-alert-sheet-footer { width: 100%; }