Commit 78961514bc066a53fa574e848e5a680c9f783d75
Merge branch 'ww' into 'main'
feat: add global alarm notify See merge request huang/yun-teng-iot-front!346
Showing
10 changed files
with
177 additions
and
10 deletions
| @@ -42,3 +42,5 @@ VITE_GLOB_CONFIGURATION = /thingskit-drawio | @@ -42,3 +42,5 @@ VITE_GLOB_CONFIGURATION = /thingskit-drawio | ||
| 42 | # Content Security Policy | 42 | # Content Security Policy |
| 43 | VITE_CONTENT_SECURITY_POLICY = false | 43 | VITE_CONTENT_SECURITY_POLICY = false |
| 44 | 44 | ||
| 45 | +# Alarm Notify Polling Interval Time | ||
| 46 | +VITE_ALARM_NOTIFY_POLLING_INTERVAL_TIME = 5000 |
| @@ -42,3 +42,6 @@ VITE_GLOB_CONFIGURATION = /thingskit-scada | @@ -42,3 +42,6 @@ VITE_GLOB_CONFIGURATION = /thingskit-scada | ||
| 42 | 42 | ||
| 43 | # Content Security Policy | 43 | # Content Security Policy |
| 44 | VITE_CONTENT_SECURITY_POLICY = false | 44 | VITE_CONTENT_SECURITY_POLICY = false |
| 45 | + | ||
| 46 | +# Alarm Notify Polling Interval Time | ||
| 47 | +VITE_ALARM_NOTIFY_POLLING_INTERVAL_TIME = 60000 |
| @@ -8,6 +8,7 @@ | @@ -8,6 +8,7 @@ | ||
| 8 | 8 | ||
| 9 | <script lang="ts" setup> | 9 | <script lang="ts" setup> |
| 10 | import { ConfigProvider } from 'ant-design-vue'; | 10 | import { ConfigProvider } from 'ant-design-vue'; |
| 11 | + import { useAlarmNotify } from './views/alarm/log/hook/useAlarmNotify'; | ||
| 11 | import { AppProvider } from '/@/components/Application'; | 12 | import { AppProvider } from '/@/components/Application'; |
| 12 | import { useTitle } from '/@/hooks/web/useTitle'; | 13 | import { useTitle } from '/@/hooks/web/useTitle'; |
| 13 | import { useLocale } from '/@/locales/useLocale'; | 14 | import { useLocale } from '/@/locales/useLocale'; |
| @@ -15,4 +16,5 @@ | @@ -15,4 +16,5 @@ | ||
| 15 | const { getAntdLocale } = useLocale(); | 16 | const { getAntdLocale } = useLocale(); |
| 16 | 17 | ||
| 17 | useTitle(); | 18 | useTitle(); |
| 18 | -</script> | ||
| 19 | + useAlarmNotify(); | ||
| 20 | +</script> |
| @@ -6,6 +6,8 @@ import { | @@ -6,6 +6,8 @@ import { | ||
| 6 | DeviceQueryParam, | 6 | DeviceQueryParam, |
| 7 | } from '/@/api/device/model/deviceModel'; | 7 | } from '/@/api/device/model/deviceModel'; |
| 8 | import { ChildDeviceParams } from './model/deviceModel'; | 8 | import { ChildDeviceParams } from './model/deviceModel'; |
| 9 | +import { PaginationResult } from '/#/axios'; | ||
| 10 | +import { AlarmLogItem } from './model/deviceConfigModel'; | ||
| 9 | enum DeviceManagerApi { | 11 | enum DeviceManagerApi { |
| 10 | /** | 12 | /** |
| 11 | * 设备URL | 13 | * 设备URL |
| @@ -94,7 +96,7 @@ export const getDeviceDetail = (id: string) => { | @@ -94,7 +96,7 @@ export const getDeviceDetail = (id: string) => { | ||
| 94 | 96 | ||
| 95 | // 查询设备详情中的告警 | 97 | // 查询设备详情中的告警 |
| 96 | export const getDeviceAlarm = (params?: DeviceProfileQueryParam) => { | 98 | export const getDeviceAlarm = (params?: DeviceProfileQueryParam) => { |
| 97 | - return defHttp.get({ | 99 | + return defHttp.get<PaginationResult<AlarmLogItem>>({ |
| 98 | url: DeviceManagerApi.DEVICE_ALARM_URL, | 100 | url: DeviceManagerApi.DEVICE_ALARM_URL, |
| 99 | params, | 101 | params, |
| 100 | }); | 102 | }); |
| @@ -165,3 +165,37 @@ export interface IDeviceConfigAddOrEditModel { | @@ -165,3 +165,37 @@ export interface IDeviceConfigAddOrEditModel { | ||
| 165 | updateTime?: '2021-12-15T02:17:26.645Z'; | 165 | updateTime?: '2021-12-15T02:17:26.645Z'; |
| 166 | updater?: string; | 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 | import { BasicPageParams } from '/@/api/model/baseModel'; | 1 | import { BasicPageParams } from '/@/api/model/baseModel'; |
| 2 | +import { AlarmStatus } from '/@/views/alarm/log/config/detail.config'; | ||
| 2 | export enum DeviceState { | 3 | export enum DeviceState { |
| 3 | INACTIVE = 'INACTIVE', | 4 | INACTIVE = 'INACTIVE', |
| 4 | ONLINE = 'ONLINE', | 5 | ONLINE = 'ONLINE', |
| @@ -17,6 +18,7 @@ export type DeviceParam = { | @@ -17,6 +18,7 @@ export type DeviceParam = { | ||
| 17 | }; | 18 | }; |
| 18 | export type DeviceProfileParam = { | 19 | export type DeviceProfileParam = { |
| 19 | name?: string; | 20 | name?: string; |
| 21 | + status?: AlarmStatus; | ||
| 20 | }; | 22 | }; |
| 21 | export type DeviceId = { | 23 | export type DeviceId = { |
| 22 | id?: string; | 24 | id?: string; |
| @@ -2,6 +2,21 @@ import { alarmLevel, statusType } from '/@/views/device/list/config/detail.confi | @@ -2,6 +2,21 @@ import { alarmLevel, statusType } from '/@/views/device/list/config/detail.confi | ||
| 2 | import { FormSchema } from '/@/components/Form'; | 2 | import { FormSchema } from '/@/components/Form'; |
| 3 | import { BasicColumn } from '/@/components/Table'; | 3 | import { BasicColumn } from '/@/components/Table'; |
| 4 | import moment from 'moment'; | 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 | export const alarmSearchSchemas: FormSchema[] = [ | 20 | export const alarmSearchSchemas: FormSchema[] = [ |
| 6 | { | 21 | { |
| 7 | field: 'status', | 22 | field: 'status', |
| @@ -11,20 +26,20 @@ export const alarmSearchSchemas: FormSchema[] = [ | @@ -11,20 +26,20 @@ export const alarmSearchSchemas: FormSchema[] = [ | ||
| 11 | componentProps: { | 26 | componentProps: { |
| 12 | options: [ | 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 | + const getAlarmLog = async () => { | ||
| 31 | + try { | ||
| 32 | + const { items = [] } = | ||
| 33 | + (await getDeviceAlarm({ status: alarmNotifyStatus, page: 1, pageSize: 10 })) || {}; | ||
| 34 | + | ||
| 35 | + if (items.length) { | ||
| 36 | + const first = items.at(0)!; | ||
| 37 | + const { deviceName, id, severity } = first; | ||
| 38 | + | ||
| 39 | + let key: Nullable<string> = `open-notify-${id}`; | ||
| 40 | + | ||
| 41 | + const severityMean = alarmLevel(severity); | ||
| 42 | + | ||
| 43 | + console.log('enter'); | ||
| 44 | + notification.open({ | ||
| 45 | + message: '设备告警', | ||
| 46 | + duration: null, | ||
| 47 | + key, | ||
| 48 | + description: h('div', {}, [ | ||
| 49 | + h('div', { style: { marginRight: '5px' } }, [ | ||
| 50 | + h('span', { style: { marginRight: '5px' } }, '设备:'), | ||
| 51 | + h('span', {}, `[${deviceName}]`), | ||
| 52 | + ]), | ||
| 53 | + h('div', { style: { marginTop: '5px' } }, [ | ||
| 54 | + h('span', { style: { marginRight: '5px' } }, '告警状态:'), | ||
| 55 | + h(Tag, { color }, () => `${alarmNotifyStatusMean}`), | ||
| 56 | + ]), | ||
| 57 | + h('div', { style: { marginTop: '5px' } }, [ | ||
| 58 | + h('span', { style: { marginRight: '5px' } }, '告警级别:'), | ||
| 59 | + h(Tag, { color: '#f50' }, () => `${severityMean}`), | ||
| 60 | + ]), | ||
| 61 | + ]), | ||
| 62 | + icon: h(ExclamationCircleOutlined, { style: { color: '#faa22d' } }), | ||
| 63 | + onClose: () => { | ||
| 64 | + console.log('enter close'); | ||
| 65 | + // currentNotifyId = null; | ||
| 66 | + key = null; | ||
| 67 | + }, | ||
| 68 | + btn: h( | ||
| 69 | + Button, | ||
| 70 | + { | ||
| 71 | + type: 'primary', | ||
| 72 | + size: 'small', | ||
| 73 | + onClick: async () => { | ||
| 74 | + await handleMarkRead(id); | ||
| 75 | + notification.close(key!); | ||
| 76 | + key = null; | ||
| 77 | + }, | ||
| 78 | + }, | ||
| 79 | + () => '标记已读' | ||
| 80 | + ), | ||
| 81 | + }); | ||
| 82 | + } | ||
| 83 | + } catch (error) {} | ||
| 84 | + }; | ||
| 85 | + | ||
| 86 | + const polling = () => { | ||
| 87 | + timeout = setInterval(() => { | ||
| 88 | + getAlarmLog(); | ||
| 89 | + }, interval); | ||
| 90 | + }; | ||
| 91 | + | ||
| 92 | + onMounted(() => { | ||
| 93 | + polling(); | ||
| 94 | + }); | ||
| 95 | + | ||
| 96 | + onUnmounted(() => { | ||
| 97 | + clearInterval(timeout as NodeJS.Timer); | ||
| 98 | + timeout = null; | ||
| 99 | + // currentNotifyId = null; | ||
| 100 | + }); | ||
| 101 | +} |
| @@ -42,3 +42,8 @@ export interface UploadFileParams { | @@ -42,3 +42,8 @@ export interface UploadFileParams { | ||
| 42 | filename?: string; | 42 | filename?: string; |
| 43 | [key: string]: any; | 43 | [key: string]: any; |
| 44 | } | 44 | } |
| 45 | + | ||
| 46 | +export interface PaginationResult<T = Recordable> { | ||
| 47 | + items: T[]; | ||
| 48 | + total: number; | ||
| 49 | +} |
| @@ -72,6 +72,7 @@ declare global { | @@ -72,6 +72,7 @@ declare global { | ||
| 72 | VITE_USE_IMAGEMIN: boolean; | 72 | VITE_USE_IMAGEMIN: boolean; |
| 73 | VITE_GENERATE_UI: string; | 73 | VITE_GENERATE_UI: string; |
| 74 | VITE_CONTENT_SECURITY_POLICY: boolean; | 74 | VITE_CONTENT_SECURITY_POLICY: boolean; |
| 75 | + VITE_ALARM_NOTIFY_POLLING_INTERVAL_TIME: number; | ||
| 75 | } | 76 | } |
| 76 | 77 | ||
| 77 | declare function parseInt(s: string | number, radix?: number): number; | 78 | declare function parseInt(s: string | number, radix?: number): number; |