feat(web): refine Semi UI alert center

This commit is contained in:
lingniu
2026-07-20 00:01:47 +08:00
parent e20f320f6c
commit cd13a2952d
5 changed files with 149 additions and 76 deletions

View File

@@ -1,67 +1,78 @@
# Historical Data Semi UI Design QA # Alert Center Semi UI Design QA
## Evidence ## Evidence
- Source visual truth: `/tmp/semi-ui-reference-1440.png` (accepted Semi UI workspace direction). - Accepted visual direction: `/tmp/semi-ui-reference-1440.png`.
- Existing flow source: `/tmp/history-before-desktop.jpg` (historical data before this iteration). - Existing flow baseline: `/tmp/alerts-before-desktop.jpg`.
- Implementation screenshot: `/tmp/history-after-desktop.jpg`. - Final desktop implementation: `/tmp/alerts-after-desktop-v2.jpg`.
- Responsive screenshots: `/tmp/history-after-1024.jpg`, `/tmp/history-after-mobile-sheet.jpg`. - Final desktop inspector state: `/tmp/alerts-after-inspector.jpg`.
- Full-view comparison: `/tmp/semi-history-design-comparison.jpg`. - Final mobile queue: `/tmp/alerts-after-mobile-v2.jpg`.
- Same-route iteration comparison: `/tmp/history-before-after-comparison.jpg`. - Final mobile disposition sheet: `/tmp/alerts-after-mobile-detail.jpg`.
- Requested desktop viewport: 1440 × 900. The embedded browser surface captured 1280 × 720; an additional exact 1024 × 768 capture was checked. - System comparison: `/tmp/semi-alerts-design-comparison.jpg`.
- Mobile viewport: 390 × 844. - Same-route comparison: `/tmp/alerts-before-after-comparison.jpg`.
- State: location history query for one vehicle, empty local result, desktop query collapsed; mobile filter sheet open. - Desktop capture: 1280 × 720 (the embedded browser's available desktop surface).
- Responsive viewport: 390 × 844.
## Full-view comparison ## Full-view comparison
The implementation keeps the accepted product skeleton: persistent left navigation, white header, blue/gray visual hierarchy, restrained cards, compact controls, table-first workspace, and the same icon family. The historical-data workflow intentionally keeps its evidence table instead of copying the mileage-specific hero and mileage matrix. The final alert center preserves the accepted Semi UI product skeleton: persistent navigation, white operational surfaces, cool-gray canvas, blue primary actions, compact segmented tabs, restrained status colors, and one list-first work surface.
The before/after comparison shows the material layout change: the applied query remains readable in the command rail while the full desktop filter panel can collapse, returning the vertical workspace to the historical detail table. Compared with the baseline, the filter band is 14px shorter, the misleading radio-selection column is removed, row priority can be scanned through semantic left rails, and the desktop table no longer produces 160px of avoidable horizontal overflow. The recovered height is returned to the event queue.
## Focused comparison ## Focused comparison
The mobile filter sheet was checked separately because its labels and controls are too small to assess reliably in the desktop comparison. At 390 × 844, the summary rail, date range, type/source selects, export action, and sticky reset/apply actions remain visible and touch-safe without horizontal overflow. The selected-event state was checked independently because the evidence inspector changes the page grid. At 1280 × 720 the final split is 628px / 390px with zero page overflow; the list remains interactive and the inspector keeps vehicle identity, trigger evidence, disposition controls and audit progress visible.
The 390 × 844 mobile state shows four high-density event cards in one screen. Opening a card produces one bottom SideSheet with the same evidence and disposition order as desktop, without horizontal overflow.
## Fidelity ledger ## Fidelity ledger
| Surface | Source evidence | Render evidence | Result | | Surface | Source evidence | Render evidence | Result |
| --- | --- | --- | --- | | --- | --- | --- | --- |
| Typography | Compact Semi UI chrome with strong page/task titles and smaller utility copy | History title, command rail, metric values, panel title, filters and mobile sheet preserve the same hierarchy | Passed | | Typography | Strong task title, compact utility copy, tabular values | Command title, queue title, vehicle identity, evidence values and helper copy retain the shared Semi hierarchy | Passed |
| Layout rhythm | Persistent navigation, 1218px page gutters, one main data surface | Query rail, three metrics and the table align on one grid; collapsed state increases table height without adding another card family | Passed | | Spacing and density | 1218px workspace gutters and compact operational controls | Filter height reduced from 68px to 54px; table rows remain 52px and mobile cards preserve four visible records | Passed |
| Colors/tokens | White surfaces, cool gray canvas, blue primary, green healthy state | Existing shared tokens and Semi UI variants are reused; no new palette or gradient was introduced | Passed | | Colors and states | White/cool-gray surfaces, blue action state, restrained semantic colors | Severity rails and tags use existing critical/major/minor tokens; no new palette or decorative gradient was introduced | Passed |
| Icons/assets | Supplied brand logo and Semi icon family | Brand assets are unchanged; filter, calendar, search, setting, export and more actions use the same Semi icon family | Passed | | Icons and assets | Brand logo plus Semi icon family | Existing logo and alarm, filter, refresh, settings, bell and chevron icons are retained | Passed |
| Copy/content | Query, range, data type/source and result actions are business-native | No business fields or routes were added or removed; the applied scope reuses existing query values | Passed | | Table and queue | List-first operational workspace | Radio chrome removed; fixed table layout eliminates desktop horizontal overflow and selected rows use the shared blue focus state | Passed |
| Responsive behavior | Dense desktop workspace with task-focused mobile disclosure | 1280 × 720, 1024 × 768 and 390 × 844 checks show no clipped primary action or horizontal page overflow | Passed | | Evidence inspector | On-demand detail without replacing the queue | Desktop 628/390 split and mobile SideSheet preserve trigger, threshold, delta, notes and audit timeline | Passed |
| Interaction | Query controls should not compete with results after submission | Collapse/expand, apply/reset, mobile filter sheet and row/detail paths are covered by interaction tests | Passed | | Responsive behavior | Dense desktop with focused mobile disclosure | 1280 × 720 and 390 × 844 have zero page-level horizontal overflow; mobile title, metrics, cards and pagination remain reachable | Passed |
| Interaction | Rows, filters, tabs and actions must remain functional | Whole-row keyboard/click selection, advanced filter SideSheet, status metrics, rule tabs and mobile disposition sheet are test-covered | Passed |
## Above-the-fold copy diff ## Above-the-fold copy diff
- Added visible control labels: `收起条件` and `修改条件`, required for the new query disclosure. - Changed `告警事件` to `当前事件队列` to clarify the primary work surface.
- Existing task title, description, status, query labels and result labels are preserved. - Changed table header `严重程度` to `级别` and `触发 / 阈值` to `触发证据` for faster scanning.
- No unapproved marketing copy, badge, module, navigation item or business claim was added. - Changed the list meta from only `共 N 条` to `N 条待处置 · 共 N 条`.
- Existing business claims, tab names, statuses, rules, routes and disposition actions are preserved.
## Comparison history ## Comparison history
1. Earlier finding — P2: the desktop filter panel permanently occupied roughly 108px after a query, reducing the primary evidence-table viewport. 1. P2 — The baseline desktop table had a 160px horizontal overflow at 1280px, despite all columns being operationally compact.
- Fix: added a lightweight desktop-only disclosure control and an applied-scope summary for time, category and source. - Fix: removed the redundant radio column, tightened column widths and applied a fixed table layout.
- Post-fix evidence: `/tmp/history-before-after-comparison.jpg`; the table begins materially higher while the query remains recoverable through `修改条件`. - Post-fix measurement: `scrollWidth 1015px = clientWidth 1015px`.
2. Earlier finding — P2: mobile record quality relied on a small text indicator only. 2. P2 — The first compact-inspector pass still overflowed because the final cascade restored a 650px + 420px grid.
- Fix: added a three-pixel semantic quality rail with selected-state override while retaining the existing quality label. - Fix: changed the final grid to `minmax(0, 1fr) minmax(360px, 390px)` and tightened compact columns.
- Post-fix evidence: component test confirms semantic quality classes; no additional card height is introduced. - Post-fix measurement: `628px / 390px`, page overflow `0`.
3. P2 — The first mobile capture hid the queue title and left a visually empty header area.
- Fix: hide only the description on mobile while retaining the `当前事件队列` heading.
- Post-fix evidence: `/tmp/alerts-after-mobile-v2.jpg`.
## Findings ## Findings
- No actionable P0, P1 or P2 visual differences remain. - No actionable P0, P1 or P2 visual differences remain.
- P3 follow-up: Semi UI 2.101 still emits its existing `findDOMNode` StrictMode warning; it does not affect the rendered layout or interaction path in this iteration. - P3 follow-up: Semi UI 2.101 still emits its existing `findDOMNode` StrictMode warning in tests; it does not affect this layout or interaction path.
## Core interaction verification ## Core interaction verification
- Desktop query collapse and re-open. - Desktop keyword, severity and status query.
- Query submission collapses the desktop panel and preserves applied scope. - Advanced filter SideSheet open, apply and reset.
- Reset restores the full desktop query panel. - Metric shortcuts for active and ended states.
- Mobile filter sheet open, reset, apply and close. - Whole-row mouse and keyboard selection.
- Historical trend, field settings, export task menu, pagination and row-detail interaction remain covered by the full test suite. - Desktop inspector open, switch event and close.
- Browser console contains no application exception or framework overlay; only the existing Semi UI StrictMode deprecation warning appears. - Mobile filter sheet open/apply and mobile event SideSheet open/close.
- Rule configuration and notification tabs remain covered by the full suite.
- Full suite: 73 files, 366 tests passed.
- Production build gate: passed.
## Final result ## Final result

View File

@@ -157,9 +157,12 @@ test('clears old alert rows and inspector evidence when the event scope changes'
expect(screen.getByRole('button', { name: '查看未读通知,共 0 条' }).closest('[role="listitem"]')).toHaveClass('is-warning', 'is-secondary'); expect(screen.getByRole('button', { name: '查看未读通知,共 0 条' }).closest('[role="listitem"]')).toHaveClass('is-warning', 'is-secondary');
expect(view.container.querySelector('.v2-alert-event-table.semi-table-wrapper')).toBeInTheDocument(); expect(view.container.querySelector('.v2-alert-event-table.semi-table-wrapper')).toBeInTheDocument();
expect(view.container.querySelector('.v2-alert-event-table.semi-table-wrapper')).not.toHaveClass('is-compact'); expect(view.container.querySelector('.v2-alert-event-table.semi-table-wrapper')).not.toHaveClass('is-compact');
expect(screen.getByRole('columnheader', { name: '触发 / 阈值' })).toBeInTheDocument(); expect(screen.getByRole('heading', { level: 5, name: '当前事件队列' })).toBeInTheDocument();
expect(screen.getByText('1 条待处置 · 共 1 条')).toBeInTheDocument();
expect(screen.getByRole('columnheader', { name: '触发证据' })).toBeInTheDocument();
expect(screen.getByRole('columnheader', { name: '位置' })).toBeInTheDocument(); expect(screen.getByRole('columnheader', { name: '位置' })).toBeInTheDocument();
expect(view.container.querySelector('.v2-alert-event-table .v2-alert-protocol-tag.semi-tag')).toHaveTextContent('JT/T 808'); expect(view.container.querySelector('.v2-alert-event-table .v2-alert-protocol-tag.semi-tag')).toHaveTextContent('JT/T 808');
expect(view.container.querySelector('.v2-alert-event-table input[type="radio"]')).not.toBeInTheDocument();
expect(view.container.querySelector('.v2-alert-table-scroll > table')).not.toBeInTheDocument(); expect(view.container.querySelector('.v2-alert-table-scroll > table')).not.toBeInTheDocument();
expect(view.container.querySelector('.v2-alert-mobile-list')).not.toBeInTheDocument(); expect(view.container.querySelector('.v2-alert-mobile-list')).not.toBeInTheDocument();
expect(view.container.querySelector('time[datetime="2026-07-16T04:00:00Z"]')).toHaveTextContent('07-16 12:00:00'); expect(view.container.querySelector('time[datetime="2026-07-16T04:00:00Z"]')).toHaveTextContent('07-16 12:00:00');
@@ -172,7 +175,8 @@ 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-inspector.semi-card')).toBeInTheDocument();
expect(view.container.querySelector('.v2-alert-workspace')).toHaveClass('is-inspector-open'); expect(view.container.querySelector('.v2-alert-workspace')).toHaveClass('is-inspector-open');
expect(view.container.querySelector('.v2-alert-event-table.semi-table-wrapper')).toHaveClass('is-compact'); expect(view.container.querySelector('.v2-alert-event-table.semi-table-wrapper')).toHaveClass('is-compact');
expect(screen.queryByRole('columnheader', { name: '触发 / 阈值' })).not.toBeInTheDocument(); expect(desktopAlertRow).toHaveClass('is-severity-major', 'is-selected');
expect(screen.queryByRole('columnheader', { name: '触发证据' })).not.toBeInTheDocument();
expect(screen.queryByRole('columnheader', { name: '位置' })).not.toBeInTheDocument(); expect(screen.queryByRole('columnheader', { name: '位置' })).not.toBeInTheDocument();
expect(screen.getByText('已展开处置详情 · 点击其他事件快速切换')).toBeInTheDocument(); expect(screen.getByText('已展开处置详情 · 点击其他事件快速切换')).toBeInTheDocument();
expect(desktopAlertRow).toHaveAttribute('aria-expanded', 'true'); expect(desktopAlertRow).toHaveAttribute('aria-expanded', 'true');
@@ -194,7 +198,7 @@ test('clears old alert rows and inspector evidence when the event scope changes'
fireEvent.click(screen.getByRole('button', { name: '关闭告警详情' })); fireEvent.click(screen.getByRole('button', { name: '关闭告警详情' }));
expect(view.container.querySelector('.v2-alert-inspector')).not.toBeInTheDocument(); expect(view.container.querySelector('.v2-alert-inspector')).not.toBeInTheDocument();
expect(view.container.querySelector('.v2-alert-event-table.semi-table-wrapper')).not.toHaveClass('is-compact'); expect(view.container.querySelector('.v2-alert-event-table.semi-table-wrapper')).not.toHaveClass('is-compact');
expect(screen.getByRole('columnheader', { name: '触发 / 阈值' })).toBeInTheDocument(); expect(screen.getByRole('columnheader', { name: '触发证据' })).toBeInTheDocument();
expect(screen.getByRole('columnheader', { name: '位置' })).toBeInTheDocument(); expect(screen.getByRole('columnheader', { name: '位置' })).toBeInTheDocument();
expect(desktopAlertRow).toHaveAttribute('aria-expanded', 'false'); expect(desktopAlertRow).toHaveAttribute('aria-expanded', 'false');
fireEvent.click(desktopAlertRow); fireEvent.click(desktopAlertRow);

View File

@@ -1,5 +1,5 @@
import { IconAlarm, IconBell, IconChevronRight, IconClose, IconFilter, IconPlus, IconRefresh, IconSearch, IconSetting } from '@douyinfe/semi-icons'; import { IconAlarm, IconBell, IconChevronRight, IconClose, IconFilter, IconPlus, IconRefresh, IconSearch, IconSetting } from '@douyinfe/semi-icons';
import { Button, Card, CardGroup, Collapse, Descriptions, Empty, Input, Radio, Select, Spin, Table, Tag, TextArea, Timeline, Typography } from '@douyinfe/semi-ui'; import { Button, Card, CardGroup, Collapse, Descriptions, Empty, Input, Select, Spin, Table, Tag, TextArea, Timeline, Typography } from '@douyinfe/semi-ui';
import { useMutation, useQuery, useQueryClient } from '@tanstack/react-query'; import { useMutation, useQuery, useQueryClient } from '@tanstack/react-query';
import { FormEvent, memo, useCallback, useEffect, useMemo, useState, type ReactNode } from 'react'; import { FormEvent, memo, useCallback, useEffect, useMemo, useState, type ReactNode } from 'react';
import { Link, useSearchParams } from 'react-router-dom'; import { Link, useSearchParams } from 'react-router-dom';
@@ -32,13 +32,12 @@ const PROTOCOLS = ['GB32960', 'JT808', 'YUTONG_MQTT'];
const NUMERIC_OPERATORS = ['gt', 'gte', 'lt', 'lte', 'eq', 'neq', 'between', 'outside']; const NUMERIC_OPERATORS = ['gt', 'gte', 'lt', 'lte', 'eq', 'neq', 'between', 'outside'];
const BOOLEAN_OPERATORS = ['eq', 'neq', 'changed']; const BOOLEAN_OPERATORS = ['eq', 'neq', 'changed'];
const COMPACT_ALERT_COLUMN_WIDTHS: Record<string, number> = { const COMPACT_ALERT_COLUMN_WIDTHS: Record<string, number> = {
selection: 40, severity: 70,
severity: 80, plate: 116,
plate: 132, ruleName: 124,
ruleName: 148, protocol: 86,
protocol: 100, triggeredAt: 110,
triggeredAt: 120, status: 72
status: 78
}; };
type RuleTemplate = { id: string; title: string; summary: string; draft: AlertRuleInput }; type RuleTemplate = { id: string; title: string; summary: string; draft: AlertRuleInput };
@@ -90,24 +89,20 @@ function AlertTime({ value, className = '' }: { value?: string; className?: stri
const AlertEventTable = memo(function AlertEventTable({ rows, selectedID, compact, onSelect }: { rows: AlertEvent[]; selectedID: string; compact: boolean; onSelect: (id: string) => void }) { const AlertEventTable = memo(function AlertEventTable({ rows, selectedID, compact, onSelect }: { rows: AlertEvent[]; selectedID: string; compact: boolean; onSelect: (id: string) => void }) {
const columns = useMemo(() => { const columns = useMemo(() => {
const allColumns = [ const allColumns = [
{ title: '级别', dataIndex: 'severity', width: 78, render: (_: AlertEvent['severity'], event: AlertEvent) => <SeverityTag severity={event.severity} /> },
{ {
title: '', dataIndex: 'selection', width: 42, title: '车牌 / VIN', dataIndex: 'plate', width: 132,
render: (_: unknown, event: AlertEvent) => <Radio name="alert-event" checked={selectedID === event.id} onChange={() => onSelect(event.id)} aria-label={`选择 ${event.plate || event.vin} ${event.ruleName}`} />
},
{ title: '严重程度', dataIndex: 'severity', width: 90, render: (_: AlertEvent['severity'], event: AlertEvent) => <SeverityTag severity={event.severity} /> },
{
title: '车牌 / VIN', dataIndex: 'plate', width: 142,
render: (_: string, event: AlertEvent) => <div className="v2-alert-event-vehicle"><strong>{event.plate || '未绑定车牌'}</strong><small>{event.vin}</small></div> render: (_: string, event: AlertEvent) => <div className="v2-alert-event-vehicle"><strong>{event.plate || '未绑定车牌'}</strong><small>{event.vin}</small></div>
}, },
{ title: '规则', dataIndex: 'ruleName', width: 170, render: (value: string) => <span className="v2-alert-event-rule" title={value}>{value}</span> }, { title: '规则', dataIndex: 'ruleName', width: 146, render: (value: string) => <span className="v2-alert-event-rule" title={value}>{value}</span> },
{ title: '协议', dataIndex: 'protocol', width: 112, render: (value: string) => <ProtocolTag className="v2-alert-protocol-tag" protocol={value} compact /> }, { title: '协议', dataIndex: 'protocol', width: 98, render: (value: string) => <ProtocolTag className="v2-alert-protocol-tag" protocol={value} compact /> },
{ title: '触发时间', dataIndex: 'triggeredAt', width: 132, render: (value: string) => <AlertTime value={value} /> }, { title: '触发时间', dataIndex: 'triggeredAt', width: 124, render: (value: string) => <AlertTime value={value} /> },
{ title: '状态', dataIndex: 'status', width: 90, render: (_: AlertEvent['status'], event: AlertEvent) => <StatusTag status={event.status} /> }, { title: '状态', dataIndex: 'status', width: 78, render: (_: AlertEvent['status'], event: AlertEvent) => <StatusTag status={event.status} /> },
{ {
title: '触发 / 阈值', dataIndex: 'evidence', width: 164, title: '触发证据', dataIndex: 'evidence', width: 150,
render: (_: unknown, event: AlertEvent) => <div className="v2-alert-event-evidence"><strong>{alertValue(event)}</strong><small>{thresholdText(event)}</small></div> render: (_: unknown, event: AlertEvent) => <div className="v2-alert-event-evidence"><strong>{alertValue(event)}</strong><small>{thresholdText(event)}</small></div>
}, },
{ title: '位置', dataIndex: 'location', width: 180, render: (value: string) => <span className="v2-alert-event-location" title={value}>{value || '—'}</span> }, { title: '位置', dataIndex: 'location', width: 164, render: (value: string) => <span className="v2-alert-event-location" title={value}>{value || '—'}</span> },
]; ];
return compact return compact
? allColumns ? allColumns
@@ -124,7 +119,7 @@ const AlertEventTable = memo(function AlertEventTable({ rows, selectedID, compac
pagination={false} pagination={false}
empty={null} empty={null}
onRow={(event) => event ? detailTriggerRow({ onRow={(event) => event ? detailTriggerRow({
className: selectedID === event.id ? 'is-selected' : '', className: [`is-severity-${event.severity}`, selectedID === event.id ? 'is-selected' : ''].filter(Boolean).join(' '),
expanded: selectedID === event.id, expanded: selectedID === event.id,
label: `查看 ${event.plate || event.vin} ${event.ruleName} 告警详情`, label: `查看 ${event.plate || event.vin} ${event.ruleName} 告警详情`,
testId: `alert-row-${event.id}`, testId: `alert-row-${event.id}`,
@@ -354,9 +349,9 @@ function EventWorkspace({ filters, draft, setDraft, setFilters, rules, unread, e
<div className={`v2-alert-workspace${selectedID && !mobileLayout ? ' is-inspector-open' : ''}`}> <div className={`v2-alert-workspace${selectedID && !mobileLayout ? ' is-inspector-open' : ''}`}>
<Card className="v2-alert-table-card" bodyStyle={{ padding: 0 }}> <Card className="v2-alert-table-card" bodyStyle={{ padding: 0 }}>
<WorkspacePanelHeader <WorkspacePanelHeader
title="告警事件" title="当前事件队列"
description={selectedID && !mobileLayout ? '已展开处置详情 · 点击其他事件快速切换' : '点击事件查看触发证据、处理进度与处置动作'} description={selectedID && !mobileLayout ? '已展开处置详情 · 点击其他事件快速切换' : '点击事件查看触发证据、处理进度与处置动作'}
meta={`${(events.data?.total ?? 0).toLocaleString('zh-CN')}`} meta={`${Number(sums?.unprocessed ?? 0).toLocaleString('zh-CN')} 条待处置 · ${(events.data?.total ?? 0).toLocaleString('zh-CN')}`}
actions={<Button theme="borderless" icon={<IconRefresh />} loading={events.isFetching} onClick={() => Promise.all([events.refetch(), summary.refetch(), ...(selectedID ? [detail.refetch()] : [])])}></Button>} actions={<Button theme="borderless" icon={<IconRefresh />} loading={events.isFetching} onClick={() => Promise.all([events.refetch(), summary.refetch(), ...(selectedID ? [detail.refetch()] : [])])}></Button>}
/> />
<div <div

View File

@@ -198,7 +198,8 @@ describe('V2 production entry', () => {
expect(corePageSources.AlertsPage).toContain('<Input'); expect(corePageSources.AlertsPage).toContain('<Input');
expect(corePageSources.AlertsPage).toContain('<Select'); expect(corePageSources.AlertsPage).toContain('<Select');
expect(corePageSources.AlertsPage).toContain('<TextArea'); expect(corePageSources.AlertsPage).toContain('<TextArea');
expect(corePageSources.AlertsPage).toContain('<Radio'); expect(corePageSources.AlertsPage).not.toContain('<Radio');
expect(corePageSources.AlertsPage).toContain('is-severity-${event.severity}');
expect(corePageSources.AlertsPage).toContain('<Card key={event.id} className={`v2-alert-mobile-card'); expect(corePageSources.AlertsPage).toContain('<Card key={event.id} className={`v2-alert-mobile-card');
expect(corePageSources.AlertsPage).toContain('<WorkspaceMetricRail'); expect(corePageSources.AlertsPage).toContain('<WorkspaceMetricRail');
expect(corePageSources.AlertsPage).toContain('className="v2-alert-metric-rail"'); expect(corePageSources.AlertsPage).toContain('className="v2-alert-metric-rail"');

View File

@@ -8402,7 +8402,7 @@
* command rail and reserves the remaining height for the operational list. * command rail and reserves the remaining height for the operational list.
*/ */
.v2-alert-page { .v2-alert-page {
gap: 12px; gap: 10px;
padding: 14px 18px 18px; padding: 14px 18px 18px;
background: #f6f8fb; background: #f6f8fb;
} }
@@ -8503,6 +8503,7 @@
.v2-alert-filter-card.semi-card { .v2-alert-filter-card.semi-card {
overflow: visible; overflow: visible;
border-color: #d8e1ec;
} }
.v2-alert-filter-card > .semi-card-body > .v2-workspace-filter-heading { .v2-alert-filter-card > .semi-card-body > .v2-workspace-filter-heading {
@@ -8510,24 +8511,36 @@
} }
.v2-alert-filter.v2-alert-filter-primary { .v2-alert-filter.v2-alert-filter-primary {
min-height: 70px; min-height: 54px;
grid-template-columns: minmax(250px, 1.45fr) minmax(122px, .62fr) minmax(136px, .68fr) auto auto auto; grid-template-columns: minmax(250px, 1.45fr) minmax(122px, .62fr) minmax(136px, .68fr) auto auto auto;
gap: 10px; gap: 10px;
padding: 11px 13px; align-items: center;
padding: 8px 10px;
} }
.v2-alert-filter.v2-alert-filter-primary label { .v2-alert-filter.v2-alert-filter-primary label {
position: relative;
gap: 5px; gap: 5px;
color: #66778e; color: #66778e;
font-size: 11px; font-size: 11px;
font-weight: 600; font-weight: 600;
} }
.v2-alert-filter.v2-alert-filter-primary label > span {
position: absolute;
width: 1px;
height: 1px;
overflow: hidden;
clip: rect(0 0 0 0);
clip-path: inset(50%);
white-space: nowrap;
}
.v2-alert-filter.v2-alert-filter-primary label > div, .v2-alert-filter.v2-alert-filter-primary label > div,
.v2-alert-filter.v2-alert-filter-primary .semi-select, .v2-alert-filter.v2-alert-filter-primary .semi-select,
.v2-alert-filter.v2-alert-filter-primary .semi-input-wrapper, .v2-alert-filter.v2-alert-filter-primary .semi-input-wrapper,
.v2-alert-filter.v2-alert-filter-primary > .semi-button { .v2-alert-filter.v2-alert-filter-primary > .semi-button {
min-height: 38px; min-height: 36px;
border-radius: 8px; border-radius: 8px;
} }
@@ -8588,7 +8601,7 @@
} }
.v2-alert-workspace.is-inspector-open { .v2-alert-workspace.is-inspector-open {
grid-template-columns: minmax(680px, 1fr) 390px; grid-template-columns: minmax(0, 1fr) minmax(360px, 390px);
} }
.v2-alert-table-card > .semi-card-body > .v2-workspace-panel-header { .v2-alert-table-card > .semi-card-body > .v2-workspace-panel-header {
@@ -8646,12 +8659,51 @@
font-size: 12px; font-size: 12px;
} }
.v2-alert-table-scroll .v2-alert-event-table .semi-table-tbody > .semi-table-row {
position: relative;
transition: background-color .16s ease, box-shadow .16s ease;
}
.v2-alert-table-scroll .v2-alert-event-table .semi-table-tbody > .semi-table-row::before {
position: absolute;
z-index: 2;
top: 7px;
bottom: 7px;
left: 0;
width: 3px;
border-radius: 0 4px 4px 0;
background: #c2cad5;
content: "";
pointer-events: none;
}
.v2-alert-table-scroll .v2-alert-event-table .semi-table-tbody > .semi-table-row.is-severity-critical::before {
background: #e5484d;
}
.v2-alert-table-scroll .v2-alert-event-table .semi-table-tbody > .semi-table-row.is-severity-major::before {
background: #ed8d32;
}
.v2-alert-table-scroll .v2-alert-event-table .semi-table-tbody > .semi-table-row.is-severity-minor::before {
background: #d9a31d;
}
.v2-alert-table-scroll .v2-alert-event-table .semi-table-tbody > .semi-table-row:hover {
background: #f7faff;
}
.v2-alert-table-scroll .v2-alert-event-table .semi-table-tbody > .semi-table-row.is-selected {
background: #eef5ff;
box-shadow: inset 0 0 0 1px rgba(18, 104, 243, .16);
}
.v2-alert-event-table.semi-table-wrapper { .v2-alert-event-table.semi-table-wrapper {
min-width: 1102px; min-width: 970px;
} }
.v2-alert-event-table.is-compact.semi-table-wrapper { .v2-alert-event-table.is-compact.semi-table-wrapper {
min-width: 758px; min-width: 660px;
} }
.v2-alert-event-evidence { .v2-alert-event-evidence {
@@ -9529,10 +9581,15 @@
padding: 5px 8px; padding: 5px 8px;
} }
.v2-alert-table-card .v2-workspace-panel-copy > .semi-typography { .v2-alert-table-card .v2-workspace-panel-copy > .semi-typography:not(h5) {
display: none; display: none;
} }
.v2-alert-table-card .v2-workspace-panel-copy > h5.semi-typography {
display: block;
font-size: 13px;
}
.v2-alert-table-scroll.is-mobile-scroll { .v2-alert-table-scroll.is-mobile-scroll {
min-height: 0; min-height: 0;
flex: 1 1 0; flex: 1 1 0;
@@ -25382,8 +25439,8 @@
} }
.v2-alert-filter.v2-alert-filter-primary { .v2-alert-filter.v2-alert-filter-primary {
min-height: 66px; min-height: 52px;
padding: 9px 13px; padding: 7px 10px;
} }
.v2-alert-filter.v2-alert-filter-primary label > div, .v2-alert-filter.v2-alert-filter-primary label > div,
@@ -25417,13 +25474,18 @@
} }
.v2-alert-table-scroll .v2-alert-event-table.semi-table-wrapper .semi-table-tbody > .semi-table-row > .semi-table-row-cell { .v2-alert-table-scroll .v2-alert-event-table.semi-table-wrapper .semi-table-tbody > .semi-table-row > .semi-table-row-cell {
height: 56px; height: 52px;
padding: 8px 10px; padding: 7px 10px;
color: #405269; color: #405269;
font-size: 12.5px; font-size: 12.5px;
transition: background-color .16s ease; transition: background-color .16s ease;
} }
.v2-alert-event-table .semi-table {
width: 100%;
table-layout: fixed;
}
.v2-alert-table-scroll .v2-alert-event-table.semi-table-wrapper .semi-table-tbody > .semi-table-row:hover > .semi-table-row-cell { .v2-alert-table-scroll .v2-alert-event-table.semi-table-wrapper .semi-table-tbody > .semi-table-row:hover > .semi-table-row-cell {
background: #f6f9fe; background: #f6f9fe;
} }
@@ -25445,7 +25507,7 @@
} }
.v2-alert-workspace.is-inspector-open { .v2-alert-workspace.is-inspector-open {
grid-template-columns: minmax(650px, 1fr) 420px; grid-template-columns: minmax(0, 1fr) minmax(360px, 390px);
} }
.v2-alert-workspace.is-inspector-open .v2-alert-event-table.is-compact.semi-table-wrapper { .v2-alert-workspace.is-inspector-open .v2-alert-event-table.is-compact.semi-table-wrapper {