xieyonghong vor 3 Jahren
Ursprung
Commit
ea3272c740
80 geänderte Dateien mit 2176 neuen und 1471 gelöschten Zeilen
  1. BIN
      public/images/network/01.jpg
  2. BIN
      public/images/network/01.png
  3. BIN
      public/images/network/02.jpg
  4. BIN
      public/images/network/03.png
  5. 0 0
      public/images/network/04.jpg
  6. BIN
      public/images/network/05.jpg
  7. BIN
      public/images/network/06.jpg
  8. 1 1
      src/components/ProTableCard/CardItems/Scene/index.tsx
  9. 84 0
      src/components/ProTableCard/CardItems/noticeTemplate.tsx
  10. 1 1
      src/components/RadioCard/index.tsx
  11. 1 1
      src/pages/link/DataCollect/components/Channel/Save/index.tsx
  12. 28 19
      src/pages/link/DataCollect/components/Channel/index.tsx
  13. 107 67
      src/pages/link/DataCollect/components/Device/Save/index.tsx
  14. 2 2
      src/pages/link/DataCollect/components/Device/index.tsx
  15. 151 0
      src/pages/DataCollect/Collector/components/Point/CollectorCard/WritePoint.tsx
  16. 1 1
      src/pages/link/DataCollect/components/Point/CollectorCard/index.less
  17. 2 2
      src/pages/link/DataCollect/components/Point/CollectorCard/index.tsx
  18. 1 1
      src/pages/link/DataCollect/components/Point/Save/BatchUpdate.tsx
  19. 0 0
      src/pages/DataCollect/Collector/components/Point/Save/components/MyInput.tsx
  20. 0 0
      src/pages/DataCollect/Collector/components/Point/Save/components/MySelect.tsx
  21. 0 0
      src/pages/DataCollect/Collector/components/Point/Save/components/RemoveData.tsx
  22. 122 36
      src/pages/link/DataCollect/components/Point/Save/modbus.tsx
  23. 20 1
      src/pages/link/DataCollect/components/Point/Save/opc-ua.tsx
  24. 0 0
      src/pages/DataCollect/Collector/components/Point/Save/scan.less
  25. 3 3
      src/pages/link/DataCollect/components/Point/Save/scan.tsx
  26. 192 175
      src/pages/link/DataCollect/components/Point/index.tsx
  27. 1 2
      src/pages/link/DataCollect/components/Tree/index.less
  28. 257 0
      src/pages/DataCollect/Collector/components/Tree/index.tsx
  29. 0 0
      src/pages/DataCollect/Collector/components/index.less
  30. 0 0
      src/pages/DataCollect/Collector/index.less
  31. 41 0
      src/pages/DataCollect/Collector/index.tsx
  32. 0 0
      src/pages/DataCollect/Dashboard/index.less
  33. 0 0
      src/pages/DataCollect/Dashboard/index.tsx
  34. 3 3
      src/pages/link/DataCollect/service.ts
  35. 0 0
      src/pages/DataCollect/typings.d.ts
  36. 12 3
      src/pages/account/Center/index.tsx
  37. 14 13
      src/pages/device/Instance/Detail/MetadataLog/Property/index.tsx
  38. 26 5
      src/pages/device/Instance/Detail/Parsing/index.tsx
  39. 103 3
      src/pages/device/Instance/Detail/Running/Property/EditProperty.tsx
  40. 37 31
      src/pages/device/Instance/Detail/Tags/Edit.tsx
  41. 16 16
      src/pages/device/components/Metadata/Base/index.tsx
  42. 2 1
      src/pages/device/components/Metadata/Cat/index.tsx
  43. 2 0
      src/pages/edge/Resource/Issue/Result.tsx
  44. 2 0
      src/pages/home/index.tsx
  45. 1 0
      src/pages/link/AccessConfig/Detail/components/CTWing/index.less
  46. 21 8
      src/pages/link/AccessConfig/Detail/components/CTWing/index.tsx
  47. 49 8
      src/pages/link/AccessConfig/Detail/components/OneNet/index.tsx
  48. 1 1
      src/pages/link/AccessConfig/service.ts
  49. 0 79
      src/pages/link/DataCollect/DataGathering/index.tsx
  50. 0 49
      src/pages/link/DataCollect/IntegratedQuery/index.tsx
  51. 0 79
      src/pages/link/DataCollect/components/Point/CollectorCard/WritePoint.tsx
  52. 0 389
      src/pages/link/DataCollect/components/Tree/index.tsx
  53. 2 2
      src/pages/media/Cascade/Channel/index.tsx
  54. 8 6
      src/pages/notice/Config/Debug/index.tsx
  55. 133 52
      src/pages/notice/Config/Detail/index.tsx
  56. 4 4
      src/pages/notice/Config/Log/index.tsx
  57. 4 7
      src/pages/notice/Config/SyncUser/index.tsx
  58. 21 59
      src/pages/notice/Config/index.tsx
  59. 8 9
      src/pages/notice/Template/Debug/index.tsx
  60. 160 43
      src/pages/notice/Template/Detail/index.tsx
  61. 4 4
      src/pages/notice/Template/Log/index.tsx
  62. 27 59
      src/pages/notice/Template/index.tsx
  63. 8 0
      src/pages/notice/Template/service.ts
  64. 131 52
      src/pages/rule-engine/Alarm/Config/index.tsx
  65. 2 1
      src/pages/rule-engine/Scene/Save/action/DeviceOutput/actions/ObjModel.tsx
  66. 2 2
      src/pages/rule-engine/Scene/Save/action/DeviceOutput/actions/TypeModel.tsx
  67. 1 1
      src/pages/rule-engine/Scene/Save/action/DeviceOutput/actions/index.tsx
  68. 11 5
      src/pages/rule-engine/Scene/Save/action/DeviceOutput/device/index.tsx
  69. 12 5
      src/pages/rule-engine/Scene/Save/action/DeviceOutput/index.tsx
  70. 2 2
      src/pages/rule-engine/Scene/Save/action/ListItem/Item.tsx
  71. 1 1
      src/pages/rule-engine/Scene/Save/action/notify/VariableDefinitions.tsx
  72. 9 4
      src/pages/rule-engine/Scene/Save/action/notify/index.tsx
  73. 10 19
      src/pages/rule-engine/Scene/index.tsx
  74. 2 2
      src/pages/rule-engine/Scene/service.ts
  75. 12 1
      src/pages/system/DataSource/Management/DataTable.tsx
  76. 12 12
      src/pages/system/DataSource/Save/index.tsx
  77. 273 107
      src/pages/system/Menu/Setting/baseMenu.ts
  78. 1 0
      src/pages/user/Login/index.tsx
  79. 6 6
      src/utils/menu/index.ts
  80. 6 6
      src/utils/menu/router.ts

BIN
public/images/network/01.jpg


BIN
public/images/network/01.png


BIN
public/images/network/02.jpg


BIN
public/images/network/03.png


public/images/network/03.jpg → public/images/network/04.jpg


BIN
public/images/network/05.jpg


BIN
public/images/network/06.jpg


+ 1 - 1
src/components/ProTableCard/CardItems/Scene/index.tsx

