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 5258af81..7b97a78d 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 @@ -151,10 +151,11 @@ test('clears old alert rows and inspector evidence when the event scope changes' expect(view.container.querySelector('.v2-alert-inspector.semi-card')).toBeInTheDocument(); expect(view.container.querySelector('.v2-alert-workspace')).toHaveClass('is-inspector-open'); expect(desktopAlertRow).toHaveAttribute('aria-expanded', 'true'); - expect(screen.getByText('事件信息').closest('.semi-card')).toHaveClass('v2-alert-detail-card'); - expect(screen.getByText('证据对比').closest('.semi-card')).toHaveClass('v2-alert-evidence-card'); + expect(view.container.querySelector('.v2-alert-focus-card[aria-label="告警处置摘要"]')).toHaveClass('v2-alert-detail-card'); + expect(view.container.querySelector('.v2-alert-technical-collapse.semi-collapse')).toBeInTheDocument(); + expect(screen.getByText('技术详情')).toBeInTheDocument(); expect(view.container.querySelector('.v2-alert-inspector-heading')).toHaveClass('v2-workspace-panel-header'); - expect(view.container.querySelectorAll('.v2-alert-descriptions.semi-descriptions')).toHaveLength(2); + expect(view.container.querySelectorAll('.v2-alert-descriptions.semi-descriptions')).toHaveLength(1); expect(screen.getByLabelText('告警处理进度')).toHaveClass('semi-timeline'); expect(screen.getByText('规则首次命中')).toBeInTheDocument(); fireEvent.click(screen.getByRole('button', { name: '关闭告警详情' })); @@ -179,6 +180,27 @@ test('clears old alert rows and inspector evidence when the event scope changes' await waitFor(() => expect(screen.queryByText('正在更新事件…')).not.toBeInTheDocument()); }); +test('keeps status shortcuts comparable while the event list is status-filtered', async () => { + mocks.alertSummaryV2.mockResolvedValue({ active: 3, unprocessed: 2, processing: 1, recovered: 4, closed: 1, ignored: 0, unreadNotifications: 0, asOf: '' }); + mocks.alertEventsV2.mockResolvedValue({ items: [], total: 2, limit: 20, offset: 0 }); + mocks.alertRulesV2.mockResolvedValue([]); + mocks.alertNotificationsV2.mockResolvedValue({ items: [], total: 0, limit: 100, offset: 0 }); + const client = new QueryClient({ defaultOptions: { queries: { retry: false } } }); + render(); + + await screen.findByText('当前筛选条件没有告警事件'); + const summaryScope = mocks.alertSummaryV2.mock.calls[0][0]; + expect(summaryScope).toEqual(expect.objectContaining({ keyword: 'TESTVIN' })); + expect(summaryScope).not.toHaveProperty('status'); + expect(screen.getByRole('button', { name: '筛选全部事件,共 8 条' })).toHaveAttribute('aria-pressed', 'false'); + expect(screen.getByRole('button', { name: '筛选未处理,共 2 条' })).toHaveAttribute('aria-pressed', 'true'); + expect(screen.getByRole('button', { name: '已恢复 4' })).toBeInTheDocument(); + + fireEvent.click(screen.getByRole('button', { name: '筛选全部事件,共 8 条' })); + await waitFor(() => expect(mocks.alertEventsV2).toHaveBeenLastCalledWith(expect.not.objectContaining({ status: expect.anything() }), expect.anything())); + expect(screen.getByRole('button', { name: '筛选全部事件,共 8 条' })).toHaveAttribute('aria-pressed', 'true'); +}); + test('renders only selectable Semi alert cards on mobile', async () => { layout.mobile = true; const event = alertEvent('mobile-event', 'MOBILEVIN', '粤A移动01'); 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 c84026ec..1087d949 100644 --- a/vehicle-data-platform/apps/web/src/v2/pages/AlertsPage.tsx +++ b/vehicle-data-platform/apps/web/src/v2/pages/AlertsPage.tsx @@ -1,5 +1,5 @@ import { IconAlarm, IconBell, IconChevronRight, IconClose, IconFilter, IconPlus, IconRefresh, IconSearch } from '@douyinfe/semi-icons'; -import { Button, Card, CardGroup, Descriptions, Empty, Input, Radio, Select, SideSheet, Spin, Table, Tag, TextArea, Timeline, Typography } from '@douyinfe/semi-ui'; +import { Button, Card, CardGroup, Collapse, Descriptions, Empty, Input, Radio, Select, SideSheet, Spin, Table, Tag, TextArea, Timeline, Typography } from '@douyinfe/semi-ui'; import { useMutation, useQuery, useQueryClient } from '@tanstack/react-query'; import { FormEvent, useEffect, useMemo, useState } from 'react'; import { Link, useSearchParams } from 'react-router-dom'; @@ -77,12 +77,12 @@ function AlertEventTable({ rows, selectedID, onSelect }: { rows: AlertEvent[]; s { title: '规则', dataIndex: 'ruleName', width: 170, render: (value: string) => {value} }, { title: '协议', dataIndex: 'protocol', width: 92, render: (value: string) => value || '—' }, { title: '触发时间', dataIndex: 'triggeredAt', width: 132, render: (value: string) => formatAlertTime(value) }, - { title: '恢复时间', dataIndex: 'recoveredAt', width: 132, render: (value: string) => formatAlertTime(value) }, { title: '状态', dataIndex: 'status', width: 90, render: (_: AlertEvent['status'], event: AlertEvent) => }, - { title: '触发值', dataIndex: 'triggerValue', width: 105, render: (_: unknown, event: AlertEvent) => alertValue(event) }, - { title: '阈值', dataIndex: 'threshold', width: 125, render: (_: unknown, event: AlertEvent) => thresholdText(event) }, + { + title: '触发 / 阈值', dataIndex: 'evidence', width: 164, + render: (_: unknown, event: AlertEvent) =>
{alertValue(event)}{thresholdText(event)}
+ }, { title: '位置', dataIndex: 'location', width: 180, render: (value: string) => {value || '—'} }, - { title: '处理人', dataIndex: 'handler', width: 110, render: (value: string) => value || '—' } ], [onSelect, selectedID]); return } title="选择告警事件" description="查看触发证据、状态时间线和处置动作。" />; return {sheet ? null :