xieyonghong hace 4 años
padre
commit
eedd2b868e
Se han modificado 28 ficheros con 495 adiciones y 175 borrados
  1. 39 0
      src/components/ProTableCard/CardItems/AlarmConfig.tsx
  2. 102 75
      src/components/SearchComponent/index.tsx
  3. 29 26
      src/pages/device/Instance/Detail/Diagnose/Status/index.tsx
  4. 2 0
      src/pages/device/Instance/Detail/Diagnose/Status/model.ts
  5. 1 1
      src/pages/device/Instance/Detail/Reation/Edit.tsx
  6. 1 0
      src/pages/device/Instance/Detail/Running/Property/Indicators.tsx
  7. 10 5
      src/pages/device/components/Metadata/Base/Edit/index.tsx
  8. 6 4
      src/pages/link/AccessConfig/Detail/Access/index.tsx
  9. 4 0
      src/pages/link/Protocol/save/index.tsx
  10. 2 0
      src/pages/notice/Config/Detail/doc/index.less
  11. 17 15
      src/pages/notice/Config/SyncUser/index.tsx
  12. 2 0
      src/pages/notice/Template/Detail/doc/index.less
  13. 25 17
      src/pages/notice/Template/Detail/index.tsx
  14. 1 1
      src/pages/rule-engine/Alarm/Configuration/Save/index.less
  15. 22 7
      src/pages/rule-engine/Alarm/Configuration/Save/index.tsx
  16. 193 10
      src/pages/rule-engine/Alarm/Configuration/index.tsx
  17. 24 0
      src/pages/rule-engine/Alarm/Configuration/service.ts
  18. 4 2
      src/pages/rule-engine/Alarm/Configuration/typings.d.ts
  19. 1 1
      src/pages/rule-engine/Scene/Save/action/action.tsx
  20. 1 2
      src/pages/rule-engine/Scene/Save/action/device/deviceModal.tsx
  21. 1 1
      src/pages/rule-engine/Scene/Save/action/device/functionCall.tsx
  22. 1 1
      src/pages/rule-engine/Scene/Save/action/device/index.tsx
  23. 1 1
      src/pages/rule-engine/Scene/Save/action/device/tagModal.tsx
  24. 2 2
      src/pages/rule-engine/Scene/Save/action/messageContent.tsx
  25. 1 1
      src/pages/rule-engine/Scene/Save/components/DatePickerFormat/index.tsx
  26. 1 1
      src/pages/rule-engine/Scene/Save/components/InputUpload/index.tsx
  27. 1 1
      src/pages/rule-engine/Scene/Save/trigger/index.tsx
  28. 1 1
      src/pages/system/Relationship/Save/index.tsx

+ 39 - 0
src/components/ProTableCard/CardItems/AlarmConfig.tsx

@@ -0,0 +1,39 @@
+import React from 'react';
+import { TableCard } from '@/components';
+import '@/style/common.less';
+import '../index.less';
+
+export interface AlarmConfigProps extends TemplateItem {
+  detail?: React.ReactNode;
+  actions?: React.ReactNode[];
+  avatarSize?: number;
+}
+
+export const aliyunSms = require('/public/images/notice/sms.png');
+
+export default (props: AlarmConfigProps) => {
+  return (
+    <TableCard actions={props.actions} showStatus={false} detail={props.detail} showMask={false}>
+      <div className={'pro-table-card-item'}>
+        <div className={'card-item-avatar'}>
+          <img width={88} height={88} src={aliyunSms} alt={props.type} />
+        </div>
+        <div className={'card-item-body'}>
+          <div className={'card-item-header'}>
+            <span className={'card-item-header-name ellipsis'}>{props.name}</span>
+          </div>
+          <div className={'card-item-content'}>
+            <div>
+              <label>告警名称</label>
+              <div className={'ellipsis'}>{props.name}</div>
+            </div>
+            <div>
+              <label>说明</label>
+              <div className={'ellipsis'}>{props.description}</div>
+            </div>
+          </div>
+        </div>
+      </div>
+    </TableCard>
+  );
+};

+ 102 - 75
src/components/SearchComponent/index.tsx

@@ -100,6 +100,10 @@ const SchemaField = createSchemaField({
   },
 });
 
