Explorar el Código

fix: 触发告警和解除告警

100011797 hace 3 años
padre
commit
a4eb5aa7d0

BIN
public/images/scene/action-alarm-icon.png


+ 52 - 10
src/pages/rule-engine/Scene/Save/action/ListItem/Item.tsx

@@ -3,7 +3,8 @@ import Modal from '../Modal/add';
 import type { ActionsType } from '@/pages/rule-engine/Scene/typings';
 import { DeleteOutlined } from '@ant-design/icons';
 import { FormModel } from '@/pages/rule-engine/Scene/Save';
-
+import './index.less';
+import TriggerAlarm from '../TriggerAlarm';
 export enum ParallelEnum {
   'parallel' = 'parallel',
   'serial' = 'serial',
@@ -16,22 +17,52 @@ interface ItemProps {
   type: ParallelType;
 }
 
+const iconMap = new Map();
+iconMap.set('alarm', require('/public/images/scene/action-alarm-icon.png'));
+
 export default (props: ItemProps) => {
   const [visible, setVisible] = useState<boolean>(false);
+  const [triggerVisible, setTriggerVisible] = useState<boolean>(false);
+
+  const contentRender = () => {
+    if (props?.data?.alarm?.mode === 'trigger') {
+      return (
+        <div>
+          满足条件后将触发关联
+          <a
+            onClick={() => {
+              setTriggerVisible(true);
+            }}
+          >
+            关联此场景的告警
+          </a>
+        </div>
+      );
+    } else if (props?.data?.alarm?.mode === 'relieve') {
+      return (
+        <div>
+          满足条件后将解除关联
+          <a
+            onClick={() => {
+              setTriggerVisible(true);
+            }}
+          >
+            关联此场景的告警
+          </a>
+        </div>
+      );
+    }
+    return '';
+  };
+
   return (
     <div className="actions-item-warp">
       <div className="actions-item">
         <div className="item-options-warp">
-          <div className="type">
-            <img src="" />
-          </div>
-          <div
-            onClick={() => {
-              setVisible(true);
-            }}
-          >
-            {'item'}
+          <div className="item-options-type">
+            <img style={{ width: 48 }} src={iconMap.get(props?.data.executor)} />
           </div>
+          <div className={'item-options-content'}>{contentRender()}</div>
         </div>
         <div className="item-number">{props.name + 1}</div>
         <div
@@ -60,6 +91,17 @@ export default (props: ItemProps) => {
           close={() => {
             setVisible(false);
           }}
+          save={(data: ActionsType) => {
+            console.log(data);
+            setVisible(false);
+          }}
+        />
+      )}
+      {triggerVisible && (
+        <TriggerAlarm
+          close={() => {
+            setTriggerVisible(false);
+          }}
         />
       )}
     </div>

+ 27 - 18
src/pages/rule-engine/Scene/Save/action/ListItem/List.tsx

@@ -1,10 +1,11 @@
-import { useState } from 'react';
+import { useEffect, useState } from 'react';
 import { AddButton } from '@/pages/rule-engine/Scene/Save/components/Buttons';
 import Modal from '../Modal/add';
 import './index.less';
 import type { ActionsType } from '@/pages/rule-engine/Scene/typings';
 import Item from './Item';
 import type { ParallelType } from './Item';
+import { FormModel } from '../..';
 interface ListProps {
   type: ParallelType;
   actions: ActionsType[];
@@ -12,30 +13,20 @@ interface ListProps {
 
 export default (props: ListProps) => {
   const [visible, setVisible] = useState<boolean>(false);
+  const [actions, setActions] = useState<ActionsType[]>(props.actions);
+
+  useEffect(() => {
+    setActions(props.actions);
+  }, [props.actions]);
 
   return (
     <div className="action-list-content">
-      {props.actions.map((item, index) => (
+      {actions.map((item, index) => (
         <Item name={index} data={item} type={props.type} key={item.key} />
       ))}
       <AddButton
         onClick={() => {
           setVisible(true);
-          // const addItem: ActionsType = {
-          //   executor: 'device',
-          //   device: {
-          //     selector: 'all',
-          //     source: 'fixed'
-          //   },
-          //   key: `${props.type}_${props.actions.length}`
-          // }
-
-          // if (props.type === 'serial') {
-          //   addItem.terms = []
-          // }
-          // console.log(addItem);
-
-          // FormModel?.actions?.push(addItem)
         }}
       >
         点击配置执行动作
@@ -43,10 +34,28 @@ export default (props: ListProps) => {
       {visible && (
         <Modal
           name={props.actions.length + 1}
-          data={{}}
+          data={{
+            key: `${props.type}_${props.actions.length}`,
+          }}
           close={() => {
             setVisible(false);
           }}
+          save={(data: any) => {
+            const { type, ...extra } = data;
+            const item: ActionsType = {
+              ...extra,
+              executor: data.type === 'trigger' || data.type === 'relieve' ? 'alarm' : data.type,
+              key: data.key,
+              alarm: {
+                mode: data.type,
+              },
+            };
+            const index = FormModel?.actions.findIndex((i) => {
+              return i.key === item.key ? item : i;
+            });
+            FormModel.actions[index] = { ...item };
+            setVisible(false);
+          }}
         />
       )}
     </div>

+ 21 - 1
src/pages/rule-engine/Scene/Save/action/ListItem/index.less

@@ -11,7 +11,6 @@
     }
   }
 }
-
 .actions-item {
   position: relative;
   margin-bottom: 24px;
@@ -19,6 +18,27 @@
   border: 1px dashed #999;
   border-radius: 2px;
 
+  .item-options-warp {
+    display: flex;
+    height: 48px;
+
+    .item-options-type {
+      display: flex;
+      align-items: center;
+      justify-content: center;
+      width: 48px;
+      margin-right: 8px;
+      background-color: #fafafa;
+    }
+
+    .item-options-content {
+      display: flex;
+      align-items: center;
+      padding: 0 8px;
+      background-color: #fafafa;
+    }
+  }
+
   .item-number {
     position: absolute;
     top: 0;

+ 5 - 3
src/pages/rule-engine/Scene/Save/action/Modal/add.tsx

@@ -3,13 +3,15 @@ import ActionsTypeComponent from '@/pages/rule-engine/Scene/Save/components/Trig
 import { useEffect, useState } from 'react';
 import Notify from '../notify';
 import type { ActionsType } from '@/pages/rule-engine/Scene/typings';
+import Device from '../DeviceOutput';
 
 interface Props {
   close: () => void;
+  save: (data: any) => void;
   data: Partial<ActionsType>;
   name: number;
 }
-import Device from '../DeviceOutput';
+
 export default (props: Props) => {
   const [form] = Form.useForm();
   const [actionType, setActionType] = useState<string>('');
@@ -31,9 +33,8 @@ export default (props: Props) => {
           <Notify
             value={props.data?.notify || {}}
             save={(data: any) => {
-              console.log(data); // value
               setActionType('');
-              props.close();
+              props.save(data);
             }}
             name={props.name}
             cancel={() => {
@@ -57,6 +58,7 @@ export default (props: Props) => {
       onOk={async () => {
         const values = await form.validateFields();
         setActionType(values.type);
+        props.save({ ...props.data, type: values.type });
       }}
     >
       <Form form={form} layout={'vertical'}>

+ 176 - 0
src/pages/rule-engine/Scene/Save/action/TriggerAlarm/index.tsx

@@ -0,0 +1,176 @@
+import { Badge, Modal, Tooltip } from 'antd';
+import ProTable, { ActionType, ProColumns } from '@jetlinks/pro-table';
+import { useEffect, useRef, useState } from 'react';
+import { Store } from 'jetlinks-store';
+import { useIntl } from '@@/plugin-locale/localeExports';
+import { queryDefaultLevel, queryAlarmList, queryAlarmCount } from '../service';
+import encodeQuery from '@/utils/encodeQuery';
+import { FormModel } from '@/pages/rule-engine/Scene/Save';
+
+interface Props {
+  close: () => void;
+}
+
+export default (props: Props) => {
+  const actionRef = useRef<ActionType>();
+  const intl = useIntl();
+  const [count, setCount] = useState<number>(0);
+
+  useEffect(() => {
+    queryAlarmCount(
+      encodeQuery({
+        terms: {
+          sceneId: FormModel.id,
+        },
+      }),
+    ).then((resp) => {
+      if (resp.status === 200) {
+        setCount(resp.result);
+      }
+    });
+  }, []);
+
+  const columns: ProColumns<ConfigurationItem>[] = [
+    {
+      dataIndex: 'name',
+      title: '名称',
+      ellipsis: true,
+      fixed: 'left',
+    },
+    {
+      title: '类型',
+      dataIndex: 'targetType',
+      renderText: (text: string) => {
+        const map = {
+          product: '产品',
+          device: '设备',
+          org: '组织',
+          other: '其他',
+        };
+        return map[text];
+      },
+      valueType: 'select',
+      valueEnum: {
+        product: {
+          text: '产品',
+          status: 'product',
+        },
+        device: {
+          text: '设备',
+          status: 'device',
+        },
+        org: {
+          text: '组织',
+          status: 'org',
+        },
+        other: {
+          text: '其他',
+          status: 'other',
+        },
+      },
+    },
+    {
+      title: '告警级别',
+      dataIndex: 'level',
+      render: (text: any) => (
+        <Tooltip
+          placement="topLeft"
+          title={
+            (Store.get('default-level') || []).find((item: any) => item?.level === text)?.title ||
+            text
+          }
+        >
+          <div className="ellipsis">
+            {(Store.get('default-level') || []).find((item: any) => item?.level === text)?.title ||
+              text}
+          </div>
+        </Tooltip>
+      ),
+      valueType: 'select',
+      request: async () => {
+        const res = await queryDefaultLevel();
+        if (res.status === 200) {
+          return (res?.result?.levels || [])
+            .filter((i: any) => i?.level && i?.title)
+            .map((item: any) => ({
+              label: item.title,
+              value: item.level,
+            }));
+        }
+        return [];
+      },
+    },
+    {
+      title: '状态',
+      dataIndex: 'state',
+      valueType: 'select',
+      renderText: (state) => (
+        <Badge text={state?.text} status={state?.value === 'disabled' ? 'error' : 'success'} />
+      ),
+      valueEnum: {
+        disabled: {
+          text: intl.formatMessage({
+            id: 'pages.device.product.status.disabled',
+            defaultMessage: '禁用',
+          }),
+          status: 'disabled',
+        },
+        enabled: {
+          text: intl.formatMessage({
+            id: 'pages.device.product.status.enabled',
+            defaultMessage: '正常',
+          }),
+          status: 'enabled',
+        },
+      },
+    },
+    {
+      title: '说明',
+      dataIndex: 'description',
+      ellipsis: true,
+    },
+  ];
+
+  return (
+    <Modal
+      title={'关联此场景的告警'}
+      open
+      width={1000}
+      onCancel={() => {
+        props.close();
+      }}
+      onOk={() => {
+        props.close();
+      }}
+    >
+      <div>关联告警数量:{count}</div>
+      <ProTable<ConfigurationItem>
+        actionRef={actionRef}
+        params={{}}
+        columns={columns}
+        search={false}
+        rowKey={'id'}
+        columnEmptyText={''}
+        tableAlertRender={false}
+        request={(params) =>
+          queryAlarmList({
+            ...params,
+            terms: [
+              ...(params?.terms || []),
+              {
+                terms: [
+                  {
+                    column: 'sceneId',
+                    value: FormModel.id,
+                  },
+                ],
+                type: 'and',
+              },
+            ],
+            sorts: [{ name: 'createTime', order: 'desc' }],
+          })
+        }
+      />
+    </Modal>
+  );
+};

+ 3 - 0
src/pages/rule-engine/Scene/Save/action/notify/NotifyConfig.tsx

@@ -49,6 +49,7 @@ export default observer(() => {
           columns={columns}
           rowKey="id"
           search={false}
+          onlyCard={true}
           gridColumn={2}
           columnEmptyText={''}
           cardRender={(record) => (
@@ -59,7 +60,9 @@ export default observer(() => {
               {...record}
             />
           )}
+          tableAlertRender={false}
           rowSelection={{
+            type: 'radio',
             selectedRowKeys: [NotifyModel.notify?.notifierId || ''],
             onChange: (selectedRowKeys) => {
               if (selectedRowKeys.length) {

+ 3 - 0
src/pages/rule-engine/Scene/Save/action/notify/NotifyTemplate.tsx

@@ -48,6 +48,7 @@ export default observer(() => {
           actionRef={actionRef}
           columns={columns}
           rowKey="id"
+          onlyCard={true}
           search={false}
           gridColumn={2}
           columnEmptyText={''}
@@ -59,7 +60,9 @@ export default observer(() => {
               {...record}
             />
           )}
+          tableAlertRender={false}
           rowSelection={{
+            type: 'radio',
             selectedRowKeys: [NotifyModel.notify?.templateId || ''],
             onChange: (selectedRowKeys) => {
               if (selectedRowKeys.length) {

+ 13 - 0
src/pages/rule-engine/Scene/Save/action/service.ts

@@ -97,3 +97,16 @@ export const getRelations = () =>
       terms: [{ termType: 'eq', column: 'objectTypeName', value: '设备' }],
     },
   });
+
+export const queryDefaultLevel = () =>
+  request(`/${SystemConst.API_BASE}/alarm/config/default/level`, {
+    method: 'GET',
+  });
+
+export const queryAlarmList = (data: any) => {
+  return request(`/${SystemConst.API_BASE}/alarm/config/_query/`, { data, method: 'POST' });
+};
+
+export const queryAlarmCount = (data: any) => {
+  return request(`/${SystemConst.API_BASE}/alarm/config/_count`, { params: data, method: 'GET' });
+};

+ 4 - 0
src/pages/rule-engine/Scene/typings.d.ts

@@ -256,6 +256,10 @@ export interface ActionsType {
   terms?: TermsType[];
   /** map中的key,用于删除 */
   key?: string;
+  /**
+   * 拓展信息,用于前端存储一些渲染数据
+   */
+  options?: any;
 }
 
 export interface FormModelType {