Commit eeb8e4d1db99b0dec1563a0bd01e085cd4289b8e

Authored by ww
1 parent 400760d8

feat: add global alarm notify

@@ -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 },
  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 +}
@@ -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;