+/**
+ * 搜索字段排序
+ * @param field
+ */
 const sortField = (field: ProColumns[]) => {
   let _temp = false;
   field.forEach((item) => {
@@ -124,11 +128,21 @@ const sortField = (field: ProColumns[]) => {
   return _.sortBy(field, (i) => i.index);
 };
 
+// 保存历史记录
+// 过滤不参与搜索的列数据 ==> 字段排序
+
+// 场景一:简单模式
+// 默认搜索参数,根据Index 来判断,或者默认name为第一组条件
+
+// 场景二:高级模式
+// 默认六组搜索条件。根据字段index排序
+
 const SearchComponent = <T extends Record<string, any>>(props: Props<T>) => {
   const { field, target, onSearch, defaultParam, enableSave = true, initParam } = props;
 
   /**
-   * 过滤不参与搜索的数据
+   * 过滤不参与搜索的数据 ?
+   * TODO Refactor 依赖透明?
    */
   const filterSearchTerm = (): ProColumns<T>[] =>
     field
@@ -136,6 +150,17 @@ const SearchComponent = <T extends Record<string, any>>(props: Props<T>) => {
       .filter((item) => !item.hideInSearch)
       .filter((item) => !['index', 'option'].includes(item.dataIndex as string));
 
+  /**
+   * 根据dataIndex 过滤不参与查询的参数
+   *
+   * @param _field 查询的列
+   * @param excludes 过滤的字段名称
+   */
+  // const filterSearchTerm2 = (_field: ProColumns<T>[] = [], excludes: string[] = []) =>
+  //   _field.filter(item => item.dataIndex)
+  //     .filter(item => !item.hideInSearch)
+  //     .filter(item => !excludes.includes(item.dataIndex as string))
+
   // 处理后的搜索条件
   const processedField = sortField(filterSearchTerm());
   const defaultTerms = (index: number) =>
@@ -144,102 +169,104 @@ const SearchComponent = <T extends Record<string, any>>(props: Props<T>) => {
       column: (processedField[index]?.dataIndex as string) || null,
       type: 'or',
     } as Partial<Term>);
+
   const intl = useIntl();
   const [expand, setExpand] = useState<boolean>(true);
   const initForm = server2Ui(initParam || [{ terms: [defaultTerms(0)] }]);
   const [aliasVisible, setAliasVisible] = useState<boolean>(false);
-
   const [initParams, setInitParams] = useState<SearchTermsUI>(initForm);
   const [history, setHistory] = useState([]);
   const [logVisible, setLogVisible] = useState<boolean>(false);
+  const uiParamRef = useRef(initParam);
+
   const form = useMemo(
     () =>
       createForm<SearchTermsUI>({
         validateFirst: true,
         initialValues: initParams,
         effects() {
-          // onFormInit((form1) => {
-          //   if (expand && !initParam) {
-          //     form1.setValues({
-          //       terms1: [{column: processedField[0]?.dataIndex, termType: 'like'}],
-          //     });
-          //   }
-          // });
           onFieldReact('*.*.column', async (typeFiled, f) => {
             const _column = (typeFiled as Field).value;
+
             const _field = field.find((item) => item.dataIndex === _column);
-            if (_field?.valueType === 'select') {
-              let option: { label: any; value: any }[] | FieldDataSource | undefined = [];
-              if (_field?.valueEnum) {
-                option = Object.values(_field?.valueEnum || {}).map((item) => ({
-                  label: item.text,
-                  value: item.status,
-                }));
-              } else if (_field?.request) {
-                option = await _field.request();
-              }
-              f.setFieldState(typeFiled.query('.termType'), async (state) => {
-                state.value = 'eq';
-              });
-              f.setFieldState(typeFiled.query('.value'), async (state) => {
-                state.componentType = 'Select';
-                // state.loading = true;
-                state.dataSource = option;
-                // state.loading = false;
-              });
-            } else if (_field?.valueType === 'treeSelect') {
-              let option: { label: any; value: any }[] | FieldDataSource | undefined = [];
-              if (_field?.valueEnum) {
-                option = Object.values(_field?.valueEnum || {}).map((item) => ({
-                  label: item.text,
-                  value: item.status,
-                }));
-              } else if (_field?.request) {
-                option = await _field.request();
-              }
-              f.setFieldState(typeFiled.query('.termType'), (_state) => {
-                _state.value = 'eq';
-              });
-              f.setFieldState(typeFiled.query('.value'), (state) => {
-                state.componentType = 'TreeSelect';
-                state.dataSource = option;
-                console.log(option, 'optin');
-                state.componentProps = {
-                  ..._field.fieldProps,
-                  treeNodeFilterProp: 'name',
-                  // filterOption: (input: string, option: any) =>
-                  //   option.label.toLowerCase().indexOf(input.toLowerCase()) >= 0,
-                };
-              });
-            } else if (_field?.valueType === 'digit') {
-              f.setFieldState(typeFiled.query('.value'), async (state) => {
-                state.componentType = 'NumberPicker';
-              });
-              f.setFieldState(typeFiled.query('.termType'), async (state) => {
-                state.value = 'eq';
-              });
-            } else if (_field?.valueType === 'dateTime') {
-              f.setFieldState(typeFiled.query('.value'), async (state) => {
-                state.componentType = 'DatePicker';
-                state.componentProps = { showTime: true };
-              });
-              f.setFieldState(typeFiled.query('.termType'), async (state) => {
-                state.value = 'gte';
-              });
-            } else {
-              f.setFieldState(typeFiled.query('.value'), async (state) => {
-                state.componentType = 'Input';
-              });
-            }
             if (_column === 'id') {
               f.setFieldState(typeFiled.query('.termType'), async (state) => {
                 state.value = 'eq';
               });
+            } else {
+              switch (_field?.valueType) {
+                case 'select':
+                  let __option: { label: any; value: any }[] | FieldDataSource | undefined = [];
+                  if (_field?.valueEnum) {
+                    __option = Object.values(_field?.valueEnum || {}).map((item) => ({
+                      label: item.text,
+                      value: item.status,
+                    }));
+                  } else if (_field?.request) {
+                    __option = await _field.request();
+                  }
+                  f.setFieldState(typeFiled.query('.termType'), async (state) => {
+                    state.value = 'eq';
+                  });
+                  f.setFieldState(typeFiled.query('.value'), async (state) => {
+                    console.log(_field, 'productName');
+
+                    state.componentType = 'Select';
+                    // state.loading = true;
+                    state.dataSource = __option;
+                    // state.loading = false;
+                  });
+                  break;
+                case 'treeSelect':
+                  let _option: { label: any; value: any }[] | FieldDataSource | undefined = [];
+                  if (_field?.valueEnum) {
+                    _option = Object.values(_field?.valueEnum || {}).map((item) => ({
+                      label: item.text,
+                      value: item.status,
+                    }));
+                  } else if (_field?.request) {
+                    _option = await _field.request();
+                  }
+                  f.setFieldState(typeFiled.query('.termType'), (_state) => {
+                    _state.value = 'eq';
+                  });
+                  f.setFieldState(typeFiled.query('.value'), (state) => {
+                    state.componentType = 'TreeSelect';
+                    state.dataSource = _option;
+                    state.componentProps = {
+                      ..._field.fieldProps,
+                      treeNodeFilterProp: 'name',
+                    };
+                  });
+                  break;
+                case 'digit':
+                  f.setFieldState(typeFiled.query('.value'), async (state) => {
+                    state.componentType = 'NumberPicker';
+                  });
+                  f.setFieldState(typeFiled.query('.termType'), async (state) => {
+                    state.value = 'eq';
+                  });
+                  break;
+                case 'dateTime':
+                  f.setFieldState(typeFiled.query('.value'), async (state) => {
+                    state.componentType = 'DatePicker';
+                    state.componentProps = { showTime: true };
+                  });
+                  f.setFieldState(typeFiled.query('.termType'), async (state) => {
+                    state.value = 'gte';
+                  });
+                  break;
+                default:
+                  f.setFieldState(typeFiled.query('.value'), async (state) => {
+                    state.componentType = 'Input';
+                  });
+                  break;
+              }
             }
           });
           onFieldValueChange('*.*.column', (field1, form1) => {
             form1.setFieldState(field1.query('.value'), (state1) => {
-              state1.value = null;
+              state1.value = undefined;
             });
           });
         },
@@ -355,8 +382,6 @@ const SearchComponent = <T extends Record<string, any>>(props: Props<T>) => {
     },
   };
 
-  const uiParamRef = useRef(initParam);
-
   const handleForm = (_expand?: boolean) => {
     const value = form.values;
     const __expand = _expand || expand;
@@ -398,12 +423,14 @@ const SearchComponent = <T extends Record<string, any>>(props: Props<T>) => {
       handleExpand();
     }
   }, [initParam]);
+
   const simpleSchema: ISchema = {
     type: 'object',
     properties: {
       terms1: createGroup('第一组'),
     },
   };
+
   const handleHistory = (item: SearchHistory) => {
     const log = JSON.parse(item.content) as SearchTermsUI;
     setLogVisible(false);

+ 29 - 26
src/pages/device/Instance/Detail/Diagnose/Status/index.tsx

@@ -84,7 +84,6 @@ const Status = observer((props: Props) => {
   const [productTemp, setProductTemp] = useState<any[]>([]);
   const [deviceTemp, setDeviceTemp] = useState<any[]>([]);
   const [gatewayTemp, setGatewayTemp] = useState<any>({});
-  const [productItem, setProductItem] = useState<any>({});
 
   const getDetail = (id: string) => {
     service.detail(id).then((response) => {
@@ -162,7 +161,6 @@ const Status = observer((props: Props) => {
       } else {
         service.queryProductState(InstanceModel.detail?.productId || '').then((resp) => {
           if (resp.status === 200) {
-            setProductItem(resp.result);
             if (resp.result.accessId) {
               service.queryGatewayState(resp.result.accessId).then((response: any) => {
                 if (response.status === 200) {
@@ -596,6 +594,7 @@ const Status = observer((props: Props) => {
     });
 
   const handleSearch = () => {
+    DiagnoseStatusModel.model = true;
     props.onChange('loading');
     DiagnoseStatusModel.list = [...initlist];
     DiagnoseStatusModel.status = {
@@ -640,6 +639,8 @@ const Status = observer((props: Props) => {
           .then(() => {
             diagnoseDeviceAuthConfig().then(() => {
               diagnoseDeviceAccess(gateway).then(() => {
+                DiagnoseStatusModel.model = false;
+                DiagnoseStatusModel.status = { ...DiagnoseStatusModel.status };
                 if (InstanceModel.detail.state?.value !== 'online') {
                   props.onChange('error');
                 } else {
@@ -669,35 +670,37 @@ const Status = observer((props: Props) => {
   }, [devicePermission]);
 
   useEffect(() => {
-    const data = { ...DiagnoseStatusModel.status };
-    const flag = Object.keys(data).every((item: any) => {
-      return data[item]?.status === 'success';
-    });
-    if (flag && InstanceModel.detail.state?.value !== 'online') {
-      // 展示诊断建议
-      if (
-        gatewayTemp.provider !== 'mqtt-server-gateway' &&
-        gatewayList.includes(gatewayTemp.provider)
-      ) {
-        service.queryProcotolDetail(gatewayTemp.provider, gatewayTemp.transport).then((resp1) => {
+    if (!DiagnoseStatusModel.model) {
+      const data = { ...DiagnoseStatusModel.status };
+      const flag = Object.keys(data).every((item: any) => {
+        return data[item]?.status === 'success';
+      });
+      if (flag && InstanceModel.detail.state?.value !== 'online') {
+        // 展示诊断建议
+        if (
+          gatewayTemp.provider !== 'mqtt-server-gateway' &&
+          gatewayList.includes(gatewayTemp.provider)
+        ) {
+          service.queryProcotolDetail(gatewayTemp.provider, gatewayTemp.transport).then((resp1) => {
+            setDiagnoseData({
+              product: productTemp,
+              device: deviceTemp,
+              id: InstanceModel.detail?.productId,
+              provider: gatewayTemp.provider,
+              routes: resp1.result?.routes || [],
+            });
+            setDiagnoseVisible(true);
+          });
+        } else {
           setDiagnoseData({
             product: productTemp,
             device: deviceTemp,
-            id: productItem.id,
-            provider: gatewayTemp.provider,
-            routes: resp1.result?.routes || [],
+            id: InstanceModel.detail?.productId,
+            provider: gatewayTemp?.provider,
+            routes: [],
           });
           setDiagnoseVisible(true);
-        });
-      } else {
-        setDiagnoseData({
-          product: productTemp,
-          device: deviceTemp,
-          id: productItem.i,
-          provider: gatewayTemp?.provider,
-          routes: [],
-        });
-        setDiagnoseVisible(true);
+        }
       }
     }
   }, [DiagnoseStatusModel.status]);

+ 2 - 0
src/pages/device/Instance/Detail/Diagnose/Status/model.ts

@@ -26,6 +26,7 @@ export const DiagnoseStatusModel = model<{
     other?: StatusProps;
   };
   list: ListProps[];
+  model: boolean;
 }>({
   status: {
     config: {
@@ -85,4 +86,5 @@ export const DiagnoseStatusModel = model<{
       desc: '诊断设备状态是否已启用,未启用的状态将导致连接失败',
     },
   ],
+  model: true,
 });

+ 1 - 1
src/pages/device/Instance/Detail/Reation/Edit.tsx

@@ -3,7 +3,7 @@ import { createForm } from '@formily/core';
 import { createSchemaField } from '@formily/react';
 import { InstanceModel, service } from '@/pages/device/Instance';
 import type { ISchema } from '@formily/json-schema';
-import { Form, FormGrid, FormItem, Select, PreviewText } from '@formily/antd';
+import { Form, FormGrid, FormItem, PreviewText, Select } from '@formily/antd';
 import { useParams } from 'umi';
 import { Button, Drawer, message, Space } from 'antd';
 import { action } from '@formily/reactive';

+ 1 - 0
src/pages/device/Instance/Detail/Running/Property/Indicators.tsx

@@ -15,6 +15,7 @@ import { createSchemaField } from '@formily/react';
 import type { PropertyMetadata } from '@/pages/device/Product/typings';
 import { useEffect, useState } from 'react';
 import { InstanceModel, service } from '@/pages/device/Instance';
+
 interface Props {
   data: Partial<PropertyMetadata>;
   onCancel: () => void;

+ 10 - 5
src/pages/device/components/Metadata/Base/Edit/index.tsx

@@ -2,7 +2,7 @@ import { Button, Drawer, Dropdown, Menu, message } from 'antd';
 import { createSchemaField, observer } from '@formily/react';
 import MetadataModel from '../model';
 import type { Field, IFieldState } from '@formily/core';
-import { createForm, onFieldReact, registerValidateRules } from '@formily/core';
+import { createForm, onFieldInit, onFieldReact, registerValidateRules } from '@formily/core';
 import {
   ArrayItems,
   Checkbox,
@@ -59,6 +59,12 @@ const Edit = observer((props: Props) => {
       createForm({
         initialValues: MetadataModel.item as Record<string, unknown>,
         effects: () => {
+          onFieldInit('expands.metrics.*.id', (field) => {
+            const id = field as Field;
+            if (id.value && !id.modified) {
+              (field as any).disabled = true;
+            }
+          });
           onFieldReact('expands.metrics.*.*', (field, form1) => {
             const type = field.query('valueType.type').take() as Field;
             const componentMap = {
@@ -88,10 +94,6 @@ const Edit = observer((props: Props) => {
               }
             });
           });
-          /// 处理Boolean 类型
-          // expands.metrics.0.edit.space.value.0 路径
-          // const metricsPath = field.query('expands.metrics.value.0');
-          // form.setValuesIn('expands.metrics.value.0', 'testtttt')
         },
       }),
     [],
@@ -437,6 +439,7 @@ const Edit = observer((props: Props) => {
       ],
     },
   } as any;
+  console.log(props.type, 'type');
   const propertySchema: ISchema = {
     type: 'object',
     properties: {
@@ -665,6 +668,7 @@ const Edit = observer((props: Props) => {
             'x-component': 'ArrayItems',
             'x-decorator': 'FormItem',
             title: '指标配置',
+            'x-visible': props.type === 'product',
             items: {
               type: 'object',
               'x-decorator': 'ArrayItems.Item',
@@ -833,6 +837,7 @@ const Edit = observer((props: Props) => {
               fulfill: {
                 state: {
                   visible:
+                    props.type === 'product' &&
                     "{{['int','float','double','long','date','string','boolean'].includes($deps[0])}}",
                 },
               },

+ 6 - 4
src/pages/link/AccessConfig/Detail/Access/index.tsx

@@ -61,6 +61,7 @@ const Access = (props: Props) => {
   ProcotoleMapping.set('mqtt-client-gateway', 'MQTT');
   ProcotoleMapping.set('mqtt-server-gateway', 'MQTT');
   ProcotoleMapping.set('tcp-server-gateway', 'TCP');
+  ProcotoleMapping.set('child-device', 'Gateway');
 
   const queryNetworkList = (id: string, params?: any) => {
     service.getNetworkList(MetworkTypeMapping.get(id), params).then((resp) => {
@@ -412,8 +413,8 @@ const Access = (props: Props) => {
                 onClick={() => {
                   const url = getMenuPathByCode(MENUS_CODE[`link/Protocol`]);
                   const tab: any = window.open(`${origin}/#${url}?save=true`);
-                  tab!.onTabSaveSuccess = (value: any) => {
-                    if (value) {
+                  tab!.onTabSaveSuccess = (resp: any) => {
+                    if (resp.status === 200) {
                       queryProcotolList(props.provider?.id);
                     }
                   };
@@ -461,8 +462,9 @@ const Access = (props: Props) => {
                         onClick={() => {
                           const url = getMenuPathByCode(MENUS_CODE[`link/Protocol`]);
                           const tab: any = window.open(`${origin}/#${url}?save=true`);
-                          tab!.onTabSaveSuccess = (value: any) => {
-                            if (value) {
+                          tab!.onTabSaveSuccess = (resp: any) => {
+                            if (resp.status === 200) {
+                              console.log(props.provider);
                               queryProcotolList(props.provider?.id);
                             }
                           };

+ 4 - 0
src/pages/link/Protocol/save/index.tsx

@@ -214,6 +214,10 @@ const Save = (props: Props) => {
         await service.modifyState(value.id, 'deploy');
       }
       props.reload();
+      if ((window as any).onTabSaveSuccess) {
+        (window as any).onTabSaveSuccess(response);
+        setTimeout(() => window.close(), 300);
+      }
     }
   };
 

+ 2 - 0
src/pages/notice/Config/Detail/doc/index.less

@@ -1,5 +1,7 @@
 .doc {
+  height: 750px;
   padding: 24px;
+  overflow-y: auto;
   color: rgba(#000, 0.8);
   font-size: 14px;
   background-color: #fafafa;

+ 17 - 15
src/pages/notice/Config/SyncUser/index.tsx

@@ -65,22 +65,24 @@ const SyncUser = observer(() => {
           </Button>
         </Tooltip>,
         <Tooltip title={'解绑用户'} key="unbind">
-          <Button type="link">
-            <Popconfirm
-              title={'确认解绑'}
-              onConfirm={async () => {
-                if (record?.bindingId) {
-                  const resp = await service.syncUser.unBindUser(record.bindingId);
-                  if (resp.status === 200) {
-                    message.success('操作成功!');
-                    actionRef.current?.reload();
+          {record?.userId && (
+            <Button type="link">
+              <Popconfirm
+                title={'确认解绑'}
+                onConfirm={async () => {
+                  if (record?.bindingId) {
+                    const resp = await service.syncUser.unBindUser(record.bindingId);
+                    if (resp.status === 200) {
+                      message.success('操作成功!');
+                      actionRef.current?.reload();
+                    }
                   }
-                }
-              }}
-            >
-              <DisconnectOutlined />
-            </Popconfirm>
-          </Button>
+                }}
+              >
+                <DisconnectOutlined />
+              </Popconfirm>
+            </Button>
+          )}
         </Tooltip>,
       ],
     },

+ 2 - 0
src/pages/notice/Template/Detail/doc/index.less

@@ -1,5 +1,7 @@
 .doc {
+  height: 750px;
   padding: 24px;
+  overflow-y: auto;
   color: rgba(#000, 0.8);
   font-size: 14px;
   background-color: #fafafa;

+ 25 - 17
src/pages/notice/Template/Detail/index.tsx

@@ -12,7 +12,6 @@ import {
   Radio,
   Select,
   Space,
-  Submit,
   Switch,
 } from '@formily/antd';
 import type { Field } from '@formily/core';
@@ -45,6 +44,8 @@ import AliyunSms from '@/pages/notice/Template/Detail/doc/AliyunSms';
 import Email from '@/pages/notice/Template/Detail/doc/Email';
 import { Store } from 'jetlinks-store';
 import FAutoComplete from '@/components/FAutoComplete';
+import { PermissionButton } from '@/components';
+import usePermissions from '@/hooks/permission';
 
 export const docMap = {
   weixin: {
@@ -264,7 +265,7 @@ const Detail = observer(() => {
                   : {
                       id: item,
                       type: 'string',
-                      format: '--',
+                      format: 's%',
                     },
               );
               form1.setValuesIn('variableDefinitions', _result);
@@ -312,7 +313,7 @@ const Detail = observer(() => {
                   : {
                       id: item,
                       type: 'string',
-                      format: '--',
+                      format: 's%',
                     },
               );
               form1.setValuesIn('variableDefinitions', _result);
@@ -324,7 +325,6 @@ const Detail = observer(() => {
           });
           onFieldReact('variableDefinitions.*.type', (field) => {
             const value = (field as Field).value;
-            console.log(value, 'value');
             const formatPath = FormPath.transform(
               field.path,
               /\d+/,
@@ -636,6 +636,7 @@ const Detail = observer(() => {
                         'x-component-props': {
                           placeholder: '请选择消息模版',
                         },
+                        required: true,
                         'x-decorator-props': {
                           gridSpan: 1,
                           tooltip: '微信公众号中配置的消息模版',
@@ -785,12 +786,12 @@ const Detail = observer(() => {
                         'x-component-props': {
                           placeholder: '请选择收信人',
                         },
-                        'x-reactions': {
-                          dependencies: ['configId'],
-                          fulfill: {
-                            run: '{{useAsyncDataSource(getDingTalkUser($deps[0]))}}',
-                          },
-                        },
+                        // 'x-reactions': {
+                        //   dependencies: ['configId'],
+                        //   fulfill: {
+                        //     run: '{{useAsyncDataSource(getDingTalkUser($deps[0]))}}',
+                        //   },
+                        // },
                       },
                       departmentIdList: {
                         title: '收信部门',
@@ -803,12 +804,12 @@ const Detail = observer(() => {
                         'x-component-props': {
                           placeholder: '请选择收信部门',
                         },
-                        'x-reactions': {
-                          dependencies: ['configId'],
-                          fulfill: {
-                            run: '{{useAsyncDataSource(getDingTalkDept($deps[0]))}}',
-                          },
-                        },
+                        // 'x-reactions': {
+                        //   dependencies: ['configId'],
+                        //   fulfill: {
+                        //     run: '{{useAsyncDataSource(getDingTalkDept($deps[0]))}}',
+                        //   },
+                        // },
                       },
                     },
                   },
@@ -1253,6 +1254,7 @@ const Detail = observer(() => {
       },
     },
   };
+  const { permission } = usePermissions('notice');
   return (
     <PageContainer>
       <Card>
@@ -1278,7 +1280,13 @@ const Detail = observer(() => {
               />
               <FormButtonGroup.Sticky>
                 <FormButtonGroup.FormItem>
-                  <Submit onSubmit={handleSave}>保存</Submit>
+                  <PermissionButton
+                    type="primary"
+                    isPermission={permission.add || permission.update}
+                    onClick={handleSave}
+                  >
+                    保存
+                  </PermissionButton>
                 </FormButtonGroup.FormItem>
               </FormButtonGroup.Sticky>
             </Form>

+ 1 - 1
src/pages/rule-engine/Alarm/Configuration/Save/index.less

@@ -2,7 +2,7 @@
   :global {
     .ant-radio-button-wrapper {
       height: 100%;
-      margin-right: 15px;
+      margin: 10px 15px 0 0;
     }
   }
 }

+ 22 - 7
src/pages/rule-engine/Alarm/Configuration/Save/index.tsx

@@ -10,10 +10,12 @@ import Service from '@/pages/rule-engine/Alarm/Configuration/service';
 import { useAsyncDataSource } from '@/utils/util';
 import styles from './index.less';
 import { service as ConfigService } from '../../Config';
+import { Store } from 'jetlinks-store';
 
 interface Props {
   visible: boolean;
   close: () => void;
+  data: any;
 }
 
 const alarm1 = require('/public/images/alarm/alarm1.png');
@@ -48,7 +50,6 @@ const Save = (props: Props) => {
   const getLevel = () => {
     return ConfigService.queryLevel().then((resp) => {
       if (resp.status === 200) {
-        console.log(resp, 'resp');
         return resp.result?.levels?.map((item: { level: number; title: string }) => ({
           label: createImageLabel(LevelMap[item.level], item.title),
           value: item.level,
@@ -56,13 +57,21 @@ const Save = (props: Props) => {
       }
     });
   };
+
+  const getScene = () => {
+    return service.getScene().then((resp) => {
+      Store.set('scene-data', resp);
+      return resp;
+    });
+  };
   const form = useMemo(
     () =>
       createForm({
+        initialValues: props.data,
         validateFirst: true,
         effects() {},
       }),
-    [],
+    [props.data],
   );
 
   const getSupports = () => service.getTargetTypes();
@@ -78,11 +87,13 @@ const Save = (props: Props) => {
   });
 
   const handleSave = async () => {
-    const data: ConfigItem = await form.submit();
-    console.log(data, 'dat');
-    const resp: any = await service.update(data);
+    const data: any = await form.submit();
+    const list = Store.get('scene-data');
+    const scene = list.find((item: any) => item.value === data.sceneId);
+    const resp: any = await service.update({ ...data, sceneName: scene.label, state: 'disable' });
     if (resp.status === 200) {
       message.success('操作成功');
+      props.close();
     }
   };
 
@@ -109,7 +120,7 @@ const Save = (props: Props) => {
               placeholder: '请输入名称',
             },
           },
-          type: {
+          targetType: {
             title: '类型',
             'x-decorator': 'FormItem',
             'x-component': 'Select',
@@ -140,6 +151,7 @@ const Save = (props: Props) => {
         title: '关联触发场景',
         'x-decorator': 'FormItem',
         'x-component': 'Select',
+        'x-reactions': '{{useAsyncDataSource(getScene)}}',
         'x-decorator-props': {
           gridSpan: 1,
           addonAfter: (
@@ -194,7 +206,10 @@ const Save = (props: Props) => {
       title="新增告警"
     >
       <Form className={styles.form} form={form} layout="vertical">
-        <SchemaField schema={schema} scope={{ useAsyncDataSource, getSupports, getLevel }} />
+        <SchemaField
+          schema={schema}
+          scope={{ useAsyncDataSource, getSupports, getLevel, getScene }}
+        />
       </Form>
     </Modal>
   );

+ 193 - 10
src/pages/rule-engine/Alarm/Configuration/index.tsx

@@ -1,18 +1,32 @@
 import { PageContainer } from '@ant-design/pro-layout';
 import SearchComponent from '@/components/SearchComponent';
-import { ActionType, ProColumns } from '@jetlinks/pro-table';
+import type { ActionType, ProColumns } from '@jetlinks/pro-table';
 import { PermissionButton } from '@/components';
-import { DeleteOutlined, EditOutlined, PlusOutlined } from '@ant-design/icons';
+import {
+  CloseCircleOutlined,
+  DeleteOutlined,
+  EditOutlined,
+  LikeOutlined,
+  PlayCircleOutlined,
+  PlusOutlined,
+} from '@ant-design/icons';
 import { useIntl } from '@@/plugin-locale/localeExports';
 import { useRef, useState } from 'react';
-import { Space } from 'antd';
+import { message, Space } from 'antd';
 import ProTableCard from '@/components/ProTableCard';
 import Save from './Save';
+import Service from '@/pages/rule-engine/Alarm/Configuration/service';
+import AlarmConfig from '@/components/ProTableCard/CardItems/AlarmConfig';
+
+const service = new Service('alarm/config');
 
 const Configuration = () => {
   const intl = useIntl();
+  const [visible, setVisible] = useState<boolean>(false);
+  const actionRef = useRef<ActionType>();
 
-  const columns: ProColumns<ConfigItem>[] = [
+  const [current, setCurrent] = useState<any>();
+  const columns: ProColumns<ConfigurationItem>[] = [
     {
       dataIndex: 'name',
       title: '名称',
@@ -32,6 +46,7 @@ const Configuration = () => {
     {
       title: '状态',
       dataIndex: 'state',
+      renderText: (state) => state.text,
     },
     {
       title: '说明',
@@ -42,22 +57,92 @@ const Configuration = () => {
       valueType: 'option',
       align: 'center',
       render: (_, record) => [
+        record.sceneTriggerType === 'manual' && (
+          <PermissionButton
+            key="trigger"
+            isPermission={true}
+            popConfirm={{
+              title: '确认手动触发?',
+              onConfirm: async () => {
+                await service._execute(record.sceneId);
+                message.success(
+                  intl.formatMessage({
+                    id: 'pages.data.option.success',
+                    defaultMessage: '操作成功!',
+                  }),
+                );
+                actionRef.current?.reload();
+              },
+            }}
+          >
+            <LikeOutlined />
+          </PermissionButton>
+        ),
         <PermissionButton
           isPermission={true}
+          key="edit"
+          style={{ padding: 0 }}
           tooltip={{
             title: intl.formatMessage({
               id: 'pages.data.option.edit',
               defaultMessage: '编辑',
             }),
           }}
+          type="link"
           onClick={() => {
-            console.log(record);
+            setVisible(true);
+            setCurrent(record);
           }}
         >
           <EditOutlined />
         </PermissionButton>,
         <PermissionButton
           isPermission={true}
+          key="action"
+          style={{ padding: 0 }}
+          popConfirm={{
+            title: intl.formatMessage({
+              id: `pages.data.option.${
+                record.state?.value === 'disable' ? 'disabled' : 'enabled'
+              }.tips`,
+              defaultMessage: `确认${record.state.value === 'disable' ? '禁用' : '启用'}?`,
+            }),
+            onConfirm: async () => {
+              if (record.state?.value === 'disable') {
+                await service._enable(record.id);
+              } else {
+                await service._disable(record.id);
+              }
+              setVisible(true);
+              setCurrent(record);
+              message.success(
+                intl.formatMessage({
+                  id: 'pages.data.option.success',
+                  defaultMessage: '操作成功!',
+                }),
+              );
+              actionRef.current?.reload();
+            },
+          }}
+          tooltip={{
+            title: intl.formatMessage({
+              id: `pages.data.option.${record.state.value === 'disable' ? 'disabled' : 'enabled'}`,
+              defaultMessage: record.state.value === 'disable' ? '禁用' : '启用',
+            }),
+          }}
+          type="link"
+        >
+          {record.state.value === 'disable' ? <CloseCircleOutlined /> : <PlayCircleOutlined />}
+        </PermissionButton>,
+        <PermissionButton
+          type="link"
+          isPermission={true}
+          key="delete"
+          style={{ padding: 0 }}
+          popConfirm={{
+            title: '确认删除?',
+            onConfirm: () => service.remove(record.id),
+          }}
           tooltip={{
             title: intl.formatMessage({
               id: 'pages.data.option.remove',
@@ -71,11 +156,8 @@ const Configuration = () => {
     },
   ];
 
-  const actionRef = useRef<ActionType>();
-
   const [param, setParam] = useState({});
 
-  const [visible, setVisible] = useState<boolean>(false);
   return (
     <PageContainer>
       <SearchComponent
@@ -85,17 +167,118 @@ const Configuration = () => {
           setParam(data);
         }}
       />
-      <ProTableCard<ConfigItem>
+      <ProTableCard<any>
         actionRef={actionRef}
         rowKey="id"
         search={false}
         params={param}
         columns={columns}
+        request={(params) => service.query(params)}
+        gridColumn={3}
+        cardRender={(record) => (
+          <AlarmConfig
+            {...record}
+            actions={[
+              record.sceneTriggerType === 'manual' && (
+                <PermissionButton
+                  key="trigger"
+                  isPermission={true}
+                  popConfirm={{
+                    title: '确认手动触发?',
+                    onConfirm: async () => {
+                      await service._execute(record.sceneId);
+                      message.success(
+                        intl.formatMessage({
+                          id: 'pages.data.option.success',
+                          defaultMessage: '操作成功!',
+                        }),
+                      );
+                      actionRef.current?.reload();
+                    },
+                  }}
+                >
+                  <LikeOutlined />
+                  手动触发
+                </PermissionButton>
+              ),
+              <PermissionButton
+                isPermission={true}
+                key="edit"
+                onClick={() => {
+                  setCurrent(record);
+                  setVisible(true);
+                }}
+              >
+                <EditOutlined />
+                编辑
+              </PermissionButton>,
+              <PermissionButton
+                isPermission={true}
+                style={{ padding: 0 }}
+                popConfirm={{
+                  title: intl.formatMessage({
+                    id: `pages.data.option.${
+                      record.state?.value === 'disable' ? 'disabled' : 'enabled'
+                    }.tips`,
+                    defaultMessage: `确认${record.state.value === 'disable' ? '禁用' : '启用'}?`,
+                  }),
+                  onConfirm: async () => {
+                    if (record.state?.value === 'disable') {
+                      await service._enable(record.id);
+                    } else {
+                      await service._disable(record.id);
+                    }
+                    setVisible(true);
+                    setCurrent(record);
+                    message.success(
+                      intl.formatMessage({
+                        id: 'pages.data.option.success',
+                        defaultMessage: '操作成功!',
+                      }),
+                    );
+                    actionRef.current?.reload();
+                  },
+                }}
+                tooltip={{
+                  title: intl.formatMessage({
+                    id: `pages.data.option.${
+                      record.state.value === 'disable' ? 'disabled' : 'enabled'
+                    }`,
+                    defaultMessage: record.state.value === 'disable' ? '禁用' : '启用',
+                  }),
+                }}
+                key="action"
+                type="link"
+              >
+                {record.state.value === 'disable' ? (
+                  <CloseCircleOutlined />
+                ) : (
+                  <PlayCircleOutlined />
+                )}
+                {record.state.value === 'disable' ? '禁用' : '启用'}
+              </PermissionButton>,
+              <PermissionButton
+                popConfirm={{
+                  title: '确认删除?',
+                  onConfirm: async () => {
+                    await service.remove(record.id);
+                    actionRef.current?.reset?.();
+                  },
+                }}
+                isPermission={true}
+                key="delete"
+              >
+                <DeleteOutlined />
+              </PermissionButton>,
+            ]}
+          />
+        )}
         headerTitle={
           <Space>
             <PermissionButton
               isPermission={true}
               onClick={() => {
+                setCurrent(undefined);
                 setVisible(true);
               }}
               key="button"
@@ -110,7 +293,7 @@ const Configuration = () => {
           </Space>
         }
       />
-      <Save visible={visible} close={() => setVisible(false)} />
+      <Save data={current} visible={visible} close={() => setVisible(false)} />
     </PageContainer>
   );
 };

+ 24 - 0
src/pages/rule-engine/Alarm/Configuration/service.ts

@@ -12,6 +12,30 @@ class Service extends BaseService<ConfigItem> {
         value: item.id,
       }));
     });
+
+  public getScene = () =>
+    request(`/${SystemConst.API_BASE}/scene/_query/no-paging?paging=false`, {
+      method: 'GET',
+    }).then((resp) => {
+      return resp.result.map((item: { id: string; name: string }) => ({
+        label: item.name,
+        value: item.id,
+      }));
+    });
+
+  public _enable = (id: string) =>
+    request(`/${SystemConst.API_BASE}/alarm/config/${id}/_enable`, {
+      method: 'POST',
+    });
+
+  public _disable = (id: string) =>
+    request(`/${SystemConst.API_BASE}/alarm/config/${id}/_disable`, {
+      method: 'POST',
+    });
+  public _execute = (id: string) =>
+    request(`/${SystemConst.API_BASE}/scene/${id}/_execute`, {
+      method: 'POST',
+    });
 }
 
 export default Service;

+ 4 - 2
src/pages/rule-engine/Alarm/Configuration/typings.d.ts

@@ -1,9 +1,11 @@
-type ConfigItem = {
+type ConfigurationItem = {
   name: string;
   targetType: string;
   level: number;
   sceneName: string;
   sceneId: string;
-  state: string;
+  state: { text: string; value: string };
   description: string;
+  id: string;
+  sceneTriggerType: string;
 };

+ 1 - 1
src/pages/rule-engine/Scene/Save/action/action.tsx

@@ -3,10 +3,10 @@ import type { FormInstance } from 'antd';
 import { useCallback, useEffect, useState } from 'react';
 import { useRequest } from 'umi';
 import {
-  queryMessageType,
   queryMessageConfig,
   queryMessageTemplate,
   queryMessageTemplateDetail,
+  queryMessageType,
 } from './service';
 import MessageContent from './messageContent';
 import DeviceSelect, { MessageTypeEnum } from './device';

+ 1 - 2
src/pages/rule-engine/Scene/Save/action/device/deviceModal.tsx

@@ -1,10 +1,9 @@
 import { Badge, Input, message, Modal } from 'antd';
 import { useEffect, useRef, useState } from 'react';
-import { ActionType, ProColumns } from '@jetlinks/pro-table';
+import ProTable, { ActionType, ProColumns } from '@jetlinks/pro-table';
 import { DeviceItem } from '@/pages/system/Department/typings';
 import { useIntl } from '@@/plugin-locale/localeExports';
 import SearchComponent from '@/components/SearchComponent';
-import ProTable from '@jetlinks/pro-table';
 import { queryDevice } from '@/pages/rule-engine/Scene/Save/action/device/service';
 
 interface DeviceModelProps {

+ 1 - 1
src/pages/rule-engine/Scene/Save/action/device/functionCall.tsx

@@ -2,9 +2,9 @@ import type { ProColumns } from '@jetlinks/pro-table';
 import { EditableProTable } from '@jetlinks/pro-table';
 import { Input, InputNumber, Select } from 'antd';
 import React, { useEffect, useRef, useState } from 'react';
-import ProForm from '@ant-design/pro-form';
 import type { ProFormInstance } from '@ant-design/pro-form';
 import { DatePickerFormat } from '@/pages/rule-engine/Scene/Save/components';
+import ProForm from '@ant-design/pro-form';
 
 type FunctionTableDataType = {
   id: string;

+ 1 - 1
src/pages/rule-engine/Scene/Save/action/device/index.tsx

@@ -1,5 +1,5 @@
-import { Form, Input, Select } from 'antd';
 import type { FormInstance } from 'antd';
+import { Form, Input, Select } from 'antd';
 import { useEffect, useState } from 'react';
 import { getProductList } from '@/pages/rule-engine/Scene/Save/action/device/service';
 import Device from './deviceModal';

+ 1 - 1
src/pages/rule-engine/Scene/Save/action/device/tagModal.tsx

@@ -1,4 +1,4 @@
-import { Modal, Input, Space, Select, InputNumber, DatePicker, Row, Col, Button } from 'antd';
+import { Button, Col, DatePicker, Input, InputNumber, Modal, Row, Select, Space } from 'antd';
 import { useEffect, useState } from 'react';
 import moment from 'moment';
 import { DeleteOutlined, PlusOutlined } from '@ant-design/icons';

+ 2 - 2
src/pages/rule-engine/Scene/Save/action/messageContent.tsx

@@ -1,10 +1,10 @@
-import { Col, Form, Row } from 'antd';
 import type { FormInstance } from 'antd';
+import { Col, Form, Row } from 'antd';
 import {
   BuiltIn,
   OrgList,
-  UserList,
   TagSelect,
+  UserList,
 } from '@/pages/rule-engine/Scene/Save/action/VariableItems';
 import { InputFile } from '@/pages/rule-engine/Scene/Save/components';
 

+ 1 - 1
src/pages/rule-engine/Scene/Save/components/DatePickerFormat/index.tsx

@@ -1,5 +1,5 @@
-import { DatePicker } from 'antd';
 import type { DatePickerProps } from 'antd';
+import { DatePicker } from 'antd';
 import moment from 'moment';
 
 interface DatePickerFormat extends Omit<DatePickerProps, 'onChange'> {

+ 1 - 1
src/pages/rule-engine/Scene/Save/components/InputUpload/index.tsx

@@ -1,5 +1,5 @@
 import { useEffect, useState } from 'react';
-import { Upload, Input, Button } from 'antd';
+import { Button, Input, Upload } from 'antd';
 import { UploadChangeParam } from 'antd/lib/upload/interface';
 import SystemConst from '@/utils/const';
 import Token from '@/utils/token';

+ 1 - 1
src/pages/rule-engine/Scene/Save/trigger/index.tsx

@@ -1,6 +1,6 @@
 import { useCallback, useEffect, useState } from 'react';
-import { Col, Form, Row, Select, Space, TreeSelect } from 'antd';
 import type { FormInstance } from 'antd';
+import { Col, Form, Row, Select, Space, TreeSelect } from 'antd';
 import { TimingTrigger } from '@/pages/rule-engine/Scene/Save/components';
 import { getProductList } from '@/pages/rule-engine/Scene/Save/action/device/service';
 import { queryOrgTree, querySelector } from '@/pages/rule-engine/Scene/Save/trigger/service';

+ 1 - 1
src/pages/system/Relationship/Save/index.tsx

@@ -1,6 +1,6 @@
 import { useIntl } from 'umi';
-import { onFieldValueChange, onFieldInputValueChange, createForm } from '@formily/core';
 import type { Field } from '@formily/core';
+import { createForm, onFieldInputValueChange, onFieldValueChange } from '@formily/core';
 import { createSchemaField } from '@formily/react';
 import React from 'react';
 import * as ICONS from '@ant-design/icons';