refine Semi UI alert workspace

This commit is contained in:
lingniu
2026-07-18 09:19:23 +08:00
parent 1688ba1b50
commit 71f334d259
4 changed files with 239 additions and 50 deletions

View File

@@ -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(<QueryClientProvider client={client}><MemoryRouter future={ROUTER_FUTURE} initialEntries={['/alerts?keyword=TESTVIN&status=unprocessed']}><AlertsPage /></MemoryRouter></QueryClientProvider>);
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');

View File

@@ -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) => <span className="v2-alert-event-rule" title={value}>{value}</span> },
{ 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) => <StatusTag status={event.status} /> },
{ 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) => <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: 'handler', width: 110, render: (value: string) => value || '—' }
], [onSelect, selectedID]);
return <Table
@@ -106,22 +106,16 @@ function EventInspector({ event, note, acting, actionError, editable, onNote, on
if (!event) return <Card className="v2-alert-inspector" bodyStyle={{ padding: 0 }}><Empty className="v2-alert-inspector-empty" image={<IconAlarm />} title="选择告警事件" description="查看触发证据、状态时间线和处置动作。" /></Card>;
return <Card className={`v2-alert-inspector${sheet ? ' is-sheet' : ''}`} bodyStyle={{ padding: 0 }}><WorkspacePanelHeader className="v2-alert-inspector-heading" title={event.ruleName} actions={<><span className="v2-alert-inspector-status"><SeverityTag severity={event.severity} /><StatusTag status={event.status} /></span>{sheet ? null : <Button className="v2-alert-inspector-close" theme="borderless" icon={<IconClose />} aria-label="关闭告警详情" onClick={onClose} />}</>} />
<div className="v2-alert-inspector-content">
<Card className="v2-alert-detail-card" title={<strong></strong>} headerLine bodyStyle={{ padding: 0 }}>
<Descriptions className="v2-alert-descriptions" align="left" size="small" data={[
{ key: '事件 ID', value: <code>{event.id}</code> },
{ key: '规则 / 版本', value: `${event.ruleId} / v${event.ruleVersion}` },
{ key: '车辆 / VIN', value: `${event.plate || '—'} / ${event.vin}` },
{ key: '触发时间', value: formatAlertTime(event.triggeredAt) },
{ key: '恢复时间', value: formatAlertTime(event.recoveredAt) }
]} />
</Card>
<Card className="v2-alert-detail-card v2-alert-evidence-card" title={<strong></strong>} headerLine bodyStyle={{ padding: 0 }}>
<Card className="v2-alert-detail-card v2-alert-focus-card" bodyStyle={{ padding: 0 }} aria-label="告警处置摘要">
<header className="v2-alert-focus-identity">
<span><small></small><strong>{event.plate || '未绑定车牌'}</strong><code>{event.vin}</code></span>
<Tag color="blue" type="light" size="small">{event.protocol || '未知协议'}</Tag>
</header>
<div className="v2-alert-focus-facts">
<span><small></small><strong>{formatAlertTime(event.triggeredAt)}</strong></span>
<span><small></small><strong>{statusLabels[event.status]}</strong></span>
</div>
<div className="v2-alert-evidence"><Card className="v2-alert-evidence-value is-trigger"><small></small><strong>{alertValue(event)}</strong></Card><Tag color="grey" type="light" size="small">VS</Tag><Card className="v2-alert-evidence-value"><small></small><strong>{thresholdText(event)}</strong></Card></div>
<Descriptions className="v2-alert-descriptions" align="left" size="small" data={[
{ key: '来源事件 ID', value: event.sourceEventId || '—' },
{ key: '协议', value: <Tag color="blue" type="light" size="small">{event.protocol || '—'}</Tag> },
{ key: '事件 / 接收', value: `${formatAlertTime(event.eventAt)} / ${formatAlertTime(event.receivedAt)}` }
]} />
</Card>
<Card className="v2-alert-detail-card v2-alert-progress-card" title={<strong></strong>} headerLine>
{event.actions?.length ? <Timeline className="v2-alert-timeline" aria-label="告警处理进度">{event.actions.map((item, index) => <Timeline.Item key={item.id} type={index === event.actions!.length - 1 ? 'ongoing' : 'default'} time={<span>{item.actor} · {formatAlertTime(item.createdAt)}</span>}><strong>{actionLabels[item.action] ?? item.action}</strong>{item.note ? <p>{item.note}</p> : null}</Timeline.Item>)}</Timeline> : <Empty className="v2-alert-timeline-empty" title="暂无处理记录" description="事件被确认、关闭或忽略后,将在这里形成审计履历。" />}
@@ -129,6 +123,21 @@ function EventInspector({ event, note, acting, actionError, editable, onNote, on
<Card className="v2-alert-detail-card v2-alert-disposition-card" title={<strong></strong>} headerLine>
{editable ? <><TextArea maxCount={200} autosize={{ minRows: 2, maxRows: 5 }} placeholder="请输入处置说明(选填)" value={note} onChange={onNote} />{actionError ? <p className="v2-alert-action-error">{actionError}</p> : null}<div className="v2-alert-actions"><Button theme="solid" className="is-primary" disabled={acting || !canAct(event.status, 'acknowledge')} onClick={() => onAction('acknowledge')}></Button><Button theme="light" disabled={acting || !canAct(event.status, 'close')} onClick={() => onAction('close')}></Button><Button theme="borderless" disabled={acting || !canAct(event.status, 'ignore')} onClick={() => onAction('ignore')}></Button></div></> : <p className="v2-role-notice"></p>}
</Card>
<Collapse className="v2-alert-technical-collapse" keepDOM>
<Collapse.Panel
itemKey="technical-evidence"
header={<span className="v2-alert-technical-heading"><strong></strong><small></small></span>}
>
<Descriptions className="v2-alert-descriptions" align="left" size="small" data={[
{ key: '事件 ID', value: <code>{event.id}</code> },
{ key: '来源事件 ID', value: event.sourceEventId || '—' },
{ key: '规则 / 版本', value: `${event.ruleId} / v${event.ruleVersion}` },
{ key: '事件 / 接收', value: `${formatAlertTime(event.eventAt)} / ${formatAlertTime(event.receivedAt)}` },
{ key: '恢复时间', value: formatAlertTime(event.recoveredAt) },
{ key: '处理人', value: event.handler || '—' }
]} />
</Collapse.Panel>
</Collapse>
</div>
<nav className="v2-alert-links"><Link to={`/vehicles/${encodeURIComponent(event.vin)}`}></Link><Link to={`/tracks?vin=${encodeURIComponent(event.vin)}`}></Link><Link to={`/history?vin=${encodeURIComponent(event.vin)}`}></Link></nav>
</Card>;
@@ -145,8 +154,13 @@ function EventWorkspace({ filters, draft, setDraft, setFilters, rules, unread, e
const mobileLayout = useMobileLayout();
const query: AlertQuery = useMemo(() => ({ ...Object.fromEntries(Object.entries(filters).filter(([, value]) => value)), limit, offset }), [filters, limit, offset]);
const baseQuery = useMemo(() => ({ ...query, limit: undefined, offset: undefined }), [query]);
const summaryQuery = useMemo(() => {
const scope = { ...baseQuery };
delete scope.status;
return scope;
}, [baseQuery]);
const eventScope = useMemo(() => queryScopeKey(baseQuery), [baseQuery]);
const summary = useQuery({ queryKey: ['alert-summary-v2', baseQuery], queryFn: ({ signal }) => api.alertSummaryV2(baseQuery, signal), staleTime: 8_000, gcTime: QUERY_MEMORY.summaryGcTime });
const summary = useQuery({ queryKey: ['alert-summary-v2', summaryQuery], queryFn: ({ signal }) => api.alertSummaryV2(summaryQuery, signal), staleTime: 8_000, gcTime: QUERY_MEMORY.summaryGcTime });
const events = useQuery<Page<AlertEvent>>({ queryKey: ['alert-events-v2', eventScope, limit, offset], queryFn: ({ signal }) => api.alertEventsV2(query, signal), placeholderData: retainPreviousPageWithinScope<Page<AlertEvent>>(eventScope), staleTime: 5_000, gcTime: QUERY_MEMORY.highVolumeGcTime });
const rows = events.data?.items ?? [];
const selectedID = selection?.scope === eventScope ? selection.id : '';
@@ -174,6 +188,7 @@ function EventWorkspace({ filters, draft, setDraft, setFilters, rules, unread, e
const sums = summary.data;
const activeFilterCount = Object.values(filters).filter(Boolean).length;
const advancedFilterCount = [filters.ruleId, filters.protocol, filters.dateFrom, filters.dateTo].filter(Boolean).length;
const allEventCount = Number(sums?.active ?? 0) + Number(sums?.recovered ?? 0) + Number(sums?.closed ?? 0) + Number(sums?.ignored ?? 0);
const inspector = <EventInspector event={selectedEvent} note={note} acting={action.isPending} actionError={action.error instanceof Error ? action.error.message : undefined} editable={editable} onNote={setNote} onAction={(name) => { const event = detail.data; if (event) action.mutate({ name, event }); }} onClose={closeDetail} />;
return <>
<WorkspaceFilterPanel
@@ -198,7 +213,7 @@ function EventWorkspace({ filters, draft, setDraft, setFilters, rules, unread, e
{summary.isError ? <InlineError message={summary.error instanceof Error ? summary.error.message : '告警汇总读取失败'} onRetry={() => summary.refetch()} /> : null}
<Card className="v2-alert-kpis-card v2-alert-context-card" bodyStyle={{ padding: 0 }}>
<section className="v2-alert-kpis" aria-label="活跃告警概览">{[
{ label: '活跃告警', value: sums?.active, tone: 'active', status: '' },
{ label: '全部事件', value: allEventCount, tone: 'active', status: '' },
{ label: '未处理', value: sums?.unprocessed, tone: 'unprocessed', status: 'unprocessed' },
{ label: '处理中', value: sums?.processing, tone: 'processing', status: 'processing' },
{ label: '未读通知', value: unread, tone: 'notice', status: 'notice' }
@@ -229,7 +244,7 @@ function EventWorkspace({ filters, draft, setDraft, setFilters, rules, unread, e
aria-label={mobileLayout ? '告警事件列表,可上下滚动' : undefined}
>
{mobileLayout
? <div className="v2-alert-mobile-list">{rows.map((event) => <Card key={event.id} className={`v2-alert-mobile-card${selectedID === event.id ? ' is-selected' : ''}`} bodyStyle={{ padding: 0 }}><Button theme="borderless" type="tertiary" className="v2-alert-mobile-action" aria-pressed={selectedID === event.id} aria-expanded={selectedID === event.id} aria-label={`查看 ${event.plate || event.vin} ${event.ruleName} 告警详情`} onClick={() => setSelection({ scope: eventScope, id: event.id })}><span className="v2-alert-mobile-card-content"><header><strong>{event.plate || '未绑定车牌'}</strong><span><SeverityTag severity={event.severity} /><StatusTag status={event.status} /></span></header><p>{event.ruleName}</p><dl><div><dt></dt><dd>{formatAlertTime(event.triggeredAt)}</dd></div><div><dt></dt><dd>{event.protocol || '—'}</dd></div><div><dt></dt><dd>{alertValue(event)}</dd></div><div><dt></dt><dd>{thresholdText(event)}</dd></div></dl><footer><IconChevronRight /></footer></span></Button></Card>)}</div>
? <div className="v2-alert-mobile-list">{rows.map((event) => <Card key={event.id} className={`v2-alert-mobile-card${selectedID === event.id ? ' is-selected' : ''}`} bodyStyle={{ padding: 0 }}><Button theme="borderless" type="tertiary" className="v2-alert-mobile-action" aria-pressed={selectedID === event.id} aria-expanded={selectedID === event.id} aria-label={`查看 ${event.plate || event.vin} ${event.ruleName} 告警详情`} onClick={() => setSelection({ scope: eventScope, id: event.id })}><span className="v2-alert-mobile-card-content"><header><strong>{event.plate || '未绑定车牌'}</strong><span><SeverityTag severity={event.severity} /><StatusTag status={event.status} /></span></header><p>{event.ruleName}</p><dl><div><dt></dt><dd>{formatAlertTime(event.triggeredAt)}</dd></div><div><dt></dt><dd>{event.protocol || '—'}</dd></div><div><dt> / </dt><dd>{alertValue(event)} / {thresholdText(event)}</dd></div></dl><footer><IconChevronRight /></footer></span></Button></Card>)}</div>
: <AlertEventTable rows={rows} selectedID={selectedID} onSelect={(id) => setSelection({ scope: eventScope, id })} />}
{events.isFetching ? <PanelLoading className="v2-alert-loading" title="正在更新事件…" description="告警列表返回后会自动更新。" compact={Boolean(rows.length)} /> : null}
{!events.isFetching && !rows.length ? <PanelEmpty className="v2-alert-empty" title="当前筛选条件没有告警事件" description="调整车辆、严重程度、状态或时间范围后重试。" /> : null}

View File

@@ -177,8 +177,8 @@ describe('V2 production entry', () => {
expect(corePageSources.AlertsPage).toContain('<Descriptions className="v2-alert-descriptions"');
expect(corePageSources.AlertsPage).toContain('<Timeline className="v2-alert-timeline"');
expect(corePageSources.AlertsPage).toContain('<Empty className="v2-alert-inspector-empty"');
expect(corePageSources.AlertsPage).toContain('<Card className="v2-alert-detail-card"');
expect(corePageSources.AlertsPage).toContain('<Card className="v2-alert-detail-card v2-alert-evidence-card"');
expect(corePageSources.AlertsPage).toContain('<Card className="v2-alert-detail-card v2-alert-focus-card"');
expect(corePageSources.AlertsPage).toContain('<Collapse className="v2-alert-technical-collapse"');
expect(corePageSources.AlertsPage).not.toContain('<section><h3>事件信息</h3>');
expect(corePageSources.AlertsPage).not.toContain('<div className="v2-alert-timeline">');
expect(corePageSources.AlertsPage).toContain('<Card className="v2-alert-rule-list"');

View File

@@ -6645,6 +6645,30 @@
font-size: 12px;
}
.v2-alert-event-evidence {
display: grid;
min-width: 0;
gap: 2px;
}
.v2-alert-event-evidence strong,
.v2-alert-event-evidence small {
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
.v2-alert-event-evidence strong {
color: #b83f39;
font-size: 12px;
font-variant-numeric: tabular-nums;
}
.v2-alert-event-evidence small {
color: #7d8b9e;
font-size: 10px;
}
.v2-alert-table-card > .semi-card-body > footer {
min-height: 46px;
padding: 6px 12px;
@@ -6703,6 +6727,134 @@
background: #fff;
}
.v2-alert-focus-card > .semi-card-body {
background: linear-gradient(145deg, #fff 0%, #f7faff 100%);
}
.v2-alert-focus-identity {
display: flex;
min-width: 0;
align-items: center;
justify-content: space-between;
gap: 12px;
border-bottom: 1px solid #e6ebf2;
padding: 13px 14px 12px;
}
.v2-alert-focus-identity > span {
display: grid;
min-width: 0;
gap: 3px;
}
.v2-alert-focus-identity small,
.v2-alert-focus-facts small {
color: #7e8c9f;
font-size: 10px;
}
.v2-alert-focus-identity strong {
overflow: hidden;
color: #233b56;
font-size: 18px;
line-height: 1.2;
text-overflow: ellipsis;
white-space: nowrap;
}
.v2-alert-focus-identity code {
overflow: hidden;
color: #7b899c;
font: 10px/1.4 ui-monospace, SFMono-Regular, Menlo, monospace;
text-overflow: ellipsis;
white-space: nowrap;
}
.v2-alert-focus-identity > .semi-tag {
flex: 0 0 auto;
border-radius: 999px;
font-size: 9px;
}
.v2-alert-focus-facts {
display: grid;
grid-template-columns: repeat(2, minmax(0, 1fr));
border-bottom: 1px solid #e6ebf2;
}
.v2-alert-focus-facts > span {
display: grid;
min-width: 0;
gap: 4px;
padding: 9px 14px;
}
.v2-alert-focus-facts > span + span {
border-left: 1px solid #e6ebf2;
}
.v2-alert-focus-facts strong {
overflow: hidden;
color: #3d5068;
font-size: 12px;
font-variant-numeric: tabular-nums;
text-overflow: ellipsis;
white-space: nowrap;
}
.v2-alert-focus-card .v2-alert-evidence {
padding: 11px 12px 12px;
}
.v2-alert-technical-collapse.semi-collapse {
flex: 0 0 auto;
overflow: hidden;
border: 1px solid #dfe6ef;
border-radius: 10px;
background: #fff;
box-shadow: 0 3px 12px rgba(31, 53, 80, .035);
}
.v2-alert-technical-collapse > .semi-collapse-item {
margin: 0;
border: 0;
}
.v2-alert-technical-collapse .semi-collapse-header {
min-height: 48px;
align-items: center;
padding: 8px 12px;
}
.v2-alert-technical-collapse .semi-collapse-header:hover {
background: #f7faff;
}
.v2-alert-technical-heading {
display: grid;
gap: 2px;
}
.v2-alert-technical-heading strong {
color: #33475f;
font-size: 12px;
}
.v2-alert-technical-heading small {
color: #8491a3;
font-size: 9px;
font-weight: 400;
}
.v2-alert-technical-collapse .semi-collapse-content {
border-top: 1px solid #e7ecf2;
background: #fff;
}
.v2-alert-technical-collapse .semi-collapse-content-wrapper {
padding: 0;
}
.v2-alert-detail-card.v2-alert-progress-card > .semi-card-body,
.v2-alert-detail-card.v2-alert-disposition-card > .semi-card-body {
padding: 10px !important;
@@ -7032,7 +7184,7 @@
}
.v2-alert-context-card.semi-card {
min-height: 104px;
min-height: 88px;
}
.v2-alert-context-card > .semi-card-body {
@@ -7040,18 +7192,18 @@
}
.v2-alert-context-card .v2-alert-kpis {
height: 64px;
min-height: 64px;
height: 54px;
min-height: 54px;
grid-template-columns: repeat(4, minmax(0, 1fr));
}
.v2-alert-context-card .v2-alert-kpis .v2-metric-action,
.v2-alert-context-card .v2-alert-kpis .v2-metric-action-content {
min-height: 64px;
min-height: 54px;
}
.v2-alert-context-card .v2-alert-kpis .v2-metric-action {
padding: 7px 5px;
padding: 5px 4px;
}
.v2-alert-context-card .v2-alert-kpis .v2-metric-action.is-notice {
@@ -7070,18 +7222,18 @@
.v2-alert-context-card .v2-alert-kpis strong {
margin-top: 0;
font-size: 18px;
font-size: 16px;
}
.v2-alert-context-card .v2-alert-secondary-states {
min-height: 40px;
min-height: 34px;
border-top: 1px solid #e4e9f0;
border-left: 0;
padding: 3px 4px;
}
.v2-alert-context-card .v2-alert-secondary-states > .semi-button {
min-height: 34px;
min-height: 28px;
padding: 2px 5px;
}
@@ -7090,7 +7242,7 @@
}
.v2-alert-context-card .v2-alert-secondary-states b {
font-size: 13px;
font-size: 12px;
}
.v2-alert-page.is-events .v2-alert-workspace {
@@ -7106,9 +7258,9 @@
}
.v2-alert-table-card > .semi-card-body > .v2-workspace-panel-header {
min-height: 50px;
min-height: 44px;
align-items: center;
padding: 6px 8px;
padding: 5px 8px;
}
.v2-alert-table-card .v2-workspace-panel-copy > .semi-typography {
@@ -7144,17 +7296,17 @@
}
.v2-alert-mobile-list {
gap: 8px;
padding: 8px;
gap: 6px;
padding: 6px;
}
.v2-alert-mobile-list > .v2-alert-mobile-card.semi-card {
border-radius: 9px;
contain-intrinsic-size: auto 110px;
contain-intrinsic-size: auto 96px;
}
.v2-alert-mobile-card-content {
padding: 10px 11px;
padding: 8px 10px;
}
.v2-alert-mobile-card-content header > strong {
@@ -7175,7 +7327,7 @@
}
.v2-alert-mobile-card-content p {
margin: 6px 0 7px;
margin: 4px 0 5px;
overflow: hidden;
color: #405168;
font-size: 12px;
@@ -7185,15 +7337,15 @@
}
.v2-alert-mobile-card-content dl {
grid-template-columns: repeat(4, minmax(0, 1fr));
grid-template-columns: .92fr .7fr 1.38fr;
gap: 0;
padding-top: 7px;
padding-top: 5px;
}
.v2-alert-mobile-card-content dl > div {
display: block;
min-width: 0;
min-height: 32px;
min-height: 28px;
padding: 0 5px;
}
@@ -7214,7 +7366,7 @@
.v2-alert-mobile-card-content dd {
min-width: 0;
margin: 3px 0 0;
margin: 2px 0 0;
overflow: hidden;
font-size: 10px;
line-height: 13px;
@@ -7228,7 +7380,7 @@
}
.v2-alert-table-card > .semi-card-body > footer:has(.v2-table-pagination-controls) {
min-height: 50px;
min-height: 44px;
flex-direction: row;
align-items: center;
padding: 5px 6px;
@@ -7241,7 +7393,7 @@
.v2-alert-table-card > .semi-card-body > footer .v2-table-pagination-button.semi-button,
.v2-alert-table-card > .semi-card-body > footer .v2-table-pagination-current,
.v2-alert-table-card > .semi-card-body > footer .v2-table-pagination-size.semi-select {
height: 34px;
height: 32px;
}
.v2-alert-rules {