refine Semi UI alert workspace
This commit is contained in:
@@ -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');
|
||||
|
||||
@@ -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}
|
||||
|
||||
@@ -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"');
|
||||
|
||||
@@ -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 {
|
||||
|
||||
Reference in New Issue
Block a user