@@ -92,7 +92,7 @@ const deviceRender = (data: ActionsType | undefined) => {
         data?.options?.type + tags + data?.options?.productName + data?.options?.properties
       }`;
     case 'relation':
-      return `${data?.options?.type}与${data?.options?.name}具有相同${data?.options?.relationName}的${data?.options?.productName}设备的${data?.options?.properties}`;
+      return `${data?.options?.type}与${data?.options?.triggerName}具有相同${data?.options?.relationName}的${data?.options?.productName}设备的${data?.options?.properties}`;
     default:
       return null;
   }

+ 84 - 0
src/components/ProTableCard/CardItems/noticeTemplate.tsx

@@ -39,6 +39,63 @@ export const imgMap = {
   },
 };
 
+export const typeObj = {
+  dingTalk: {
+    text: '钉钉',
+    status: 'dingTalk',
+  },
+  weixin: {
+    text: '微信',
+    status: 'weixin',
+  },
+  email: {
+    text: '邮件',
+    status: 'email',
+  },
+  voice: {
+    text: '语音',
+    status: 'voice',
+  },
+  sms: {
+    text: '短信',
+    status: 'sms',
+  },
+  webhook: {
+    text: 'webhook',
+    status: 'webhook',
+  },
+};
+export const providerObj = {
+  corpMessage: {
+    text: '企业消息',
+    status: 'corpMessage',
+  },
+  dingTalkMessage: {
+    text: '钉钉消息',
+    status: 'dingTalkMessage',
+  },
+  dingTalkRobotWebHook: {
+    text: '群机器人消息',
+    status: 'dingTalkRobotWebHook',
+  },
+  aliyun: {
+    text: '阿里云语音',
+    status: 'aliyun',
+  },
+  aliyunSms: {
+    text: '阿里云短信',
+    status: 'aliyunSms',
+  },
+  embedded: {
+    text: '邮件',
+    status: 'embedded',
+  },
+  http: {
+    text: 'Webhook',
+    status: 'http',
+  },
+};
+
 export const typeList = {
   weixin: {
     corpMessage: '企业消息',
@@ -62,6 +119,33 @@ export const typeList = {
   },
 };
 
+export const typeArray = [
+  {
+    text: '钉钉',
+    status: 'dingTalk',
+  },
+  {
+    text: '微信',
+    status: 'weixin',
+  },
+  {
+    text: '邮件',
+    status: 'email',
+  },
+  {
+    text: '语音',
+    status: 'voice',
+  },
+  {
+    text: '短信',
+    status: 'sms',
+  },
+  {
+    text: 'webhook',
+    status: 'webhook',
+  },
+];
+
 export const ExtraNoticeTemplateCard = (props: NoticeCardProps) => {
   return (
     <TableCard

+ 1 - 1
src/components/RadioCard/index.tsx

@@ -34,7 +34,7 @@ export default (props: RadioCardProps) => {
 
   useEffect(() => {
     // 初始化
-    setKeys(value ? (isArray(value) ? value : [value]) : []);
+    setKeys(value !== undefined ? (isArray(value) ? value : [value]) : []);
   }, [props.value]);
 
   const getNode = (_keys: string[]) =>

+ 1 - 1
src/pages/link/DataCollect/components/Channel/Save/index.tsx

@@ -5,7 +5,7 @@ import React, { useEffect, useState } from 'react';
 import * as ICONS from '@ant-design/icons';
 import { Form, FormGrid, FormItem, Input, Select, NumberPicker, Password } from '@formily/antd';
 import type { ISchema } from '@formily/json-schema';
-import service from '@/pages/link/DataCollect/service';
+import service from '@/pages/DataCollect/service';
 import { onlyMessage, testDomain, testIP, testIPv6 } from '@/utils/util';
 import { action } from '@formily/reactive';
 import { RadioCard } from '@/components';

+ 28 - 19
src/pages/link/DataCollect/components/Channel/index.tsx

@@ -1,21 +1,18 @@
+import { PageContainer } from '@ant-design/pro-layout';
 import { observer } from '@formily/react';
-import SearchComponent from '@/components/SearchComponent';
-import { ProColumns } from '@jetlinks/pro-table';
-import { useEffect, useState } from 'react';
+import { model } from '@formily/reactive';
+import { useIntl } from '@@/plugin-locale/localeExports';
 import { useDomFullHeight } from '@/hooks';
-import service from '@/pages/link/DataCollect/service';
-import ChannelCard from '@/components/ProTableCard/CardItems/DataCollect/channel';
+import { useEffect, useState } from 'react';
 import { Empty, PermissionButton } from '@/components';
+import { ProColumns } from '@jetlinks/pro-table';
+import service from '@/pages/DataCollect/service';
+import SearchComponent from '@/components/SearchComponent';
+import { Card, Col, Pagination, Row } from 'antd';
+import ChannelCard from '@/components/ProTableCard/CardItems/DataCollect/channel';
 import { DeleteOutlined, EditOutlined, PlayCircleOutlined, StopOutlined } from '@ant-design/icons';
 import { onlyMessage } from '@/utils/util';
-import { useIntl } from '@@/plugin-locale/localeExports';
-import { Card, Col, Pagination, Row } from 'antd';
-import { model } from '@formily/reactive';
-import Save from '@/pages/link/DataCollect/components/Channel/Save';
-
-interface Props {
-  type: boolean; // true: 综合查询  false: 数据采集
-}
+import Save from '@/pages/DataCollect/Channel/Save';
 
 const ChannelModel = model<{
   visible: boolean;
@@ -27,11 +24,11 @@ const ChannelModel = model<{
   currentPage: 0,
 });
 
-export default observer((props: Props) => {
+export default observer(() => {
   const intl = useIntl();
   const { minHeight } = useDomFullHeight(`.data-collect-channel-card`, 24);
   const [param, setParam] = useState({ pageSize: 12, terms: [] });
-  const { permission } = PermissionButton.usePermission('link/DataCollect/DataGathering');
+  const { permission } = PermissionButton.usePermission('DataCollect/Channel');
   const [loading, setLoading] = useState<boolean>(true);
   const [dataSource, setDataSource] = useState<any>({
     data: [],
@@ -39,7 +36,6 @@ export default observer((props: Props) => {
     pageIndex: 0,
     total: 0,
   });
-
   const columns: ProColumns<ChannelItem>[] = [
     {
       title: '通道名称',
@@ -154,7 +150,7 @@ export default observer((props: Props) => {
   };
 
   return (
-    <div>
+    <PageContainer>
       <SearchComponent<ChannelItem>
         field={columns}
         target="data-collect-channel"
@@ -172,11 +168,24 @@ export default observer((props: Props) => {
         style={{ position: 'relative', minHeight }}
         className={'data-collect-channel-card'}
       >
+        <div style={{ width: '100%', display: 'flex', justifyContent: 'flex-start' }}>
+          <PermissionButton
+            isPermission={permission.add}
+            onClick={() => {
+              ChannelModel.visible = true;
+              ChannelModel.current = {};
+            }}
+            key="button"
+            type="primary"
+          >
+            新增通道
+          </PermissionButton>
+        </div>
         <div style={{ height: '100%', paddingBottom: 48 }}>
           {dataSource?.data.length > 0 ? (
             <Row gutter={[24, 24]} style={{ marginTop: 10 }}>
               {(dataSource?.data || []).map((record: any) => (
-                <Col key={record.id} span={props.type ? 8 : 12}>
+                <Col key={record.id} span={8}>
                   <ChannelCard
                     {...record}
                     state={getState(record)}
@@ -331,6 +340,6 @@ export default observer((props: Props) => {
           }}
         />
       )}
-    </div>
+    </PageContainer>
   );
 });

+ 107 - 67
src/pages/link/DataCollect/components/Device/Save/index.tsx

@@ -1,63 +1,92 @@
 import { Button, Modal } from 'antd';
-import { createForm, Field, onFieldReact } from '@formily/core';
+import { createForm, Field, onFieldReact, onFieldValueChange, onFormInit } from '@formily/core';
 import { createSchemaField } from '@formily/react';
-import React, { useEffect } from 'react';
+import React, { useMemo, useRef } from 'react';
 import * as ICONS from '@ant-design/icons';
 import { Form, FormGrid, FormItem, Input, Select, NumberPicker, Password } from '@formily/antd';
 import type { ISchema } from '@formily/json-schema';
-import service from '@/pages/link/DataCollect/service';
+import service from '@/pages/DataCollect/service';
 import { onlyMessage } from '@/utils/util';
 import { RadioCard } from '@/components';
 
 interface Props {
-  channelId?: string;
   data: Partial<CollectorItem>;
   close: () => void;
   reload: () => void;
-  provider?: 'OPC_UA' | 'MODBUS_TCP';
 }
 
 export default (props: Props) => {
-  const form = createForm({
-    validateFirst: true,
-    initialValues: Object.keys(props.data).length
-      ? props.data
-      : {
-          circuitBreaker: {
-            type: 'LowerFrequency',
-          },
-        },
-    effects: () => {
-      onFieldReact('circuitBreaker.type', async (field, f) => {
-        const func = (field as Field).value;
-        f.setFieldState('circuitBreaker.type', (state) => {
-          let tooltip = '';
-          if (func === 'LowerFrequency') {
-            tooltip =
-              '连续20次异常,降低连接频率至原有频率的1/10(重试间隔不超过1分钟),故障处理后自动恢复至设定连接频率';
-          } else if (func === 'Break') {
-            tooltip = '连续10分钟异常,停止采集数据进入熔断状态,设备重新启用后恢复采集状态';
-          } else if (func === 'Ignore') {
-            tooltip = '忽略异常,保持原采集频率超时时间为5s';
-          }
-          state.decoratorProps = {
-            tooltip: tooltip,
-            gridSpan: 2,
-          };
-        });
-      });
-    },
-  });
+  const channelRef = useRef<any>(null);
+  const channelListRef = useRef<any[]>([]);
 
-  useEffect(() => {
-    if (props.data?.id) {
-      service.queryCollectorByID(props.data?.id).then((resp) => {
-        if (resp.status === 200) {
-          form.setValues(resp.result);
-        }
-      });
-    }
-  }, [props.data]);
+  const form = useMemo(
+    () =>
+      createForm({
+        validateFirst: true,
+        effects: () => {
+          onFormInit(async (f) => {
+            const response = await service.queryChannelNoPaging({});
+            if (response.status === 200) {
+              channelListRef.current = response.result;
+              f.setFieldState('channelId', (state) => {
+                state.dataSource = (response?.result || []).map((item: ChannelItem) => {
+                  return {
+                    value: item.id,
+                    label: item.name,
+                  };
+                });
+              });
+            }
+            if (props.data?.id) {
+              const resp = await service.queryCollectorByID(props.data?.id);
+              if (resp.status === 200) {
+                form.setInitialValues(resp.result);
+              }
+            } else {
+              f.setInitialValues({
+                circuitBreaker: {
+                  type: 'LowerFrequency',
+                },
+              });
+            }
+          });
+          onFieldValueChange('channelId', (field, f) => {
+            const value = (field as Field).value;
+            if (value) {
+              const dt = channelListRef.current.find((item) => item.id === value);
+              channelRef.current = dt;
+              if (dt?.provider && dt?.provider === 'MODBUS_TCP') {
+                f.setFieldState('configuration.unitId', (state) => {
+                  state.visible = true;
+                });
+                f.setFieldState('configuration.endian', (state) => {
+                  state.visible = true;
+                });
+              }
+            }
+          });
+          onFieldReact('circuitBreaker.type', async (field, f) => {
+            const func = (field as Field).value;
+            f.setFieldState('circuitBreaker.type', (state) => {
+              let tooltip = '';
+              if (func === 'LowerFrequency') {
+                tooltip =
+                  '连续20次异常,降低连接频率至原有频率的1/10(重试间隔不超过1分钟),故障处理后自动恢复至设定连接频率';
+              } else if (func === 'Break') {
+                tooltip = '连续10分钟异常,停止采集数据进入熔断状态,设备重新启用后恢复采集状态';
+              } else if (func === 'Ignore') {
+                tooltip = '忽略异常,保持原采集频率超时时间为5s';
+              }
+              state.decoratorProps = {
+                tooltip: tooltip,
+                gridSpan: 2,
+              };
+            });
+          });
+        },
+      }),
+    [props.data?.id],
+  );
 
   const SchemaField = createSchemaField({
     components: {
@@ -88,6 +117,23 @@ export default (props: Props) => {
           columnGap: 24,
         },
         properties: {
+          channelId: {
+            title: '所属通道',
+            'x-component': 'Select',
+            'x-decorator': 'FormItem',
+            'x-decorator-props': {
+              gridSpan: 2,
+            },
+            'x-component-props': {
+              placeholder: '请选择所属通道',
+            },
+            'x-validator': [
+              {
+                required: true,
+                message: '请选择所属通道',
+              },
+            ],
+          },
           name: {
             title: '采集器名称',
             'x-component': 'Input',
@@ -119,7 +165,7 @@ export default (props: Props) => {
             'x-component-props': {
               placeholder: '请输入从机地址',
             },
-            'x-visible': props.provider === 'MODBUS_TCP',
+            'x-visible': false,
             'x-validator': [
               {
                 required: true,
@@ -167,15 +213,15 @@ export default (props: Props) => {
             ],
           },
           'configuration.endian': {
-            title: '大小端',
+            title: '高低位切换',
             'x-component': 'RadioCard',
             'x-decorator': 'FormItem',
             'x-decorator-props': {
               gridSpan: 2,
-              tooltip: '统一配置所有点位的大小端',
+              tooltip: '统一配置所有点位的高低位切换',
             },
             'x-component-props': {
-              placeholder: '请选择大小端',
+              placeholder: '请选择高低位切换',
               model: 'singular',
               itemStyle: {
                 display: 'flex',
@@ -185,11 +231,11 @@ export default (props: Props) => {
                 height: '50px',
               },
               options: [
-                { label: '大端', value: 'BIG' },
-                { label: '小端', value: 'LITTLE' },
+                { label: 'AB', value: 'BIG' },
+                { label: 'BA', value: 'LITTLE' },
               ],
             },
-            'x-visible': props.provider === 'MODBUS_TCP',
+            'x-visible': false,
             default: 'BIG',
             'x-validator': [
               {
@@ -223,21 +269,15 @@ export default (props: Props) => {
     if (props.data?.id) {
       response = await service.updateCollector(props.data?.id, { ...props.data, ...value });
     } else {
-      if (props.channelId) {
-        const resp = await service.queryChannelByID(props.channelId);
-        if (resp.status === 200) {
-          const obj = {
-            ...value,
-            provider: resp.result.provider,
-            channelId: props.channelId,
-            channelName: resp.result.name,
-            configuration: {
-              ...value.configuration,
-            },
-          };
-          response = await service.saveCollector({ ...obj });
-        }
-      }
+      const obj = {
+        ...value,
+        provider: channelRef.current?.provider,
+        channelName: channelRef.current?.name,
+        configuration: {
+          ...value.configuration,
+        },
+      };
+      response = await service.saveCollector({ ...obj });
     }
     if (response && response?.status === 200) {
       onlyMessage('操作成功');
@@ -249,7 +289,7 @@ export default (props: Props) => {
     <Modal
       title={props?.data?.id ? '编辑' : '新增'}
       maskClosable={false}
-      visible
+      open
       onCancel={props.close}
       width={700}
       footer={[

+ 2 - 2
src/pages/link/DataCollect/components/Device/index.tsx

@@ -3,7 +3,7 @@ import SearchComponent from '@/components/SearchComponent';
 import type { ProColumns } from '@jetlinks/pro-table';
 import { useEffect, useState } from 'react';
 import { useDomFullHeight } from '@/hooks';
-import service from '@/pages/link/DataCollect/service';
+import service from '@/pages/DataCollect/service';
 import CollectorCard from '@/components/ProTableCard/CardItems/DataCollect/device';
 import { Empty, PermissionButton } from '@/components';
 import { useIntl } from '@@/plugin-locale/localeExports';
@@ -17,7 +17,7 @@ import {
 import { onlyMessage } from '@/utils/util';
 import { Card, Col, Pagination, Row } from 'antd';
 import { model } from '@formily/reactive';
-import Save from '@/pages/link/DataCollect/components/Device/Save/index';
+import Save from '@/pages/DataCollect/Collector/components/Device/Save';
 
 interface Props {
   type: boolean; // true: 综合查询  false: 数据采集

+ 151 - 0
src/pages/DataCollect/Collector/components/Point/CollectorCard/WritePoint.tsx

@@ -0,0 +1,151 @@
+import { Modal } from 'antd';
+import { FormItem, Input, Form, Select, ArrayTable, DatePicker, NumberPicker } from '@formily/antd';
+import { createForm, onFormInit } from '@formily/core';
+import { createSchemaField } from '@formily/react';
+import service from '../../../../service';
+import { onlyMessage } from '@/utils/util';
+import GeoComponent from '@/pages/device/Instance/Detail/Tags/location/GeoComponent';
+import { MetadataJsonInput } from '@/components';
+import { useMemo } from 'react';
+
+interface Props {
+  data: Partial<PointItem>;
+  onCancel: () => void;
+}
+
+const WritePoint = (props: Props) => {
+  const { data } = props;
+
+  const SchemaField = createSchemaField({
+    components: {
+      Input,
+      FormItem,
+      Select,
+      ArrayTable,
+      DatePicker,
+      NumberPicker,
+      GeoComponent,
+      MetadataJsonInput,
+    },
+  });
+
+  const form = useMemo(
+    () =>
+      createForm({
+        effects() {
+          onFormInit((f) => {
+            let valueType: string =
+              props.data?.provider === 'OPC_UA'
+                ? props?.data?.configuration?.type || 'Number'
+                : props.data?.configuration?.codec?.provider || 'int8';
+            valueType = valueType.toLocaleLowerCase();
+            switch (valueType) {
+              case 'boolean':
+                f.setFieldState('propertyValue', async (state) => {
+                  state.dataSource = [
+                    {
+                      label: '是',
+                      value: true,
+                    },
+                    {
+                      label: '否',
+                      value: false,
+                    },
+                  ];
+                  state.componentProps = {
+                    placeholder: '请选择',
+                  };
+                  state.componentType = 'Select';
+                });
+                break;
+              case 'int8':
+              case 'int16':
+              case 'int32':
+              case 'int64':
+              case 'ieee754_float':
+              case 'ieee754_double':
+              case 'hex':
+              case 'number':
+                f.setFieldState('propertyValue', (state) => {
+                  state.componentType = 'NumberPicker';
+                  state.componentProps = {
+                    placeholder: '请输入',
+                  };
+                });
+                break;
+              case 'date':
+                f.setFieldState('propertyValue', (state) => {
+                  state.componentType = 'DatePicker';
+                  state.componentProps = {
+                    placeholder: '请选择',
+                    format: 'YYYY-MM-DD HH:mm:ss',
+                  };
+                });
+                break;
+              default:
+                f.setFieldState('propertyValue', (state) => {
+                  state.componentType = 'Input';
+                  state.componentProps = {
+                    placeholder: '请输入',
+                  };
+                });
+                break;
+            }
+          });
+        },
+      }),
+    [props.data?.id],
+  );
+  const schema = {
+    type: 'object',
+    properties: {
+      propertyValue: {
+        type: 'string',
+        title: data?.name || '自定义属性',
+        required: true,
+        'x-decorator': 'FormItem',
+        'x-component': 'Input',
+        'x-component-props': {
+          placeholder: '请输入',
+        },
+      },
+    },
+  };
+
+  const handleSetPropertyValue = async (propertyValue: string) => {
+    if (data?.collectorId && data?.id) {
+      const resp = await service.writePoint(data.collectorId, [
+        {
+          pointId: data.id,
+          value: propertyValue,
+        },
+      ]);
+      if (resp.status === 200) {
+        onlyMessage('操作成功');
+      }
+      props.onCancel();
+    }
+  };
+  return (
+    <Modal
+      maskClosable={false}
+      title="写入"
+      visible
+      onOk={async () => {
+        const values: any = await form.submit();
+        if (!!values) {
+          handleSetPropertyValue(values?.propertyValue);
+        }
+      }}
+      onCancel={() => {
+        props.onCancel();
+      }}
+    >
+      <Form form={form} layout="vertical">
+        <SchemaField schema={schema} />
+      </Form>
+    </Modal>
+  );
+};
+
+export default WritePoint;

+ 1 - 1
src/pages/link/DataCollect/components/Point/CollectorCard/index.less

@@ -1,4 +1,4 @@
-@import '~antd/es/style/themes/default.less';
+@import '../../../../../../../node_modules/antd/es/style/themes/default.less';
 
 .card-item {
   display: flex;

+ 2 - 2
src/pages/link/DataCollect/components/Point/CollectorCard/index.tsx

@@ -9,7 +9,7 @@ import {
   FormOutlined,
   RedoOutlined,
 } from '@ant-design/icons';
-import service from '@/pages/link/DataCollect/service';
+import service from '@/pages/DataCollect/service';
 import { onlyMessage } from '@/utils/util';
 import moment from 'moment';
 import classNames from 'classnames';
@@ -29,7 +29,7 @@ const modbusImage = require('/public/images/DataCollect/device-modbus.png');
 const CollectorCard = (props: PointCardProps) => {
   const { item, wsValue } = props;
   const [spinning, setSpinning] = useState<boolean>(false);
-  const { permission } = PermissionButton.usePermission('link/DataCollect/DataGathering');
+  const { permission } = PermissionButton.usePermission('DataCollect/Collector');
 
   const read = async () => {
     if (item?.collectorId && item?.id) {

+ 1 - 1
src/pages/link/DataCollect/components/Point/Save/BatchUpdate.tsx

@@ -3,7 +3,7 @@ import { FormItem, Checkbox, NumberPicker, FormGrid, Form } from '@formily/antd'
 import { createForm, registerValidateRules } from '@formily/core';
 import { createSchemaField } from '@formily/react';
 import RadioCard from '@/components/RadioCard';
-import service from '@/pages/link/DataCollect/service';
+import service from '@/pages/DataCollect/service';
 import { onlyMessage } from '@/utils/util';
 
 interface Props {

src/pages/link/DataCollect/components/Point/Save/components/MyInput.tsx → src/pages/DataCollect/Collector/components/Point/Save/components/MyInput.tsx


src/pages/link/DataCollect/components/Point/Save/components/MySelect.tsx → src/pages/DataCollect/Collector/components/Point/Save/components/MySelect.tsx


src/pages/link/DataCollect/components/Point/Save/components/RemoveData.tsx → src/pages/DataCollect/Collector/components/Point/Save/components/RemoveData.tsx


+ 122 - 36
src/pages/link/DataCollect/components/Point/Save/modbus.tsx

@@ -12,9 +12,10 @@ import {
   NumberPicker,
   Password,
   Checkbox,
+  Switch,
 } from '@formily/antd';
 import type { ISchema } from '@formily/json-schema';
-import service from '@/pages/link/DataCollect/service';
+import service from '@/pages/DataCollect/service';
 import { onlyMessage } from '@/utils/util';
 import { action } from '@formily/reactive';
 import { RadioCard } from '@/components';
@@ -27,7 +28,7 @@ interface Props {
 }
 
 export default (props: Props) => {
-  const [data, setData] = useState<Partial<PointItem>>(props.data);
+  const [data, setData] = useState<any>(props.data);
   useEffect(() => {
     setData({
       ...props.data,
@@ -37,6 +38,16 @@ export default (props: Props) => {
       features: props.data?.features
         ? (props.data?.features || []).map((item: any) => item?.value)
         : [],
+      configuration: {
+        ...props.data?.configuration,
+        parameter: {
+          ...props.data?.configuration?.parameter,
+          writeByteCount: [props.data?.configuration?.parameter?.writeByteCount],
+        },
+      },
+      nspwc:
+        props?.data?.configuration?.parameter?.writeByteCount ||
+        props?.data?.configuration?.parameter?.byteCount,
     });
   }, [props.data]);
 
@@ -55,6 +66,7 @@ export default (props: Props) => {
       FormGrid,
       Checkbox,
       RadioCard,
+      Switch,
     },
     scope: {
       icon(name: any) {
@@ -197,39 +209,6 @@ export default (props: Props) => {
               },
             ],
           },
-          // 'configuration.codec.configuration.readIndex': {
-          //   title: '起始位置',
-          //   'x-component': 'NumberPicker',
-          //   'x-decorator': 'FormItem',
-          //   'x-decorator-props': {
-          //     gridSpan: 2,
-          //   },
-          //   'x-component-props': {
-          //     placeholder: '请输入起始位置',
-          //     stringMode: true,
-          //   },
-          //   'x-reactions': {
-          //     dependencies: ['...function'],
-          //     fulfill: {
-          //       state: {
-          //         visible: '{{$deps[0] === "HoldingRegisters"}}',
-          //       },
-          //     },
-          //   },
-          //   'x-validator': [
-          //     {
-          //       required: true,
-          //       message: '请输入起始位置',
-          //     },
-          //     {
-          //       min: 1,
-          //       message: '请输入非0正整数',
-          //     },
-          //     {
-          //       checkLength: true,
-          //     },
-          //   ],
-          // },
           'configuration.parameter.quantity': {
             title: '寄存器数量',
             'x-component': 'NumberPicker',
@@ -349,6 +328,111 @@ export default (props: Props) => {
               },
             ],
           },
+          nspwc: {
+            title: '非标准协议写入配置',
+            'x-component': 'Switch',
+            'x-decorator': 'FormItem',
+            'x-decorator-props': {
+              gridSpan: 2,
+              layout: 'level',
+            },
+            'x-component-props': {
+              placeholder: '请选择非标准协议写入配置',
+            },
+            'x-reactions': {
+              dependencies: ['.accessModes', 'configuration.function'],
+              fulfill: {
+                state: {
+                  visible: '{{$deps[0].includes("write") && $deps[1] === "HoldingRegisters"}}',
+                },
+              },
+            },
+          },
+          byte: {
+            type: 'void',
+            'x-decorator': 'FormItem',
+            'x-decorator-props': {
+              gridSpan: 2,
+              style: {
+                backgroundColor: '#fafafa',
+                padding: 20,
+              },
+            },
+            'x-component': 'FormGrid',
+            'x-component-props': {
+              maxColumns: 2,
+              minColumns: 2,
+              columnGap: 24,
+            },
+            'x-reactions': {
+              dependencies: ['.nspwc'],
+              fulfill: {
+                state: {
+                  visible: '{{!!$deps[0]}}',
+                },
+              },
+            },
+            properties: {
+              'configuration.parameter.writeByteCount': {
+                title: '是否写入数据区长度',
+                'x-component': 'RadioCard',
+                'x-decorator': 'FormItem',
+                'x-decorator-props': {
+                  gridSpan: 2,
+                  layout: 'vertical',
+                  labelAlign: 'left',
+                },
+                'x-component-props': {
+                  placeholder: '请选择是否写入数据区长度',
+                  model: 'singular',
+                  itemStyle: {
+                    display: 'flex',
+                    flexDirection: 'column',
+                    justifyContent: 'space-around',
+                    minWidth: '130px',
+                    height: '50px',
+                  },
+                  options: [
+                    { label: '是', value: true },
+                    { label: '否', value: false },
+                  ],
+                },
+                'x-validator': [
+                  {
+                    required: true,
+                    message: '请选择是否写入数据区长度',
+                  },
+                ],
+              },
+              'configuration.parameter.byteCount': {
+                title: '自定义数据区长度(byte)',
+                'x-component': 'NumberPicker',
+                'x-decorator': 'FormItem',
+                'x-decorator-props': {
+                  gridSpan: 2,
+                  layout: 'vertical',
+                  labelAlign: 'left',
+                },
+                'x-component-props': {
+                  placeholder: '请输入自定义数据区长度(byte)',
+                },
+                'x-validator': [
+                  {
+                    required: true,
+                    message: '请输入自定义数据区长度(byte)',
+                  },
+                ],
+                'x-reactions': {
+                  dependencies: ['configuration.parameter.quantity'],
+                  fulfill: {
+                    state: {
+                      value: '{{$deps[0]*2}}',
+                    },
+                  },
+                },
+              },
+            },
+          },
           'configuration.interval': {
             title: '采集频率',
             'x-component': 'NumberPicker',
@@ -425,11 +509,13 @@ export default (props: Props) => {
   };
 
   const save = async () => {
-    const value = await form.submit<PointItem>();
+    const value = await form.submit<any>();
+    console.log(value);
     const obj = {
       provider: props?.collector?.provider || 'MODBUS_TCP',
       collectorId: props?.collector?.id,
     };
+    delete value.nspwc;
     const response: any = props.data?.id
       ? await service.updatePoint(props.data?.id, {
           ...props.data,

+ 20 - 1
src/pages/link/DataCollect/components/Point/Save/opc-ua.tsx

@@ -14,7 +14,7 @@ import {
   Checkbox,
 } from '@formily/antd';
 import type { ISchema } from '@formily/json-schema';
-import service from '@/pages/link/DataCollect/service';
+import service from '@/pages/DataCollect/service';
 import { onlyMessage } from '@/utils/util';
 import { action } from '@formily/reactive';
 import { RadioCard } from '@/components';
@@ -129,6 +129,25 @@ export default (props: Props) => {
               },
             ],
           },
+          'configuration.type': {
+            title: '数据类型',
+            'x-component': 'Select',
+            'x-decorator': 'FormItem',
+            'x-decorator-props': {
+              gridSpan: 2,
+            },
+            'x-component-props': {
+              placeholder: '请选择数据类型',
+            },
+            enum: [
+              { value: 'Number', label: '数值类型' },
+              { value: 'DateTime', label: '时间类型' },
+              { value: 'Array', label: '数组类型' },
+              { value: 'String', label: '文本类型' },
+              { value: 'Boolean', label: '布尔' },
+              // {value: 'date', label: '时间类型'}
+            ],
+          },
           accessModes: {
             title: '访问类型',
             type: 'array',

src/pages/link/DataCollect/components/Point/Save/scan.less → src/pages/DataCollect/Collector/components/Point/Save/scan.less


+ 3 - 3
src/pages/link/DataCollect/components/Point/Save/scan.tsx

@@ -1,12 +1,12 @@
 import { Button, Col, Modal, Row, Spin, Tree, Checkbox } from 'antd';
 import { useEffect, useMemo, useState } from 'react';
-import service from '@/pages/link/DataCollect/service';
+import service from '@/pages/DataCollect/service';
 import './scan.less';
 import { onlyMessage } from '@/utils/util';
 import { createSchemaField, FormProvider } from '@formily/react';
 import { ArrayTable, FormItem, Input, Select } from '@formily/antd';
-import MyInput from '@/pages/link/DataCollect/components/Point/Save/components/MyInput';
-import MySelect from '@/pages/link/DataCollect/components/Point/Save/components/MySelect';
+import MyInput from '@/pages/DataCollect/Collector/components/Point/Save/components/MyInput';
+import MySelect from '@/pages/DataCollect/Collector/components/Point/Save/components/MySelect';
 import {
   createForm,
   Field,

+ 192 - 175
src/pages/link/DataCollect/components/Point/index.tsx

@@ -2,29 +2,27 @@ import { observer } from '@formily/react';
 import SearchComponent from '@/components/SearchComponent';
 import type { ProColumns } from '@jetlinks/pro-table';
 import { useEffect, useRef, useState } from 'react';
-import { useDomFullHeight } from '@/hooks';
-import service from '@/pages/link/DataCollect/service';
-import CollectorCard from './CollectorCard/index';
+// import { useDomFullHeight } from '@/hooks';
+import service from '@/pages/DataCollect/service';
+import CollectorCard from './CollectorCard';
 import { Empty, PermissionButton } from '@/components';
 import { Button, Card, Checkbox, Col, Dropdown, Menu, Pagination, Row } from 'antd';
 import { model } from '@formily/reactive';
-import ModbusSave from '@/pages/link/DataCollect/components/Point/Save/modbus';
-import Scan from '@/pages/link/DataCollect/components/Point/Save/scan';
+import ModbusSave from '@/pages/DataCollect/Collector/components/Point/Save/modbus';
+import Scan from '@/pages/DataCollect/Collector/components/Point/Save/scan';
 import { map } from 'rxjs/operators';
 import useSendWebsocketMessage from '@/hooks/websocket/useSendWebsocketMessage';
-import OpcSave from '@/pages/link/DataCollect/components/Point/Save/opc-ua';
-import WritePoint from '@/pages/link/DataCollect/components/Point/CollectorCard/WritePoint';
+import OpcSave from '@/pages/DataCollect/Collector/components/Point/Save/opc-ua';
+import WritePoint from '@/pages/DataCollect/Collector/components/Point/CollectorCard/WritePoint';
 import BatchUpdate from './Save/BatchUpdate';
 import { onlyMessage } from '@/utils/util';
 import { DeleteOutlined, EditOutlined, PlusOutlined, RedoOutlined } from '@ant-design/icons';
 interface Props {
-  type: boolean; // true: 综合查询  false: 数据采集
   data?: Partial<CollectorItem>;
   provider?: 'OPC_UA' | 'MODBUS_TCP';
 }
 
 interface PointCardProps {
-  type: boolean; // true: 综合查询  false: 数据采集
   data?: Partial<CollectorItem>;
   provider?: 'OPC_UA' | 'MODBUS_TCP';
   reload: boolean; // 变化时刷新
@@ -61,10 +59,10 @@ const PointModel = model<{
 
 const PointCard = observer((props: PointCardProps) => {
   const [subscribeTopic] = useSendWebsocketMessage();
-  const { minHeight } = useDomFullHeight(`.data-collect-point`);
+  // const { minHeight } = useDomFullHeight(`.data-collect-point`);
   const [param, setParam] = useState({ pageSize: 12, terms: [] });
   const [loading, setLoading] = useState<boolean>(true);
-  const { permission } = PermissionButton.usePermission('link/DataCollect/DataGathering');
+  const { permission } = PermissionButton.usePermission('DataCollect/Collector');
   const [propertyValue, setPropertyValue] = useState<any>({});
   const [dataSource, setDataSource] = useState<any>({
     data: [],
@@ -119,7 +117,9 @@ const PointCard = observer((props: PointCardProps) => {
         terms: [
           ...params?.terms,
           {
-            terms: [{ column: 'collectorId', value: props.data?.id }],
+            terms: [
+              { column: 'collectorId', value: props.data?.id !== '*' ? props.data?.id : undefined },
+            ],
           },
         ],
         sorts: [{ name: 'id', order: 'desc' }],
@@ -230,12 +230,12 @@ const PointCard = observer((props: PointCardProps) => {
         loading={loading}
         bordered={false}
         className={'data-collect-point'}
-        style={{ position: 'relative', minHeight }}
-        bodyStyle={{ paddingTop: !props.type ? 4 : 24 }}
+        style={{ position: 'relative', minHeight: 600 }}
+        bodyStyle={{ paddingTop: 4 }}
       >
         <div>
           <div style={{ height: '100%', paddingBottom: 48 }}>
-            {!props.type && (
+            {props.data?.id !== '*' && (
               <div style={{ width: '100%', display: 'flex', justifyContent: 'flex-start' }}>
                 <PermissionButton
                   isPermission={permission.add}
@@ -290,7 +290,7 @@ const PointCard = observer((props: PointCardProps) => {
                   {(dataSource?.data || []).map((record: any) => (
                     <Col
                       key={record.id}
-                      xl={props.type ? 12 : 24}
+                      xl={24}
                       xxl={12}
                       span={24}
                       onClick={() => {
@@ -365,7 +365,7 @@ const PointCard = observer((props: PointCardProps) => {
                 </div>
               </>
             ) : (
-              <div style={{ height: minHeight - 150 }}>
+              <div style={{ height: 600 }}>
                 <Empty />
               </div>
             )}
@@ -377,170 +377,187 @@ const PointCard = observer((props: PointCardProps) => {
 });
 
 export default observer((props: Props) => {
-  const columns: ProColumns<PointItem>[] = props.type
-    ? [
-        {
-          title: '点位名称',
-          dataIndex: 'name',
-        },
-        {
-          title: '通讯协议',
-          dataIndex: 'provider',
-          valueType: 'select',
-          valueEnum: {
-            OPC_UA: {
-              text: 'OPC_UA',
-              status: 'OPC_UA',
-            },
-            MODBUS_TCP: {
-              text: 'MODBUS_TCP',
-              status: 'MODBUS_TCP',
-            },
+  const columns: ProColumns<PointItem>[] =
+    // props.type
+    // ? [
+    //     {
+    //       title: '点位名称',
+    //       dataIndex: 'name',
+    //     },
+    //     {
+    //       title: '通讯协议',
+    //       dataIndex: 'provider',
+    //       valueType: 'select',
+    //       valueEnum: {
+    //         OPC_UA: {
+    //           text: 'OPC_UA',
+    //           status: 'OPC_UA',
+    //         },
+    //         MODBUS_TCP: {
+    //           text: 'MODBUS_TCP',
+    //           status: 'MODBUS_TCP',
+    //         },
+    //       },
+    //     },
+    //     {
+    //       title: '访问类型',
+    //       dataIndex: 'accessModes$in$any',
+    //       valueType: 'select',
+    //       valueEnum: {
+    //         read: {
+    //           text: '读',
+    //           status: 'read',
+    //         },
+    //         write: {
+    //           text: '写',
+    //           status: 'write',
+    //         },
+    //         subscribe: {
+    //           text: '订阅',
+    //           status: 'subscribe',
+    //         },
+    //       },
+    //       renderText: (_, record) => record.accessModes,
+    //     },
+    //     // {
+    //     //   title: '状态',
+    //     //   dataIndex: 'state',
+    //     //   valueType: 'select',
+    //     //   valueEnum: {
+    //     //     enabled: {
+    //     //       text: '正常',
+    //     //       status: 'enabled',
+    //     //     },
+    //     //     disabled: {
+    //     //       text: '禁用',
+    //     //       status: 'disabled',
+    //     //     },
+    //     //   },
+    //     // },
+    //     {
+    //       title: '运行状态',
+    //       dataIndex: 'runningState',
+    //       valueType: 'select',
+    //       valueEnum: {
+    //         running: {
+    //           text: '运行中',
+    //           status: 'running',
+    //         },
+    //         partialError: {
+    //           text: '部分错误',
+    //           status: 'partialError',
+    //         },
+    //         failed: {
+    //           text: '错误',
+    //           status: 'failed',
+    //         },
+    //         stopped: {
+    //           text: '已停止',
+    //           status: 'stopped',
+    //         },
+    //       },
+    //     },
+    //     {
+    //       title: '说明',
+    //       dataIndex: 'description',
+    //     },
+    //   ]
+    // :
+    [
+      {
+        title: '点位名称',
+        dataIndex: 'name',
+      },
+      {
+        title: '通讯协议',
+        dataIndex: 'provider',
+        valueType: 'select',
+        valueEnum: {
+          OPC_UA: {
+            text: 'OPC_UA',
+            status: 'OPC_UA',
           },
-        },
-        {
-          title: '访问类型',
-          dataIndex: 'accessModes$in$any',
-          valueType: 'select',
-          valueEnum: {
-            read: {
-              text: '读',
-              status: 'read',
-            },
-            write: {
-              text: '写',
-              status: 'write',
-            },
-            subscribe: {
-              text: '订阅',
-              status: 'subscribe',
-            },
+          MODBUS_TCP: {
+            text: 'MODBUS_TCP',
+            status: 'MODBUS_TCP',
           },
-          renderText: (_, record) => record.accessModes,
-        },
-        // {
-        //   title: '状态',
-        //   dataIndex: 'state',
-        //   valueType: 'select',
-        //   valueEnum: {
-        //     enabled: {
-        //       text: '正常',
-        //       status: 'enabled',
-        //     },
-        //     disabled: {
-        //       text: '禁用',
-        //       status: 'disabled',
-        //     },
-        //   },
-        // },
-        {
-          title: '运行状态',
-          dataIndex: 'runningState',
-          valueType: 'select',
-          valueEnum: {
-            running: {
-              text: '运行中',
-              status: 'running',
-            },
-            partialError: {
-              text: '部分错误',
-              status: 'partialError',
-            },
-            failed: {
-              text: '错误',
-              status: 'failed',
-            },
-            stopped: {
-              text: '已停止',
-              status: 'stopped',
-            },
-          },
-        },
-        {
-          title: '说明',
-          dataIndex: 'description',
-        },
-      ]
-    : [
-        {
-          title: '点位名称',
-          dataIndex: 'name',
         },
-        {
-          title: '访问类型',
-          dataIndex: 'accessModes$in$any',
-          valueType: 'select',
-          renderText: (_, record) => record.accessModes,
-          valueEnum:
-            props?.provider === 'MODBUS_TCP'
-              ? {
-                  read: {
-                    text: '读',
-                    status: 'read',
-                  },
-                  write: {
-                    text: '写',
-                    status: 'write',
-                  },
-                }
-              : {
-                  read: {
-                    text: '读',
-                    status: 'read',
-                  },
-                  write: {
-                    text: '写',
-                    status: 'write',
-                  },
-                  subscribe: {
-                    text: '订阅',
-                    status: 'subscribe',
-                  },
+      },
+      {
+        title: '访问类型',
+        dataIndex: 'accessModes$in$any',
+        valueType: 'select',
+        renderText: (_, record) => record.accessModes,
+        valueEnum:
+          props?.provider === 'MODBUS_TCP'
+            ? {
+                read: {
+                  text: '读',
+                  status: 'read',
                 },
-        },
-        // {
-        //   title: '状态',
-        //   dataIndex: 'state',
-        //   valueType: 'select',
-        //   valueEnum: {
-        //     enabled: {
-        //       text: '正常',
-        //       status: 'enabled',
-        //     },
-        //     disabled: {
-        //       text: '禁用',
-        //       status: 'disabled',
-        //     },
-        //   },
-        // },
-        {
-          title: '运行状态',
-          dataIndex: 'runningState',
-          valueType: 'select',
-          valueEnum: {
-            running: {
-              text: '运行中',
-              status: 'running',
-            },
-            partialError: {
-              text: '部分错误',
-              status: 'partialError',
-            },
-            failed: {
-              text: '错误',
-              status: 'failed',
-            },
-            stopped: {
-              text: '已停止',
-              status: 'stopped',
-            },
+                write: {
+                  text: '写',
+                  status: 'write',
+                },
+              }
+            : {
+                read: {
+                  text: '读',
+                  status: 'read',
+                },
+                write: {
+                  text: '写',
+                  status: 'write',
+                },
+                subscribe: {
+                  text: '订阅',
+                  status: 'subscribe',
+                },
+              },
+      },
+      // {
+      //   title: '状态',
+      //   dataIndex: 'state',
+      //   valueType: 'select',
+      //   valueEnum: {
+      //     enabled: {
+      //       text: '正常',
+      //       status: 'enabled',
+      //     },
+      //     disabled: {
+      //       text: '禁用',
+      //       status: 'disabled',
+      //     },
+      //   },
+      // },
+      {
+        title: '运行状态',
+        dataIndex: 'runningState',
+        valueType: 'select',
+        valueEnum: {
+          running: {
+            text: '运行中',
+            status: 'running',
+          },
+          partialError: {
+            text: '部分错误',
+            status: 'partialError',
+          },
+          failed: {
+            text: '错误',
+            status: 'failed',
+          },
+          stopped: {
+            text: '已停止',
+            status: 'stopped',
           },
         },
-        {
-          title: '说明',
-          dataIndex: 'description',
-        },
-      ];
+      },
+      {
+        title: '说明',
+        dataIndex: 'description',
+      },
+    ];
   return (
     <div>
       <PointCard columns={columns} {...props} reload={PointModel.reload} />

+ 1 - 2
src/pages/link/DataCollect/components/Tree/index.less

@@ -8,14 +8,13 @@
   display: flex;
   align-items: center;
   justify-content: space-between;
-  width: 240px;
+  width: 220px;
   overflow: hidden;
 }
 
 .title {
   display: flex;
   align-items: center;
-  width: 200px;
 }
 
 .iconColor {

+ 257 - 0
src/pages/DataCollect/Collector/components/Tree/index.tsx

@@ -0,0 +1,257 @@
+import {
+  PlusOutlined,
+  FormOutlined,
+  DeleteOutlined,
+  StopOutlined,
+  PlayCircleOutlined,
+  DownOutlined,
+} from '@ant-design/icons';
+import { Button, Input, Tree, Space, Popconfirm, Tooltip, Tag } from 'antd';
+import { observer } from '@formily/react';
+import { model } from '@formily/reactive';
+import { Empty, PermissionButton } from '@/components';
+import styles from './index.less';
+import service from '@/pages/DataCollect/service';
+import { useEffect } from 'react';
+import CollectorSave from '../Device/Save';
+import { onlyMessage } from '@/utils/util';
+import { useIntl } from '@@/plugin-locale/localeExports';
+
+const TreeModel = model<{
+  selectedKeys: string[];
+  dataSource: any[];
+  loading: boolean;
+  param: any;
+  visible: boolean;
+  current: any;
+}>({
+  selectedKeys: ['*'],
+  dataSource: [],
+  loading: true,
+  param: {},
+  visible: false,
+  current: {},
+});
+interface Props {
+  change: (data?: any) => void;
+}
+
+export default observer((props: Props) => {
+  // const deviceImg = require('/public/images/DataCollect/tree-device.png');
+  const { permission } = PermissionButton.usePermission('DataCollect/Collector');
+  const intl = useIntl();
+
+  const handleSearch = (params: any) => {
+    TreeModel.loading = true;
+    TreeModel.param = params;
+    service
+      .queryCollector({ ...params, paging: false, sorts: [{ name: 'createTime', order: 'desc' }] })
+      .then((resp) => {
+        if (resp.status === 200) {
+          TreeModel.dataSource = [
+            {
+              id: '*',
+              name: '全部',
+              children: resp.result,
+            },
+          ];
+          props.change(TreeModel.dataSource[0]);
+        }
+        TreeModel.loading = false;
+      });
+  };
+
+  useEffect(() => {
+    handleSearch(TreeModel.param);
+  }, [TreeModel.param]);
+
+  const getState = (record: any) => {
+    if (record) {
+      const colorMap = new Map();
+      colorMap.set('running', 'success');
+      colorMap.set('partialError', 'warning');
+      colorMap.set('failed', 'error');
+      colorMap.set('stopped', 'default');
+      if (record?.state?.value === 'enabled') {
+        return (
+          record?.runningState && (
+            <Tag color={colorMap.get(record?.runningState?.value)}>
+              {record?.runningState?.text}
+            </Tag>
+          )
+        );
+      } else {
+        return <Tag color="processing">禁用</Tag>;
+      }
+    } else {
+      return '';
+    }
+  };
+
+  return (
+    <div>
+      <div>
+        <Input.Search
+          placeholder="请输入名称"
+          allowClear
+          onSearch={(val) => {
+            TreeModel.param = {
+              terms: [{ column: 'name', value: `%${val}%`, termType: 'like' }],
+            };
+          }}
+          style={{ width: '100%' }}
+        />
+      </div>
+      <div style={{ margin: '16px 0' }}>
+        <Button
+          type="primary"
+          ghost
+          style={{ width: '100%' }}
+          icon={<PlusOutlined />}
+          onClick={() => {
+            TreeModel.visible = true;
+            TreeModel.current = {};
+          }}
+        >
+          新增采集器
+        </Button>
+      </div>
+      <div>
+        {TreeModel.dataSource.length ? (
+          <Tree
+            style={{ overflow: 'hidden' }}
+            className={styles['data-collect-tree']}
+            showIcon
+            height={500}
+            selectedKeys={TreeModel.selectedKeys}
+            defaultExpandAll
+            switcherIcon={<DownOutlined />}
+            fieldNames={{
+              title: 'name',
+              key: 'id',
+            }}
+            titleRender={(i) => (
+              <div className={i.id !== '*' ? styles.treeTitle : {}}>
+                <div
+                  className={styles.title}
+                  onClick={() => {
+                    TreeModel.selectedKeys = [i.id];
+                    props.change(i);
+                  }}
+                >
+                  {/*{*/}
+                  {/*  i.id !== '*' && <img width={'20px'} style={{ marginRight: 5, width: 10 }} src={deviceImg} />*/}
+                  {/*}*/}
+                  <div style={{ display: 'flex' }}>
+                    <span
+                      className={'ellipsis'}
+                      style={{
+                        marginRight: 5,
+                        maxWidth: 85,
+                        color: 'rgba(0, 0, 0, 0.6)',
+                      }}
+                    >
+                      {i.name}
+                    </span>
+                    {i.id !== '*' && getState(i)}
+                  </div>
+                </div>
+                {i.id !== '*' && (
+                  <div>
+                    <Space className={styles.iconColor}>
+                      <Tooltip title={!permission.update ? '暂无权限,请联系管理员' : ''}>
+                        <FormOutlined
+                          onClick={() => {
+                            if (permission.update) {
+                              TreeModel.current = i;
+                              TreeModel.visible = true;
+                            }
+                          }}
+                        />
+                      </Tooltip>
+                      <Popconfirm
+                        title={intl.formatMessage({
+                          id: `pages.data.option.${
+                            i?.state?.value !== 'disabled' ? 'disabled' : 'enabled'
+                          }.tips`,
+                          defaultMessage: '确认禁用?',
+                        })}
+                        onConfirm={async () => {
+                          const resp =
+                            i?.state?.value !== 'disabled'
+                              ? await service.updateCollector(i.id, {
+                                  state: 'disabled',
+                                  runningState: 'stopped',
+                                })
+                              : await service.updateCollector(i.id, {
+                                  state: 'enabled',
+                                  runningState: 'running',
+                                });
+                          if (resp.status === 200) {
+                            TreeModel.param = {};
+                            handleSearch(TreeModel.param);
+                            onlyMessage('操作成功');
+                          } else {
+                            onlyMessage('操作失败!', 'error');
+                          }
+                        }}
+                      >
+                        <Tooltip title={!permission.action ? '暂无权限,请联系管理员' : ''}>
+                          {i?.state?.value !== 'disabled' ? (
+                            <StopOutlined />
+                          ) : (
+                            <PlayCircleOutlined />
+                          )}
+                        </Tooltip>
+                      </Popconfirm>
+                      <Popconfirm
+                        title={'该操作将会删除下属点位,确定删除?'}
+                        disabled={i?.state?.value !== 'disabled'}
+                        onConfirm={async () => {
+                          const resp = await service.removeCollector(i.id);
+                          if (resp.status === 200) {
+                            TreeModel.param = {};
+                            handleSearch(TreeModel.param);
+                            onlyMessage('操作成功');
+                          }
+                        }}
+                      >
+                        <Tooltip
+                          title={
+                            !permission.delete
+                              ? '暂无权限,请联系管理员'
+                              : i?.state?.value !== 'disabled'
+                              ? '正常的采集器不能删除'
+                              : ''
+                          }
+                        >
+                          <DeleteOutlined />
+                        </Tooltip>
+                      </Popconfirm>
+                    </Space>
+                  </div>
+                )}
+              </div>
+            )}
+            treeData={TreeModel.dataSource}
+          ></Tree>
+        ) : (
+          <Empty />
+        )}
+      </div>
+      {TreeModel.visible && (
+        <CollectorSave
+          data={TreeModel.current}
+          close={() => {
+            TreeModel.visible = false;
+          }}
+          reload={() => {
+            TreeModel.visible = false;
+            TreeModel.param = {};
+            handleSearch(TreeModel.param);
+          }}
+        />
+      )}
+    </div>
+  );
+});

src/pages/link/DataCollect/components/index.less → src/pages/DataCollect/Collector/components/index.less


src/pages/link/DataCollect/DataGathering/index.less → src/pages/DataCollect/Collector/index.less


+ 41 - 0
src/pages/DataCollect/Collector/index.tsx

@@ -0,0 +1,41 @@
+import { PageContainer } from '@ant-design/pro-layout';
+import { Card } from 'antd';
+import styles from './index.less';
+import CollectorTree from './components/Tree';
+import { observer } from '@formily/reactive-react';
+import { model } from '@formily/reactive';
+import Point from './components/Point';
+
+export const DataCollectModel = model<{
+  provider: 'OPC_UA' | 'MODBUS_TCP';
+  data: any;
+  reload: boolean;
+  refresh: boolean;
+}>({
+  provider: 'MODBUS_TCP',
+  data: {},
+  reload: false,
+  refresh: false,
+});
+
+export default observer(() => {
+  return (
+    <PageContainer>
+      <Card bordered={false} bodyStyle={{ paddingTop: 0 }}>
+        <div className={styles.container}>
+          <div className={styles.left}>
+            <CollectorTree
+              change={(data) => {
+                DataCollectModel.provider = data.provider;
+                DataCollectModel.data = data || {};
+              }}
+            />
+          </div>
+          <div className={styles.right}>
+            <Point provider={DataCollectModel.provider} data={DataCollectModel.data} />
+          </div>
+        </div>
+      </Card>
+    </PageContainer>
+  );
+});

src/pages/link/DataCollect/Dashboard/index.less → src/pages/DataCollect/Dashboard/index.less


src/pages/link/DataCollect/Dashboard/index.tsx → src/pages/DataCollect/Dashboard/index.tsx


+ 3 - 3
src/pages/link/DataCollect/service.ts

@@ -53,7 +53,7 @@ class Service {
       data: params,
     });
   public queryCollector = (params: any) =>
-    request(`/${SystemConst.API_BASE}/data-collect/collector/_query`, {
+    request(`/${SystemConst.API_BASE}/data-collect/collector/_query/no-paging?paging=false`, {
       method: 'POST',
       data: params,
     });
@@ -104,8 +104,8 @@ class Service {
     request(`/${SystemConst.API_BASE}/data-collect/channel/${id}`, {
       method: 'DELETE',
     });
-  public queryChannelTree = (params: ChannelItem) =>
-    request(`/${SystemConst.API_BASE}/data-collect/channel/_all/tree`, {
+  public queryChannelNoPaging = (params: any) =>
+    request(`/${SystemConst.API_BASE}/data-collect/channel/_query/no-paging?paging=false`, {
       method: 'POST',
       data: params,
     });

src/pages/link/DataCollect/typings.d.ts → src/pages/DataCollect/typings.d.ts


+ 12 - 3
src/pages/account/Center/index.tsx

@@ -11,7 +11,7 @@ import {
   Row,
   Upload,
 } from 'antd';
-import { useEffect, useState } from 'react';
+import { useEffect, useRef, useState } from 'react';
 import styles from './index.less';
 import { UploadProps } from 'antd/lib/upload';
 import Token from '@/utils/token';
@@ -38,6 +38,8 @@ const Center = () => {
   const [password, setPassword] = useState<boolean>(false);
   const [bindList, setBindList] = useState<any>([]);
   const [apiUser, setApiUser] = useState<any>();
+  const orgRef = useRef<any>('');
+  const roleRef = useRef<any>('');
 
   const iconMap = new Map();
   iconMap.set('dingtalk-ent-app', require('/public/images/notice/dingtalk.png'));
@@ -56,6 +58,9 @@ const Center = () => {
   const getDetail = () => {
     service.getUserDetail().subscribe((res) => {
       setData(res.result);
+      console.log('-------', res.result);
+      orgRef.current = res.result.orgList.map((item: any) => item.name).join(',');
+      roleRef.current = res.result.roleList.map((item: any) => item.name).join(',');
       setImageUrl(res.result.avatar);
     });
   };
@@ -201,8 +206,12 @@ const Center = () => {
               </Descriptions.Item>
               <Descriptions.Item label="电话">{data?.telephone || '-'}</Descriptions.Item>
               <Descriptions.Item label="姓名">{data?.name}</Descriptions.Item>
-              <Descriptions.Item label="角色">{data?.roleList[0]?.name || '-'}</Descriptions.Item>
-              <Descriptions.Item label="组织">{data?.orgList[0]?.name || '-'}</Descriptions.Item>
+              <Descriptions.Item label="角色">
+                <Ellipsis title={roleRef.current || '-'} tooltip={{ placement: 'topLeft' }} />
+              </Descriptions.Item>
+              <Descriptions.Item label="组织">
+                <Ellipsis title={orgRef.current || '-'} tooltip={{ placement: 'topLeft' }} />
+              </Descriptions.Item>
               <Descriptions.Item label="邮箱">{data?.email || '-'}</Descriptions.Item>
             </Descriptions>
           </div>

+ 14 - 13
src/pages/device/Instance/Detail/MetadataLog/Property/index.tsx

@@ -68,9 +68,9 @@ const PropertyLog = (props: Props) => {
       title: '操作',
       dataIndex: 'action',
       key: 'action',
-      render: (text: any, record: any) => (
-        <a>
-          {data.valueType?.type === 'file' && data?.valueType?.fileType == 'url' ? (
+      render: (text: any, record: any) => [
+        data.valueType?.type === 'file' ? (
+          <a style={{ marginRight: 20 }}>
             <ATooltip title="下载">
               <DownloadOutlined
                 onClick={() => {
@@ -89,16 +89,17 @@ const PropertyLog = (props: Props) => {
                 }}
               />
             </ATooltip>
-          ) : (
-            <SearchOutlined
-              onClick={() => {
-                setDetailVisible(true);
-                setCurrent(record);
-              }}
-            />
-          )}
-        </a>
-      ),
+          </a>
+        ) : null,
+        <a>
+          <SearchOutlined
+            onClick={() => {
+              setDetailVisible(true);
+              setCurrent(record);
+            }}
+          />
+        </a>,
+      ],
     },
   ];
 

+ 26 - 5
src/pages/device/Instance/Detail/Parsing/index.tsx

@@ -34,6 +34,8 @@ const Parsing = (props: Props) => {
   // const [data, setData] = useState<any>({});
   const [readOnly, setReadOnly] = useState<boolean>(true);
   const [topTitle, setTopTitle] = useState<string>();
+  const [disabled, setDisabled] = useState<boolean>(false);
+  const [isTest, setIsTest] = useState<boolean>(false);
 
   const editorDidMountHandle = (editor: any) => {
     editor.getAction('editor.action.formatDocument').run();
@@ -115,12 +117,12 @@ const Parsing = (props: Props) => {
     service.testCode(dataTest).then((res) => {
       if (res.status === 200) {
         setLoading(false);
-        onlyMessage('调试成功');
+        // onlyMessage('调试成功');
         setResultValue(res?.result);
-        console.log(res.result);
+        // console.log(res.result);
       } else {
         setLoading(false);
-        onlyMessage('调试失败', 'error');
+        // onlyMessage('调试失败', 'error');
       }
     });
   };
@@ -163,6 +165,14 @@ const Parsing = (props: Props) => {
     }
   }, []);
 
+  useEffect(() => {
+    if (value === '' && simulation === '') {
+      setDisabled(true);
+    } else {
+      setDisabled(false);
+    }
+  }, [value, simulation]);
+
   return (
     <Card className="parsing" style={{ minHeight }}>
       <div>
@@ -340,7 +350,12 @@ const Parsing = (props: Props) => {
               <div style={{ fontWeight: 600, fontSize: 14, marginTop: 10 }}>运行结果</div>
               <Input.TextArea
                 autoSize={{ minRows: 5 }}
-                style={{ marginTop: 10 }}
+                style={
+                  resultValue
+                    ? { marginTop: 10, borderColor: `${resultValue.success ? 'green' : 'red'}` }
+                    : { marginTop: 10 }
+                }
+                status={''}
                 value={
                   resultValue.success
                     ? JSON.stringify(resultValue.outputs?.[0])
@@ -356,7 +371,7 @@ const Parsing = (props: Props) => {
           <Button
             type="primary"
             loading={loading}
-            disabled={value !== '' && !simulation}
+            disabled={disabled}
             onClick={() => {
               if (type === 'MQTT') {
                 if (topic !== '') {
@@ -371,6 +386,7 @@ const Parsing = (props: Props) => {
                     provider: 'jsr223',
                     payload: simulation,
                   });
+                  setIsTest(true);
                 } else {
                   message.error('请输入topic');
                 }
@@ -387,6 +403,7 @@ const Parsing = (props: Props) => {
                     },
                     payload: simulation,
                   });
+                  setIsTest(true);
                 } else {
                   message.error('请输入url');
                 }
@@ -401,6 +418,10 @@ const Parsing = (props: Props) => {
           key={'update'}
           style={{ marginLeft: 10 }}
           isPermission={permission.update}
+          disabled={!isTest}
+          tooltip={{
+            title: isTest ? '' : '请先调试',
+          }}
           onClick={() => {
             if (props.tag === 'device') {
               saveDeviceCode(props.data.productId, props.data.id, {

+ 103 - 3
src/pages/device/Instance/Detail/Running/Property/EditProperty.tsx

@@ -1,11 +1,13 @@
 import { Alert, Modal } from 'antd';
-import { FormItem, Input } from '@formily/antd';
-import { createForm } from '@formily/core';
+import { ArrayTable, DatePicker, FormItem, Input, NumberPicker, Select } from '@formily/antd';
+import { createForm, onFormInit } from '@formily/core';
 import { createSchemaField, FormProvider } from '@formily/react';
 import { service } from '@/pages/device/Instance';
 import { useParams } from 'umi';
 import type { PropertyMetadata } from '@/pages/device/Product/typings';
 import { onlyMessage } from '@/utils/util';
+import GeoComponent from '@/pages/device/Instance/Detail/Tags/location/GeoComponent';
+import { MetadataJsonInput } from '@/components';
 
 interface Props {
   data: Partial<PropertyMetadata>;
@@ -20,10 +22,108 @@ const EditProperty = (props: Props) => {
     components: {
       Input,
       FormItem,
+      Select,
+      ArrayTable,
+      DatePicker,
+      NumberPicker,
+      GeoComponent,
+      MetadataJsonInput,
     },
   });
 
-  const form = createForm();
+  const form = createForm({
+    effects() {
+      onFormInit((f) => {
+        const valueType = data?.valueType?.type || data?.dataType;
+        switch (valueType) {
+          case 'enum':
+            f.setFieldState('propertyValue', async (state) => {
+              state.dataSource = (data?.valueType?.elements || []).map((i: any) => {
+                return {
+                  label: i?.text,
+                  value: i?.value,
+                };
+              });
+              state.componentProps = {
+                placeholder: '请选择',
+              };
+              state.componentType = 'Select';
+            });
+            break;
+          case 'boolean':
+            f.setFieldState('propertyValue', async (state) => {
+              state.dataSource = [
+                {
+                  label: data?.valueType?.trueText || '是',
+                  value: data?.valueType?.trueValue || true,
+                },
+                {
+                  label: data?.valueType?.falseText || '否',
+                  value: data?.valueType?.falseValue || false,
+                },
+              ];
+              state.componentProps = {
+                placeholder: '请选择',
+              };
+              state.componentType = 'Select';
+            });
+            break;
+          case 'int':
+          case 'long':
+          case 'float':
+          case 'double':
+            f.setFieldState('propertyValue', (state) => {
+              state.componentType = 'NumberPicker';
+              state.componentProps = {
+                placeholder: '请输入',
+              };
+            });
+            break;
+          case 'geoPoint':
+            f.setFieldState('propertyValue', (state) => {
+              state.componentType = 'GeoComponent';
+              state.componentProps = {
+                placeholder: '请输入',
+              };
+            });
+            break;
+          case 'object':
+            f.setFieldState('propertyValue', (state) => {
+              state.componentType = 'MetadataJsonInput';
+              state.componentProps = {
+                placeholder: '请输入',
+              };
+            });
+            break;
+          case 'array':
+            f.setFieldState('propertyValue', (state) => {
+              state.componentType = 'Input';
+              state.componentProps = {
+                placeholder: '多个数据用英文,分割',
+              };
+            });
+            break;
+          case 'date':
+            f.setFieldState('propertyValue', (state) => {
+              state.componentType = 'DatePicker';
+              state.componentProps = {
+                placeholder: '请选择',
+                format: 'YYYY-MM-DD HH:mm:ss',
+              };
+            });
+            break;
+          default:
+            f.setFieldState('propertyValue', (state) => {
+              state.componentType = 'Input';
+              state.componentProps = {
+                placeholder: '请输入',
+              };
+            });
+            break;
+        }
+      });
+    },
+  });
   const schema = {
     type: 'object',
     properties: {

+ 37 - 31
src/pages/device/Instance/Detail/Tags/Edit.tsx

@@ -147,6 +147,9 @@ const Edit = (props: Props) => {
                   type: 'string',
                   'x-decorator': 'FormItem',
                   'x-component': 'Input',
+                  'x-component-props': {
+                    readOnly: true,
+                  },
                 },
               },
             },
@@ -165,6 +168,9 @@ const Edit = (props: Props) => {
                   type: 'string',
                   'x-decorator': 'FormItem',
                   'x-component': 'Input',
+                  'x-component-props': {
+                    readOnly: true,
+                  },
                 },
               },
             },
@@ -201,39 +207,39 @@ const Edit = (props: Props) => {
                 },
               },
             },
-            column4: {
-              type: 'void',
-              'x-component': 'ArrayTable.Column',
-              'x-component-props': {
-                width: 100,
-                title: '操作',
-                dataIndex: 'operations',
-              },
-              properties: {
-                item: {
-                  type: 'void',
-                  'x-component': 'FormItem',
-                  properties: {
-                    remove: {
-                      type: 'void',
-                      'x-component': 'RemoveData',
-                      // 'x-component-props': {
-                      //   tags: tags,
-                      // },
-                    },
-                  },
-                },
-              },
-            },
-          },
-        },
-        properties: {
-          add: {
-            type: 'void',
-            'x-component': 'ArrayTable.Addition',
-            title: '添加',
+            // column4: {
+            //   type: 'void',
+            //   'x-component': 'ArrayTable.Column',
+            //   'x-component-props': {
+            //     width: 100,
+            //     title: '操作',
+            //     dataIndex: 'operations',
+            //   },
+            //   properties: {
+            //     item: {
+            //       type: 'void',
+            //       'x-component': 'FormItem',
+            //       properties: {
+            //         remove: {
+            //           type: 'void',
+            //           'x-component': 'RemoveData',
+            //           // 'x-component-props': {
+            //           //   tags: tags,
+            //           // },
+            //         },
+            //       },
+            //     },
+            //   },
+            // },
           },
         },
+        // properties: {
+        //   add: {
+        //     type: 'void',
+        //     'x-component': 'ArrayTable.Addition',
+        //     title: '添加',
+        //   },
+        // },
       },
     },
   };

+ 16 - 16
src/pages/device/components/Metadata/Base/index.tsx

@@ -5,14 +5,14 @@ import { useParams } from 'umi';
 import DB from '@/db';
 import type { MetadataItem, MetadataType } from '@/pages/device/Product/typings';
 import MetadataMapping from './columns';
-import { DeleteOutlined, EditOutlined, ImportOutlined, PlusOutlined } from '@ant-design/icons';
+import { DeleteOutlined, EditOutlined, PlusOutlined } from '@ant-design/icons';
 import Edit from './Edit';
 import { observer } from '@formily/react';
 import MetadataModel from './model';
 import { Store } from 'jetlinks-store';
 import SystemConst from '@/utils/const';
 import { useIntl } from '@@/plugin-locale/localeExports';
-import PropertyImport from '@/pages/device/Product/Detail/PropertyImport';
+// import PropertyImport from '@/pages/device/Product/Detail/PropertyImport';
 import { productModel } from '@/pages/device/Product';
 import { InstanceModel } from '@/pages/device/Instance';
 import { asyncUpdateMedata, removeMetadata } from '../metadata';
@@ -185,19 +185,19 @@ const BaseMetadata = observer((props: Props) => {
           },
         }}
         toolBarRender={() => [
-          props.type === 'properties' && target === 'device' && (
-            <PermissionButton
-              isPermission={props.permission.update}
-              onClick={() => {
-                MetadataModel.importMetadata = true;
-              }}
-              key="button"
-              icon={<ImportOutlined />}
-              type="ghost"
-            >
-              导入属性
-            </PermissionButton>
-          ),
+          // props.type === 'properties' && target === 'device' && (
+          //   <PermissionButton
+          //     isPermission={props.permission.update}
+          //     onClick={() => {
+          //       MetadataModel.importMetadata = true;
+          //     }}
+          //     key="button"
+          //     icon={<ImportOutlined />}
+          //     type="ghost"
+          //   >
+          //     导入属性
+          //   </PermissionButton>
+          // ),
           <PermissionButton
             isPermission={props.permission.update}
             key={'add'}
@@ -221,7 +221,7 @@ const BaseMetadata = observer((props: Props) => {
           </PermissionButton>,
         ]}
       />
-      {MetadataModel.importMetadata && <PropertyImport type={target} />}
+      {/*{MetadataModel.importMetadata && <PropertyImport type={target} />}*/}
       {MetadataModel.edit && <Edit type={target} tabs={type} />}
     </>
   );

+ 2 - 1
src/pages/device/components/Metadata/Cat/index.tsx

@@ -106,7 +106,8 @@ const Cat = observer((props: Props) => {
               }
             }}
           >
-            导出模型文件
+            {/*导出模型文件*/}
+            导出
           </Button>
         </Space>
       }

+ 2 - 0
src/pages/edge/Resource/Issue/Result.tsx

@@ -24,11 +24,13 @@ const Publish = (props: Props) => {
     const errMessages: any[] = [];
     const _terms = {
       deviceId: (props.list || []).map((item) => item.id),
+      // params: {
       name: props.data.name,
       targetId: props.data.targetId,
       targetType: props.data.targetType,
       category: props.data.category,
       metadata: encodeURIComponent(props.data?.metadata || ''),
+      // }
     };
     const url = new URLSearchParams();
     Object.keys(_terms).forEach((key) => {

+ 2 - 0
src/pages/home/index.tsx

@@ -9,6 +9,7 @@ import Service from './service';
 import { Skeleton } from 'antd';
 import { useModel } from '@@/plugin-model/useModel';
 import { isNoCommunity } from '@/utils/util';
+import SystemConst from '@/utils/const';
 
 export const service = new Service();
 const Home = () => {
@@ -49,6 +50,7 @@ const Home = () => {
         });
       }
     });
+    console.log('版本:' + localStorage.getItem(SystemConst.Version_Code));
   }, []);
   useEffect(() => {
     if (isNoCommunity) {

+ 1 - 0
src/pages/link/AccessConfig/Detail/components/CTWing/index.less

@@ -1,6 +1,7 @@
 .doc {
   height: 550px;
   padding: 24px;
+  overflow-x: hidden;
   overflow-y: auto;
   color: rgba(#000, 0.8);
   font-size: 14px;

+ 21 - 8
src/pages/link/AccessConfig/Detail/components/CTWing/index.tsx

@@ -1,6 +1,7 @@
 import { Button, Col, Form, Input, Row, Image } from 'antd';
 import { useEffect } from 'react';
 import styles from './index.less';
+import { randomString } from '@/utils/util';
 
 interface Props {
   next: (data: any) => void;
@@ -9,10 +10,10 @@ interface Props {
 
 const CTWing = (props: Props) => {
   const [form] = Form.useForm();
-  // const img = require('/public/images/network/CTWing.jpg');
-  const img1 = require('/public/images/network/01.jpg');
+  const img1 = require('/public/images/network/01.png');
   const img2 = require('/public/images/network/02.jpg');
-  const img3 = require('/public/images/network/03.jpg');
+  const img3 = require('/public/images/network/03.png');
+  const img4 = require('/public/images/network/04.jpg');
 
   useEffect(() => {
     form.setFieldsValue({
@@ -81,24 +82,36 @@ const CTWing = (props: Props) => {
       <Col span={8}>
         <div className={styles.doc}>
           <h1>操作指引:</h1>
-          <div>1、创建类型为CTWing的设备接入网关。</div>
+          <div>1、CTWing端创建产品、设备,以及一个第三方应用</div>
           <div>
-            2、创建产品,并选中接入方式为CTWing,选中后需填写CTWing平台中的产品ID、Master-APIkey。
+            2、CTWing端配置产品/设备/分组级订阅,订阅方URL地址请填写:
+            <div style={{ wordWrap: 'break-word' }}>
+              {origin}/api/ctwing/{randomString()}/notify
+            </div>
           </div>
           <div className={styles.image}>
             <Image width="100%" src={img1} />
           </div>
+          <div>3、IOT端创建类型为CTWing的设备接入网关</div>
           <div>
-            3、添加设备,为每一台设备设置唯一的IMEI、SN、IMSI、PSK码(需与CTWing平台中填写的值一致,若CTWing平台没有对应的设备,将会通过CTWing平台提供的LWM2M协议自动创建)
+            4、IOT端创建产品,选中接入方式为CTWing,填写CTWing平台中的产品ID、Master-APIkey。
           </div>
           <div className={styles.image}>
             <Image width="100%" src={img2} />
           </div>
-          <h1>配置说明</h1>
-          <div>1.请将CTWing的AEP平台-应用管理中的App Key和App Secret复制到当前页面</div>
+          <div>5、IOT端添加设备,为每一台设备设置唯一的IMEI(需与CTWing平台中填写的值一致)</div>
           <div className={styles.image}>
             <Image width="100%" src={img3} />
           </div>
+          <h1>设备接入网关配置说明</h1>
+          <div>1.请将CTWing的AEP平台-应用管理中的App Key和App Secret复制到当前页面</div>
+          <div className={styles.image}>
+            <Image width="100%" src={img4} />
+          </div>
+          <h1>其他说明</h1>
+          <div>
+            1.在IOT端启用设备时,若CTWing平台没有与之对应的设备,则将在CTWing端自动创建新设备
+          </div>
         </div>
       </Col>
     </Row>

+ 49 - 8
src/pages/link/AccessConfig/Detail/components/OneNet/index.tsx

@@ -1,7 +1,8 @@
 import { QuestionCircleOutlined } from '@ant-design/icons';
-import { Button, Col, Form, Image, Input, Row, Tooltip } from 'antd';
+import { Button, Col, Descriptions, Form, Image, Input, Row, Tooltip } from 'antd';
 import { useEffect } from 'react';
 import styles from './index.less';
+import { randomString } from '@/utils/util';
 
 interface Props {
   next: (data: any) => void;
@@ -10,6 +11,8 @@ interface Props {
 
 const OneNet = (props: Props) => {
   const img = require('/public/images/network/OneNet.jpg');
+  const img1 = require('/public/images/network/05.jpg');
+  const img2 = require('/public/images/network/06.jpg');
 
   const [form] = Form.useForm();
 
@@ -109,18 +112,56 @@ const OneNet = (props: Props) => {
       <Col span={8}>
         <div className={styles.doc}>
           <h1>操作指引:</h1>
-          <div>1、创建类型为OneNet的设备接入网关</div>
-          <div>2、创建产品,并选中接入方式为OneNet</div>
+          <div>1、OneNet端创建产品、设备,并配置HTTP推送</div>
+          <div>2、IOT端创建类型为OneNet的设备接入网关</div>
           <div>
-            3、添加设备,为每一台设备设置唯一的IMEI、IMSI码(需与OneNet平台中填写的值一致,若OneNet平台没有对应的设备,将会通过OneNet平台提供的LWM2M协议自动创建
+            3、IOT端创建产品,选中接入方式为OneNet类型的设备接入网关,填写Master-APIkey(OneNet端的产品Key
           </div>
           <div className={styles.image}>
+            <Image width="100%" src={img1} />
+          </div>
+          <div>
+            4、IOT端添加设备,在设备实例页面为每一台设备设置唯一的IMEI、IMSI码(需与OneNet平台中的值一致)
+          </div>
+          <div className={styles.image}>
+            <Image width="100%" src={img2} />
+          </div>
+          <h1>HTTP推送配置说明</h1>
+          <div className={styles.image}>
             <Image width="100%" src={img} />
           </div>
-          <h1>配置说明</h1>
-          <div>1.接口地址需要与OneNet数据推送配置中地址一致</div>
-          <div>2.通知Token需要与OneNet数据推送配置中Token一致</div>
-          <div>3.aesKey需要与OneNet数据推送配置中aesKey一致</div>
+          <div>HTTP推送配置路径:应用开发&gt;数据推送</div>
+          <Descriptions bordered size={'small'} column={1} labelStyle={{ width: 100 }}>
+            <Descriptions.Item label="参数">说明</Descriptions.Item>
+            <Descriptions.Item label="实例名称">推送实例的名称</Descriptions.Item>
+            <Descriptions.Item label="推送地址">
+              用于接收OneNet推送设备数据的地址物联网平台地址:
+              <div style={{ wordWrap: 'break-word' }}>
+                {origin}/api/one-net/{randomString()}/notify
+              </div>
+            </Descriptions.Item>
+            <Descriptions.Item label="Token">
+              自定义token,可用于验证请求是否来自OneNet
+            </Descriptions.Item>
+            <Descriptions.Item label="消息加密">
+              采用AES加密算法对推送的数据进行数据加密,AesKey为加密秘钥
+            </Descriptions.Item>
+          </Descriptions>
+          <h1>设备接入网关配置说明</h1>
+          <Descriptions bordered size={'small'} column={1} labelStyle={{ width: 100 }}>
+            <Descriptions.Item label="参数">说明</Descriptions.Item>
+            <Descriptions.Item label="apiKey">OneNet平台中具体产品的Key</Descriptions.Item>
+            <Descriptions.Item label="通知Token">
+              填写OneNet数据推送配置中设置的Token
+            </Descriptions.Item>
+            <Descriptions.Item label="aesKey">
+              若OneNet数据推送配置了消息加密,此处填写OneNet端数据推送配置中设置的aesKey
+            </Descriptions.Item>
+          </Descriptions>
+          <h1>其他说明</h1>
+          <div>
+            1.在IOT端启用设备时,若OneNet平台没有与之对应的设备,则将在OneNet端自动创建新设备
+          </div>
         </div>
       </Col>
     </Row>

+ 1 - 1
src/pages/link/AccessConfig/service.ts

@@ -27,7 +27,7 @@ class Service extends BaseService<AccessItem> {
       method: 'GET',
     });
   public getNetworkList = (networkType: string, params?: any) =>
-    request(`/${SystemConst.API_BASE}/network/config/${networkType}/_alive`, {
+    request(`/${SystemConst.API_BASE}/network/config/${networkType}/_detail`, {
       method: 'GET',
       params,
     });

+ 0 - 79
src/pages/link/DataCollect/DataGathering/index.tsx

@@ -1,79 +0,0 @@
-import { PageContainer } from '@ant-design/pro-layout';
-import { Card } from 'antd';
-import styles from './index.less';
-import ChannelTree from '../components/Tree';
-import { observer } from '@formily/reactive-react';
-import { model } from '@formily/reactive';
-import Device from '../components/Device';
-import Point from '../components/Point';
-import { Empty } from '@/components';
-
-export const DataCollectModel = model<{
-  id: Partial<string>;
-  type: 'channel' | 'device' | undefined;
-  provider: 'OPC_UA' | 'MODBUS_TCP';
-  data: any;
-  reload: boolean;
-  refresh: boolean;
-}>({
-  type: 'channel',
-  id: '',
-  provider: 'MODBUS_TCP',
-  data: {},
-  reload: false,
-  refresh: false,
-});
-
-export default observer(() => {
-  const onReload = () => {
-    DataCollectModel.reload = !DataCollectModel.reload;
-  };
-
-  const obj = {
-    channel: (
-      <Device
-        reload={onReload}
-        type={false}
-        id={DataCollectModel.id}
-        provider={DataCollectModel.provider}
-        refresh={DataCollectModel.refresh}
-      />
-    ),
-    device: (
-      <Point type={false} provider={DataCollectModel.provider} data={DataCollectModel.data} />
-    ),
-  };
-
-  return (
-    <PageContainer>
-      <Card bordered={false} bodyStyle={{ paddingTop: 0 }}>
-        <div className={styles.container}>
-          <div className={styles.left}>
-            <ChannelTree
-              change={(key, type, provider, data) => {
-                DataCollectModel.type = undefined;
-                DataCollectModel.id = key;
-                DataCollectModel.provider = provider;
-                DataCollectModel.data = data || {};
-                setTimeout(() => {
-                  DataCollectModel.type = type;
-                }, 0);
-              }}
-              reload={DataCollectModel.reload}
-              onReload={() => {
-                DataCollectModel.refresh = !DataCollectModel.refresh;
-              }}
-            />
-          </div>
-          {DataCollectModel?.id ? (
-            <div className={styles.right}>
-              {DataCollectModel.type ? obj[DataCollectModel.type] : ''}
-            </div>
-          ) : (
-            <Empty style={{ marginTop: 100 }} />
-          )}
-        </div>
-      </Card>
-    </PageContainer>
-  );
-});

+ 0 - 49
src/pages/link/DataCollect/IntegratedQuery/index.tsx

@@ -1,49 +0,0 @@
-import { PageContainer } from '@ant-design/pro-layout';
-import { observer } from '@formily/reactive-react';
-import { model } from '@formily/reactive';
-import Point from '../components/Point';
-import Device from '../components/Device';
-import Channel from '../components/Channel';
-import { useEffect } from 'react';
-
-const dataModel = model<{
-  tab: string;
-}>({
-  tab: 'channel',
-});
-
-export default observer(() => {
-  const list = [
-    {
-      key: 'channel',
-      tab: '通道',
-      component: <Channel type={true} />,
-    },
-    {
-      key: 'device',
-      tab: '采集器',
-      component: <Device type={true} />,
-    },
-    {
-      key: 'point',
-      tab: '点位',
-      component: <Point type={true} />,
-    },
-  ];
-
-  useEffect(() => {
-    dataModel.tab = 'channel';
-  }, []);
-
-  return (
-    <PageContainer
-      tabList={list}
-      tabActiveKey={dataModel.tab}
-      onTabChange={(key: string) => {
-        dataModel.tab = key;
-      }}
-    >
-      {list.find((item) => item.key === dataModel.tab)?.component}
-    </PageContainer>
-  );
-});

+ 0 - 79
src/pages/link/DataCollect/components/Point/CollectorCard/WritePoint.tsx

@@ -1,79 +0,0 @@
-import { Modal } from 'antd';
-import { FormItem, Input, Form } from '@formily/antd';
-import { createForm } from '@formily/core';
-import { createSchemaField } from '@formily/react';
-import service from '../../../service';
-import { onlyMessage } from '@/utils/util';
-
-interface Props {
-  data: Partial<PointItem>;
-  onCancel: () => void;
-}
-
-const WritePoint = (props: Props) => {
-  const { data } = props;
-
-  const SchemaField = createSchemaField({
-    components: {
-      Input,
-      FormItem,
-      Form,
-    },
-  });
-
-  const form = createForm();
-  const schema = {
-    type: 'object',
-    properties: {
-      propertyValue: {
-        type: 'string',
-        title: data?.name || '自定义属性',
-        required: true,
-        'x-decorator': 'FormItem',
-        'x-component': 'Input',
-        'x-component-props': {
-          placeholder: '请输入',
-        },
-      },
-    },
-  };
-
-  const handleSetPropertyValue = async (propertyValue: string) => {
-    if (data?.collectorId && data?.id) {
-      const resp = await service.writePoint(data.collectorId, [
-        {
-          pointId: data.id,
-          value: propertyValue,
-        },
-      ]);
-      if (resp.status === 200) {
-        onlyMessage('操作成功');
-      }
-      props.onCancel();
-    }
-  };
-  return (
-    <Modal
-      maskClosable={false}
-      title="编辑"
-      visible
-      onOk={async () => {
-        const values: any = await form.submit();
-        if (!!values) {
-          handleSetPropertyValue(values?.propertyValue);
-        }
-      }}
-      onCancel={() => {
-        props.onCancel();
-      }}
-    >
-      <div style={{ marginTop: '30px' }}>
-        <Form form={form} layout="vertical">
-          <SchemaField schema={schema} />
-        </Form>
-      </div>
-    </Modal>
-  );
-};
-
-export default WritePoint;

+ 0 - 389
src/pages/link/DataCollect/components/Tree/index.tsx

@@ -1,389 +0,0 @@
-import {
-  DownOutlined,
-  PlusOutlined,
-  FormOutlined,
-  DeleteOutlined,
-  StopOutlined,
-  PlayCircleOutlined,
-} from '@ant-design/icons';
-import { Button, Input, Tree, Space, Popconfirm, Tooltip, Tag } from 'antd';
-import { observer } from '@formily/react';
-import { model } from '@formily/reactive';
-import { Ellipsis, Empty, PermissionButton } from '@/components';
-import styles from './index.less';
-import service from '@/pages/link/DataCollect/service';
-import { useEffect } from 'react';
-import Save from '../../components/Channel/Save/index';
-import CollectorSave from '../../components/Device/Save/index';
-import { onlyMessage } from '@/utils/util';
-// import { StatusColorEnum } from '@/components/BadgeStatus';
-import { useIntl } from '@@/plugin-locale/localeExports';
-import { DataCollectModel } from '../../DataGathering';
-
-const TreeModel = model<{
-  selectedKeys: string[];
-  dataSource: any[];
-  loading: boolean;
-  param: any;
-  visible: boolean;
-  current: any;
-  c_visible: boolean;
-  c_current: any;
-}>({
-  selectedKeys: [],
-  dataSource: [],
-  loading: true,
-  param: {},
-  visible: false,
-  current: {},
-  c_visible: false,
-  c_current: {},
-});
-interface Props {
-  change: (
-    key: string,
-    type: 'channel' | 'device',
-    provider: 'OPC_UA' | 'MODBUS_TCP',
-    data?: any,
-  ) => void;
-  reload?: boolean;
-  onReload: () => void;
-}
-
-export default observer((props: Props) => {
-  const channelImg = require('/public/images/DataCollect/tree-channel.png');
-  const deviceImg = require('/public/images/DataCollect/tree-device.png');
-  const { permission } = PermissionButton.usePermission('link/DataCollect/DataGathering');
-  const intl = useIntl();
-
-  const handleSearch = (params: any, reload?: boolean) => {
-    TreeModel.loading = true;
-    TreeModel.param = params;
-    service
-      .queryChannelTree({ ...params, sorts: [{ name: 'createTime', order: 'desc' }] })
-      .then((resp) => {
-        if (resp.status === 200) {
-          TreeModel.dataSource = resp.result;
-          if (resp.result.length) {
-            if (!reload) {
-              TreeModel.selectedKeys = [resp.result[0].id];
-            }
-            const provider = !reload ? resp.result[0].provider : DataCollectModel.provider;
-            props.change(TreeModel.selectedKeys[0], 'channel', provider);
-          }
-        }
-        TreeModel.loading = false;
-      });
-  };
-
-  useEffect(() => {
-    handleSearch(TreeModel.param, false);
-  }, [TreeModel.param]);
-
-  useEffect(() => {
-    handleSearch(TreeModel.param, true);
-  }, [props.reload]);
-
-  const getState = (record: any) => {
-    if (record) {
-      const colorMap = new Map();
-      colorMap.set('running', 'success');
-      colorMap.set('partialError', 'warning');
-      colorMap.set('failed', 'error');
-      colorMap.set('stopped', 'default');
-      if (record?.state?.value === 'enabled') {
-        return (
-          record?.runningState && (
-            <Tag color={colorMap.get(record?.runningState?.value)}>
-              {record?.runningState?.text}
-            </Tag>
-          )
-        );
-      } else {
-        return <Tag color="processing">禁用</Tag>;
-      }
-    } else {
-      return '';
-    }
-  };
-
-  return (
-    <div>
-      <div>
-        <Input.Search
-          placeholder="请输入名称"
-          allowClear
-          onSearch={(val) => {
-            TreeModel.param = {
-              terms: [{ column: 'name', value: `%${val}%`, termType: 'like' }],
-            };
-          }}
-          style={{ width: '100%' }}
-        />
-      </div>
-      <div style={{ margin: '16px 0' }}>
-        <Button
-          type="primary"
-          ghost
-          style={{ width: '100%' }}
-          icon={<PlusOutlined />}
-          onClick={() => {
-            TreeModel.visible = true;
-            TreeModel.current = {};
-          }}
-        >
-          新增通道
-        </Button>
-      </div>
-      <div>
-        {TreeModel.dataSource.length ? (
-          <Tree
-            style={{ overflow: 'hidden' }}
-            className={styles['data-collect-tree']}
-            showIcon
-            height={500}
-            selectedKeys={TreeModel.selectedKeys}
-            switcherIcon={<DownOutlined />}
-          >
-            {(TreeModel.dataSource || []).map((item: any) => (
-              <Tree.TreeNode
-                key={item.id}
-                title={() => {
-                  return (
-                    <div className={styles.treeTitle}>
-                      <div
-                        className={styles.title}
-                        onClick={() => {
-                          TreeModel.selectedKeys = [item.id];
-                          props.change(item.id, 'channel', item.provider);
-                        }}
-                      >
-                        <img width={'20px'} style={{ marginRight: 5 }} src={channelImg} />
-                        <div style={{ display: 'flex' }}>
-                          <Ellipsis title={item.name} style={{ marginRight: 5, maxWidth: 120 }} />
-                          {getState(item)}
-                        </div>
-                      </div>
-                      <div>
-                        <Space className={styles.iconColor}>
-                          <Tooltip title={!permission.update ? '暂无权限,请联系管理员' : ''}>
-                            <FormOutlined
-                              onClick={() => {
-                                if (permission.update) {
-                                  TreeModel.current = item;
-                                  TreeModel.visible = true;
-                                }
-                              }}
-                            />
-                          </Tooltip>
-                          <Popconfirm
-                            title={intl.formatMessage({
-                              id: `pages.data.option.${
-                                item?.state?.value !== 'disabled' ? 'disabled' : 'enabled'
-                              }.tips`,
-                              defaultMessage: '确认禁用?',
-                            })}
-                            onConfirm={async () => {
-                              const resp =
-                                item?.state?.value !== 'disabled'
-                                  ? await service.updateChannel(item.id, {
-                                      state: 'disabled',
-                                      runningState: 'stopped',
-                                    })
-                                  : await service.updateChannel(item.id, {
-                                      state: 'enabled',
-                                      runningState: 'running',
-                                    });
-                              if (resp.status === 200) {
-                                TreeModel.param = {};
-                                handleSearch(TreeModel.param);
-                                props.onReload();
-                                onlyMessage('操作成功');
-                              } else {
-                                onlyMessage('操作失败!', 'error');
-                              }
-                            }}
-                          >
-                            <Tooltip title={!permission.action ? '暂无权限,请联系管理员' : ''}>
-                              {item?.state?.value !== 'disabled' ? (
-                                <StopOutlined />
-                              ) : (
-                                <PlayCircleOutlined />
-                              )}
-                            </Tooltip>
-                          </Popconfirm>
-                          <Popconfirm
-                            title={'该操作将会删除下属采集器与点位,确定删除?'}
-                            disabled={item?.state?.value !== 'disabled'}
-                            onConfirm={async () => {
-                              const resp = await service.removeChannel(item.id);
-                              if (resp.status === 200) {
-                                TreeModel.param = {};
-                                handleSearch(TreeModel.param);
-                                onlyMessage('操作成功');
-                              }
-                            }}
-                          >
-                            <Tooltip
-                              title={
-                                !permission.delete
-                                  ? '暂无权限,请联系管理员'
-                                  : item?.state?.value !== 'disabled'
-                                  ? '正常的通道不能删除'
-                                  : ''
-                              }
-                            >
-                              <DeleteOutlined />
-                            </Tooltip>
-                          </Popconfirm>
-                        </Space>
-                      </div>
-                    </div>
-                  );
-                }}
-              >
-                {(item?.collectors || []).map((i: any) => (
-                  <Tree.TreeNode
-                    key={i.id}
-                    title={() => {
-                      return (
-                        <div className={styles.treeTitle} style={{ paddingRight: 24 }}>
-                          <div
-                            className={styles.title}
-                            onClick={() => {
-                              TreeModel.selectedKeys = [i.id];
-                              props.change(i.id, 'device', item.provider, i);
-                            }}
-                          >
-                            <img width={'20px'} style={{ marginRight: 5 }} src={deviceImg} />
-                            <div style={{ display: 'flex' }}>
-                              <span
-                                className={'ellipsis'}
-                                style={{
-                                  marginRight: 5,
-                                  maxWidth: 90,
-                                  color: 'rgba(0, 0, 0, 0.6)',
-                                }}
-                              >
-                                {i.name}
-                              </span>
-                              {getState(i)}
-                            </div>
-                          </div>
-                          <div>
-                            <Space className={styles.iconColor}>
-                              <Tooltip title={!permission.update ? '暂无权限,请联系管理员' : ''}>
-                                <FormOutlined
-                                  onClick={() => {
-                                    if (permission.update) {
-                                      TreeModel.current = item;
-                                      TreeModel.c_visible = true;
-                                      TreeModel.c_current = i;
-                                    }
-                                  }}
-                                />
-                              </Tooltip>
-                              <Popconfirm
-                                title={intl.formatMessage({
-                                  id: `pages.data.option.${
-                                    i?.state?.value !== 'disabled' ? 'disabled' : 'enabled'
-                                  }.tips`,
-                                  defaultMessage: '确认禁用?',
-                                })}
-                                onConfirm={async () => {
-                                  const resp =
-                                    i?.state?.value !== 'disabled'
-                                      ? await service.updateCollector(i.id, {
-                                          state: 'disabled',
-                                          runningState: 'stopped',
-                                        })
-                                      : await service.updateCollector(i.id, {
-                                          state: 'enabled',
-                                          runningState: 'running',
-                                        });
-                                  if (resp.status === 200) {
-                                    TreeModel.param = {};
-                                    handleSearch(TreeModel.param);
-                                    props.onReload();
-                                    onlyMessage('操作成功');
-                                  } else {
-                                    onlyMessage('操作失败!', 'error');
-                                  }
-                                }}
-                              >
-                                <Tooltip title={!permission.action ? '暂无权限,请联系管理员' : ''}>
-                                  {i?.state?.value !== 'disabled' ? (
-                                    <StopOutlined />
-                                  ) : (
-                                    <PlayCircleOutlined />
-                                  )}
-                                </Tooltip>
-                              </Popconfirm>
-                              <Popconfirm
-                                title={'该操作将会删除下属点位,确定删除?'}
-                                disabled={i?.state?.value !== 'disabled'}
-                                onConfirm={async () => {
-                                  const resp = await service.removeCollector(i.id);
-                                  if (resp.status === 200) {
-                                    TreeModel.param = {};
-                                    handleSearch(TreeModel.param);
-                                    onlyMessage('操作成功');
-                                  }
-                                }}
-                              >
-                                <Tooltip
-                                  title={
-                                    !permission.delete
-                                      ? '暂无权限,请联系管理员'
-                                      : i?.state?.value !== 'disabled'
-                                      ? '正常的采集器不能删除'
-                                      : ''
-                                  }
-                                >
-                                  <DeleteOutlined />
-                                </Tooltip>
-                              </Popconfirm>
-                            </Space>
-                          </div>
-                        </div>
-                      );
-                    }}
-                  />
-                ))}
-              </Tree.TreeNode>
-            ))}
-          </Tree>
-        ) : (
-          <Empty />
-        )}
-      </div>
-      {TreeModel.visible && (
-        <Save
-          data={TreeModel.current}
-          close={() => {
-            TreeModel.visible = false;
-          }}
-          reload={() => {
-            TreeModel.visible = false;
-            handleSearch(TreeModel.param);
-          }}
-        />
-      )}
-      {TreeModel.c_visible && (
-        <CollectorSave
-          data={TreeModel.c_current}
-          channelId={TreeModel.current.id}
-          provider={TreeModel.current.provider}
-          close={() => {
-            TreeModel.c_visible = false;
-          }}
-          reload={() => {
-            TreeModel.c_visible = false;
-            TreeModel.param = {};
-            handleSearch(TreeModel.param);
-          }}
-        />
-      )}
-    </div>
-  );
-});

+ 2 - 2
src/pages/media/Cascade/Channel/index.tsx

@@ -73,7 +73,7 @@ const Channel = () => {
           onClick={async () => {
             if (!!data) {
               if (data.length <= 64) {
-                if (pass) {
+                if (pass && !isnull) {
                   const resp: any = await service.editBindInfo(record.id, {
                     gbChannelId: data,
                   });
@@ -83,7 +83,7 @@ const Channel = () => {
                     setPopvisible('');
                   }
                 } else {
-                  message.error('该国标ID在同一设备下已存在');
+                  message.error('请填写正确的国标ID');
                 }
               } else {
                 message.error('最多可输入64个字符');

+ 8 - 6
src/pages/notice/Config/Debug/index.tsx

@@ -15,15 +15,15 @@ import {
 import { ISchema } from '@formily/json-schema';
 import { service, state } from '@/pages/notice/Config';
 import { service as TemplateService } from '@/pages/notice/Template';
-import { useLocation } from 'umi';
+// import { useLocation } from 'umi';
 import { onlyMessage, useAsyncDataSource } from '@/utils/util';
 import { Store } from 'jetlinks-store';
 import FUpload from '@/components/Upload';
 import { FDatePicker } from '@/components';
 
 const Debug = observer(() => {
-  const location = useLocation<{ id: string }>();
-  const id = (location as any).query?.id;
+  // const location = useLocation<{ id: string }>();
+  const id = state.current?.type; // (location as any).query?.id;
   const variableRef = useRef<any>([]);
 
   const form = useMemo(
@@ -95,19 +95,21 @@ const Debug = observer(() => {
                 switch (businessType) {
                   case 'org':
                     // 获取org
-                    const orgList = await TemplateService[id].getDepartments(state.current?.id);
+                    const orgList = await TemplateService[id].getDepartments(
+                      state.current?.id || '',
+                    );
                     format.setComponent(Select);
                     format.setDataSource(orgList);
                     break;
                   case 'user':
                     // 获取user
-                    const userList = await TemplateService[id].getUser(state.current?.id);
+                    const userList = await TemplateService[id].getUser(state.current?.id || '');
                     format.setComponent(Select);
                     format.setDataSource(userList);
                     break;
                   case 'tag':
                     // 获取user
-                    const tagList = await TemplateService[id].getTags(state.current?.id);
+                    const tagList = await TemplateService[id]?.getTags(state.current?.id || '');
                     format.setComponent(Select);
                     format.setDataSource(tagList);
                     break;

+ 133 - 52
src/pages/notice/Config/Detail/index.tsx

@@ -1,8 +1,8 @@
 import { PageContainer } from '@ant-design/pro-layout';
-import { createForm, onFieldValueChange } from '@formily/core';
+import { createForm, onFieldValueChange, onFormInit } from '@formily/core';
 import { Card, Col, Input, Row } from 'antd';
 import { ISchema } from '@formily/json-schema';
-import { useEffect, useMemo, useState } from 'react';
+import { useMemo, useState } from 'react';
 import { createSchemaField, observer } from '@formily/react';
 import {
   ArrayTable,
@@ -19,7 +19,7 @@ import {
   Switch,
 } from '@formily/antd';
 import styles from './index.less';
-import { service, state } from '@/pages/notice/Config';
+import { service } from '@/pages/notice/Config';
 import { onlyMessage, useAsyncDataSource } from '@/utils/util';
 import { useParams } from 'umi';
 import { typeList } from '@/pages/notice';
@@ -35,7 +35,8 @@ import { PermissionButton } from '@/components';
 import usePermissions from '@/hooks/permission';
 import FAutoComplete from '@/components/FAutoComplete';
 import Webhook from './doc/Webhook';
-import { useModel } from '@@/plugin-model/useModel';
+// import { useModel } from '@@/plugin-model/useModel';
+import { typeArray } from '@/components/ProTableCard/CardItems/noticeTemplate';
 
 export const docMap = {
   weixin: {
@@ -62,50 +63,64 @@ export const docMap = {
 
 const Detail = observer(() => {
   const { id } = useParams<{ id: string }>();
-  const { initialState } = useModel('@@initialState');
-  const [provider, setProvider] = useState<string>('embedded');
+  // const { initialState } = useModel('@@initialState');
+  const [typeItem, setTypeItem] = useState<string>('email');
+  const [providerItem, setProviderItem] = useState<string>('embedded');
+  const [loading, setLoading] = useState<boolean>(false);
+
   const form = useMemo(
     () =>
       createForm({
         validateFirst: true,
         effects() {
-          onFieldValueChange('type', async (field) => {
-            const type = field.value;
-            if (!type) return;
-            // f.setFieldState('provider', (state1) => {
-            // state1.value = undefined;
-            // state.dataSource = providerRef.current
-            //   .find((item) => type === item.id)
-            //   ?.providerInfos.map((i) => ({ label: i.name, value: i.id }));
-            // });
+          onFormInit(async (form1) => {
+            if (id === ':id' || !id) {
+              form1.setValues({
+                type: 'email',
+                provider: 'embedded',
+              });
+            } else {
+              const resp = await service.detail(id);
+              if (resp.status === 200) {
+                setTypeItem(resp.result.type);
+                setProviderItem(resp.result.provider);
+                form1.setValues(resp.result);
+              }
+            }
+          });
+          onFieldValueChange('type', async (field, f) => {
+            const value = field.value;
+            setTypeItem(value);
+            if (!value) return;
+            f.setFieldState('provider', (state1) => {
+              if (id === ':id' || !id) {
+                state1.value = typeList[value][0].value;
+              }
+              state1.dataSource = typeList[value];
+            });
           });
           onFieldValueChange('provider', async (field) => {
-            if (id === 'email') {
-              setProvider('embedded');
+            const _type = field.query('.type').get('value');
+            if (_type === 'email') {
+              setProviderItem('embedded');
             } else {
-              setProvider(field.value);
+              setProviderItem(field.value);
             }
           });
         },
       }),
-    [id],
+    [],
   );
 
-  useEffect(() => {
-    setTimeout(() => {
-      if (initialState?.settings?.title) {
-        document.title = `通知配置 - ${initialState?.settings?.title}`;
-      } else {
-        document.title = '通知配置';
-      }
-    }, 0);
-    if (id === 'webhook') {
-      setProvider('http');
-    }
-    if (state.current) {
-      form.setValues(state.current);
-    }
-  }, []);
+  // useEffect(() => {
+  //   setTimeout(() => {
+  //     if (initialState?.settings?.title) {
+  //       document.title = `通知配置 - ${initialState?.settings?.title}`;
+  //     } else {
+  //       document.title = '通知配置';
+  //     }
+  //   }, 0);
+  // }, []);
 
   const SchemaField = createSchemaField({
     components: {
@@ -136,6 +151,24 @@ const Detail = observer(() => {
   const schema: ISchema = {
     type: 'object',
     properties: {
+      type: {
+        title: '通知方式',
+        'x-component': 'Select',
+        'x-decorator': 'FormItem',
+        'x-component-props': {
+          placeholder: '请选择通知方式',
+        },
+        'x-disabled': !!id && id !== ':id',
+        'x-validator': [
+          {
+            required: true,
+            message: '请选择通知方式',
+          },
+        ],
+        enum: typeArray.map((item) => {
+          return { label: item.text, value: item.status };
+        }),
+      },
       name: {
         title: '名称',
         required: true,
@@ -146,17 +179,15 @@ const Detail = observer(() => {
         },
         'x-validator': [
           {
+            required: true,
+            message: '请输入名称',
+          },
+          {
             max: 64,
             message: '最多可输入64个字符',
           },
         ],
       },
-      type: {
-        title: '分类',
-        'x-component': 'Input',
-        'x-value': id,
-        'x-hidden': true,
-      },
       provider: {
         title: '类型',
         type: 'string',
@@ -165,17 +196,34 @@ const Detail = observer(() => {
         'x-component-props': {
           optionType: 'button',
         },
-        required: true,
-        'x-visible': typeList[id]?.length > 0,
-        'x-hidden': id === 'email' || id === 'webhook',
-        'x-value': typeList[id]?.[0]?.value,
-        enum: typeList[id] || [],
+        'x-reactions': {
+          dependencies: ['type'],
+          fulfill: {
+            state: {
+              visible: '{{!!$deps[0] && $deps[0] !== "email"}}',
+            },
+          },
+        },
+        'x-validator': [
+          {
+            required: true,
+            message: '请选择类型',
+          },
+        ],
       },
       configuration: {
         type: 'object',
         properties: {
           weixin: {
             type: 'void',
+            'x-reactions': {
+              dependencies: ['type'],
+              fulfill: {
+                state: {
+                  visible: '{{$deps[0]==="weixin"}}',
+                },
+              },
+            },
             properties: {
               corpId: {
                 title: 'corpId',
@@ -259,11 +307,18 @@ const Detail = observer(() => {
                 },
               },
             },
-            'x-visible': id === 'weixin',
           },
           dingTalk: {
             type: 'void',
-            'x-visible': id === 'dingTalk',
+            // 'x-visible': id === 'dingTalk',
+            'x-reactions': {
+              dependencies: ['type'],
+              fulfill: {
+                state: {
+                  visible: '{{$deps[0]==="dingTalk"}}',
+                },
+              },
+            },
             properties: {
               appKey: {
                 title: 'AppKey',
@@ -333,7 +388,15 @@ const Detail = observer(() => {
           // 阿里云语音/短信
           voiceOrSms: {
             type: 'void',
-            'x-visible': id === 'voice' || id === 'sms',
+            // 'x-visible': id === 'voice' || id === 'sms',
+            'x-reactions': {
+              dependencies: ['type'],
+              fulfill: {
+                state: {
+                  visible: '{{$deps[0]==="voice" || $deps[0]==="sms"}}',
+                },
+              },
+            },
             properties: {
               regionId: {
                 title: 'RegionId',
@@ -384,7 +447,15 @@ const Detail = observer(() => {
           },
           email: {
             type: 'void',
-            'x-visible': id === 'email',
+            // 'x-visible': id === 'email',
+            'x-reactions': {
+              dependencies: ['type'],
+              fulfill: {
+                state: {
+                  visible: '{{$deps[0]==="email"}}',
+                },
+              },
+            },
             properties: {
               space: {
                 title: '服务器地址',
@@ -513,7 +584,15 @@ const Detail = observer(() => {
             },
           },
           webhook: {
-            'x-visible': id === 'webhook',
+            // 'x-visible': id === 'webhook',
+            'x-reactions': {
+              dependencies: ['type'],
+              fulfill: {
+                state: {
+                  visible: '{{$deps[0]==="webhook"}}',
+                },
+              },
+            },
             type: 'void',
             properties: {
               url: {
@@ -630,13 +709,14 @@ const Detail = observer(() => {
 
   const handleSave = async () => {
     const data: ConfigItem = await form.submit();
+    setLoading(true);
     let response;
     if (data.id) {
       response = await service.update(data);
     } else {
       response = await service.save(data);
     }
-
+    setLoading(false);
     if (response?.status === 200) {
       onlyMessage('保存成功');
       history.back();
@@ -657,6 +737,7 @@ const Detail = observer(() => {
                   <PermissionButton
                     type="primary"
                     onClick={handleSave}
+                    loading={loading}
                     isPermission={getOtherPermission(['add', 'update'])}
                   >
                     保存
@@ -666,7 +747,7 @@ const Detail = observer(() => {
             </Form>
           </Col>
           <Col span={12} push={2}>
-            {docMap?.[id]?.[provider]}
+            {docMap?.[typeItem]?.[providerItem]}
           </Col>
         </Row>
       </Card>

+ 4 - 4
src/pages/notice/Config/Log/index.tsx

@@ -3,13 +3,13 @@ import { observer } from '@formily/react';
 import { service, state } from '..';
 import ProTable, { ActionType, ProColumns } from '@jetlinks/pro-table';
 import SearchComponent from '@/components/SearchComponent';
-import { useLocation } from 'umi';
+// import { useLocation } from 'umi';
 import { InfoCircleOutlined } from '@ant-design/icons';
 import { useRef, useState } from 'react';
 
 const Log = observer(() => {
-  const location = useLocation<{ id: string }>();
-  const id = (location as any).query?.id;
+  // const location = useLocation<{ id: string }>();
+  // const id = (location as any).query?.id;
 
   const columns: ProColumns<LogItem>[] = [
     {
@@ -99,7 +99,7 @@ const Log = observer(() => {
     >
       <SearchComponent
         model="simple"
-        defaultParam={[{ column: 'notifyType$IN', value: id }]}
+        defaultParam={[{ column: 'notifyType$IN', value: state.current?.type || '' }]}
         field={columns}
         onSearch={(data) => {
           actionRef.current?.reset?.();

+ 4 - 7
src/pages/notice/Config/SyncUser/index.tsx

@@ -4,7 +4,7 @@ import { service, state } from '..';
 import type { ActionType, ProColumns } from '@jetlinks/pro-table';
 import ProTable from '@jetlinks/pro-table';
 import { useEffect, useRef, useState } from 'react';
-import { history, useLocation } from 'umi';
+import { history } from 'umi';
 import { DisconnectOutlined, EditOutlined } from '@ant-design/icons';
 import BindUser from '../BindUser';
 import { onlyMessage } from '@/utils/util';
@@ -12,8 +12,8 @@ import { Empty } from '@/components';
 
 const SyncUser = observer(() => {
   const [dept, setDept] = useState<string>();
-  const location = useLocation<{ id: string }>();
-  const id = (location as any).query?.id;
+  // const location = useLocation<{ id: string }>();
+  const id = state.current?.type || 'dingtalk'; // (location as any).query?.id;
   const [visible, setVisible] = useState<boolean>(false);
   const [current, setCurrent] = useState<any>({});
   const [list, setList] = useState<any[]>([]);
@@ -190,10 +190,7 @@ const SyncUser = observer(() => {
                 request={(params) =>
                   service
                     .queryZipSyncUser(
-                      {
-                        dingTalk: 'dingtalk',
-                        weixin: 'wechat',
-                      }[id],
+                      id === 'dingTalk' ? 'dingTalk' : 'wechat',
                       id,
                       state.current?.provider || '',
                       state.current?.id || '',

+ 21 - 59
src/pages/notice/Config/index.tsx

@@ -20,14 +20,14 @@ import Service from '@/pages/notice/Config/service';
 import { observer } from '@formily/react';
 import SearchComponent from '@/components/SearchComponent';
 import { getMenuPathByParams, MENUS_CODE } from '@/utils/menu';
-import { history, useLocation } from 'umi';
+import { history } from 'umi';
 import { model } from '@formily/reactive';
 import moment from 'moment';
 import { PermissionButton, ProTableCard } from '@/components';
 import NoticeConfig from '@/components/ProTableCard/CardItems/noticeConfig';
 import Debug from '@/pages/notice/Config/Debug';
 import Log from '@/pages/notice/Config/Log';
-import { typeList } from '@/components/ProTableCard/CardItems/noticeTemplate';
+import { providerObj, typeList, typeObj } from '@/components/ProTableCard/CardItems/noticeTemplate';
 import usePermissions from '@/hooks/permission';
 import SyncUser from '@/pages/notice/Config/SyncUser';
 
@@ -43,60 +43,14 @@ export const state = model<{
   log: false,
   syncUser: false,
 });
-const list = {
-  weixin: {
-    corpMessage: {
-      text: '企业消息',
-      status: 'corpMessage',
-    },
-    // officialMessage: {
-    //   text: '服务号消息',
-    //   status: 'officialMessage',
-    // },
-  },
-  dingTalk: {
-    dingTalkMessage: {
-      text: '钉钉消息',
-      status: 'dingTalkMessage',
-    },
-    dingTalkRobotWebHook: {
-      text: '群机器人消息',
-      status: 'dingTalkRobotWebHook',
-    },
-  },
-  voice: {
-    aliyun: {
-      text: '阿里云语音',
-      status: 'aliyun',
-    },
-  },
-  sms: {
-    aliyunSms: {
-      text: '阿里云短信',
-      status: 'aliyunSms',
-    },
-  },
-  email: {
-    embedded: {
-      text: '邮件',
-      status: 'embedded',
-    },
-  },
-  webhook: {
-    http: {
-      text: 'Webhook',
-      status: 'http',
-    },
-  },
-};
 
 const Config = observer(() => {
   const intl = useIntl();
   const actionRef = useRef<ActionType>();
-  const location = useLocation<{ id: string }>();
+  // const location = useLocation<{ id: string }>();
 
   const { permission: configPermission } = usePermissions('notice');
-  const id = (location as any).query?.id;
+  // const id = (location as any).query?.id;
 
   const columns: ProColumns<ConfigItem>[] = [
     {
@@ -107,11 +61,20 @@ const Config = observer(() => {
       width: '25%',
     },
     {
-      dataIndex: 'provider',
+      dataIndex: 'type',
       title: '通知方式',
-      renderText: (text, record) => typeList[record.type][record.provider],
+      renderText: (text, record) => typeObj?.[record.type]?.text || record.type,
+      valueType: 'select',
+      valueEnum: typeObj,
+    },
+    {
+      dataIndex: 'provider',
+      title: '类型',
+      renderText: (text, record) => {
+        return typeList[record?.type][record?.provider];
+      },
       valueType: 'select',
-      valueEnum: list[id],
+      valueEnum: providerObj,
     },
     {
       dataIndex: 'description',
@@ -151,7 +114,7 @@ const Config = observer(() => {
           onClick={async () => {
             // setLoading(true);
             state.current = record;
-            history.push(getMenuPathByParams(MENUS_CODE['notice/Config/Detail'], id));
+            history.push(getMenuPathByParams(MENUS_CODE['notice/Config/Detail'], record.id));
           }}
           tooltip={{
             title: intl.formatMessage({
@@ -251,7 +214,7 @@ const Config = observer(() => {
   return (
     <PageContainer>
       <SearchComponent
-        defaultParam={[{ column: 'type$IN', value: id }]}
+        // defaultParam={[{ column: 'type$IN', value: id }]}
         field={columns}
         onSearch={(data) => {
           actionRef.current?.reset?.();
@@ -272,7 +235,7 @@ const Config = observer(() => {
               isPermission={configPermission.add}
               onClick={() => {
                 state.current = undefined;
-                history.push(getMenuPathByParams(MENUS_CODE['notice/Config/Detail'], id));
+                history.push(getMenuPathByParams(MENUS_CODE['notice/Config/Detail']));
               }}
               key="button"
               icon={<PlusOutlined />}
@@ -341,13 +304,12 @@ const Config = observer(() => {
         cardRender={(record) => (
           <NoticeConfig
             {...record}
-            type={id}
             detail={
               <div
                 style={{ fontSize: 18, padding: 8 }}
                 onClick={() => {
                   state.current = record;
-                  history.push(getMenuPathByParams(MENUS_CODE['notice/Config/Detail'], id));
+                  history.push(getMenuPathByParams(MENUS_CODE['notice/Config/Detail'], record.id));
                 }}
               >
                 <EyeOutlined />
@@ -374,7 +336,7 @@ const Config = observer(() => {
                 key="edit"
                 onClick={async () => {
                   state.current = record;
-                  history.push(getMenuPathByParams(MENUS_CODE['notice/Config/Detail'], id));
+                  history.push(getMenuPathByParams(MENUS_CODE['notice/Config/Detail'], record.id));
                 }}
               >
                 <EditOutlined />

+ 8 - 9
src/pages/notice/Template/Debug/index.tsx

@@ -13,15 +13,15 @@ import {
 } from '@formily/antd';
 import { ISchema } from '@formily/json-schema';
 import { configService, service, state } from '@/pages/notice/Template';
-import { useLocation } from 'umi';
+// import { useLocation } from 'umi';
 import { onlyMessage, useAsyncDataSource } from '@/utils/util';
 import { Store } from 'jetlinks-store';
 import { FDatePicker } from '@/components';
 import FUpload from '@/components/Upload';
 
 const Debug = observer(() => {
-  const location = useLocation<{ id: string }>();
-  const id = (location as any).query?.id;
+  // const location = useLocation<{ id: string }>();
+  // const id = (location as any).query?.id;
 
   const variableRef = useRef<any>([]);
 
@@ -78,27 +78,26 @@ const Debug = observer(() => {
             }
             if (variableRef.current) {
               const a = variableRef.current?.find((i: any) => i.id === _id.value);
-              console.log(a, 'a');
               const _configId = configId.value();
               const businessType = a?.expands?.businessType;
-              if (id === 'dingTalk' || id === 'weixin') {
+              if (state.current?.type === 'dingTalk' || state.current?.type === 'weixin') {
                 if (_configId) {
                   switch (businessType) {
                     case 'org':
                       // 获取org
-                      const orgList = await service[id].getDepartments(_configId);
+                      const orgList = await service[state.current?.type].getDepartments(_configId);
                       format.setComponent(Select);
                       format.setDataSource(orgList);
                       break;
                     case 'user':
                       // 获取user
-                      const userList = await service[id].getUser(_configId);
+                      const userList = await service[state.current?.type].getUser(_configId);
                       format.setComponent(Select);
                       format.setDataSource(userList);
                       break;
                     case 'tag':
                       // 获取user
-                      const tagList = await service[id].getTags(_configId);
+                      const tagList = await service[state.current?.type].getTags(_configId);
                       format.setComponent(Select);
                       format.setDataSource(tagList);
                       break;
@@ -164,7 +163,7 @@ const Debug = observer(() => {
     configService
       .queryNoPagingPost({
         terms: [
-          { column: 'type$IN', value: id },
+          { column: 'type$IN', value: state.current?.type },
           { column: 'provider', value: state.current?.provider },
         ],
       })

+ 160 - 43
src/pages/notice/Template/Detail/index.tsx

@@ -21,18 +21,19 @@ import {
   onFieldInit,
   onFieldReact,
   onFieldValueChange,
+  onFormInit,
   registerValidateRules,
 } from '@formily/core';
 import { createSchemaField, observer } from '@formily/react';
 import type { ISchema } from '@formily/json-schema';
 import styles from './index.less';
-import { useEffect, useMemo, useRef, useState } from 'react';
+import { useMemo, useRef, useState } from 'react';
 import FUpload from '@/components/Upload';
 import { useParams } from 'umi';
 import { PageContainer } from '@ant-design/pro-layout';
 import { Card, Col, Row, Tooltip } from 'antd';
 import { typeList } from '@/pages/notice';
-import { configService, service, state } from '@/pages/notice/Template';
+import { configService, service } from '@/pages/notice/Template';
 import FBraftEditor from '@/components/FBraftEditor';
 import { onlyMessage, phoneRegEx, useAsyncDataSource } from '@/utils/util';
 import WeixinCorp from '@/pages/notice/Template/Detail/doc/WeixinCorp';
@@ -48,8 +49,9 @@ import { PermissionButton } from '@/components';
 import usePermissions from '@/hooks/permission';
 import FMonacoEditor from '@/components/FMonacoEditor';
 import Webhook from './doc/Webhook';
-import { useModel } from '@@/plugin-model/useModel';
+// import { useModel } from '@@/plugin-model/useModel';
 import { QuestionCircleOutlined } from '@ant-design/icons';
+import { typeArray } from '@/components/ProTableCard/CardItems/noticeTemplate';
 
 export const docMap = {
   weixin: {
@@ -76,19 +78,21 @@ export const docMap = {
 
 const Detail = observer(() => {
   const { id } = useParams<{ id: string }>();
-  const [provider, setProvider] = useState<string>('embedded');
-  const { initialState } = useModel('@@initialState');
+  const [typeItem, setTypeItem] = useState<string>('email');
+  const [providerItem, setProviderItem] = useState<string>('embedded');
+  const [loading, setLoading] = useState<boolean>(false);
+  // const { initialState } = useModel('@@initialState');
   // 正则提取${}里面的值
   const pattern = /(?<=\$\{).*?(?=\})/g;
 
   // 提取微信服务号里面的值 {{}}
   const weixinPattern = /(?<=\{\{).*?(?=\.DATA}})/g;
 
-  const getConfig = (provider1: string) =>
+  const getConfig = (type1: string, provider1: string) =>
     configService
       .queryNoPagingPost({
         terms: [
-          { column: 'type$IN', value: id },
+          { column: 'type$IN', value: type1 },
           { column: 'provider', value: provider1 },
         ],
       })
@@ -122,7 +126,20 @@ const Detail = observer(() => {
       createForm({
         validateFirst: true,
         effects() {
-          onFieldInit('template.message', (field) => {
+          onFormInit(async (form1) => {
+            if (id === ':id' || !id) {
+              form1.setValues({
+                type: 'email',
+                provider: 'embedded',
+              });
+            } else {
+              const resp = await service.detail(id);
+              if (resp.status === 200) {
+                form1.setValues(resp.result);
+              }
+            }
+          });
+          onFieldInit('template.message', async (field) => {
             if (id === 'email') {
               field.setComponent(FBraftEditor, {
                 placeholder:
@@ -146,17 +163,34 @@ const Detail = observer(() => {
             //   field.disabled = false
             // }
           });
+          onFieldValueChange('type', async (field, f) => {
+            const value = field.value;
+            setTypeItem(value);
+            if (!value) return;
+            f.setFieldState('provider', (state1) => {
+              if (id === ':id' || !id) {
+                state1.value = typeList[value][0].value;
+              }
+              state1.dataSource = typeList[value];
+            });
+          });
           onFieldValueChange('provider', (field, form1) => {
             const value = field.value;
-            setProvider(value);
             if (field.modified) {
               form1.setValuesIn('configId', null);
               form1.setValuesIn('template', null);
             }
+            const _type = field.query('type').value();
             // 设置绑定配置的数据
             form1.setFieldState('configId', async (state1) => {
-              state1.dataSource = await getConfig(value);
+              state1.dataSource = await getConfig(_type, value);
             });
+            console.log(_type, value);
+            if (_type === 'email') {
+              setProviderItem('embedded');
+            } else {
+              setProviderItem(value);
+            }
 
             if (value === 'officialMessage') {
               form1.setFieldState('template.message', (state5) => {
@@ -336,7 +370,7 @@ const Detail = observer(() => {
           onFieldValueChange('template.body', (field, form1) => {
             const value = (field as Field).value;
             // console.log(value);
-            const idList = value.match(pattern)?.filter((i: string) => i);
+            const idList = (value || '').match(pattern)?.filter((i: string) => i);
             form1.setFieldState('variableDefinitions', (state1) => {
               state1.visible = !!idList && idList.length > 0;
             });
@@ -440,18 +474,18 @@ const Detail = observer(() => {
     [id],
   );
 
-  useEffect(() => {
-    setTimeout(() => {
-      if (initialState?.settings?.title) {
-        document.title = `通知模板 - ${initialState?.settings?.title}`;
-      } else {
-        document.title = '通知模板';
-      }
-    }, 0);
-    if (state.current) {
-      form.setValues(state.current);
-    }
-  }, []);
+  // useEffect(() => {
+  //   setTimeout(() => {
+  //     if (initialState?.settings?.title) {
+  //       document.title = `通知模板 - ${initialState?.settings?.title}`;
+  //     } else {
+  //       document.title = '通知模板';
+  //     }
+  //   }, 0);
+  //   // if (state.current) {
+  //   //   form.setValues(state.current);
+  //   // }
+  // }, []);
 
   const SchemaField = createSchemaField({
     components: {
@@ -476,7 +510,7 @@ const Detail = observer(() => {
 
   const handleSave = async () => {
     const data: TemplateItem = await form.submit();
-
+    setLoading(true);
     // dingTalkRobotWebHook
 
     // 提交的时候处理内容
@@ -503,7 +537,7 @@ const Detail = observer(() => {
           data.template.link.text = data.template.message;
       }
     }
-    if (id === 'email') {
+    if (data.type === 'email') {
       data.provider = 'embedded';
       data.template.text = data.template.message;
     }
@@ -514,7 +548,7 @@ const Detail = observer(() => {
     } else {
       response = await service.save(data);
     }
-
+    setLoading(false);
     if (response?.status === 200) {
       onlyMessage('保存成功');
       history.back();
@@ -540,6 +574,24 @@ const Detail = observer(() => {
   const schema: ISchema = {
     type: 'object',
     properties: {
+      type: {
+        title: '通知方式',
+        'x-component': 'Select',
+        'x-decorator': 'FormItem',
+        'x-component-props': {
+          placeholder: '请选择通知方式',
+        },
+        'x-disabled': !!id && id !== ':id',
+        'x-validator': [
+          {
+            required: true,
+            message: '请选择通知方式',
+          },
+        ],
+        enum: typeArray.map((item) => {
+          return { label: item.text, value: item.status };
+        }),
+      },
       name: {
         title: '名称',
         type: 'string',
@@ -560,11 +612,6 @@ const Detail = observer(() => {
           },
         ],
       },
-      type: {
-        title: '类型',
-        'x-value': id,
-        'x-hidden': true,
-      },
       provider: {
         title: '类型',
         type: 'string',
@@ -574,11 +621,24 @@ const Detail = observer(() => {
           optionType: 'button',
           placeholder: '请选择类型',
         },
-        required: true,
-        'x-visible': typeList[id]?.length > 0,
-        'x-hidden': id === 'email' || id === 'webhook',
-        'x-value': typeList[id]?.[0]?.value,
-        enum: typeList[id] || [],
+        'x-validator': [
+          {
+            required: true,
+            message: '请选择类型',
+          },
+        ],
+        // 'x-visible': typeList[typeItem]?.length > 0,
+        // 'x-hidden': typeItem === 'email' || typeItem === 'webhook',
+        // 'x-value': typeList[typeItem]?.[0]?.value,
+        // enum: typeList[typeItem] || [],
+        'x-reactions': {
+          dependencies: ['type'],
+          fulfill: {
+            state: {
+              visible: '{{!!$deps[0] && $deps[0] !== "email" && $deps[0] !== "webhook"}}',
+            },
+          },
+        },
       },
       configId: {
         title: '绑定配置',
@@ -592,14 +652,30 @@ const Detail = observer(() => {
         'x-decorator-props': {
           tooltip: '使用固定的通知配置来发送此通知模版',
         },
-        'x-visible': id !== 'email',
+        // 'x-visible': id !== 'email',
+        'x-reactions': {
+          dependencies: ['type'],
+          fulfill: {
+            state: {
+              visible: '{{$deps[0] !=="email"}}',
+            },
+          },
+        },
       },
       template: {
         type: 'object',
         properties: {
           weixin: {
             type: 'void',
-            'x-visible': id === 'weixin',
+            // 'x-visible': id === 'weixin',
+            'x-reactions': {
+              dependencies: ['type'],
+              fulfill: {
+                state: {
+                  visible: '{{$deps[0]==="weixin"}}',
+                },
+              },
+            },
             properties: {
               corpMessage: {
                 type: 'void',
@@ -829,7 +905,15 @@ const Detail = observer(() => {
           },
           dingTalk: {
             type: 'void',
-            'x-visible': id === 'dingTalk',
+            // 'x-visible': id === 'dingTalk',
+            'x-reactions': {
+              dependencies: ['type'],
+              fulfill: {
+                state: {
+                  visible: '{{$deps[0]==="dingTalk"}}',
+                },
+              },
+            },
             properties: {
               dingTalkMessage: {
                 type: 'void',
@@ -1019,7 +1103,15 @@ const Detail = observer(() => {
             type: 'void',
             properties: {
               voice: {
-                'x-visible': id === 'voice',
+                // 'x-visible': id === 'voice',
+                'x-reactions': {
+                  dependencies: ['type'],
+                  fulfill: {
+                    state: {
+                      visible: '{{$deps[0]==="voice"}}',
+                    },
+                  },
+                },
                 type: 'void',
                 properties: {
                   templateType: {
@@ -1156,7 +1248,15 @@ const Detail = observer(() => {
                 },
               },
               sms: {
-                'x-visible': id === 'sms',
+                // 'x-visible': id === 'sms',
+                'x-reactions': {
+                  dependencies: ['type'],
+                  fulfill: {
+                    state: {
+                      visible: '{{$deps[0]==="sms"}}',
+                    },
+                  },
+                },
                 type: 'void',
                 properties: {
                   layout: {
@@ -1225,7 +1325,15 @@ const Detail = observer(() => {
           },
           email: {
             type: 'void',
-            'x-visible': id === 'email',
+            // 'x-visible': id === 'email',
+            'x-reactions': {
+              dependencies: ['type'],
+              fulfill: {
+                state: {
+                  visible: '{{$deps[0]==="email"}}',
+                },
+              },
+            },
             properties: {
               subject: {
                 'x-component': 'Input',
@@ -1318,6 +1426,14 @@ const Detail = observer(() => {
           webhook: {
             type: 'void',
             'x-visible': id === 'webhook',
+            'x-reactions': {
+              dependencies: ['type'],
+              fulfill: {
+                state: {
+                  visible: '{{$deps[0]==="webhook"}}',
+                },
+              },
+            },
             properties: {
               contextAsBody: {
                 title: '请求体',
@@ -1565,6 +1681,7 @@ const Detail = observer(() => {
                 <FormButtonGroup.FormItem>
                   <PermissionButton
                     type="primary"
+                    loading={loading}
                     isPermission={permission.add || permission.update}
                     onClick={handleSave}
                   >
@@ -1575,7 +1692,7 @@ const Detail = observer(() => {
             </Form>
           </Col>
           <Col span={12} push={2}>
-            {docMap?.[id]?.[provider]}
+            {docMap?.[typeItem]?.[providerItem]}
           </Col>
         </Row>
       </Card>

+ 4 - 4
src/pages/notice/Template/Log/index.tsx

@@ -3,13 +3,13 @@ import { observer } from '@formily/react';
 import { service, state } from '..';
 import ProTable, { ActionType, ProColumns } from '@jetlinks/pro-table';
 import SearchComponent from '@/components/SearchComponent';
-import { useLocation } from 'umi';
+// import { useLocation } from 'umi';
 import { InfoCircleOutlined } from '@ant-design/icons';
 import { useRef, useState } from 'react';
 
 const Log = observer(() => {
-  const location = useLocation<{ id: string }>();
-  const id = (location as any).query?.id;
+  // const location = useLocation<{ id: string }>();
+  // const id = (location as any).query?.id;
 
   const columns: ProColumns<LogItem>[] = [
     {
@@ -100,7 +100,7 @@ const Log = observer(() => {
       visible={state.log && !!state.current?.id}
     >
       <SearchComponent
-        defaultParam={[{ column: 'notifyType$IN', value: id }]}
+        defaultParam={[{ column: 'notifyType$IN', value: state.current?.type || '' }]}
         field={columns}
         model="simple"
         onSearch={(data) => {

+ 27 - 59
src/pages/notice/Template/index.tsx

@@ -15,7 +15,7 @@ import { useIntl } from '@@/plugin-locale/localeExports';
 import Service from '@/pages/notice/Template/service';
 import ConfigService from '@/pages/notice/Config/service';
 import SearchComponent from '@/components/SearchComponent';
-import { history, useLocation } from 'umi';
+import { history } from 'umi';
 import { getMenuPathByParams, MENUS_CODE } from '@/utils/menu';
 import { model } from '@formily/reactive';
 import Debug from './Debug';
@@ -23,7 +23,11 @@ import Log from '@/pages/notice/Template/Log';
 import { downloadObject, onlyMessage } from '@/utils/util';
 import moment from 'moment';
 import { PermissionButton, ProTableCard } from '@/components';
-import NoticeCard, { typeList } from '@/components/ProTableCard/CardItems/noticeTemplate';
+import NoticeCard, {
+  typeList,
+  typeObj,
+  providerObj,
+} from '@/components/ProTableCard/CardItems/noticeTemplate';
 import { observer } from '@formily/react';
 import usePermissions from '@/hooks/permission';
 
@@ -39,57 +43,10 @@ export const state = model<{
   log: false,
 });
 
-const list = {
-  weixin: {
-    corpMessage: {
-      text: '企业消息',
-      status: 'corpMessage',
-    },
-    // officialMessage: {
-    //   text: '服务号消息',
-    //   status: 'officialMessage',
-    // },
-  },
-  dingTalk: {
-    dingTalkMessage: {
-      text: '钉钉消息',
-      status: 'dingTalkMessage',
-    },
-    dingTalkRobotWebHook: {
-      text: '群机器人消息',
-      status: 'dingTalkRobotWebHook',
-    },
-  },
-  voice: {
-    aliyun: {
-      text: '阿里云语音',
-      status: 'aliyun',
-    },
-  },
-  sms: {
-    aliyunSms: {
-      text: '阿里云短信',
-      status: 'aliyunSms',
-    },
-  },
-  email: {
-    embedded: {
-      text: '邮件',
-      status: 'embedded',
-    },
-  },
-  webhook: {
-    http: {
-      text: 'Webhook',
-      status: 'http',
-    },
-  },
-};
-
 const Template = observer(() => {
   const intl = useIntl();
-  const location = useLocation<{ id: string }>();
-  const id = (location as any).query?.id;
+  // const location = useLocation<{ id: string }>();
+  // const id = (location as any).query?.id;
   const actionRef = useRef<ActionType>();
 
   const { permission: templatePermission } = usePermissions('notice');
@@ -101,13 +58,20 @@ const Template = observer(() => {
       ellipsis: true,
     },
     {
-      dataIndex: 'provider',
+      dataIndex: 'type',
       title: '通知方式',
+      renderText: (text, record) => typeObj?.[record.type]?.text || record.type,
+      valueType: 'select',
+      valueEnum: typeObj,
+    },
+    {
+      dataIndex: 'provider',
+      title: '类型',
       renderText: (text, record) => {
         return typeList[record?.type][record?.provider];
       },
       valueType: 'select',
-      valueEnum: list[id],
+      valueEnum: providerObj,
     },
     {
       dataIndex: 'description',
@@ -130,7 +94,7 @@ const Template = observer(() => {
           isPermission={templatePermission.update}
           onClick={() => {
             state.current = record;
-            history.push(getMenuPathByParams(MENUS_CODE['notice/Template/Detail'], id));
+            history.push(getMenuPathByParams(MENUS_CODE['notice/Template/Detail'], record.id));
           }}
           tooltip={{
             title: intl.formatMessage({
@@ -216,7 +180,7 @@ const Template = observer(() => {
   return (
     <PageContainer>
       <SearchComponent
-        defaultParam={[{ column: 'type$IN', value: id }]}
+        // defaultParam={[{ column: 'type$IN', value: id }]}
         field={columns}
         onSearch={(data) => {
           actionRef.current?.reset?.();
@@ -237,7 +201,7 @@ const Template = observer(() => {
               isPermission={templatePermission.add}
               onClick={() => {
                 state.current = undefined;
-                history.push(getMenuPathByParams(MENUS_CODE['notice/Template/Detail'], id));
+                history.push(getMenuPathByParams(MENUS_CODE['notice/Template/Detail']));
               }}
               key="button"
               icon={<PlusOutlined />}
@@ -304,13 +268,15 @@ const Template = observer(() => {
         cardRender={(record) => (
           <NoticeCard
             {...record}
-            type={id}
+            // type={id}
             detail={
               <div
                 style={{ fontSize: 18, padding: 8 }}
                 onClick={() => {
                   state.current = record;
-                  history.push(getMenuPathByParams(MENUS_CODE['notice/Template/Detail'], id));
+                  history.push(
+                    getMenuPathByParams(MENUS_CODE['notice/Template/Detail'], record.id),
+                  );
                 }}
               >
                 <EyeOutlined />
@@ -322,7 +288,9 @@ const Template = observer(() => {
                 key="edit"
                 onClick={() => {
                   state.current = record;
-                  history.push(getMenuPathByParams(MENUS_CODE['notice/Template/Detail'], id));
+                  history.push(
+                    getMenuPathByParams(MENUS_CODE['notice/Template/Detail'], record.id),
+                  );
                 }}
               >
                 <EditOutlined />

+ 8 - 0
src/pages/notice/Template/service.ts

@@ -73,6 +73,14 @@ class Service extends BaseService<TemplateItem> {
           value: item.id,
         }));
       }),
+
+    getTags: (id: string) =>
+      request(`${SystemConst.API_BASE}/notifier/dingtalk/corp/${id}/tags`).then((resp: any) => {
+        return resp.result?.map((item: any) => ({
+          label: item.name,
+          value: item.id,
+        }));
+      }),
   };
 
   weixin = {

+ 131 - 52
src/pages/rule-engine/Alarm/Config/index.tsx

@@ -28,60 +28,98 @@ const Config = () => {
 
   const outputData = [
     {
-      key: 'alarmName',
-      name: '告警名称',
+      key: 'alarmConfigName',
+      name: '告警配置名称',
       type: 'string',
-      desc: '推送的告警名称',
+      desc: '推送的告警配置名称',
+      example: '烟感告警',
     },
     {
-      key: 'id',
-      name: '告警ID',
+      key: 'alarmConfigId',
+      name: '告警配置ID',
+      type: 'string',
+      desc: '推送的告警配置ID',
+      example: '1605111722418597888',
+    },
+    {
+      key: 'Id',
+      name: '告警数据ID',
       type: 'string',
       desc: '告警唯一性标识',
+      example: '1515992841393119232',
+    },
+    {
+      key: 'alarmRecordId',
+      name: '告警记录ID',
+      type: 'string',
+      desc: '告警记录的唯一标识,可根据此ID处理告警',
+      example: 'ba33a59ca5ebe3dccfcd75fd0575be4e',
     },
     {
       key: 'targetType',
-      name: '告警类型',
+      name: '告警目标类型',
       type: 'string',
-      desc: '告警所属的业务类型,具体有产品、设备、组织、其他',
+      desc: '告警所属的业务类型,具体有产品、设备、部门、其他',
+      example: '产品',
     },
     {
       key: 'targetId',
       name: '告警目标ID',
       type: 'string',
       desc: '告警目标唯一性标识',
+      example: '1583300346713661440',
     },
     {
       key: 'targetName',
       name: '告警目标名称',
       type: 'string',
       desc: '告警目标实例名称',
+      example: '海康烟感',
+    },
+    {
+      key: 'alarmTime',
+      name: '告警时间',
+      type: 'long',
+      desc: '告警触发时间',
+      example: '1651233650840',
+    },
+    {
+      key: 'sourceType',
+      name: '告警源类型',
+      type: 'string',
+      desc: '触发告警的源类型。当前只有device',
+      example: 'device',
     },
     {
-      key: 'alarmDate',
-      name: '最近告警时间',
-      type: 'date',
-      desc: '最近一次的告警触发时间',
+      key: 'sourceId',
+      name: '告警源ID',
+      type: 'string',
+      desc: '触发告警的源Id。如设备Id',
+      example: '1605138218826821632',
+    },
+    {
+      key: 'sourceName',
+      name: '告警源名称',
+      type: 'string',
+      desc: '触发告警的源名称。如设备名称',
+      example: '1楼烟感S01',
     },
     {
       key: 'level',
       name: '告警级别',
       type: 'int',
       desc: '告警严重程度指标',
-    },
-    {
-      key: 'state',
-      name: '告警状态',
-      type: 'object',
-      desc: 'value:告警状态 text:告警值',
+      example: 1,
     },
     {
       key: 'description',
       name: '告警说明',
       type: 'string',
       desc: '告警规则说明',
+      example: '1楼烟感统一告警规则设置',
     },
   ];
+
   const outputColumns = [
     {
       title: '名称',
@@ -105,49 +143,85 @@ const Config = () => {
       title: '说明',
       dataIndex: 'desc',
       key: 'desc',
+      width: 100,
+      ellipsis: true,
+    },
+    {
+      title: '示例值',
+      dataIndex: 'example',
+      key: 'example',
+      width: 100,
       ellipsis: true,
     },
   ];
 
+  const subColumns = [...outputColumns];
+  subColumns.splice(3, 0, {
+    title: '必填',
+    dataIndex: 'require',
+    key: 'require',
+    ellipsis: true,
+  });
+
   const subData = [
     {
-      key: 'id',
-      name: '告警ID',
+      key: 'alarmRecordId',
+      name: '告警记录ID',
       type: 'string',
       require: '是',
-      desc: '订阅的告警唯一性标识',
+      desc: '告警记录的唯一标识,可根据此ID处理告警',
+      example: 'ba33a59ca5ebe3dccfcd75fd0575be4e',
     },
     {
-      key: 'describe',
-      name: '处理内容',
+      key: 'alarmConfigId',
+      name: '告警配置ID',
       type: 'string',
       require: '是',
-      desc: '告警处理内容详细描述说明',
+      desc: '推送的告警配置ID',
+      example: '1605111722418597888',
     },
     {
-      key: 'state',
-      name: '告警状态',
-      type: 'string',
+      key: 'alarmTime',
+      name: '告警时间',
+      type: 'long',
       require: '是',
-      desc: '告警中, 无告警',
+      desc: '告警触发时间',
+      example: '1651233650840',
     },
     {
       key: 'handleTime',
       name: '处理时间',
       type: 'long',
-      require: '',
+      require: '',
       desc: '告警处理时间,不填是默认为消息处理时间',
+      example: '1651233650840',
+    },
+    {
+      key: 'describe',
+      name: '处理说明',
+      type: 'string',
+      require: '是',
+      desc: '告警处理内容详细描述说明',
+      example: '已联系第三方人员进行告警处理,现告警已恢复',
+    },
+    {
+      key: 'type',
+      name: '处理类型',
+      type: 'enum',
+      require: '是',
+      desc: '支持system、user',
+      example: 'user',
+    },
+    {
+      key: 'state',
+      name: '处理后的状态',
+      type: 'enum',
+      require: '是',
+      desc: 'warning、normal',
+      example: 'normal',
     },
   ];
 
-  const subColumns = [...outputColumns];
-  subColumns.splice(3, 0, {
-    title: '必填',
-    dataIndex: 'require',
-    key: 'require',
-    ellipsis: true,
-  });
-
   const SchemaField = createSchemaField({
     components: {
       FormItem,
@@ -280,19 +354,20 @@ const Config = () => {
   const outputText = `
   ~~~json
   {
-    "id": "1518055745863864320",
-    "alarmConfigId": "1511601633016569856",
-    "alarmName": "一楼烟感告警",
-    "targetType": "product",
-    "targetId": "product-01",
-    "targetName": "产品-001",
-    "alarmTime": "1651233650840",
-    "level": 3,
-    "state": {
-      "text": "告警中",
-      "value": "warning"
-    },
-    "description": "一楼烟感告警设置"
+    "alarmConfigId": "1605111722418597888",
+    "id": "1515992841393119232",
+    "alarmConfigId": "1586989804257853441",
+    "alarmConfigName": "烟感告警",
+    "alarmRecordId": "ba33a59ca5ebe3dccfcd75fd0575be4e",
+    "level": "3",
+    "description": "设备温度过高",
+    "alarmTime": "1667202964007",
+    "sourceType": "device",
+    "sourceId": "1605138218826821632",
+    "sourceName": "1楼烟感S01",
+    "targetType": "device",
+    "targetName": "温度探测设备",
+    "targetId": "1583300346713661440"
   }
   ~~~
   `;
@@ -300,9 +375,13 @@ const Config = () => {
   const subText = `
   ~~~json
   {
-    "id": "1518055745863864320",
-    "state": "normal",
-     "describe": "已处理"
+    "alarmRecordId": "ba33a59ca5ebe3dccfcd75fd0575be4e",
+    "alarmConfigId": "1605111722418597888",
+    "alarmTime": "1651233650840",
+    "handleTime": "1651233650841",
+    "describe": "已联系第三方人员进行告警处理,现告警已恢复",
+    "type": "user",
+    "state": "normal"
   }
   ~~~
   `;

+ 2 - 1
src/pages/rule-engine/Scene/Save/action/DeviceOutput/actions/ObjModel.tsx

@@ -34,7 +34,8 @@ export default (props: Props) => {
       zIndex={1050}
       onCancel={() => props.close()}
       onOk={() => {
-        props.ok(value);
+        props.ok(JSON.parse(value));
+        console.log(value, JSON.parse(value));
       }}
     >
       <div

+ 2 - 2
src/pages/rule-engine/Scene/Save/action/DeviceOutput/actions/TypeModel.tsx

@@ -326,9 +326,9 @@ export default observer((props: Props) => {
           }}
           ok={(param) => {
             if (props.onChange) {
-              props.onChange(param);
+              props.onChange(JSON.stringify(param));
             }
-            setValue(param);
+            setValue(JSON.stringify(param));
             setObjVisable(false);
           }}
         />

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

@@ -164,7 +164,7 @@ export default observer((props: Props) => {
             <ReadProperty
               properties={properties}
               onChange={(_, text) => {
-                console.log(text);
+                // console.log(text);
                 DeviceModel.propertiesName = text;
               }}
             />

+ 11 - 5
src/pages/rule-engine/Scene/Save/action/DeviceOutput/device/index.tsx

@@ -449,7 +449,6 @@ export default observer((props: Props) => {
       form.setFieldsValue({ selector: DeviceModel.selector });
     }
     sourceChangeEvent();
-    // console.log('-----deviceid-----', DeviceModel.deviceId);
     if (DeviceModel.deviceId) {
       service.detail(DeviceModel.deviceId).then((res) => {
         if (res.status === 200) {
@@ -459,16 +458,23 @@ export default observer((props: Props) => {
     }
   }, []);
 
-  // useEffect(()=>{
-  //   console.log('----------------',props.branchGroup,props.thenName,props.name)
-  // },[props.branchGroup,props.thenName])
-
   useEffect(() => {
     if (DeviceModel.productDetail) {
       const metadata = JSON.parse(DeviceModel.productDetail?.metadata || '{}');
       setTagList(metadata.tags);
       filterType();
     }
+    //处理变量
+    if (builtInList && builtInList.length !== 0) {
+      const param = DeviceModel.selectorValues?.[0]?.value;
+      const isVariable = builtInList.find((item: any) => {
+        return item.children.find((i: any) => i.id === param);
+      });
+      if (isVariable) {
+        form.setFieldsValue({ selector: 'variable' });
+      }
+      // console.log(isVariable);
+    }
   }, [DeviceModel.productDetail, builtInList]);
 
   useEffect(() => {

+ 12 - 5
src/pages/rule-engine/Scene/Save/action/DeviceOutput/index.tsx

@@ -65,7 +65,7 @@ export default observer((props: Props) => {
   const next = () => {
     if (
       (DeviceModel.current === 0 && DeviceModel.productId) ||
-      (DeviceModel.current === 1 && DeviceModel.deviceId)
+      (DeviceModel.current === 1 && (DeviceModel.deviceId || DeviceModel.selector === 'tag'))
     ) {
       return (DeviceModel.current += 1);
     } else {
@@ -98,6 +98,10 @@ export default observer((props: Props) => {
       productId: DeviceModel.productId,
       message: value.message,
     };
+    //处理按变量
+    if (DeviceModel.selector === 'variable') {
+      item.selector = 'fixed';
+    }
     // console.log(item, value);
 
     const _options: any = {
@@ -108,6 +112,7 @@ export default observer((props: Props) => {
       selector: DeviceModel.selector, //选择器标识
       productName: DeviceModel.productDetail.name,
       relationName: DeviceModel.relationName,
+      triggerName: FormModel.current.options?.trigger?.name || '触发设备',
       taglist: [],
       columns: [],
       otherColumns: [],
@@ -139,9 +144,11 @@ export default observer((props: Props) => {
         type: it.type ? (it.type === 'and' ? '并且' : '或者') : '',
         value: it.value,
       }));
-      // console.log(_options.taglist, 'taglist')
     }
-    // console.log(DeviceModel.propertiesValue, _options);
+    if (_options.selector === 'variable') {
+      _options.name = DeviceModel.selectorValues?.[0]?.name;
+    }
+    // console.log("----------",item,_options)
     props.save(item, _options);
     init();
   };
@@ -166,8 +173,8 @@ export default observer((props: Props) => {
 
   useEffect(() => {
     const item = FormModel.current?.branches?.[0].then?.[0]?.actions?.[0].device?.productId;
-    console.log(item);
     formProductIdRef.current = item;
+    // console.log('---------', FormModel.current.options?.trigger?.name)
   }, []);
 
   return (
@@ -233,7 +240,7 @@ export default observer((props: Props) => {
                   }
                 });
               }
-              return DeviceModel.deviceId
+              return DeviceModel.deviceId || DeviceModel.selector === 'tag'
                 ? (DeviceModel.current = 2)
                 : onlyMessage('请选择设备', 'error');
             } else {

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

@@ -246,7 +246,7 @@ export default (props: ItemProps) => {
           <div>
             <AIcon type={typeIconMap[data!.device!.message!.messageType]} />
             {data?.options?.type}
-            {data.options?.taglist.map((item: any) => (
+            {data.options?.taglist?.map((item: any) => (
               <span>
                 {item.type}
                 {item.name}
@@ -261,7 +261,7 @@ export default (props: ItemProps) => {
         return (
           <div>
             <AIcon type={typeIconMap[data!.device!.message!.messageType]} />
-            {data?.options?.type}与<span>{data?.options?.name}</span>具有相同
+            {data?.options?.type}与<span>{data?.options?.triggerName}</span>具有相同
             {data?.options?.relationName}的{data?.options?.productName}设备的
             {data?.options?.properties}
           </div>

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

@@ -183,7 +183,7 @@ export default forwardRef((props: Props, ref) => {
               name={item.id}
               label={item.name}
               initialValue={initialValue}
-              required={true}
+              required={type !== 'file' ? true : false}
               rules={rules}
             >
               {typeComponents(item)}

+ 9 - 4
src/pages/rule-engine/Scene/Save/action/notify/index.tsx

@@ -203,12 +203,17 @@ export default observer((props: Props) => {
           onChange={async (value: number) => {
             if (value === 0) {
               NotifyModel.current = value;
-            }
-            if (value === 1) {
-              if (NotifyModel.notify.notifyType) {
+            } else if (value === 1) {
+              const val = await WayRef.current?.save();
+              if (val) {
+                NotifyModel.notify.notifyType = val;
                 NotifyModel.current = value;
               } else {
-                onlyMessage('请选择通知方式', 'error');
+                if (NotifyModel.notify.notifyType) {
+                  NotifyModel.current = value;
+                } else {
+                  onlyMessage('请选择通知方式', 'error');
+                }
               }
             } else if (value === 2) {
               if (NotifyModel.notify.notifierId) {

+ 10 - 19
src/pages/rule-engine/Scene/index.tsx

@@ -19,7 +19,7 @@ import { onlyMessage } from '@/utils/util';
 import useHistory from '@/hooks/route/useHistory';
 import Save from './Save/save';
 import { getMenuPathByCode } from '@/utils/menu';
-import { Spin } from 'antd';
+// import { Spin } from 'antd';
 
 export const service = new Service('scene');
 
@@ -32,7 +32,7 @@ const Scene = () => {
   const [current, setCurrent] = useState<Partial<SceneItem>>({});
   const history = useHistory();
   const [loading, setLoading] = useState<boolean>(true);
-  const [title, setTitle] = useState<string>('确定删除?');
+  const [title, setTitle] = useState<string>('');
 
   const deleteById = async (id: string) => {
     // const alarmResp = await service.sceneByAlarm(id);
@@ -159,27 +159,18 @@ const Scene = () => {
         disabled={record.state.value === 'started'}
         onClick={async () => {
           const res = await service.sceneByAlarm(record.id);
-          if (res.status === 200) {
-            // setTimeout(()=>{})
-            setLoading(false);
-            if (res.result !== 0) {
-              setTitle('该场景已绑定告警,确定删除?');
-            } else {
-              setTitle('确定删除?');
-            }
+          if (res.status === 200 && res.result.data.length !== 0) {
+            setTitle('该场景已绑定告警,确定删除?');
           } else {
-            setLoading(false);
+            setTitle('确定删除?');
           }
         }}
         popConfirm={{
-          title: <>{loading ? <Spin /> : title}</>,
-          okButtonProps: {
-            loading: loading,
-          },
-          onCancel: () => {
-            setTitle('');
-            setLoading(false);
-          },
+          // title: loading ? <Spin /> : title,
+          // okButtonProps: {
+          //   loading: loading,
+          // },
+          title: '确定删除?',
           disabled: record.state.value === 'started',
           onConfirm: () => {
             deleteById(record.id);

+ 2 - 2
src/pages/rule-engine/Scene/service.ts

@@ -19,9 +19,9 @@ class Service extends BaseService<SceneItem> {
     }).then((resp) => resp.result);
 
   sceneByAlarm = (id: string) =>
-    request(`${SystemConst.API_BASE}/alarm/config/_count`, {
+    request(`${SystemConst.API_BASE}/alarm/config/_query`, {
       method: 'POST',
-      data: { terms: [{ column: 'sceneId', value: id }] },
+      data: { terms: [{ column: 'id', value: id, termType: 'rule-bind-alarm' }] },
     });
   _execute = (id: string) =>
     request(`/${SystemConst.API_BASE}/scene/${id}/_execute`, {

+ 12 - 1
src/pages/system/DataSource/Management/DataTable.tsx

@@ -1,5 +1,5 @@
 import { Form, FormGrid, FormItem, Input, Password, Select } from '@formily/antd';
-import { createForm } from '@formily/core';
+import { createForm, registerValidateRules } from '@formily/core';
 import type { ISchema } from '@formily/react';
 import { createSchemaField } from '@formily/react';
 import { Modal } from 'antd';
@@ -26,6 +26,14 @@ const DataTable = (props: Props) => {
     },
   });
 
+  registerValidateRules({
+    validateId(value) {
+      if (!value) return '';
+      const reg = new RegExp('^[0-9a-zA-Z_\\\\-]+$');
+      return reg.exec(value) ? '' : 'ID只能由数字、字母、下划线、中划线组成';
+    },
+  });
+
   const schema: ISchema = {
     type: 'object',
     properties: {
@@ -50,6 +58,9 @@ const DataTable = (props: Props) => {
             required: true,
             message: '请输入名称',
           },
+          {
+            validateId: true,
+          },
         ],
         required: true,
       },

+ 12 - 12
src/pages/system/DataSource/Save/index.tsx

@@ -112,10 +112,10 @@ const Save = (props: Props) => {
               placeholder: '请输入r2bdc或者jdbc连接地址,示例:r2dbc:mysql://127.0.0.1:3306/test',
             },
             'x-validator': [
-              {
-                format: 'url',
-                message: '请输入正确的URL',
-              },
+              // {
+              //   format: 'url',
+              //   message: '请输入正确的URL',
+              // },
               {
                 required: true,
                 message: '请输入URL',
@@ -144,10 +144,10 @@ const Save = (props: Props) => {
               placeholder: '请输入管理地址,示例:http://localhost:15672',
             },
             'x-validator': [
-              {
-                format: 'url',
-                message: '请输入正确的管理地址',
-              },
+              // {
+              //   format: 'url',
+              //   message: '请输入正确的管理地址',
+              // },
               {
                 required: true,
                 message: '请输入管理地址',
@@ -176,10 +176,10 @@ const Save = (props: Props) => {
               placeholder: '请输入链接地址,示例:localhost:5672',
             },
             'x-validator': [
-              {
-                format: 'url',
-                message: '请输入正确的链接地址',
-              },
+              // {
+              //   format: 'url',
+              //   message: '请输入正确的链接地址',
+              // },
               {
                 required: true,
                 message: '请输入链接地址',

+ 273 - 107
src/pages/system/Menu/Setting/baseMenu.ts

@@ -49,147 +49,313 @@ export default [
         owner: 'iot',
         //parentId: '1',
         //id: '1-2',
-        url: '/iot/notice/Type',
+        url: '/iot/notice',
         icon: 'icon-tongzhiguanli',
         sortIndex: 2,
         showPage: ['template', 'notifier'],
         permissions: [],
-        buttons: [
-          {
-            id: 'bind',
-            name: '同步用户',
-            permissions: [
-              {
-                permission: 'notifier',
-                actions: ['query'],
-              },
-              {
-                permission: 'template',
-                actions: ['query'],
-              },
-              {
-                permission: 'user-third-party-manager',
-                actions: ['query', 'save'],
-              },
-              {
-                permission: 'user',
-                actions: ['query'],
-              },
-            ],
-          },
+        children: [
           {
-            id: 'view',
-            name: '查看',
-            permissions: [
+            code: 'notice',
+            name: '通知配置',
+            owner: 'iot',
+            //parentId: '1',
+            //id: '1-2',
+            url: '/iot/notice/Config',
+            icon: 'icon-tongzhiguanli',
+            sortIndex: 1,
+            showPage: ['notifier'],
+            permissions: [],
+            buttons: [
               {
-                permission: 'notifier',
-                actions: ['query'],
+                id: 'bind',
+                name: '同步用户',
+                permissions: [
+                  {
+                    permission: 'notifier',
+                    actions: ['query'],
+                  },
+                  {
+                    permission: 'template',
+                    actions: ['query'],
+                  },
+                  {
+                    permission: 'user-third-party-manager',
+                    actions: ['query', 'save'],
+                  },
+                  {
+                    permission: 'user',
+                    actions: ['query'],
+                  },
+                ],
               },
               {
-                permission: 'template',
-                actions: ['query'],
+                id: 'view',
+                name: '查看',
+                permissions: [
+                  {
+                    permission: 'notifier',
+                    actions: ['query'],
+                  },
+                  {
+                    permission: 'template',
+                    actions: ['query'],
+                  },
+                ],
               },
-            ],
-          },
-          {
-            id: 'log',
-            name: '通知记录',
-            permissions: [
               {
-                permission: 'notifier',
-                actions: ['query'],
+                id: 'log',
+                name: '通知记录',
+                permissions: [
+                  {
+                    permission: 'notifier',
+                    actions: ['query'],
+                  },
+                  {
+                    permission: 'template',
+                    actions: ['query'],
+                  },
+                ],
               },
               {
-                permission: 'template',
-                actions: ['query'],
+                id: 'debug',
+                name: '调试',
+                permissions: [
+                  {
+                    permission: 'notifier',
+                    actions: ['query', 'send'],
+                  },
+                  {
+                    permission: 'template',
+                    actions: ['query'],
+                  },
+                  {
+                    permission: 'user',
+                    actions: ['query'],
+                  },
+                ],
               },
-            ],
-          },
-          {
-            id: 'debug',
-            name: '调试',
-            permissions: [
               {
-                permission: 'notifier',
-                actions: ['query', 'send'],
+                id: 'export',
+                name: '导出',
+                permissions: [
+                  {
+                    permission: 'notifier',
+                    actions: ['query'],
+                  },
+                  {
+                    permission: 'template',
+                    actions: ['query'],
+                  },
+                ],
               },
               {
-                permission: 'template',
-                actions: ['query'],
+                id: 'import',
+                name: '导入',
+                permissions: [
+                  {
+                    permission: 'notifier',
+                    actions: ['query', 'save'],
+                  },
+                  {
+                    permission: 'template',
+                    actions: ['query', 'save'],
+                  },
+                ],
               },
               {
-                permission: 'user',
-                actions: ['query'],
+                id: 'delete',
+                name: '删除',
+                permissions: [
+                  {
+                    permission: 'notifier',
+                    actions: ['query', 'delete'],
+                  },
+                  {
+                    permission: 'template',
+                    actions: ['query', 'delete'],
+                  },
+                ],
               },
-            ],
-          },
-          {
-            id: 'export',
-            name: '导出',
-            permissions: [
               {
-                permission: 'notifier',
-                actions: ['query'],
+                id: 'update',
+                name: '编辑',
+                permissions: [
+                  {
+                    permission: 'notifier',
+                    actions: ['query', 'save'],
+                  },
+                  {
+                    permission: 'template',
+                    actions: ['query', 'save'],
+                  },
+                ],
               },
               {
-                permission: 'template',
-                actions: ['query'],
+                id: 'add',
+                name: '新增',
+                permissions: [
+                  {
+                    permission: 'notifier',
+                    actions: ['query', 'save'],
+                  },
+                  {
+                    permission: 'template',
+                    actions: ['query', 'save'],
+                  },
+                ],
               },
             ],
           },
           {
-            id: 'import',
-            name: '导入',
-            permissions: [
+            code: 'notice',
+            name: '通知模板',
+            owner: 'iot',
+            //parentId: '1',
+            //id: '1-2',
+            url: '/iot/notice/Template',
+            icon: 'icon-tongzhiguanli',
+            sortIndex: 2,
+            showPage: ['template'],
+            permissions: [],
+            buttons: [
+              // {
+              //   id: 'bind',
+              //   name: '同步用户',
+              //   permissions: [
+              //     {
+              //       permission: 'notifier',
+              //       actions: ['query'],
+              //     },
+              //     {
+              //       permission: 'template',
+              //       actions: ['query'],
+              //     },
+              //     {
+              //       permission: 'user-third-party-manager',
+              //       actions: ['query', 'save'],
+              //     },
+              //     {
+              //       permission: 'user',
+              //       actions: ['query'],
+              //     },
+              //   ],
+              // },
               {
-                permission: 'notifier',
-                actions: ['query', 'save'],
+                id: 'view',
+                name: '查看',
+                permissions: [
+                  {
+                    permission: 'notifier',
+                    actions: ['query'],
+                  },
+                  {
+                    permission: 'template',
+                    actions: ['query'],
+                  },
+                ],
               },
               {
-                permission: 'template',
-                actions: ['query', 'save'],
+                id: 'log',
+                name: '通知记录',
+                permissions: [
+                  {
+                    permission: 'notifier',
+                    actions: ['query'],
+                  },
+                  {
+                    permission: 'template',
+                    actions: ['query'],
+                  },
+                ],
               },
-            ],
-          },
-          {
-            id: 'delete',
-            name: '删除',
-            permissions: [
               {
-                permission: 'notifier',
-                actions: ['query', 'delete'],
+                id: 'debug',
+                name: '调试',
+                permissions: [
+                  {
+                    permission: 'notifier',
+                    actions: ['query', 'send'],
+                  },
+                  {
+                    permission: 'template',
+                    actions: ['query'],
+                  },
+                  {
+                    permission: 'user',
+                    actions: ['query'],
+                  },
+                ],
               },
               {
-                permission: 'template',
-                actions: ['query', 'delete'],
+                id: 'export',
+                name: '导出',
+                permissions: [
+                  {
+                    permission: 'notifier',
+                    actions: ['query'],
+                  },
+                  {
+                    permission: 'template',
+                    actions: ['query'],
+                  },
+                ],
               },
-            ],
-          },
-          {
-            id: 'update',
-            name: '编辑',
-            permissions: [
               {
-                permission: 'notifier',
-                actions: ['query', 'save'],
+                id: 'import',
+                name: '导入',
+                permissions: [
+                  {
+                    permission: 'notifier',
+                    actions: ['query', 'save'],
+                  },
+                  {
+                    permission: 'template',
+                    actions: ['query', 'save'],
+                  },
+                ],
               },
               {
-                permission: 'template',
-                actions: ['query', 'save'],
+                id: 'delete',
+                name: '删除',
+                permissions: [
+                  {
+                    permission: 'notifier',
+                    actions: ['query', 'delete'],
+                  },
+                  {
+                    permission: 'template',
+                    actions: ['query', 'delete'],
+                  },
+                ],
               },
-            ],
-          },
-          {
-            id: 'add',
-            name: '新增',
-            permissions: [
               {
-                permission: 'notifier',
-                actions: ['query', 'save'],
+                id: 'update',
+                name: '编辑',
+                permissions: [
+                  {
+                    permission: 'notifier',
+                    actions: ['query', 'save'],
+                  },
+                  {
+                    permission: 'template',
+                    actions: ['query', 'save'],
+                  },
+                ],
               },
               {
-                permission: 'template',
-                actions: ['query', 'save'],
+                id: 'add',
+                name: '新增',
+                permissions: [
+                  {
+                    permission: 'notifier',
+                    actions: ['query', 'save'],
+                  },
+                  {
+                    permission: 'template',
+                    actions: ['query', 'save'],
+                  },
+                ],
               },
             ],
           },
@@ -1375,7 +1541,7 @@ export default [
         permissions: [],
         children: [
           {
-            code: 'link/DataCollect/Dashboard',
+            code: 'DataCollect/Dashboard',
             name: '仪表盘',
             owner: 'iot',
             sortIndex: 1,
@@ -1398,11 +1564,11 @@ export default [
             buttons: [],
           },
           {
-            code: 'link/DataCollect/DataGathering',
-            name: '数据采集',
+            code: 'DataCollect/Channel',
+            name: '通道管理',
             owner: 'iot',
             sortIndex: 2,
-            url: '/iot/DataCollect/DataGathering',
+            url: '/iot/DataCollect/Channel',
             icon: 'icon-rizhifuwu',
             showPage: [
               'data-collect-channel',
@@ -1529,11 +1695,11 @@ export default [
             ],
           },
           {
-            code: 'link/DataCollect/IntegratedQuery',
-            name: '综合查询',
+            code: 'DataCollect/Collector',
+            name: '采集器',
             owner: 'iot',
             sortIndex: 3,
-            url: '/iot/DataCollect/IntegratedQuery',
+            url: '/iot/DataCollect/Collector',
             icon: 'icon-yingyongguanli',
             showPage: [
               'data-collect-channel',

+ 1 - 0
src/pages/user/Login/index.tsx

@@ -87,6 +87,7 @@ const Login: React.FC = () => {
   useEffect(getCode, []);
 
   useEffect(() => {
+    console.log('版本:' + localStorage.getItem(SystemConst.Version_Code));
     localStorage.clear();
     Service.getSystemVersion().then((resp) => {
       if (resp && resp.status === 200 && resp.result) {

+ 6 - 6
src/utils/menu/index.ts

@@ -12,12 +12,12 @@ const DetailCode = 'detail';
 
 // 额外子级路由F
 const extraRouteObj = {
-  notice: {
-    children: [
-      { code: 'Config', name: '通知配置' },
-      { code: 'Template', name: '通知模版' },
-    ],
-  },
+  // notice: {
+  //   children: [
+  //     { code: 'Config', name: '通知配置' },
+  //     { code: 'Template', name: '通知模版' },
+  //   ],
+  // },
   'media/Cascade': {
     children: [
       { code: 'Save', name: '新增' },

+ 6 - 6
src/utils/menu/router.ts

@@ -37,9 +37,9 @@ export enum MENUS_CODE {
   'link/Protocol' = 'link/Protocol',
   'link/Type' = 'link/Type',
   'link/AccessConfig' = 'link/AccessConfig',
-  'link/DataCollect/Dashboard' = 'link/DataCollect/Dashboard',
-  'link/DataCollect/DataGathering' = 'link/DataCollect/DataGathering',
-  'link/DataCollect/IntegratedQuery' = 'link/DataCollect/IntegratedQuery',
+  'DataCollect/Dashboard' = 'DataCollect/Dashboard',
+  'DataCollect/Channel' = 'DataCollect/Channel',
+  'DataCollect/Collector' = 'DataCollect/Collector',
   'edge/Device' = 'edge/Device',
   'edge/Resource' = 'edge/Resource',
   'Log' = 'Log',
@@ -55,10 +55,10 @@ export enum MENUS_CODE {
   'media/Stream' = 'media/Stream',
   'media/Stream/Detail' = 'media/Stream/Detail',
   'media/DashBoard' = 'media/DashBoard',
-  'notice/Type' = 'notice/Type',
-  'notice/Config' = 'notice/Config',
   'media/SplitScreen' = 'media/SplitScreen',
-  'notice/Type/Config' = 'notice/Config',
+  // 'notice/Type' = 'notice/Type',
+  // 'notice/Type/Config' = 'notice/Config',
+  'notice/Config' = 'notice/Config',
   'notice/Config/Detail' = 'notice/Config/Detail',
   'notice/Template' = 'notice/Template',
   'notice/Template/Detail' = 'notice/Template/Detail',