Commit 78961514bc066a53fa574e848e5a680c9f783d75

Authored by xp.Huang
2 parents 400760d8 55550f58

Merge branch 'ww' into 'main'

feat: add global alarm notify

See merge request huang/yun-teng-iot-front!346
... ... @@ -42,3 +42,5 @@ VITE_GLOB_CONFIGURATION = /thingskit-drawio
42 42 # Content Security Policy
43 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 42
43 43 # Content Security Policy
44 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 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 },
... ...
  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 42 filename?: string;
43 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 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;
... ...