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%;
}