refine Semi UI alert disposition

This commit is contained in:
lingniu
2026-07-18 17:27:08 +08:00
parent 29b9ce9594
commit 82d78926c6
5 changed files with 228 additions and 11 deletions

View File

@@ -1,11 +1,17 @@
import { describe, expect, it } from 'vitest'; import { describe, expect, it } from 'vitest';
import { alertValue, canAct, formatAlertTime, ruleCondition, thresholdText } from './alert'; import { alertDeltaText, alertValue, canAct, formatAlertTime, ruleCondition, thresholdText } from './alert';
describe('alert domain helpers', () => { describe('alert domain helpers', () => {
it('keeps trigger evidence and duration explicit', () => { it('keeps trigger evidence and duration explicit', () => {
const event = { triggerValue: 96, threshold: 80, thresholdHigh: 0, operator: 'gt', unit: 'km/h', durationSec: 60 }; const event = { triggerValue: 96, threshold: 80, thresholdHigh: 0, operator: 'gt', unit: 'km/h', durationSec: 60 };
expect(alertValue(event)).toBe('96 km/h'); expect(alertValue(event)).toBe('96 km/h');
expect(thresholdText(event)).toBe('> 80 km/h持续 60 秒'); expect(thresholdText(event)).toBe('> 80 km/h持续 60 秒');
expect(alertDeltaText(event)).toBe('+16 km/h');
});
it('describes low-bound and non-numeric trigger outcomes without inventing a delta', () => {
expect(alertDeltaText({ triggerValue: 12, threshold: 20, operator: 'lt', unit: '%' })).toBe('+8 %');
expect(alertDeltaText({ triggerValue: 1, threshold: 1, operator: 'changed', unit: '' })).toBe('规则已命中');
}); });
it('enforces valid workflow transitions', () => { it('enforces valid workflow transitions', () => {

View File

@@ -35,6 +35,17 @@ export function thresholdText(event: Pick<AlertEvent, 'operator' | 'threshold' |
return `${operatorLabels[event.operator] ?? event.operator} ${formatZhNumber(Number(event.threshold.toFixed(2)), 2)} ${event.unit}${duration}`.trim(); return `${operatorLabels[event.operator] ?? event.operator} ${formatZhNumber(Number(event.threshold.toFixed(2)), 2)} ${event.unit}${duration}`.trim();
} }
export function alertDeltaText(event: Pick<AlertEvent, 'operator' | 'triggerValue' | 'threshold' | 'unit'>) {
if (!Number.isFinite(event.triggerValue) || !Number.isFinite(event.threshold)) return '规则已命中';
const delta = event.operator === 'gt' || event.operator === 'gte'
? event.triggerValue - event.threshold
: event.operator === 'lt' || event.operator === 'lte'
? event.threshold - event.triggerValue
: Number.NaN;
if (!Number.isFinite(delta)) return '规则已命中';
return `+${formatZhNumber(Number(Math.max(0, delta).toFixed(2)), 2)} ${event.unit}`.trim();
}
export function ruleCondition(rule: AlertRule, labels: Record<string, string> = metricLabels) { export function ruleCondition(rule: AlertRule, labels: Record<string, string> = metricLabels) {
const threshold = rule.operator === 'changed' ? '状态变化' : rule.operator === 'between' || rule.operator === 'outside' ? `${operatorLabels[rule.operator]} ${rule.threshold}${rule.thresholdHigh}` : rule.valueType === 'boolean' ? (rule.booleanThreshold ? '是' : '否') : `${operatorLabels[rule.operator] ?? rule.operator} ${rule.threshold}`; const threshold = rule.operator === 'changed' ? '状态变化' : rule.operator === 'between' || rule.operator === 'outside' ? `${operatorLabels[rule.operator]} ${rule.threshold}${rule.thresholdHigh}` : rule.valueType === 'boolean' ? (rule.booleanThreshold ? '是' : '否') : `${operatorLabels[rule.operator] ?? rule.operator} ${rule.threshold}`;
return `${labels[rule.metric] ?? rule.metric} ${threshold}${rule.durationSec ? ` · ${rule.durationSec}` : ''}`; return `${labels[rule.metric] ?? rule.metric} ${threshold}${rule.durationSec ? ` · ${rule.durationSec}` : ''}`;

View File

@@ -163,6 +163,10 @@ test('clears old alert rows and inspector evidence when the event scope changes'
expect(screen.getByText('已展开处置详情 · 点击其他事件快速切换')).toBeInTheDocument(); expect(screen.getByText('已展开处置详情 · 点击其他事件快速切换')).toBeInTheDocument();
expect(desktopAlertRow).toHaveAttribute('aria-expanded', 'true'); expect(desktopAlertRow).toHaveAttribute('aria-expanded', 'true');
expect(view.container.querySelector('.v2-alert-focus-card[aria-label="告警处置摘要"]')).toHaveClass('v2-alert-detail-card'); expect(view.container.querySelector('.v2-alert-focus-card[aria-label="告警处置摘要"]')).toHaveClass('v2-alert-detail-card');
expect(screen.getByLabelText('告警事件上下文')).toHaveTextContent('数据来源JT808触发时间07-16 12:00:00处理记录1 条');
expect(screen.getByLabelText('告警触发证据')).toHaveTextContent('触发值90 km/h阈值条件> 80 km/h持续 60 秒超限幅度+10 km/h');
expect(screen.getByText('1 条记录').closest('.semi-tag')).toBeInTheDocument();
expect(screen.getByText('可操作').closest('.semi-tag')).toBeInTheDocument();
expect(view.container.querySelector('.v2-alert-technical-collapse.semi-collapse')).toBeInTheDocument(); expect(view.container.querySelector('.v2-alert-technical-collapse.semi-collapse')).toBeInTheDocument();
expect(screen.getByText('技术详情')).toBeInTheDocument(); expect(screen.getByText('技术详情')).toBeInTheDocument();
expect(view.container.querySelector('.v2-alert-inspector-heading')).toHaveClass('v2-workspace-panel-header'); expect(view.container.querySelector('.v2-alert-inspector-heading')).toHaveClass('v2-workspace-panel-header');
@@ -233,12 +237,15 @@ test('renders only selectable Semi alert cards on mobile', async () => {
expect(view.container.querySelector('.v2-alert-table-scroll.is-mobile-scroll')).toHaveAttribute('aria-label', '告警事件列表,可上下滚动'); expect(view.container.querySelector('.v2-alert-table-scroll.is-mobile-scroll')).toHaveAttribute('aria-label', '告警事件列表,可上下滚动');
expect(view.container.querySelector('.v2-alert-event-table')).not.toBeInTheDocument(); expect(view.container.querySelector('.v2-alert-event-table')).not.toBeInTheDocument();
expect(action).toHaveAttribute('aria-expanded', 'false'); expect(action).toHaveAttribute('aria-expanded', 'false');
expect(action).toHaveTextContent('超限幅度+10 km/h');
expect(action).toHaveTextContent('核验并处置');
fireEvent.click(action); fireEvent.click(action);
expect(action).toHaveAttribute('aria-pressed', 'true'); expect(action).toHaveAttribute('aria-pressed', 'true');
expect(action).toHaveAttribute('aria-expanded', 'true'); expect(action).toHaveAttribute('aria-expanded', 'true');
expect(await screen.findByRole('dialog', { name: '告警事件详情' })).toBeInTheDocument(); 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')).toHaveClass('semi-sidesheet-bottom');
expect(document.querySelector('.v2-alert-detail-sidesheet .semi-sidesheet-inner')).toHaveStyle({ height: 'min(86dvh, 760px)' }); expect(document.querySelector('.v2-alert-detail-sidesheet .semi-sidesheet-inner')).toHaveStyle({ height: 'min(86dvh, 760px)' });
expect(screen.getByLabelText('告警触发证据')).toHaveTextContent('超限幅度+10 km/h');
expect(await screen.findByText('mobile-event')).toBeInTheDocument(); expect(await screen.findByText('mobile-event')).toBeInTheDocument();
fireEvent.click(screen.getByRole('button', { name: '关闭告警详情' })); fireEvent.click(screen.getByRole('button', { name: '关闭告警详情' }));
expect(screen.queryByText('mobile-event')).not.toBeInTheDocument(); expect(screen.queryByText('mobile-event')).not.toBeInTheDocument();

View File

@@ -5,7 +5,7 @@ import { FormEvent, memo, useCallback, useEffect, useMemo, useState } from 'reac
import { Link, useSearchParams } from 'react-router-dom'; import { Link, useSearchParams } from 'react-router-dom';
import { api } from '../../api/client'; import { api } from '../../api/client';
import type { AlertEvent, AlertQuery, AlertRule, AlertRuleInput, AlertStatus, MetricDefinition, Page } from '../../api/types'; import type { AlertEvent, AlertQuery, AlertRule, AlertRuleInput, AlertStatus, MetricDefinition, Page } from '../../api/types';
import { actionLabels, alertValue, canAct, formatAlertTime, operatorLabels, ruleCondition, severityLabels, statusLabels, thresholdText } from '../domain/alert'; import { actionLabels, alertDeltaText, alertValue, canAct, formatAlertTime, operatorLabels, ruleCondition, severityLabels, statusLabels, thresholdText } from '../domain/alert';
import { InlineError, PanelEmpty, PanelLoading } from '../shared/AsyncState'; import { InlineError, PanelEmpty, PanelLoading } from '../shared/AsyncState';
import { MetricActionButton } from '../shared/MetricActionButton'; import { MetricActionButton } from '../shared/MetricActionButton';
import { SegmentedTabs } from '../shared/SegmentedTabs'; import { SegmentedTabs } from '../shared/SegmentedTabs';
@@ -114,23 +114,30 @@ const AlertEventTable = memo(function AlertEventTable({ rows, selectedID, compac
function EventInspector({ event, note, acting, actionError, editable, onNote, onAction, onClose, sheet = false }: { event?: AlertEvent; note: string; acting: boolean; actionError?: string; editable: boolean; onNote: (value: string) => void; onAction: (action: 'acknowledge' | 'close' | 'ignore') => void; onClose: () => void; sheet?: boolean }) { function EventInspector({ event, note, acting, actionError, editable, onNote, onAction, onClose, sheet = false }: { event?: AlertEvent; note: string; acting: boolean; actionError?: string; editable: boolean; onNote: (value: string) => void; onAction: (action: 'acknowledge' | 'close' | 'ignore') => void; onClose: () => void; sheet?: boolean }) {
if (!event) return <Card className="v2-alert-inspector" bodyStyle={{ padding: 0 }}><Empty className="v2-alert-inspector-empty" image={<IconAlarm />} title="选择告警事件" description="查看触发证据、状态时间线和处置动作。" /></Card>; 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} />}</>} /> const actionCount = event.actions?.length ?? 0;
return <Card className={`v2-alert-inspector${sheet ? ' is-sheet' : ''}`} bodyStyle={{ padding: 0 }}><WorkspacePanelHeader className="v2-alert-inspector-heading" title="事件处置" description={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"> <div className="v2-alert-inspector-content">
<Card className="v2-alert-detail-card v2-alert-focus-card" bodyStyle={{ padding: 0 }} aria-label="告警处置摘要"> <Card className="v2-alert-detail-card v2-alert-focus-card" bodyStyle={{ padding: 0 }} aria-label="告警处置摘要">
<header className="v2-alert-focus-identity"> <header className="v2-alert-focus-identity">
<span><small></small><strong>{event.plate || '未绑定车牌'}</strong><code>{event.vin}</code></span> <span><small></small><strong>{event.plate || '未绑定车牌'}</strong><code>{event.vin}</code></span>
<Tag color="blue" type="light" size="small">{event.protocol || '未知协议'}</Tag> <span className="v2-alert-focus-current"><small></small><StatusTag status={event.status} /></span>
</header> </header>
<div className="v2-alert-focus-facts"> <div className="v2-alert-focus-facts" aria-label="告警事件上下文">
<span><small></small><strong><Tag color="blue" type="light" size="small">{event.protocol || '未知协议'}</Tag></strong></span>
<span><small></small><strong><AlertTime value={event.triggeredAt} /></strong></span> <span><small></small><strong><AlertTime value={event.triggeredAt} /></strong></span>
<span><small></small><strong>{statusLabels[event.status]}</strong></span> <span><small></small><strong>{actionCount} </strong></span>
</div> </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> <div className="v2-alert-evidence" aria-label="告警触发证据">
<Card className="v2-alert-evidence-value is-trigger"><small></small><strong>{alertValue(event)}</strong></Card>
<Card className="v2-alert-evidence-value"><small></small><strong>{thresholdText(event)}</strong></Card>
<Card className="v2-alert-evidence-value is-delta"><small></small><strong>{alertDeltaText(event)}</strong></Card>
</div>
<div className="v2-alert-focus-rule"><Tag color="grey" type="light" size="small"></Tag><span>{event.ruleName}</span></div>
</Card> </Card>
<Card className="v2-alert-detail-card v2-alert-progress-card" title={<strong></strong>} headerLine> <Card className="v2-alert-detail-card v2-alert-progress-card" title={<span className="v2-alert-detail-title"><strong></strong><Tag color={actionCount ? 'blue' : 'amber'} type="light" size="small">{actionCount ? `${actionCount} 条记录` : '待处置'}</Tag></span>} 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} · <AlertTime value={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="事件被确认、关闭或忽略后,将在这里形成审计履历。" />} {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} · <AlertTime value={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="事件被确认、关闭或忽略后,将在这里形成审计履历。" />}
</Card> </Card>
<Card className="v2-alert-detail-card v2-alert-disposition-card" title={<strong></strong>} headerLine> <Card className="v2-alert-detail-card v2-alert-disposition-card" title={<span className="v2-alert-detail-title"><strong></strong><Tag color={editable ? 'green' : 'grey'} type="light" size="small">{editable ? '可操作' : '只读'}</Tag></span>} 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>} {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> </Card>
<Collapse className="v2-alert-technical-collapse" keepDOM> <Collapse className="v2-alert-technical-collapse" keepDOM>
@@ -255,7 +262,7 @@ function EventWorkspace({ filters, draft, setDraft, setFilters, rules, unread, e
aria-label={mobileLayout ? '告警事件列表,可上下滚动' : undefined} aria-label={mobileLayout ? '告警事件列表,可上下滚动' : undefined}
> >
{mobileLayout {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={() => selectEvent(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><AlertTime value={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> ? <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={() => selectEvent(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><AlertTime value={event.triggeredAt} /></dd></div><div><dt></dt><dd>{event.protocol || '—'}</dd></div><div><dt></dt><dd>{alertDeltaText(event)}</dd></div></dl><footer><IconChevronRight /></footer></span></Button></Card>)}</div>
: <AlertEventTable rows={rows} selectedID={selectedID} compact={Boolean(selectedID)} onSelect={selectEvent} />} : <AlertEventTable rows={rows} selectedID={selectedID} compact={Boolean(selectedID)} onSelect={selectEvent} />}
{events.isFetching ? <PanelLoading className="v2-alert-loading" title="正在更新事件…" description="告警列表返回后会自动更新。" compact={Boolean(rows.length)} /> : null} {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} {!events.isFetching && !rows.length ? <PanelEmpty className="v2-alert-empty" title="当前筛选条件没有告警事件" description="调整车辆、严重程度、状态或时间范围后重试。" /> : null}

View File

@@ -14185,6 +14185,192 @@
} }
} }
/*
* Semi UI alert disposition: organize each event around the object, current
* state and the decision evidence before showing audit history and actions.
*/
.v2-alert-inspector-heading > .v2-workspace-panel-copy {
gap: 2px;
}
.v2-alert-inspector-heading > .v2-workspace-panel-copy > .semi-typography:last-child {
overflow: hidden;
max-width: 250px;
margin: 0;
color: #7c8b9d;
font-size: 10px;
line-height: 1.35;
text-overflow: ellipsis;
white-space: nowrap;
}
.v2-alert-focus-identity {
min-height: 80px;
background: linear-gradient(120deg, #f5f9ff 0%, #fff 64%, #fffaf7 100%);
}
.v2-alert-focus-current {
display: grid;
min-width: 84px;
flex: 0 0 auto;
justify-items: end;
gap: 5px;
}
.v2-alert-focus-current > .semi-tag {
min-width: 58px;
min-height: 24px;
justify-content: center;
border-radius: 999px;
font-size: 10px;
font-weight: 700;
}
.v2-alert-focus-facts {
grid-template-columns: repeat(3, minmax(0, 1fr));
background: #fbfcfe;
}
.v2-alert-focus-facts > span {
min-height: 58px;
justify-content: center;
padding: 8px 11px;
}
.v2-alert-focus-facts > span + span {
border-left: 1px solid #e6ebf2;
}
.v2-alert-focus-facts .semi-tag {
width: max-content;
max-width: 100%;
min-height: 22px;
border-radius: 999px;
font-size: 9px;
}
.v2-alert-focus-card .v2-alert-evidence {
grid-template-columns: repeat(3, minmax(0, 1fr));
gap: 7px;
padding: 10px;
}
.v2-alert-focus-card .v2-alert-evidence > .v2-alert-evidence-value.semi-card {
overflow: hidden;
border-color: #dde6f0;
border-radius: 9px;
box-shadow: none;
}
.v2-alert-focus-card .v2-alert-evidence > .v2-alert-evidence-value > .semi-card-body {
min-height: 76px;
padding: 9px 7px;
}
.v2-alert-focus-card .v2-alert-evidence > .v2-alert-evidence-value.is-trigger {
border-color: #efc7c2;
background: #fff8f7;
}
.v2-alert-focus-card .v2-alert-evidence > .v2-alert-evidence-value.is-delta {
border-color: #c8dbf7;
background: #f4f8ff;
}
.v2-alert-focus-card .v2-alert-evidence > .v2-alert-evidence-value.is-delta strong {
color: #1268f3;
}
.v2-alert-focus-rule {
display: flex;
min-height: 40px;
align-items: center;
gap: 8px;
border-top: 1px solid #e7edf4;
background: #fbfcfe;
padding: 7px 10px;
}
.v2-alert-focus-rule > .semi-tag {
min-width: 54px;
flex: 0 0 auto;
justify-content: center;
border-radius: 999px;
font-size: 9px;
font-weight: 700;
}
.v2-alert-focus-rule > span {
overflow: hidden;
color: #50647c;
font-size: 10px;
line-height: 1.45;
text-overflow: ellipsis;
white-space: nowrap;
}
.v2-alert-detail-title {
display: inline-flex;
align-items: center;
gap: 8px;
}
.v2-alert-detail-title > .semi-tag {
min-height: 22px;
justify-content: center;
border-radius: 999px;
font-size: 9px;
font-weight: 700;
}
@media (max-width: 680px) {
.v2-alert-inspector.is-sheet .v2-alert-focus-identity {
min-height: 72px;
padding: 10px 12px;
}
.v2-alert-focus-identity strong {
font-size: 17px;
}
.v2-alert-focus-facts > span {
min-height: 54px;
padding: 7px 9px;
}
.v2-alert-focus-facts small {
font-size: 9px;
}
.v2-alert-focus-facts strong {
font-size: 11px;
}
.v2-alert-focus-card .v2-alert-evidence {
gap: 5px;
padding: 8px;
}
.v2-alert-focus-card .v2-alert-evidence > .v2-alert-evidence-value > .semi-card-body {
min-height: 72px;
padding: 7px 5px;
}
.v2-alert-focus-card .v2-alert-evidence small {
font-size: 9px;
}
.v2-alert-focus-card .v2-alert-evidence strong {
font-size: 12px;
line-height: 1.35;
}
.v2-alert-focus-rule {
min-height: 38px;
padding: 6px 8px;
}
}
/* /*
* Semi UI account identity and menu permissions: one shared hierarchy from * Semi UI account identity and menu permissions: one shared hierarchy from
* account context to editable groups and the resulting customer workspace. * account context to editable groups and the resulting customer workspace.