Showing
10 changed files
with
173 additions
and
10 deletions
... | ... | @@ -8,6 +8,7 @@ |
8 | 8 | |
9 | 9 | <script lang="ts" setup> |
10 | 10 | import { ConfigProvider } from 'ant-design-vue'; |
11 | + import { useAlarmNotify } from './views/alarm/log/hook/useAlarmNotify'; | |
11 | 12 | import { AppProvider } from '/@/components/Application'; |
12 | 13 | import { useTitle } from '/@/hooks/web/useTitle'; |
13 | 14 | import { useLocale } from '/@/locales/useLocale'; |
... | ... | @@ -15,4 +16,5 @@ |
15 | 16 | const { getAntdLocale } = useLocale(); |
16 | 17 | |
17 | 18 | useTitle(); |
18 | -</script> | |
\ No newline at end of file | ||
19 | + useAlarmNotify(); | |
20 | +</script> | ... | ... |
... | ... | @@ -6,6 +6,8 @@ import { |
6 | 6 | DeviceQueryParam, |
7 | 7 | } from '/@/api/device/model/deviceModel'; |
8 | 8 | import { ChildDeviceParams } from './model/deviceModel'; |
9 | +import { PaginationResult } from '/#/axios'; | |
10 | +import { AlarmLogItem } from './model/deviceConfigModel'; | |
9 | 11 | enum DeviceManagerApi { |
10 | 12 | /** |
11 | 13 | * 设备URL |
... | ... | @@ -94,7 +96,7 @@ export const getDeviceDetail = (id: string) => { |
94 | 96 | |
95 | 97 | // 查询设备详情中的告警 |
96 | 98 | export const getDeviceAlarm = (params?: DeviceProfileQueryParam) => { |
97 | - return defHttp.get({ | |
99 | + return defHttp.get<PaginationResult<AlarmLogItem>>({ | |
98 | 100 | url: DeviceManagerApi.DEVICE_ALARM_URL, |
99 | 101 | params, |
100 | 102 | }); | ... | ... |
... | ... | @@ -165,3 +165,37 @@ export interface IDeviceConfigAddOrEditModel { |
165 | 165 | updateTime?: '2021-12-15T02:17:26.645Z'; |
166 | 166 | updater?: string; |
167 | 167 | } |
168 | + | |
169 | +export interface Data { | |
170 | + CO: number; | |
171 | +} | |
172 | + | |
173 | +export interface Details { | |
174 | + data: Data; | |
175 | +} | |
176 | + | |
177 | +export interface AlarmLogItem { | |
178 | + id: string; | |
179 | + tenantId: string; | |
180 | + creator?: any; | |
181 | + updater?: any; | |
182 | + createdTime: string; | |
183 | + updatedTime: string; | |
184 | + customerId: string; | |
185 | + tbDeviceId: string; | |
186 | + originatorType: number; | |
187 | + deviceId: string; | |
188 | + deviceName: string; | |
189 | + type: string; | |
190 | + severity: string; | |
191 | + status: string; | |
192 | + startTs: string; | |
193 | + endTs: string; | |
194 | + ackTs: string; | |
195 | + clearTs: string; | |
196 | + details: Details; | |
197 | + propagate: boolean; | |
198 | + propagateRelationTypes?: any; | |
199 | + organizationId: string; | |
200 | + organizationName: string; | |
201 | +} | ... | ... |
1 | 1 | import { BasicPageParams } from '/@/api/model/baseModel'; |
2 | +import { AlarmStatus } from '/@/views/alarm/log/config/detail.config'; | |
2 | 3 | export enum DeviceState { |
3 | 4 | INACTIVE = 'INACTIVE', |
4 | 5 | ONLINE = 'ONLINE', |
... | ... | @@ -17,6 +18,7 @@ export type DeviceParam = { |
17 | 18 | }; |
18 | 19 | export type DeviceProfileParam = { |
19 | 20 | name?: string; |
21 | + status?: AlarmStatus; | |
20 | 22 | }; |
21 | 23 | export type DeviceId = { |
22 | 24 | id?: string; | ... | ... |
... | ... | @@ -2,6 +2,21 @@ import { alarmLevel, statusType } from '/@/views/device/list/config/detail.confi |
2 | 2 | import { FormSchema } from '/@/components/Form'; |
3 | 3 | import { BasicColumn } from '/@/components/Table'; |
4 | 4 | import moment from 'moment'; |
5 | + | |
6 | +export enum AlarmStatus { | |
7 | + CLEARED_UN_ACK = 'CLEARED_UNACK', | |
8 | + ACTIVE_UN_ACK = 'ACTIVE_UNACK', | |
9 | + CLEARED_ACK = 'CLEARED_ACK', | |
10 | + ACTIVE_ACK = 'ACTIVE_ACK', | |
11 | +} | |
12 | + | |
13 | +export enum AlarmStatusMean { | |
14 | + CLEARED_UNACK = '清楚未确认', | |
15 | + ACTIVE_UNACK = '激活未确认', | |
16 | + CLEARED_ACK = '清除已确认', | |
17 | + ACTIVE_ACK = '激活已确认', | |
18 | +} | |
19 | + | |
5 | 20 | export const alarmSearchSchemas: FormSchema[] = [ |
6 | 21 | { |
7 | 22 | field: 'status', |
... | ... | @@ -11,20 +26,20 @@ export const alarmSearchSchemas: FormSchema[] = [ |
11 | 26 | componentProps: { |
12 | 27 | options: [ |
13 | 28 | { |
14 | - label: '清除未确认', | |
15 | - value: 'CLEARED_UNACK', | |
29 | + label: AlarmStatusMean[AlarmStatus.CLEARED_UN_ACK], | |
30 | + value: AlarmStatus.CLEARED_UN_ACK, | |
16 | 31 | }, |
17 | 32 | { |
18 | - label: '激活未确认', | |
19 | - value: 'ACTIVE_UNACK', | |
33 | + label: AlarmStatusMean[AlarmStatus.ACTIVE_UN_ACK], | |
34 | + value: AlarmStatus.ACTIVE_UN_ACK, | |
20 | 35 | }, |
21 | 36 | { |
22 | - label: '清除已确认', | |
23 | - value: 'CLEARED_ACK', | |
37 | + label: AlarmStatusMean[AlarmStatus.CLEARED_ACK], | |
38 | + value: AlarmStatus.CLEARED_ACK, | |
24 | 39 | }, |
25 | 40 | { |
26 | - label: '激活已确认', | |
27 | - value: 'ACTIVE_ACK', | |
41 | + label: AlarmStatusMean[AlarmStatus.ACTIVE_ACK], | |
42 | + value: AlarmStatus.ACTIVE_ACK, | |
28 | 43 | }, |
29 | 44 | ], |
30 | 45 | }, | ... | ... |
src/views/alarm/log/hook/useAlarmNotify.ts
0 → 100644
1 | +import { AlarmStatus, AlarmStatusMean } from '../config/detail.config'; | |
2 | +import { clearOrAckAlarm, getDeviceAlarm } from '/@/api/device/deviceManager'; | |
3 | +import { notification, Button, Tag } from 'ant-design-vue'; | |
4 | +import { h, onMounted, onUnmounted } from 'vue'; | |
5 | +import { ExclamationCircleOutlined } from '@ant-design/icons-vue'; | |
6 | +import { alarmLevel } from '/@/views/device/list/config/detail.config'; | |
7 | + | |
8 | +interface UseAlarmNotifyParams { | |
9 | + alarmNotifyStatus?: AlarmStatus; | |
10 | + interval?: number; | |
11 | + color?: string; | |
12 | +} | |
13 | + | |
14 | +export function useAlarmNotify(params: UseAlarmNotifyParams = {}) { | |
15 | + const { | |
16 | + alarmNotifyStatus = AlarmStatus.ACTIVE_UN_ACK, | |
17 | + interval = import.meta.env.VITE_ALARM_NOTIFY_POLLING_INTERVAL_TIME, | |
18 | + color = 'orange', | |
19 | + } = params; | |
20 | + const alarmNotifyStatusMean = AlarmStatusMean[alarmNotifyStatus]; | |
21 | + | |
22 | + const handleMarkRead = async (id: string) => { | |
23 | + try { | |
24 | + await clearOrAckAlarm(id, false); | |
25 | + } catch (error) {} | |
26 | + }; | |
27 | + | |
28 | + let timeout: Nullable<NodeJS.Timer> = null; | |
29 | + | |
30 | + let currentNotifyId: Nullable<string> = null; | |
31 | + | |
32 | + const getAlarmLog = async () => { | |
33 | + try { | |
34 | + const { items = [] } = | |
35 | + (await getDeviceAlarm({ status: alarmNotifyStatus, page: 1, pageSize: 10 })) || {}; | |
36 | + | |
37 | + if (items.length) { | |
38 | + const first = items.at(0)!; | |
39 | + const { deviceName, id, severity } = first; | |
40 | + if (currentNotifyId === id) return; | |
41 | + currentNotifyId = id; | |
42 | + | |
43 | + const key = `open-notify-${Date.now()}`; | |
44 | + | |
45 | + const severityMean = alarmLevel(severity); | |
46 | + notification.open({ | |
47 | + message: '设备告警', | |
48 | + duration: null, | |
49 | + key, | |
50 | + description: h('div', {}, [ | |
51 | + h('div', { style: { marginRight: '5px' } }, [ | |
52 | + h('span', { style: { marginRight: '5px' } }, '设备:'), | |
53 | + h('span', {}, `[${deviceName}]`), | |
54 | + ]), | |
55 | + h('div', { style: { marginTop: '5px' } }, [ | |
56 | + h('span', { style: { marginRight: '5px' } }, '告警状态:'), | |
57 | + h(Tag, { color }, () => `${alarmNotifyStatusMean}`), | |
58 | + ]), | |
59 | + h('div', { style: { marginTop: '5px' } }, [ | |
60 | + h('span', { style: { marginRight: '5px' } }, '告警级别:'), | |
61 | + h(Tag, { color: '#f50' }, () => `${severityMean}`), | |
62 | + ]), | |
63 | + ]), | |
64 | + icon: h(ExclamationCircleOutlined, { style: { color: '#faa22d' } }), | |
65 | + onClose: () => (currentNotifyId = null), | |
66 | + btn: h( | |
67 | + Button, | |
68 | + { | |
69 | + type: 'primary', | |
70 | + size: 'small', | |
71 | + onClick: async () => { | |
72 | + await handleMarkRead(id); | |
73 | + notification.close(key); | |
74 | + }, | |
75 | + }, | |
76 | + () => '标记已读' | |
77 | + ), | |
78 | + }); | |
79 | + } | |
80 | + } catch (error) {} | |
81 | + }; | |
82 | + | |
83 | + const polling = () => { | |
84 | + timeout = setInterval(() => { | |
85 | + getAlarmLog(); | |
86 | + }, interval); | |
87 | + }; | |
88 | + | |
89 | + onMounted(() => { | |
90 | + polling(); | |
91 | + }); | |
92 | + | |
93 | + onUnmounted(() => { | |
94 | + clearInterval(timeout as NodeJS.Timer); | |
95 | + timeout = null; | |
96 | + }); | |
97 | +} | ... | ... |
... | ... | @@ -72,6 +72,7 @@ declare global { |
72 | 72 | VITE_USE_IMAGEMIN: boolean; |
73 | 73 | VITE_GENERATE_UI: string; |
74 | 74 | VITE_CONTENT_SECURITY_POLICY: boolean; |
75 | + VITE_ALARM_NOTIFY_POLLING_INTERVAL_TIME: number; | |
75 | 76 | } |
76 | 77 | |
77 | 78 | declare function parseInt(s: string | number, radix?: number): number; | ... | ... |