diff --git a/ui/mock/alertAPI.ts b/ui/mock/alertAPI.ts index 8013661c3..24198fc71 100644 --- a/ui/mock/alertAPI.ts +++ b/ui/mock/alertAPI.ts @@ -4,7 +4,7 @@ export default { { description: 'string', endsAt: 0, - fingerprint: 'string', + fingerprint: 'string1', instance: { obcluster: 'testobcluster', type: 'obcluster', @@ -25,13 +25,79 @@ export default { ], rule: 'string', serverity: 'critical', - startsAt: 0, + startsAt: 1716543006, + status: { + inhibitedBy: ['string'], + silencedBy: ['string'], + state: 'suppressed', + }, + summary: 'string1', + updatedAt: 0, + }, + { + description: 'string', + endsAt: 0, + fingerprint: 'string2', + instance: { + obcluster: 'testobcluster', + type: 'obcluster', + }, + labels: [ + { + key: 'string', + value: 'string', + }, + { + key: 'string1', + value: 'string1', + }, + { + key: 'string2', + value: 'string2', + }, + ], + rule: 'string', + serverity: 'info', + startsAt: 1716548006, + status: { + inhibitedBy: ['string'], + silencedBy: ['string'], + state: 'unprocessed', + }, + summary: 'string2', + updatedAt: 0, + }, + { + description: 'string', + endsAt: 0, + fingerprint: 'string3', + instance: { + obcluster: 'testobcluster', + type: 'obcluster', + }, + labels: [ + { + key: 'string', + value: 'string', + }, + { + key: 'string1', + value: 'string1', + }, + { + key: 'string2', + value: 'string2', + }, + ], + rule: 'string', + serverity: 'warning', + startsAt: 1716548606, status: { inhibitedBy: ['string'], silencedBy: ['string'], state: 'active', }, - summary: 'string', + summary: 'string3', updatedAt: 0, }, ], @@ -43,7 +109,33 @@ export default { { comment: 'string', createdBy: 'string', - endsAt: 0, + endsAt: 1716543006, + id: 'string', + instance: { + obcluster: 'string', + observer: 'string', + obtenant: 'string', + obzone: 'string', + type: 'obcluster', + }, + matchers: [ + { + isEqual: true, + isRegex: true, + name: 'string', + value: 'string', + }, + ], + startsAt: 1716543006, + status: { + state: 'expired', + }, + updatedAt: 0, + }, + { + comment: 'string', + createdBy: 'string', + endsAt: 1716543006, id: 'string', instance: { obcluster: 'string', @@ -60,7 +152,33 @@ export default { value: 'string', }, ], - startsAt: 0, + startsAt: 1716543006, + status: { + state: 'pending', + }, + updatedAt: 0, + }, + { + comment: 'string1', + createdBy: 'string1', + endsAt: 1716548006, + id: 'string1', + instance: { + obcluster: 'string', + observer: 'string', + obtenant: 'string', + obzone: 'string', + type: 'obcluster', + }, + matchers: [ + { + isEqual: true, + isRegex: true, + name: 'string', + value: 'string', + }, + ], + startsAt: 1716548006, status: { state: 'active', }, @@ -125,6 +243,46 @@ export default { summary: 'string', type: 'builtin', }, + { + description: 'string', + duration: 0, + evaluationTime: 0, + health: 'unknown', + instanceType: 'obcluster', + keepFiringFor: 0, + labels: { + key: 'string', + value: 'string', + }, + lastError: 'string', + lastEvaluation: 0, + name: 'string', + query: 'string', + serverity: 'caution', + state: 'active', + summary: 'string', + type: 'customized', + }, + { + description: 'string', + duration: 0, + evaluationTime: 0, + health: 'unknown', + instanceType: 'obcluster', + keepFiringFor: 0, + labels: { + key: 'string', + value: 'string', + }, + lastError: 'string', + lastEvaluation: 0, + name: 'string', + query: 'string', + serverity: 'warning', + state: 'active', + summary: 'string', + type: 'builtin', + }, ], message: 'string', successful: true, diff --git a/ui/src/constants/index.ts b/ui/src/constants/index.ts index 608e2c3fc..af95e0c00 100644 --- a/ui/src/constants/index.ts +++ b/ui/src/constants/index.ts @@ -178,13 +178,21 @@ const SERVERITY_MAP = { critical: { color: 'red', label: '严重', + weight: 3, }, warning: { color: 'gold', label: '警告', + weight: 2, }, - caution: { color: 'blue', label: '注意' }, - info: { color: 'green', label: '提醒' }, + caution: { color: 'blue', label: '注意', weight: 1 }, + info: { color: 'green', label: '提醒', weight: 0 }, +}; + +const SHILED_STATUS_MAP = { + active: { text: '活跃', color: 'green', weight: 2 }, + expired: { text: '过期', color: 'gold', weight: 1 }, + pending: { text: '未生效', color: 'default', weight: 0 }, }; const OBJECT_OPTIONS_ALARM: DefaultOptionType[] = [ @@ -213,7 +221,14 @@ const CHANNEL_TYPE_OPTIONS = [ }, ]; +const ALERT_STATE_MAP = { + active: { text: '活跃', color: 'green', weight: 0 }, + unprocessed: { text: '未处理', color: 'default', weight: 1 }, + suppressed: { text: '抑制', color: 'red', weight: 2 }, +}; + export { + ALERT_STATE_MAP, BACKUP_RESULT_STATUS, BADGE_IMG_MAP, CHANNEL_TYPE_OPTIONS, @@ -233,6 +248,7 @@ export { RESULT_STATUS, SERVERITY_MAP, SERVER_IMG_MAP, + SHILED_STATUS_MAP, STATISTICS_INTERVAL, STATUS, SUFFIX_UNIT, diff --git a/ui/src/pages/Alert/Event/index.tsx b/ui/src/pages/Alert/Event/index.tsx index c6316ac44..a4c868965 100644 --- a/ui/src/pages/Alert/Event/index.tsx +++ b/ui/src/pages/Alert/Event/index.tsx @@ -5,10 +5,19 @@ import type { AlertStatus, OceanbaseOBInstance, } from '@/api/generated'; -import { SERVERITY_MAP } from '@/constants'; +import { ALERT_STATE_MAP, SERVERITY_MAP } from '@/constants'; import { history } from '@umijs/max'; import { useRequest } from 'ahooks'; -import { Button, Card, Form, Space, Table, Tag, Typography } from 'antd'; +import { + Button, + Card, + Form, + Space, + Table, + Tag, + Tooltip, + Typography, +} from 'antd'; import type { ColumnsType } from 'antd/es/table'; import moment from 'moment'; import AlarmFilter from '../AlarmFilter'; @@ -16,14 +25,25 @@ const { Text } = Typography; export default function Event() { const [form] = Form.useForm(); - const { data: listAlertsRes, run: getListAlerts } = useRequest(alert.listAlerts); + const { data: listAlertsRes, run: getListAlerts } = useRequest( + alert.listAlerts, + ); const listAlerts = listAlertsRes?.data || []; const columns: ColumnsType = [ { title: '告警事件', dataIndex: 'summary', key: 'summary', - render: (val) => , + render: (val, record) => { + return ( + + ); + }, }, { title: '告警对象', @@ -41,6 +61,12 @@ export default function Event() { title: '告警等级', dataIndex: 'serverity', key: 'serverity', + sorter: (preRecord, curRecord) => { + return ( + SERVERITY_MAP[preRecord.serverity].weight - + SERVERITY_MAP[curRecord.serverity].weight + ); + }, render: (serverity: AlarmServerity) => ( {SERVERITY_MAP[serverity]?.label} @@ -51,14 +77,24 @@ export default function Event() { title: '告警状态', dataIndex: 'status', key: 'status', - render: (status: AlertStatus) => {status.state}, + sorter: (preRecord, curRecord) => { + return ( + ALERT_STATE_MAP[preRecord.status.state].weight - + ALERT_STATE_MAP[curRecord.status.state].weight + ); + }, + render: (status: AlertStatus) => ( + + {ALERT_STATE_MAP[status.state].text || '-'} + + ), }, { title: '产生时间', dataIndex: 'startsAt', key: 'startsAt', defaultSortOrder: 'ascend', - sorter: (pre: number, cur: number) => cur - pre, + sorter: (preRecord, curRecord) => curRecord.startsAt - preRecord.startsAt, render: (startsAt: number) => ( {moment.unix(startsAt).format('YYYY-MM-DD HH:MM:SS')} ), @@ -71,7 +107,7 @@ export default function Event() { disabled={record.status.state !== 'active'} style={{ paddingLeft: 0 }} type="link" - onClick={() => + onClick={() => { history.push( `/alert/shield?instance=${JSON.stringify( record.instance, @@ -81,8 +117,8 @@ export default function Event() { value: label.value, })), )}`, - ) - } + ); + }} > 屏蔽 diff --git a/ui/src/pages/Alert/Rules/RuleDrawerForm.tsx b/ui/src/pages/Alert/Rules/RuleDrawerForm.tsx index 5caa4d01b..32e206561 100644 --- a/ui/src/pages/Alert/Rules/RuleDrawerForm.tsx +++ b/ui/src/pages/Alert/Rules/RuleDrawerForm.tsx @@ -46,6 +46,7 @@ export default function RuleDrawerForm({ preserve={false} style={{ marginBottom: 64 }} layout="vertical" + validateTrigger='onBlur' form={form} > @@ -57,6 +58,21 @@ export default function RuleDrawerForm({ required: true, message: '请输入', }, + { + validator: async (_, value) => { + const res = await alert.listRules(); + if (res.successful) { + for (const rule of res.data) { + if (rule.name === value) { + return Promise.reject( + new Error('告警规则已存在,请重新输入'), + ); + } + } + } + return Promise.resolve(); + }, + }, ]} label="告警规则名" > diff --git a/ui/src/pages/Alert/Rules/index.tsx b/ui/src/pages/Alert/Rules/index.tsx index ef357aad3..d4bee0f34 100644 --- a/ui/src/pages/Alert/Rules/index.tsx +++ b/ui/src/pages/Alert/Rules/index.tsx @@ -2,10 +2,11 @@ import { alert } from '@/api'; import type { AlarmServerity, RuleRuleResponse } from '@/api/generated'; import showDeleteConfirm from '@/components/customModal/showDeleteConfirm'; import { SERVERITY_MAP } from '@/constants'; +import { useSearchParams } from '@umijs/max'; import { useRequest } from 'ahooks'; import { Button, Card, Form, Space, Table, Tag, Typography } from 'antd'; import type { ColumnsType } from 'antd/es/table'; -import { useState } from 'react'; +import { useEffect, useState } from 'react'; import AlarmFilter from '../AlarmFilter'; import RuleDrawerForm from './RuleDrawerForm'; @@ -13,8 +14,13 @@ const { Text } = Typography; export default function Rules() { const [form] = Form.useForm(); - const { data: listRulesRes, refresh, run: getListRules } = useRequest(alert.listRules); - const [editRuleName,setEditRuleName] = useState() + const [searchParams, setSearchParams] = useSearchParams(); + const { + data: listRulesRes, + refresh, + run: getListRules, + } = useRequest(alert.listRules); + const [editRuleName, setEditRuleName] = useState(); const { run: deleteRule } = useRequest(alert.deleteRule, { onSuccess: ({ successful }) => { if (successful) { @@ -22,13 +28,20 @@ export default function Rules() { } }, }); - const [drawerOpen, setDrawerOpen] = useState(false); + const [drawerOpen, setDrawerOpen] = useState( + Boolean(searchParams.get('rule')), + ); const listRules = listRulesRes?.data || []; - const editRule = (ruleName:string)=>{ + const editRule = (ruleName: string) => { setEditRuleName(ruleName); setDrawerOpen(true); - } + }; + const drawerClose = () => { + setSearchParams(''); + setEditRuleName(undefined); + setDrawerOpen(false); + }; const columns: ColumnsType = [ { @@ -55,6 +68,9 @@ export default function Rules() { title: '告警等级', dataIndex: 'serverity', key: 'serverity', + sorter: (preRecord, curRecord) => + SERVERITY_MAP[curRecord.serverity].weight - + SERVERITY_MAP[preRecord.serverity].weight, render: (serverity: AlarmServerity) => ( {SERVERITY_MAP[serverity]?.label} @@ -65,6 +81,17 @@ export default function Rules() { title: '类型', dataIndex: 'type', key: 'type', + filters: [ + { + text: '自定义', + value: 'customized', + }, + { + text: '默认', + value: 'builtin', + }, + ], + onFilter: (value, record) => record.type === value, render: (type) => {type === 'builtin' ? '默认' : '自定义'}, }, { @@ -72,7 +99,11 @@ export default function Rules() { dataIndex: 'action', render: (_, record) => ( <> -