소스 검색

fix: merge

wzyyy 3 년 전
부모
커밋
687d6c0de9
37개의 변경된 파일953개의 추가작업 그리고 305개의 파일을 삭제
  1. 4 4
      config/proxy.ts
  2. 1 0
      package.json
  3. 181 0
      src/components/BindParentDevice/index.tsx
  4. 1 1
      src/components/NoticeIcon/index.tsx
  5. 1 0
      src/components/ProTableCard/index.tsx
  6. 11 2
      src/components/SearchComponent/index.tsx
  7. 7 2
      src/pages/Northbound/DuerOS/Detail/index.tsx
  8. 1 1
      src/pages/device/Instance/Detail/ChildDevice/BindChildDevice/index.tsx
  9. 103 0
      src/pages/device/Instance/Detail/Diagnose/Status/DiagnosticAdvice.tsx
  10. 4 2
      src/pages/device/Instance/Detail/Diagnose/Status/ManualInspection.tsx
  11. 403 138
      src/pages/device/Instance/Detail/Diagnose/Status/index.tsx
  12. 2 3
      src/pages/device/Instance/Detail/Diagnose/index.tsx
  13. 5 4
      src/pages/device/Instance/Detail/Running/Property/FileComponent/index.tsx
  14. 11 0
      src/pages/device/Instance/service.ts
  15. 13 0
      src/pages/device/Product/Detail/Access/AccessConfig/index.tsx
  16. 31 0
      src/pages/device/components/Metadata/Base/Edit/index.tsx
  17. 1 1
      src/pages/link/AccessConfig/Detail/Access/data.ts
  18. 4 1
      src/pages/link/DashBoard/index.less
  19. 18 71
      src/pages/link/DashBoard/index.tsx
  20. 1 1
      src/pages/link/Type/Detail/index.tsx
  21. 1 1
      src/pages/notice/Template/service.ts
  22. 4 4
      src/pages/rule-engine/Scene/Save/action/VariableItems/builtIn.tsx
  23. 21 20
      src/pages/rule-engine/Scene/Save/action/action.tsx
  24. 7 4
      src/pages/rule-engine/Scene/Save/action/device/ConditionalFiltering.tsx
  25. 8 10
      src/pages/rule-engine/Scene/Save/action/device/index.tsx
  26. 1 0
      src/pages/rule-engine/Scene/Save/components/BuiltInParams.tsx
  27. 23 7
      src/pages/rule-engine/Scene/Save/components/TimingTrigger/refactor.tsx
  28. 4 2
      src/pages/rule-engine/Scene/Save/index.tsx
  29. 0 1
      src/pages/rule-engine/Scene/Save/trigger/index.tsx
  30. 21 13
      src/pages/rule-engine/Scene/TriggerTerm/index.tsx
  31. 1 1
      src/pages/system/Department/Assets/deivce/index.tsx
  32. 6 1
      src/pages/system/Department/Assets/index.tsx
  33. 1 1
      src/pages/system/Department/Assets/product/bind.tsx
  34. 1 1
      src/pages/system/Department/Assets/product/index.tsx
  35. 1 1
      src/pages/system/Department/Assets/service.ts
  36. 2 1
      src/pages/system/User/Save/index.tsx
  37. 48 6
      yarn.lock

+ 4 - 4
config/proxy.ts

@@ -9,11 +9,11 @@
 export default {
   dev: {
     '/jetlinks': {
-      // target: 'http://192.168.32.44:8844/',
-      // ws: 'ws://192.168.32.44:8844/',
+      target: 'http://192.168.32.8:8844/',
+      ws: 'ws://192.168.32.8:8844/',
       // 开发环境
-      target: 'http://120.79.18.123:8844/',
-      ws: 'ws://120.79.18.123:8844/',
+      // target: 'http://120.79.18.123:8844/',
+      // ws: 'ws://120.79.18.123:8844/',
       // 测试环境
       // target: 'http://120.77.179.54:8844/',
       // ws: 'ws://120.77.179.54:8844/',

+ 1 - 0
package.json

@@ -93,6 +93,7 @@
     "omit.js": "^2.0.2",
     "react": "^17.0.0",
     "react-amap": "^1.2.8",
+    "react-custom-scrollbars": "^4.2.1",
     "react-dev-inspector": "^1.1.1",
     "react-dom": "^17.0.0",
     "react-helmet-async": "^1.0.4",

+ 181 - 0
src/components/BindParentDevice/index.tsx

@@ -0,0 +1,181 @@
+import { Badge, Button, Modal } from 'antd';
+import type { DeviceInstance } from '@/pages/device/Instance/typings';
+import SearchComponent from '@/components/SearchComponent';
+import type { ActionType, ProColumns } from '@jetlinks/pro-table';
+import ProTable from '@jetlinks/pro-table';
+import { useRef, useState } from 'react';
+import { service, statusMap } from '@/pages/device/Instance';
+import { useIntl } from 'umi';
+import moment from 'moment';
+
+interface Props {
+  data: Partial<DeviceInstance>;
+  onCancel: () => void;
+  onOk: (parentId: string) => void;
+}
+
+const BindParentDevice = (props: Props) => {
+  const intl = useIntl();
+
+  const actionRef = useRef<ActionType>();
+  const [searchParams, setSearchParams] = useState<any>({});
+  const [bindKeys, setBindKeys] = useState<any[]>([]);
+
+  const columns: ProColumns<DeviceInstance>[] = [
+    {
+      title: 'ID',
+      dataIndex: 'id',
+      ellipsis: true,
+    },
+    {
+      title: '设备名称',
+      ellipsis: true,
+      dataIndex: 'name',
+    },
+    {
+      title: '所属产品',
+      ellipsis: true,
+      dataIndex: 'productName',
+    },
+    {
+      title: '注册时间',
+      dataIndex: 'registryTime',
+      ellipsis: true,
+      width: '200px',
+      render: (text: any) => (!!text ? moment(text).format('YYYY-MM-DD HH:mm:ss') : '/'),
+      sorter: true,
+    },
+    {
+      title: '状态',
+      dataIndex: 'state',
+      ellipsis: true,
+      width: 100,
+      renderText: (record) =>
+        record ? <Badge status={statusMap.get(record.value)} text={record.text} /> : '',
+      valueType: 'select',
+      valueEnum: {
+        notActive: {
+          text: intl.formatMessage({
+            id: 'pages.device.instance.status.notActive',
+            defaultMessage: '未启用',
+          }),
+          value: 'notActive',
+        },
+        offline: {
+          text: intl.formatMessage({
+            id: 'pages.device.instance.status.offLine',
+            defaultMessage: '离线',
+          }),
+          value: 'offline',
+        },
+        online: {
+          text: intl.formatMessage({
+            id: 'pages.device.instance.status.onLine',
+            defaultMessage: '在线',
+          }),
+          value: 'online',
+        },
+      },
+    },
+  ];
+
+  const submitBtn = async () => {
+    // const resp = await service.bindDevice(InstanceModel.detail.id!, bindKeys);
+    // if (resp.status === 200) {
+    //   props.onCancel();
+    //   setBindKeys([]);
+    //   actionRef.current?.reset?.();
+    // }
+  };
+
+  return (
+    <Modal
+      maskClosable={false}
+      title="绑定父设备"
+      visible
+      width={1000}
+      onOk={() => {
+        submitBtn();
+      }}
+      onCancel={() => {
+        props.onCancel();
+        setBindKeys([]);
+      }}
+      footer={[
+        <Button
+          key="back"
+          onClick={() => {
+            props.onCancel();
+            setBindKeys([]);
+            actionRef.current?.reset?.();
+          }}
+        >
+          取消
+        </Button>,
+        <Button
+          disabled={!(bindKeys.length > 0)}
+          key="submit"
+          type="primary"
+          onClick={() => {
+            submitBtn();
+          }}
+        >
+          确认
+        </Button>,
+      ]}
+    >
+      <SearchComponent<DeviceInstance>
+        field={[...columns]}
+        target="parents-device-bind"
+        enableSave={false}
+        model={'simple'}
+        defaultParam={[
+          {
+            terms: [
+              {
+                column: 'productId$product-info',
+                value: [
+                  {
+                    column: 'deviceType',
+                    termType: 'eq',
+                    value: 'gateway',
+                  },
+                ],
+              },
+            ],
+          },
+          {
+            terms: [{ column: 'id$not', value: props.data.id!, type: 'and' }],
+          },
+        ]}
+        onSearch={(param) => {
+          actionRef.current?.reset?.();
+          setSearchParams(param);
+        }}
+      />
+      <ProTable<DeviceInstance>
+        search={false}
+        columns={columns}
+        size="small"
+        rowSelection={{
+          type: 'radio',
+          selectedRowKeys: bindKeys,
+          onChange: (selectedRowKeys, selectedRows) => {
+            setBindKeys(selectedRows.map((item) => item.id));
+          },
+        }}
+        tableAlertRender={false}
+        actionRef={actionRef}
+        params={searchParams}
+        rowKey="id"
+        toolBarRender={false}
+        pagination={{
+          pageSize: 10,
+        }}
+        request={(params) => service.query({ ...params })}
+      />
+    </Modal>
+  );
+};
+
+export default BindParentDevice;

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

@@ -110,7 +110,7 @@ const NoticeIconView = () => {
 
   const openNotification = (resp: any) => {
     notification.warning({
-      style: { width: 320 },
+      // style: { width: 320 },
       message: resp?.payload?.topicName,
       description: (
         <div

+ 1 - 0
src/components/ProTableCard/index.tsx

@@ -162,6 +162,7 @@ const ProTableCard = <
         request={async (param, sort, filter) => {
           if (request) {
             const resp = await request(param, sort, filter);
+            setLoading(false);
             setTotal(resp.result ? resp.result.total : 0);
             return {
               code: resp.message,

+ 11 - 2
src/components/SearchComponent/index.tsx

@@ -552,7 +552,16 @@ const SearchComponent = <T extends Record<string, any>>(props: Props<T>) => {
 
   useEffect(() => {
     // 防止页面下多个TabsTabPane中的查询组件共享路由中的参数
-    if (url.q && url.target && props.target && url.target === props.target) {
+    if (url.q) {
+      // if (url.target) {
+      //   if (props.target && url.target === props.target) {
+      //     form.setValues(JSON.parse(url.q));
+      //     handleSearch(false);
+      //   }
+      // } else {
+      //   form.setValues(JSON.parse(url.q));
+      //   handleSearch(false);
+      // }
       form.setValues(JSON.parse(url.q));
       handleSearch(false);
     }
@@ -560,7 +569,7 @@ const SearchComponent = <T extends Record<string, any>>(props: Props<T>) => {
 
   useEffect(() => {
     if (defaultParam) {
-      handleSearch();
+      handleSearch(!(props.model === 'simple'));
     }
   }, []);
 

+ 7 - 2
src/pages/Northbound/DuerOS/Detail/index.tsx

@@ -63,7 +63,12 @@ const Save = () => {
 
   const getProduct = (f?: Field) => {
     return service.getProduct(f?.value).then((resp) => {
-      const _temp = resp.result.map((item: any) => ({ label: item.name, value: item.id }));
+      const _temp = resp.result.map((item: any) => ({
+        label: item.name,
+        value: item.id,
+        id: item.id,
+        metadata: item.metadata,
+      }));
       Store.set('product-list', _temp);
       return _temp;
     });
@@ -273,7 +278,7 @@ const Save = () => {
               showSearch: true,
               showArrow: true,
               filterOption: (input: string, option: any) =>
-                option.name.toLowerCase().indexOf(input.toLowerCase()) >= 0,
+                option.label.toLowerCase().indexOf(input.toLowerCase()) >= 0,
             },
             'x-reactions': '{{useAsyncDataSource(getProduct)}}',
             required: true,

+ 1 - 1
src/pages/device/Instance/Detail/ChildDevice/BindChildDevice/index.tsx

@@ -133,7 +133,7 @@ const BindChildDevice = (props: Props) => {
         defaultParam={[
           {
             terms: [
-              { column: 'parentId$isnull', value: '' },
+              { column: 'parentId$isnull', value: '1' },
               { column: 'parentId$not', value: InstanceModel.detail.id!, type: 'or' },
             ],
           },

+ 103 - 0
src/pages/device/Instance/Detail/Diagnose/Status/DiagnosticAdvice.tsx

@@ -0,0 +1,103 @@
+import { TitleComponent } from '@/components';
+import { randomString } from '@/utils/util';
+import { InfoCircleOutlined, QuestionCircleOutlined } from '@ant-design/icons';
+import { Badge, Descriptions, Modal, Tooltip } from 'antd';
+import _ from 'lodash';
+import styles from './index.less';
+
+interface Props {
+  close: () => void;
+  data: any;
+}
+
+const DiagnosticAdvice = (props: Props) => {
+  const { data } = props;
+  return (
+    <Modal
+      title="设备诊断"
+      onCancel={() => {
+        props.close();
+      }}
+      onOk={() => {
+        props.close();
+      }}
+      width={900}
+      visible
+    >
+      <div>
+        <TitleComponent data="诊断建议" />
+        <div className={styles.advice}>
+          <div className={styles.alert}>
+            <InfoCircleOutlined style={{ marginRight: 10 }} />
+            所有诊断均无异常但设备任未上线,请检查以下内容
+          </div>
+          <div style={{ marginLeft: 10 }}>
+            {(data?.list || []).map((item: any) => (
+              <div className={styles.infoItem} key={randomString()} style={{ margin: '10px 0' }}>
+                {item}
+              </div>
+            ))}
+          </div>
+        </div>
+      </div>
+      <div style={{ marginTop: 15 }}>
+        <TitleComponent data="连接信息" />
+        <Descriptions column={2}>
+          <Descriptions.Item span={1} label="设备ID">
+            {data?.info?.id || ''}
+          </Descriptions.Item>
+          {data?.info?.address?.length > 0 && (
+            <Descriptions.Item span={1} label="连接地址">
+              <Tooltip
+                placement="topLeft"
+                title={
+                  <div className="serverItem">
+                    {(data?.info?.address || []).map((i: any) => (
+                      <div key={i.address}>
+                        <Badge color={i.health === -1 ? 'red' : 'green'} />
+                        {i.address}
+                      </div>
+                    ))}
+                  </div>
+                }
+              >
+                <div className="serverItem">
+                  {(data?.info?.address || []).slice(0, 1).map((i: any) => (
+                    <Badge
+                      key={i.address}
+                      color={i.health === -1 ? 'red' : 'green'}
+                      text={i.address}
+                    />
+                  ))}
+                </div>
+              </Tooltip>
+            </Descriptions.Item>
+          )}
+
+          {(_.flatten(_.map(data?.info?.config, 'properties')) || []).map((item: any) => (
+            <Descriptions.Item
+              key={randomString()}
+              span={1}
+              label={
+                item?.description ? (
+                  <div>
+                    <span style={{ marginRight: '10px' }}>{item.name}</span>
+                    <Tooltip title={item.description}>
+                      <QuestionCircleOutlined />
+                    </Tooltip>
+                  </div>
+                ) : (
+                  item.name
+                )
+              }
+            >
+              {data?.info?.configValue[item?.property] || ''}
+            </Descriptions.Item>
+          ))}
+        </Descriptions>
+      </div>
+    </Modal>
+  );
+};
+
+export default DiagnosticAdvice;

+ 4 - 2
src/pages/device/Instance/Detail/Diagnose/Status/ManualInspection.tsx

@@ -25,7 +25,7 @@ const ManualInspection = (props: Props) => {
       onCancel={() => {
         props.close();
       }}
-      width={800}
+      width={900}
       footer={[
         <Button
           key="back"
@@ -83,7 +83,9 @@ const ManualInspection = (props: Props) => {
           </div>
         </div>
         {data?.data?.description ? (
-          <div style={{ width: '50%', border: '1px solid #f0f0f0' }}>
+          <div
+            style={{ width: '50%', border: '1px solid #f0f0f0', padding: 10, borderLeft: 'none' }}
+          >
             <h4>诊断项说明</h4>
             <p>{data?.data?.description}</p>
           </div>

+ 403 - 138
src/pages/device/Instance/Detail/Diagnose/Status/index.tsx

@@ -20,18 +20,20 @@ import { useState } from 'react';
 import { useEffect } from 'react';
 import { InstanceModel, service } from '@/pages/device/Instance';
 import _ from 'lodash';
-import { onlyMessage, randomString } from '@/utils/util';
+import { onlyMessage } from '@/utils/util';
 import { getMenuPathByCode, getMenuPathByParams, MENUS_CODE } from '@/utils/menu';
 // import PermissionButton from '@/components/PermissionButton';
 import ManualInspection from './ManualInspection';
 import useHistory from '@/hooks/route/useHistory';
+import DiagnosticAdvice from './DiagnosticAdvice';
+import BindParentDevice from '@/components/BindParentDevice';
 interface Props {
-  providerType: 'network' | 'child-device' | 'media' | 'cloud' | 'channel';
+  providerType: 'network' | 'child-device' | 'media' | 'cloud' | 'channel' | undefined;
 }
 
 const Status = observer((props: Props) => {
   const { providerType } = props;
-  const time = 1000;
+  const time = 500;
   const device = { ...InstanceModel.detail };
   const history = useHistory();
 
@@ -42,6 +44,11 @@ const Status = observer((props: Props) => {
 
   const [artificialVisible, setArtificialVisible] = useState<boolean>(false);
   const [artificiaData, setArtificiaData] = useState<any>({});
+  const [diagnoseVisible, setDiagnoseVisible] = useState<boolean>(false);
+  const [diagnoseData, setDiagnoseData] = useState<any>({});
+
+  // 绑定父设备
+  const [bindParentVisible, setBindParentVisible] = useState<boolean>(false);
 
   // 跳转到产品设备接入配置
   const jumpAccessConfig = () => {
@@ -368,7 +375,7 @@ const Status = observer((props: Props) => {
 
   // 网关父设备
   const diagnoseParentDevice = () =>
-    new Promise((resolve) => {
+    new Promise(async (resolve) => {
       DiagnoseStatusModel.count++;
       if (device.state?.value === 'online') {
         setTimeout(() => {
@@ -399,7 +406,15 @@ const Status = observer((props: Props) => {
                       status="default"
                       text={
                         <span>
-                          未绑定父设备,请先<a>绑定</a>父设备后重试
+                          未绑定父设备,请先
+                          <a
+                            onClick={() => {
+                              setBindParentVisible(true);
+                            }}
+                          >
+                            绑定
+                          </a>
+                          父设备后重试
                         </span>
                       }
                     />
@@ -411,6 +426,67 @@ const Status = observer((props: Props) => {
             resolve({});
           }, time);
         } else {
+          let item: ListProps | undefined = undefined;
+          const response = await service.detail(device?.parentId);
+          if (response.status === 200) {
+            if (response?.state?.value === 'notActive') {
+              item = {
+                key: 'parent-device',
+                name: '网关父设备',
+                desc: '诊断网关父设备状态是否正常,禁用或离线将导致连接失败',
+                status: 'error',
+                text: '异常',
+                info: (
+                  <div>
+                    <div className={styles.infoItem}>
+                      <Badge
+                        status="default"
+                        text={
+                          <span>
+                            网关父设备已禁用,请先<a>启用</a>
+                          </span>
+                        }
+                      />
+                    </div>
+                  </div>
+                ),
+              };
+            } else if (response?.state?.value === 'online') {
+              item = {
+                key: 'parent-device',
+                name: '网关父设备',
+                desc: '诊断网关父设备状态是否正常,禁用或离线将导致连接失败',
+                status: 'success',
+                text: '正常',
+                info: null,
+              };
+            } else {
+              item = {
+                key: 'parent-device',
+                name: '网关父设备',
+                desc: '诊断网关父设备状态是否正常,禁用或离线将导致连接失败',
+                status: 'error',
+                text: '异常',
+                info: (
+                  <div>
+                    <div className={styles.infoItem}>
+                      <Badge
+                        status="default"
+                        text={<span>网关父设备已离线,请先排查网关设备故障</span>}
+                      />
+                    </div>
+                  </div>
+                ),
+              };
+            }
+            setTimeout(() => {
+              if (item) {
+                DiagnoseStatusModel.list = modifyArrayList(DiagnoseStatusModel.list, item);
+                DiagnoseStatusModel.count++;
+              }
+              resolve({});
+            }, time);
+          }
         }
       }
       setTimeout(() => {
@@ -546,6 +622,7 @@ const Status = observer((props: Props) => {
                           onConfirm={async () => {
                             const resp = await service.deployDevice(device?.id || '');
                             if (resp.status === 200) {
+                              InstanceModel.detail.state = { value: 'offline', text: '离线' };
                               onlyMessage('操作成功!');
                               DiagnoseStatusModel.list = modifyArrayList(DiagnoseStatusModel.list, {
                                 key: 'device',
@@ -1024,155 +1101,318 @@ const Status = observer((props: Props) => {
     });
 
   // // opc
-  // const diagnoseOpcua = () => new Promise(() => {
+  const diagnoseOpcua = () =>
+    new Promise(async (resolve) => {
+      if (device.accessProvider === 'opc-ua') {
+        let item: ListProps | undefined = undefined;
+        const response = await service.noPagingOpcua({
+          paging: false,
+          terms: [
+            {
+              column: 'id$bind-opc',
+              value: device.id,
+            },
+          ],
+        });
+        if (response.status === 200) {
+          if (response.result.length > 0) {
+            item = {
+              key: `opc-ua-config`,
+              name: `OPC UA通道配置`,
+              desc: '诊断设备是否已绑定通道,未绑定通道将导致设备连接失败',
+              status: 'success',
+              text: '正常',
+              info: null,
+            };
+          } else {
+            item = {
+              key: `opc-ua-config`,
+              name: `OPC UA通道配置`,
+              desc: '诊断设备是否已绑定通道,未绑定通道将导致设备连接失败',
+              status: 'error',
+              text: '异常',
+              info: (
+                <div>
+                  <div className={styles.infoItem}>
+                    <Badge
+                      status="default"
+                      text={
+                        <span>
+                          设备未绑定通道,请先<a>配置</a>
+                        </span>
+                      }
+                    />
+                  </div>
+                </div>
+              ),
+            };
+          }
+          setTimeout(() => {
+            if (item) {
+              DiagnoseStatusModel.list = modifyArrayList(
+                DiagnoseStatusModel.list,
+                item,
+                DiagnoseStatusModel.list.length,
+              );
+            }
+            DiagnoseStatusModel.count++;
+            resolve({});
+          }, time);
+        }
+      } else {
+        resolve({});
+      }
+    });
 
+  // opc ua 连接状态
+  // const diagnoseOpcuaState = () => new Promise(async (resolve) => {
+  //   if (device.accessProvider === 'opc-ua') {
+  //     let item: ListProps | undefined = undefined;
+  //     const response = await service.noPagingOpcua({
+  //       paging: false,
+  //       terms: [
+  //         {
+  //           column: 'id$bind-opc',
+  //           value: device.id,
+  //         },
+  //       ],
+  //     })
+  //     if (response.status === 200) {
+  //       if (response.result.length > 0) {
+  //         item = {
+  //           key: `opc-ua-state`,
+  //           name: `OPC UA通道连接状态`,
+  //           desc: '诊断通道连接状态是否已连接,未连接状态将导致设备连接失败',
+  //           status: 'success',
+  //           text: '正常',
+  //           info: null,
+  //         }
+  //       } else {
+  //         item = {
+  //           key: `opc-ua-state`,
+  //           name: `OPC UA通道连接状态`,
+  //           desc: '诊断通道连接状态是否已连接,未连接状态将导致设备连接失败',
+  //           status: 'error',
+  //           text: '异常',
+  //           info: (
+  //             <div>
+  //               <div className={styles.infoItem}>
+  //                 <Badge
+  //                   status="default"
+  //                   text={<span>通道未连接成功,请联系管理员处理</span>}
+  //                 />
+  //               </div>
+  //             </div>
+  //           ),
+  //         }
+  //       }
+  //       setTimeout(() => {
+  //         if (item) {
+  //           DiagnoseStatusModel.list = modifyArrayList(DiagnoseStatusModel.list, item, DiagnoseStatusModel.list.length);
+  //         }
+  //         DiagnoseStatusModel.count++;
+  //         resolve({});
+  //       }, time);
+  //     }
+  //   } else {
+  //     resolve({})
+  //   }
   // })
   // //modbus
-  // const diagnoseModbus = () => new Promise(() => {
+  const diagnoseModbus = () =>
+    new Promise((resolve) => {
+      if (device.accessProvider === 'modbus-tcp') {
+      } else {
+        resolve({});
+      }
+    });
 
-  // })
+  // 数据点绑定
+  const diagnoseDataPointBind = () =>
+    new Promise((resolve) => {
+      if (device.accessProvider === 'modbus-tcp' || device.accessProvider === 'opc-ua') {
+      } else {
+        resolve({});
+      }
+    });
+
+  // onenet
+  const diagnoseOnenet = () =>
+    new Promise((resolve) => {
+      if (device.accessProvider === 'OneNet') {
+      } else {
+        resolve({});
+      }
+    });
+
+  // ctwing
+  const diagnoseCTWing = () =>
+    new Promise((resolve) => {
+      if (device.accessProvider === 'Ctwing') {
+      } else {
+        resolve({});
+      }
+    });
 
   // 设备离线且全部诊断项都是正确的情况后
-  const diagnoseNetworkOtherConfig = () =>
-    new Promise(async (resolve) => {
-      if (device.state?.value != 'online') {
-        const item: ReactNode[] = [];
-        if (providerType === 'network') {
-          DiagnoseStatusModel.configuration.product.map((it) => {
-            item.push(
-              <Badge
-                status="default"
-                text={
-                  <span>
-                    产品-{it.name}规则可能有加密处理,请认真查看
-                    <a
-                      onClick={() => {
-                        jumpAccessConfig();
-                      }}
-                    >
-                      设备接入配置
-                    </a>
-                    中【消息协议】说明
-                  </span>
-                }
-              />,
-            );
-          });
-          DiagnoseStatusModel.configuration.device.map((it) => {
-            item.push(
-              <Badge
-                status="default"
-                text={
-                  <span>
-                    设备-{it.name}规则可能有加密处理,请认真查看
-                    <a
-                      onClick={() => {
-                        jumpAccessConfig();
-                      }}
-                    >
-                      设备接入配置
-                    </a>
-                    中【消息协议】说明
-                  </span>
-                }
-              />,
-            );
-          });
-          if (
-            device?.protocol &&
-            device?.accessProvider &&
-            gatewayList.includes(device?.accessProvider)
-          ) {
-            const response = await service.queryProcotolDetail(device.protocol, 'MQTT');
-            if (response.status === 200) {
-              if ((response.result?.routes || []).length > 0) {
-                item.push(
-                  <Badge
-                    status="default"
-                    text={
-                      // accessPermission.view ? (
-                      <span>
-                        请根据
-                        <a
-                          onClick={() => {
-                            jumpAccessConfig();
-                          }}
-                        >
-                          设备接入配置
-                        </a>
-                        中${urlMap.get(device?.accessProvider) || ''}信息,任意上报一条数据
-                      </span>
-                      // ) : (
-                      //   `请联系管理员提供${
-                      //     urlMap.get(device?.accessProvider) || ''
-                      //   }信息,并根据URL信息任意上报一条数据)`
-                      // )
-                    }
-                  />,
-                );
-              } else {
-                item.push(
-                  <Badge
-                    status="default"
-                    text={
-                      <span>
-                        请联系管理员提供${urlMap.get(device?.accessProvider) || ''}
-                        信息,并根据URL信息任意上报一条数据
+  const diagnoseNetworkOtherConfig = async () => {
+    if (device.state?.value != 'online') {
+      const item: ReactNode[] = [];
+      let info: any = {
+        id: device.id,
+      };
+      item.push(<Badge status="default" text="请检查设备运行状态是否正常" />);
+      if (providerType === 'network') {
+        item.push(
+          <Badge
+            status="default"
+            text={
+              (DiagnoseStatusModel.gateway?.channelInfo?.addresses || []).length > 1 ? (
+                <>
+                  请检查设备网络是否畅通,并确保设备已连接到以下地址之一:
+                  <div className="serverItem">
+                    {(DiagnoseStatusModel.gateway?.channelInfo?.addresses || []).map((i: any) => (
+                      <span style={{ marginLeft: 15 }} key={i.address}>
+                        <Badge color={i.health === -1 ? 'red' : 'green'} />
+                        {i.address}
                       </span>
-                    }
-                  />,
-                );
-              }
+                    ))}
+                  </div>
+                </>
+              ) : (
+                <>
+                  请检查设备网络是否畅通,并确保设备已连接到:
+                  {(DiagnoseStatusModel.gateway?.channelInfo?.addresses || []).map((i: any) => (
+                    <span style={{ marginLeft: 15 }} key={i.address}>
+                      <Badge color={i.health === -1 ? 'red' : 'green'} />
+                      {i.address}
+                    </span>
+                  ))}
+                </>
+              )
+            }
+          />,
+        );
+        // DiagnoseStatusModel.configuration.product.map((it) => {
+        //   item.push(
+        //     <Badge
+        //       status="default"
+        //       text={
+        //         <span>
+        //           产品-{it.name}规则可能有加密处理,请认真查看
+        //           <a
+        //             onClick={() => {
+        //               jumpAccessConfig();
+        //             }}
+        //           >
+        //             设备接入配置
+        //           </a>
+        //           中【消息协议】说明
+        //         </span>
+        //       }
+        //     />,
+        //   );
+        // });
+        // DiagnoseStatusModel.configuration.device.map((it) => {
+        //   item.push(
+        //     <Badge
+        //       status="default"
+        //       text={
+        //         <span>
+        //           设备-{it.name}规则可能有加密处理,请认真查看
+        //           <a
+        //             onClick={() => {
+        //               jumpAccessConfig();
+        //             }}
+        //           >
+        //             设备接入配置
+        //           </a>
+        //           中【消息协议】说明
+        //         </span>
+        //       }
+        //     />,
+        //   );
+        // });
+        if (
+          device?.protocol &&
+          device?.accessProvider &&
+          gatewayList.includes(device?.accessProvider)
+        ) {
+          const response = await service.queryProcotolDetail(device.protocol, 'MQTT');
+          if (response.status === 200) {
+            if ((response.result?.routes || []).length > 0) {
+              item.push(
+                <Badge
+                  status="default"
+                  text={
+                    // accessPermission.view ? (
+                    <span>
+                      请根据
+                      <a
+                        onClick={() => {
+                          jumpAccessConfig();
+                        }}
+                      >
+                        设备接入配置
+                      </a>
+                      中${urlMap.get(device?.accessProvider) || ''}信息,任意上报一条数据
+                    </span>
+                    // ) : (
+                    //   `请联系管理员提供${
+                    //     urlMap.get(device?.accessProvider) || ''
+                    //   }信息,并根据URL信息任意上报一条数据)`
+                    // )
+                  }
+                />,
+              );
+            } else {
+              item.push(
+                <Badge
+                  status="default"
+                  text={
+                    <span>
+                      请联系管理员提供${urlMap.get(device?.accessProvider) || ''}
+                      信息,并根据URL信息任意上报一条数据
+                    </span>
+                  }
+                />,
+              );
             }
           }
-        } else if (providerType === 'child-device') {
-        } else if (providerType === 'media') {
-        } else if (providerType === 'cloud') {
-        } else if (providerType === 'channel') {
         }
-        item.push(<Badge status="default" text="请检查设备是否已开机" />);
-        setTimeout(() => {
-          DiagnoseStatusModel.list = modifyArrayList(
-            DiagnoseStatusModel.list,
-            {
-              key: 'other',
-              name: '其他可能异常',
-              desc: '当以上诊断均无异常时,请检查以下内容',
-              status: 'error',
-              text: '可能存在异常',
-              info: (
-                <div>
-                  {item.map((i) => (
-                    <div key={randomString()} className={styles.infoItem}>
-                      {i}
-                    </div>
-                  ))}
-                </div>
-              ),
-            },
-            DiagnoseStatusModel.list.length,
-          );
-          DiagnoseStatusModel.count++;
-          DiagnoseStatusModel.percent = 100;
-          resolve({});
-        }, time);
-      } else {
-        DiagnoseStatusModel.state = 'success';
+        info = {
+          ...info,
+          address: DiagnoseStatusModel.gateway?.channelInfo?.addresses || [],
+          config: DiagnoseStatusModel.configuration.device || [],
+        };
+      } else if (providerType === 'child-device') {
+      } else if (providerType === 'media') {
+      } else if (providerType === 'cloud') {
+      } else if (providerType === 'channel') {
       }
-    });
+      info = {
+        ...info,
+        configValue: device?.configuration || {},
+      };
+      setDiagnoseData({
+        list: [...item],
+        info,
+      });
+      setDiagnoseVisible(true);
+    } else {
+      DiagnoseStatusModel.state = 'success';
+    }
+  };
 
   useEffect(() => {
     if (DiagnoseStatusModel.status === 'finish') {
       const list = _.uniq(_.map(DiagnoseStatusModel.list, 'status'));
       if (device.state?.value !== 'online') {
+        DiagnoseStatusModel.state = 'error';
         if (list[0] === 'success' && list.length === 1) {
           diagnoseNetworkOtherConfig();
-        } else if (list.includes('error')) {
-          DiagnoseStatusModel.percent = 100;
-          DiagnoseStatusModel.state = 'error';
         }
       } else {
-        DiagnoseStatusModel.percent = 100;
         DiagnoseStatusModel.state = 'success';
       }
     }
@@ -1207,6 +1447,13 @@ const Status = observer((props: Props) => {
       await diagnoseParentDevice();
       await diagnoseProduct();
       await diagnoseDevice();
+      DiagnoseStatusModel.percent = 60;
+      await diagnoseProductAuthConfig();
+      await diagnoseDeviceAuthConfig();
+      DiagnoseStatusModel.percent = 80;
+      await diagnoseModbus();
+      await diagnoseOpcua();
+      await diagnoseDataPointBind();
     } else if (providerType === 'media') {
       DiagnoseStatusModel.list = [...mediaInitList];
       await diagnoseGateway();
@@ -1219,22 +1466,31 @@ const Status = observer((props: Props) => {
       DiagnoseStatusModel.percent = 40;
       await diagnoseProduct();
       await diagnoseDevice();
+      DiagnoseStatusModel.percent = 80;
+      await diagnoseCTWing();
+      await diagnoseOnenet();
     } else if (providerType === 'channel') {
       DiagnoseStatusModel.list = [...channelInitList];
       await diagnoseGateway();
-      DiagnoseStatusModel.percent = 40;
+      DiagnoseStatusModel.percent = 20;
       await diagnoseProduct();
       await diagnoseDevice();
+      DiagnoseStatusModel.percent = 40;
+      await diagnoseModbus();
+      await diagnoseOpcua();
+      // await diagnoseOpcuaState();
+      await diagnoseDataPointBind();
+      DiagnoseStatusModel.percent = 80;
     }
-    DiagnoseStatusModel.percent = 90;
+    DiagnoseStatusModel.percent = 100;
     DiagnoseStatusModel.status = 'finish';
   };
 
   useEffect(() => {
-    if (DiagnoseStatusModel.state === 'loading') {
+    if (DiagnoseStatusModel.state === 'loading' && providerType) {
       handleSearch();
     }
-  }, [DiagnoseStatusModel.state]);
+  }, [DiagnoseStatusModel.state, providerType]);
 
   return (
     <div className={styles.statusBox}>
@@ -1274,6 +1530,14 @@ const Status = observer((props: Props) => {
           </div>
         ))}
       </div>
+      {diagnoseVisible && (
+        <DiagnosticAdvice
+          data={diagnoseData}
+          close={() => {
+            setDiagnoseVisible(false);
+          }}
+        />
+      )}
       {artificialVisible && (
         <ManualInspection
           data={artificiaData}
@@ -1293,6 +1557,7 @@ const Status = observer((props: Props) => {
           }}
         />
       )}
+      {bindParentVisible && <BindParentDevice data={device} onCancel={() => {}} onOk={() => {}} />}
     </div>
   );
 });

+ 2 - 3
src/pages/device/Instance/Detail/Diagnose/index.tsx

@@ -21,8 +21,8 @@ const Diagnose = observer(() => {
   const { minHeight } = useDomFullHeight(`.diagnose`, 12);
   const [current, setCurrent] = useState<string>('status');
   const [providerType, setProviderType] = useState<
-    'network' | 'child-device' | 'media' | 'cloud' | 'channel'
-  >('network');
+    undefined | 'network' | 'child-device' | 'media' | 'cloud' | 'channel'
+  >(undefined);
 
   const ViewMap = {
     status: <Status providerType={providerType} />,
@@ -44,7 +44,6 @@ const Diagnose = observer(() => {
       setProviderType('network');
     }
     DiagnoseStatusModel.state = 'loading';
-
     return () => {
       DiagnoseStatusModel.list = [];
       DiagnoseStatusModel.count = 0;

+ 5 - 4
src/pages/device/Instance/Detail/Running/Property/FileComponent/index.tsx

@@ -34,7 +34,7 @@ const FileComponent = (props: Props) => {
   const renderValue = () => {
     if (value?.formatValue !== 0 && !value?.formatValue) {
       return (
-        <div className={props.type === 'card' ? styles.cardValue : styles.otherValue}>{''}</div>
+        <div className={props.type === 'card' ? styles.cardValue : styles.otherValue}>{'--'}</div>
       );
     } else if (data?.valueType?.type === 'file') {
       if (
@@ -61,7 +61,7 @@ const FileComponent = (props: Props) => {
                 message.error('该图片无法访问');
               } else {
                 const flag =
-                  ['.jpg', '.png'].find((item) => value?.formatValue.includes(item)) || '';
+                  ['.jpg', '.png'].find((item) => value?.formatValue.includes(item)) || '--';
                 setType(flag);
                 setVisible(true);
               }
@@ -90,7 +90,8 @@ const FileComponent = (props: Props) => {
                 message.error('当前仅支持播放.mp4,.flv,.m3u8格式的视频');
               } else {
                 const flag =
-                  ['.m3u8', '.flv', '.mp4'].find((item) => value?.formatValue.includes(item)) || '';
+                  ['.m3u8', '.flv', '.mp4'].find((item) => value?.formatValue.includes(item)) ||
+                  '--';
                 setType(flag);
                 setVisible(true);
               }
@@ -107,7 +108,7 @@ const FileComponent = (props: Props) => {
         const flag =
           ['.txt', '.doc', '.xls', '.pdf', '.ppt', '.docx', '.xlsx', '.pptx'].find((item) =>
             value?.formatValue.includes(item),
-          ) || '';
+          ) || '--';
         return (
           <div className={props.type === 'card' ? styles.cardValue : styles.otherValue}>
             <img src={imgMap.get(flag.slice(1))} />

+ 11 - 0
src/pages/device/Instance/service.ts

@@ -350,6 +350,17 @@ class Service extends BaseService<DeviceInstance> {
     request(`/${SystemConst.API_BASE}/device/transparent-codec/${productId}`, {
       method: 'DELETE',
     });
+  // 查询设备下点位
+  public noPagingOpcua = (data: any) =>
+    request(`/${SystemConst.API_BASE}/opc/client/_query/no-paging`, {
+      method: 'POST',
+      data,
+    });
+  public queryMetadatabyId = (data: any) =>
+    request(`/${SystemConst.API_BASE}/modbus/master/_query/no-paging`, {
+      method: 'POST',
+      data,
+    });
 }
 
 export default Service;

+ 13 - 0
src/pages/device/Product/Detail/Access/AccessConfig/index.tsx

@@ -91,6 +91,19 @@ const AccessConfig = (props: Props) => {
       dataIndex: 'name',
     },
     {
+      title: '网关类型',
+      dataIndex: 'provider',
+      renderText: (text) => text?.text,
+      valueType: 'select',
+      request: () =>
+        service.getProviders().then((resp: any) => {
+          return (resp?.result || []).map((item: any) => ({
+            label: item.name,
+            value: item.id,
+          }));
+        }),
+    },
+    {
       title: '状态',
       dataIndex: 'state',
       valueType: 'select',

+ 31 - 0
src/pages/device/components/Metadata/Base/Edit/index.tsx

@@ -693,6 +693,9 @@ const Edit = observer((props: Props) => {
             'x-component': 'ArrayItems',
             'x-decorator': 'FormItem',
             title: '指标配置',
+            'x-decorator-props': {
+              tooltip: '场景联动页面可引用指标配置作为触发条件',
+            },
             'x-visible': props.type === 'product',
             items: {
               type: 'object',
@@ -761,6 +764,16 @@ const Edit = observer((props: Props) => {
                         labelAlign: 'left',
                         layout: 'vertical',
                       },
+                      'x-validator': [
+                        {
+                          max: 64,
+                          message: '最多可输入64个字符',
+                        },
+                        {
+                          required: true,
+                          message: '请输入名称',
+                        },
+                      ],
                     },
                     space: {
                       type: 'void',
@@ -771,6 +784,12 @@ const Edit = observer((props: Props) => {
                         labelAlign: 'left',
                         layout: 'vertical',
                       },
+                      'x-validator': [
+                        {
+                          required: true,
+                          message: '请输入指标值',
+                        },
+                      ],
                       'x-component-props': {
                         maxColumns: 12,
                         minColumns: 12,
@@ -782,6 +801,12 @@ const Edit = observer((props: Props) => {
                           'x-decorator-props': {
                             gridSpan: 5,
                           },
+                          'x-validator': [
+                            {
+                              required: true,
+                              message: '请输入',
+                            },
+                          ],
                           'x-reactions': {
                             dependencies: ['..range', 'valueType.type'],
                             fulfill: {
@@ -803,6 +828,12 @@ const Edit = observer((props: Props) => {
                           'x-decorator-props': {
                             gridSpan: 5,
                           },
+                          'x-validator': [
+                            {
+                              required: true,
+                              message: '请输入',
+                            },
+                          ],
                           'x-reactions': [
                             {
                               dependencies: ['..range', 'valueType.type'],

+ 1 - 1
src/pages/link/AccessConfig/Detail/Access/data.ts

@@ -11,7 +11,7 @@ const ProcotoleMapping = new Map();
 ProcotoleMapping.set('websocket-server', 'WebSocket');
 ProcotoleMapping.set('http-server-gateway', 'HTTP');
 ProcotoleMapping.set('udp-device-gateway', 'UDP');
-ProcotoleMapping.set('coap-server-gateway', 'COAP');
+ProcotoleMapping.set('coap-server-gateway', 'CoAP');
 ProcotoleMapping.set('mqtt-client-gateway', 'MQTT');
 ProcotoleMapping.set('mqtt-server-gateway', 'MQTT');
 ProcotoleMapping.set('tcp-server-gateway', 'TCP');

+ 4 - 1
src/pages/link/DashBoard/index.less

@@ -4,14 +4,17 @@
     display: flex;
     gap: 24px;
     margin-bottom: 24px;
+    padding: 24px;
+    background-color: #fff;
 
     .echarts-item {
       display: flex;
       flex-grow: 1;
       width: 0;
-      height: 180px;
+      height: 160px;
       padding: 16px;
       background-color: #fff;
+      border: 1px solid #e0e4e8;
 
       .echarts-item-left {
         display: flex;

+ 18 - 71
src/pages/link/DashBoard/index.tsx

@@ -285,11 +285,11 @@ export default () => {
         {
           type: 'inside',
           start: 0,
-          end: 10,
+          end: 100,
         },
         {
           start: 0,
-          end: 10,
+          end: 100,
         },
       ],
       color: ['#60DFC7'],
@@ -342,11 +342,11 @@ export default () => {
         {
           type: 'inside',
           start: 0,
-          end: 10,
+          end: 100,
         },
         {
           start: 0,
-          end: 10,
+          end: 100,
         },
       ],
       color: ['#2CB6E0'],
@@ -612,7 +612,7 @@ export default () => {
     const sub = subscribeTopic!(id, topic, {
       type: 'all',
       serverNodeId: serverId,
-      interval: '5s',
+      interval: '1s',
       agg: 'avg',
     })
       ?.pipe(map((res) => res.payload))
@@ -648,14 +648,16 @@ export default () => {
     <PageContainer>
       <div className={'link-dash-board'}>
         {serverNode && serverNode.length ? (
-          <Select
-            value={serverId}
-            options={serverNode}
-            onChange={(value) => {
-              setServerId(value);
-            }}
-            style={{ width: 300, marginBottom: 24 }}
-          />
+          <div style={{ backgroundColor: '#fff', padding: '24px 24px 0 24px' }}>
+            <Select
+              value={serverId}
+              options={serverNode}
+              onChange={(value) => {
+                setServerId(value);
+              }}
+              style={{ width: 300 }}
+            />
+          </div>
         ) : null}
         <div className={'echarts-items'}>
           <TopEchartsItemNode title={'CPU使用率'} value={topValues.cpu} />
@@ -674,69 +676,12 @@ export default () => {
             max={topValues.systemUsageTotal}
             bottom={`系统内存  ${topValues.systemUsageTotal}G`}
           />
-          {/*<div className={'echarts-item'}>*/}
-          {/*  */}
-          {/*  <Progress*/}
-          {/*    type="circle"*/}
-          {/*    strokeWidth={8}*/}
-          {/*    width={160}*/}
-          {/*    percent={topValues.cpu}*/}
-          {/*    format={(percent) => (*/}
-          {/*      <div>*/}
-          {/*        <div className={'echarts-item-title'}>CPU使用率</div>*/}
-          {/*        <div className={'echarts-item-value'}>{percent}%</div>*/}
-          {/*      </div>*/}
-          {/*    )}*/}
-          {/*  />*/}
-          {/*</div>*/}
-          {/*<div className={'echarts-item'}>*/}
-          {/*  <Progress*/}
-          {/*    type="circle"*/}
-          {/*    strokeWidth={8}*/}
-          {/*    width={160}*/}
-          {/*    percent={topValues.jvm}*/}
-          {/*    format={(percent) => (*/}
-          {/*      <div>*/}
-          {/*        <div className={'echarts-item-title'}>JVM内存</div>*/}
-          {/*        <div className={'echarts-item-value'}>{percent}%</div>*/}
-          {/*      </div>*/}
-          {/*    )}*/}
-          {/*  />*/}
-          {/*</div>*/}
-          {/*<div className={'echarts-item'}>*/}
-          {/*  <Progress*/}
-          {/*    type="circle"*/}
-          {/*    strokeWidth={8}*/}
-          {/*    width={160}*/}
-          {/*    percent={topValues.usage}*/}
-          {/*    format={(percent) => (*/}
-          {/*      <div>*/}
-          {/*        <div className={'echarts-item-title'}>磁盘占用率</div>*/}
-          {/*        <div className={'echarts-item-value'}>{percent}%</div>*/}
-          {/*      </div>*/}
-          {/*    )}*/}
-          {/*  />*/}
-          {/*</div>*/}
-          {/*<div className={'echarts-item'}>*/}
-          {/*  <Progress*/}
-          {/*    type="circle"*/}
-          {/*    strokeWidth={8}*/}
-          {/*    width={160}*/}
-          {/*    percent={topValues.systemUsage}*/}
-          {/*    format={(percent) => (*/}
-          {/*      <div>*/}
-          {/*        <div className={'echarts-item-title'}>系统内存</div>*/}
-          {/*        <div className={'echarts-item-value'}>{percent}%</div>*/}
-          {/*      </div>*/}
-          {/*    )}*/}
-          {/*  />*/}
-          {/*</div>*/}
         </div>
         <div style={{ marginBottom: 24 }}>
           <DashBoard
             title={'网络流量'}
             ref={NETWORKRef}
-            initialValues={{ type: 'bytesSent' }}
+            initialValues={{ type: 'bytesRead' }}
             height={400}
             closeInitialParams={true}
             showTimeTool={true}
@@ -756,6 +701,7 @@ export default () => {
         </div>
         <div style={{ display: 'flex', gap: 24 }}>
           <DashBoard
+            showTime
             title={'CPU使用率趋势'}
             closeInitialParams={true}
             ref={CPURef}
@@ -766,6 +712,7 @@ export default () => {
             onParamsChange={getCPUEcharts}
           />
           <DashBoard
+            showTime
             title={'JVM内存使用率趋势'}
             closeInitialParams={true}
             ref={JVMRef}

+ 1 - 1
src/pages/link/Type/Detail/index.tsx

@@ -506,7 +506,7 @@ const Save = observer(() => {
         'x-decorator-props': {
           gridSpan: 1,
           labelAlign: 'left',
-          tooltip: '对外提供访问的地址,内网环境是填写服务器的内网IP地址',
+          tooltip: '单次收发消息的最大长度,单位:字节;设置过大可能会影响性能',
           layout: 'vertical',
         },
         'x-component-props': {

+ 1 - 1
src/pages/notice/Template/service.ts

@@ -31,7 +31,7 @@ class Service extends BaseService<TemplateItem> {
       data,
     });
 
-  public debug = (id: string, templateId: string, data: Record<string, any>) =>
+  public debug = (id: string, templateId: string, data: Record<string, any> = {}) =>
     request(`${SystemConst.API_BASE}/notifier/${id}/${templateId}/_send`, {
       method: 'POST',
       data,

+ 4 - 4
src/pages/rule-engine/Scene/Save/action/VariableItems/builtIn.tsx

@@ -1,4 +1,4 @@
-import { DatePicker, Input, InputNumber, Select } from 'antd';
+import { DatePicker, Input, InputNumber, Select, TreeSelect } from 'antd';
 import { useCallback, useEffect, useState } from 'react';
 import type { FormInstance } from 'antd';
 import { queryBuiltInParams } from '@/pages/rule-engine/Scene/Save/action/service';
@@ -33,7 +33,7 @@ export default (props: BuiltInProps) => {
   const [builtInList, setBuiltInList] = useState<any[]>([]);
 
   useEffect(() => {
-    console.log(props.trigger);
+    console.log(props.parallel);
     // if (source === 'upper' && props.trigger) {
     //   getBuiltInList({ ...props.trigger });
     // }
@@ -103,9 +103,9 @@ export default (props: BuiltInProps) => {
         }}
       ></Select>
       {source === 'upper' ? (
-        <Select
+        <TreeSelect
           value={upperKey}
-          options={builtInList}
+          treeData={builtInList}
           onChange={(key) => {
             onChange(source, undefined, key);
           }}

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

@@ -131,18 +131,9 @@ export default observer((props: ActionProps) => {
 
       if (data.notify) {
         // 消息通知
-        if (data.notify.notifyType) {
-          setNotifyType(data.notify.notifyType);
-        }
-
-        if (data.notify.notifierId) {
-          setConfigId(data.notify.notifierId);
-        }
-
-        if (data.notify.templateId) {
-          // 通知模板
-          setTemplateId(data.notify.templateId);
-        }
+        setNotifyType(data.notify?.notifyType);
+        setConfigId(data.notify?.notifierId);
+        setTemplateId(data.notify?.templateId);
       }
     }
   }, []);
@@ -166,8 +157,10 @@ export default observer((props: ActionProps) => {
             style={{ width: '100%' }}
             onChange={async () => {
               setTemplateData(undefined);
-              props.form.resetFields([['actions', name, 'notify', 'notifierId']]);
-              props.form.resetFields([['actions', name, 'notify', 'templateId']]);
+              props.form.setFields([
+                { name: ['actions', name, 'notify', 'notifierId'], value: undefined },
+                { name: ['actions', name, 'notify', 'templateId'], value: undefined },
+              ]);
             }}
           />
         </Form.Item>
@@ -184,7 +177,9 @@ export default observer((props: ActionProps) => {
             fieldNames={{ value: 'id', label: 'name' }}
             onChange={() => {
               setTemplateData(undefined);
-              props.form.resetFields([['actions', name, 'notify', 'templateId']]);
+              props.form.setFields([
+                { name: ['actions', name, 'notify', 'templateId'], value: undefined },
+              ]);
             }}
             style={{ width: '100%' }}
             placeholder={'请选择通知配置'}
@@ -210,8 +205,8 @@ export default observer((props: ActionProps) => {
   );
 
   const parallelNode = (
-    <Col span={2}>
-      {!props.parallel ? (
+    <Col span={3}>
+      {props.parallel === false ? (
         <Checkbox
           checked={isFiltering}
           onChange={(e) => {
@@ -332,7 +327,7 @@ export default observer((props: ActionProps) => {
             </Col>
             {parallelNode}
           </Row>
-          {!props.parallel && isFiltering && (
+          {props.parallel === false && isFiltering && (
             <Row gutter={24}>
               <ConditionalFiltering
                 name={name}
@@ -358,7 +353,7 @@ export default observer((props: ActionProps) => {
             </Col>
             {parallelNode}
           </Row>
-          {!props.parallel && isFiltering && (
+          {props.parallel === false && isFiltering && (
             <Row gutter={24}>
               <ConditionalFiltering
                 name={name}
@@ -381,7 +376,13 @@ export default observer((props: ActionProps) => {
           </Form.Item>
           <Row gutter={24}>
             {parallelNode}
-            <ConditionalFiltering name={name} form={props.form} data={props.actionItemData.terms} />
+            {props.parallel === false && isFiltering && (
+              <ConditionalFiltering
+                name={name}
+                form={props.form}
+                data={props.actionItemData.terms}
+              />
+            )}
           </Row>
         </>
       ) : null}

+ 7 - 4
src/pages/rule-engine/Scene/Save/action/device/ConditionalFiltering.tsx

@@ -109,10 +109,12 @@ export default (props: ConditionalFilteringProps) => {
 
   useEffect(() => {
     console.log('Conditional', builtInList);
-    if (props.data[0] && props.data[0].column && builtInList && builtInList.length) {
+    if (props.data && props.data[0] && props.data[0].column && builtInList && builtInList.length) {
       getBuiltItemById(props.data[0].column);
     }
   }, [props.data, builtInList]);
+
+  console.log(props.data, props.name);
   return (
     <>
       <Col span={4}>
@@ -151,7 +153,7 @@ export default (props: ConditionalFilteringProps) => {
           />
         </Form.Item>
       </Col>
-      <Col span={2}>
+      <Col span={3}>
         <Form.Item name={[props.name, 'terms', 0, 'termType']}>
           <Select
             style={{ width: '100%' }}
@@ -161,7 +163,7 @@ export default (props: ConditionalFilteringProps) => {
           />
         </Form.Item>
       </Col>
-      <Col span={7}>
+      <Col span={8}>
         <Form.Item noStyle>
           <ItemGroup>
             <Form.Item name={[props.name, 'terms', 0, 'value', 'source']} initialValue={'fixed'}>
@@ -176,7 +178,8 @@ export default (props: ConditionalFilteringProps) => {
                 }}
               />
             </Form.Item>
-            {['nbtw', 'btw'].includes(props.data[0] && props.data[0].termType) ? (
+            {['nbtw', 'btw'].includes(props.data && props.data[0] && props.data[0].termType) &&
+            source === 'fixed' ? (
               <>
                 <Form.Item name={[props.name, 'terms', 0, 'value', 'value', 0]}>
                   {source === 'fixed' ? (

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

@@ -137,13 +137,9 @@ export default (props: DeviceProps) => {
     console.log('actions-device', props.value);
     const deviceData = props.value;
     if (deviceData) {
-      if (deviceData.productId) {
-        setProductId(deviceData.productId);
-      }
+      setProductId(deviceData.productId);
 
-      if (deviceData.selector) {
-        setSelector(deviceData.selector);
-      }
+      setSelector(deviceData.selector || SourceEnum.fixed);
 
       if (deviceData.message) {
         if (deviceData.message.messageType) {
@@ -174,10 +170,12 @@ export default (props: DeviceProps) => {
             style={{ width: '100%' }}
             listHeight={220}
             onChange={() => {
-              props.form?.resetFields([['actions', name, 'device', 'selector']]);
-              props.form?.resetFields([['actions', name, 'device', 'selectorValues']]);
-              props.form?.resetFields([['actions', name, 'device', 'message', 'functionId']]);
               // setMessageType(MessageTypeEnum.WRITE_PROPERTY)
+              props.form?.setFields([
+                { name: ['actions', name, 'device', 'selector'], value: SourceEnum.fixed },
+                { name: ['actions', name, 'device', 'selectorValues'], value: undefined },
+                { name: ['actions', name, 'device', 'message', 'functionId'], value: undefined },
+              ]);
             }}
             fieldNames={{ label: 'name', value: 'id' }}
             filterOption={(input: string, option: any) =>
@@ -190,7 +188,7 @@ export default (props: DeviceProps) => {
         <ItemGroup compact>
           <Form.Item
             name={[name, 'device', 'selector']}
-            initialValue={props.value ? props.value.selector : SourceEnum.fixed}
+            initialValue={SourceEnum.fixed}
             {...props.restField}
           >
             <Select

+ 1 - 0
src/pages/rule-engine/Scene/Save/components/BuiltInParams.tsx

@@ -20,6 +20,7 @@ export const BuiltInParamsHandleTreeData = (data: any): any[] => {
   if (data.length > 0) {
     return data.map((item: any) => {
       const name = <BuiltInParamsTitle {...item} />;
+
       if (item.children) {
         return {
           ...item,

+ 23 - 7
src/pages/rule-engine/Scene/Save/components/TimingTrigger/refactor.tsx

@@ -36,7 +36,15 @@ export default (props: TimingTrigger) => {
 
   const onChange = useCallback(() => {
     const formData = props.form.getFieldsValue();
-    setData(formData?.trigger?.timer);
+    let _data = formData;
+    name.forEach((key) => {
+      console.log(_data, key);
+      if (key in _data) {
+        _data = _data[key];
+      }
+    });
+
+    setData(_data.timer);
   }, [props.form]);
 
   const TimeTypeAfter = (
@@ -80,7 +88,15 @@ export default (props: TimingTrigger) => {
                 { label: 'cron表达式', value: TriggerEnum.cron },
               ]}
               style={{ width: '120px' }}
-              onChange={onChange}
+              onChange={() => {
+                props.form.setFields([
+                  {
+                    name: [...name, 'timer', 'when'],
+                    value: undefined,
+                  },
+                ]);
+                onChange();
+              }}
             />
           </Form.Item>
           {data?.trigger !== TriggerEnum.cron ? (
@@ -134,7 +150,7 @@ export default (props: TimingTrigger) => {
           )}
         </ItemGroup>
       </Col>
-      <Col span={12}>
+      <Col span={11}>
         {data?.trigger !== TriggerEnum.cron && (
           <ItemGroup>
             <Form.Item
@@ -177,12 +193,12 @@ export default (props: TimingTrigger) => {
           </ItemGroup>
         )}
       </Col>
-      <Col span={6}>
+      <Col span={7}>
         {data?.trigger !== TriggerEnum.cron && (
           <ItemGroup style={{ gap: 16 }}>
             {data?.mod === PeriodModEnum.period ? (
               <>
-                <div style={{ paddingBottom: 12 }}> 每 </div>
+                <div style={{ paddingBottom: 14 }}> 每 </div>
                 <Form.Item
                   name={[...name, 'timer', 'period', 'every']}
                   rules={[{ required: true, message: '请输入时间' }]}
@@ -190,7 +206,7 @@ export default (props: TimingTrigger) => {
                   <InputNumber
                     placeholder={'请输入时间'}
                     addonAfter={TimeTypeAfter}
-                    style={{ flex: 1 }}
+                    style={{ maxWidth: 170 }}
                     min={0}
                     max={59}
                   />
@@ -204,7 +220,7 @@ export default (props: TimingTrigger) => {
                 </Form.Item>
               </>
             ) : null}
-            <div style={{ flex: 0, flexBasis: 64, paddingBottom: 12 }}> 执行一次 </div>
+            <div style={{ flex: 0, flexBasis: 64, paddingBottom: 14 }}> 执行一次 </div>
           </ItemGroup>
         )}
       </Col>

+ 4 - 2
src/pages/rule-engine/Scene/Save/index.tsx

@@ -12,7 +12,7 @@ import {
   Switch,
   Tooltip,
 } from 'antd';
-import { useIntl, useLocation } from 'umi';
+import { useHistory, useIntl, useLocation } from 'umi';
 import { useCallback, useEffect, useRef, useState } from 'react';
 import { PermissionButton, TitleComponent } from '@/components';
 import ActionItems from './action/action';
@@ -27,6 +27,7 @@ import { model } from '@formily/reactive';
 import type { FormModelType } from '@/pages/rule-engine/Scene/typings';
 import { onlyMessage } from '@/utils/util';
 import Explanation from './Explanation';
+import { getMenuPathByCode } from '@/utils/menu';
 
 type ShakeLimitType = {
   enabled: boolean;
@@ -48,6 +49,7 @@ export default () => {
   const [form] = Form.useForm();
   const intl = useIntl();
   const triggerRef = useRef<any>();
+  const history = useHistory();
 
   const { getOtherPermission } = PermissionButton.usePermission('rule-engine/Scene');
   const [triggerType, setTriggerType] = useState('');
@@ -132,7 +134,7 @@ export default () => {
       setLoading(false);
       if (resp.status === 200) {
         onlyMessage('操作成功');
-        history.back();
+        history.push(getMenuPathByCode('rule-engine/Scene'));
       } else {
         onlyMessage(resp.message);
       }

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

@@ -396,7 +396,6 @@ export default observer((props: TriggerProps) => {
           <Col span={6}>
             <Form.Item
               name={['trigger', 'device', 'operation', 'eventId']}
-              noStyle
               rules={[{ required: true, message: '请选择事件' }]}
             >
               <Select

+ 21 - 13
src/pages/rule-engine/Scene/TriggerTerm/index.tsx

@@ -38,7 +38,6 @@ interface Props {
   value?: Record<string, any>;
   onChange?: (value: any) => void;
 }
-
 const TriggerTerm = (props: Props, ref: any) => {
   const parseTermRef = useRef<any>();
   const getParseTerm = () =>
@@ -104,7 +103,7 @@ const TriggerTerm = (props: Props, ref: any) => {
           onFieldReact('trigger.*.terms.*.column', async (field, form1) => {
             const operator = field.query('.termType');
             const value = (field as Field).value;
-
+            const modified = (field as Field).modified;
             // 找到选中的
             const _data = await service.getParseTerm(props.params);
             if (!_data) return;
@@ -133,18 +132,25 @@ const TriggerTerm = (props: Props, ref: any) => {
                       { label: '指标', value: 'metrics' },
                     ]
                   : [{ label: '手动输入', value: 'manual' }];
-              state.value = 'manual';
+              if (modified) {
+                state.value = 'manual';
+              }
             });
             form1.setFieldState(field.query('.value.value[0]'), (state) => {
-              state.value = undefined;
+              if (modified) {
+                state.value = undefined;
+              }
             });
           });
-          onFieldReact('trigger.*.terms.*.value.source', (field, form1) => {
+          onFieldReact('trigger.*.terms.*.value.source', async (field, form1) => {
             const params = field.query('..column').value();
+            const modified = (field as Field).modified;
 
             // 找到选中的
-            const _data = Store.get('trigger-parse-term');
-            if (!_data) return;
+            let _data = Store.get('trigger-parse-term');
+            if (!_data) {
+              _data = await service.getParseTerm(props.params);
+            }
             // 树形搜索
             const treeValue = treeFilter(_data, params, 'column');
             // 找到
@@ -152,9 +158,9 @@ const TriggerTerm = (props: Props, ref: any) => {
               treeValue && treeValue[0] && treeValue[0].children
                 ? treeValue[0]?.children.find((item) => item.column === params)
                 : treeValue[0];
-
             const source = (field as Field).value;
             const value = field.query(source === 'manual' ? '.value.0' : '.metric');
+
             if (target) {
               if (source === 'manual') {
                 // 手动输入
@@ -208,7 +214,9 @@ const TriggerTerm = (props: Props, ref: any) => {
                       label: item.name,
                       value: item.id,
                     }));
-                  state.value = undefined;
+                  if (modified) {
+                    state.value = undefined;
+                  }
                 });
               }
             }
@@ -228,7 +236,7 @@ const TriggerTerm = (props: Props, ref: any) => {
         return j;
       }),
     );
-    form.setInitialValues(data);
+    form.setValues(data);
   }, [props.value]);
 
   useImperativeHandle(ref, () => ({
@@ -239,7 +247,7 @@ const TriggerTerm = (props: Props, ref: any) => {
         if (!Array.isArray(_data.trigger)) return;
         _data.trigger?.map((item: { terms: any[] }) =>
           item.terms.map((j) => {
-            if (j.value.value.length === 1) {
+            if (j.value?.value?.length === 1) {
               j.value.value = j.value.value[0];
             }
             return j;
@@ -314,7 +322,7 @@ const TriggerTerm = (props: Props, ref: any) => {
                         'x-decorator': 'FormItem',
                         'x-component': 'TreeSelect',
                         'x-decorator-props': {
-                          gridSpan: 5,
+                          gridSpan: 4,
                         },
                         required: true,
                         'x-component-props': {
@@ -342,7 +350,7 @@ const TriggerTerm = (props: Props, ref: any) => {
                         'x-component': 'FInputGroup',
                         'x-decorator': 'FormItem',
                         'x-decorator-props': {
-                          gridSpan: 4,
+                          gridSpan: 3,
                           style: {
                             width: '100%',
                           },

+ 1 - 1
src/pages/system/Department/Assets/deivce/index.tsx

@@ -54,7 +54,7 @@ export default observer((props: { parentId: string }) => {
         .unBind('device', [
           {
             targetType: 'org',
-            targetId: props.parentId,
+            targetId: AssetsModel.parentId,
             assetType: 'device',
             assetIdList: Models.unBindKeys,
           },

+ 6 - 1
src/pages/system/Department/Assets/index.tsx

@@ -7,6 +7,7 @@ import Member from '@/pages/system/Department/Member';
 import { model } from '@formily/reactive';
 import { observer } from '@formily/react';
 import { ExclamationCircleOutlined } from '@ant-design/icons';
+import { useEffect } from 'react';
 
 interface AssetsProps {
   parentId: string;
@@ -22,9 +23,11 @@ export enum ASSETS_TABS_ENUM {
 export const AssetsModel = model<{
   tabsIndex: string;
   bindModal: boolean;
+  parentId: string;
 }>({
   tabsIndex: ASSETS_TABS_ENUM.Product,
   bindModal: false,
+  parentId: '',
 });
 
 const Assets = observer((props: AssetsProps) => {
@@ -58,7 +61,9 @@ const Assets = observer((props: AssetsProps) => {
     },
   ];
 
-  console.log(AssetsModel.tabsIndex);
+  useEffect(() => {
+    AssetsModel.parentId = props.parentId;
+  }, [props.parentId]);
 
   return (
     <div style={{ position: 'relative', width: '100%' }}>

+ 1 - 1
src/pages/system/Department/Assets/product/bind.tsx

@@ -78,7 +78,7 @@ const Bind = observer((props: Props) => {
     >
       <Modal
         visible={deviceVisible}
-        width={600}
+        width={800}
         onCancel={() => {
           setDeviceVisible(false);
           props.reload();

+ 1 - 1
src/pages/system/Department/Assets/product/index.tsx

@@ -41,7 +41,7 @@ export default observer((props: { parentId: string }) => {
         .unBind('product', [
           {
             targetType: 'org',
-            targetId: props.parentId,
+            targetId: AssetsModel.parentId,
             assetType: 'product',
             assetIdList: Models.unBindKeys,
           },

+ 1 - 1
src/pages/system/Department/Assets/service.ts

@@ -88,7 +88,7 @@ class Service<T> extends BaseService<T> {
       mergeMap((result) => {
         const ids = result?.result?.data?.map((item: any) => item.id) || [];
         return from(
-          request(`${SystemConst.API_BASE}/assets/bindings/device-product/org/${parentId}/_query`, {
+          request(`${SystemConst.API_BASE}/assets/bindings/product/org/${parentId}/_query`, {
             method: 'POST',
             data: ids,
           }),

+ 2 - 1
src/pages/system/User/Save/index.tsx

@@ -422,8 +422,9 @@ const Save = (props: Props) => {
     const temp: any = {};
     temp.id = value.id;
     temp.user = value;
+    temp.user.roleList = [];
     temp.orgIdList = value.orgIdList;
-    temp.roleIdList = value.roleIdList;
+    temp.roleIdList = value.roleIdList?.length ? value.roleIdList : null;
     const response = await service.saveUser(temp, model);
     if (response.status === 200) {
       onlyMessage(

+ 48 - 6
yarn.lock

@@ -5676,6 +5676,11 @@ add-dom-event-listener@^1.1.0:
   dependencies:
     object-assign "4.x"
 
+add-px-to-style@1.0.0:
+  version "1.0.0"
+  resolved "https://registry.yarnpkg.com/add-px-to-style/-/add-px-to-style-1.0.0.tgz#d0c135441fa8014a8137904531096f67f28f263a"
+  integrity sha512-YMyxSlXpPjD8uWekCQGuN40lV4bnZagUwqa2m/uFv1z/tNImSk9fnXVMUI5qwME/zzI3MMQRvjZ+69zyfSSyew==
+
 address@1.1.2, address@^1.0.1:
   version "1.1.2"
   resolved "https://registry.yarnpkg.com/address/-/address-1.1.2.tgz#bf1116c9c758c51b7a933d296b72c221ed9428b6"
@@ -8460,6 +8465,15 @@ dom-align@^1.7.0:
   resolved "https://registry.npmmirror.com/dom-align/-/dom-align-1.12.3.tgz#a36d02531dae0eefa2abb0c4db6595250526f103"
   integrity sha512-Gj9hZN3a07cbR6zviMUBOMPdWxYhbMI+x+WS0NAIu2zFZmbK8ys9R79g+iG9qLnlCwpFoaB+fKy8Pdv470GsPA==
 
+dom-css@^2.0.0:
+  version "2.1.0"
+  resolved "https://registry.yarnpkg.com/dom-css/-/dom-css-2.1.0.tgz#fdbc2d5a015d0a3e1872e11472bbd0e7b9e6a202"
+  integrity sha512-w9kU7FAbaSh3QKijL6n59ofAhkkmMJ31GclJIz/vyQdjogfyxcB6Zf8CZyibOERI5o0Hxz30VmJS7+7r5fEj2Q==
+  dependencies:
+    add-px-to-style "1.0.0"
+    prefix-style "2.0.1"
+    to-camel-case "1.0.0"
+
 dom-serializer@0:
   version "0.2.2"
   resolved "https://registry.yarnpkg.com/dom-serializer/-/dom-serializer-0.2.2.tgz#1afb81f533717175d478655debc5e332d9f9bb51"
@@ -14182,7 +14196,7 @@ mockjs@^1.0.1-beta3, mockjs@^1.1.0:
   dependencies:
     commander "*"
 
-moment@^2.24.0:
+moment@^2.24.0, moment@^2.29.3:
   version "2.29.3"
   resolved "https://registry.npmmirror.com/moment/-/moment-2.29.3.tgz#edd47411c322413999f7a5940d526de183c031f3"
   integrity sha512-c6YRvhEo//6T2Jz/vVtYzqBzwvPT95JBQ+smCytzf7c50oMZRsR/a4w88aD34I+/QVSfnoAnSBFPJHItlOMJVw==
@@ -14192,11 +14206,6 @@ moment@^2.25.3, moment@^2.27.0, moment@^2.29.1:
   resolved "https://registry.yarnpkg.com/moment/-/moment-2.29.2.tgz#00910c60b20843bcba52d37d58c628b47b1f20e4"
   integrity sha512-UgzG4rvxYpN15jgCmVJwac49h9ly9NurikMWGPdVxm8GZD6XjkKPxDTjQQ43gtGgnV3X0cAyWDdP2Wexoquifg==
 
-moment@^2.29.3:
-  version "2.29.3"
-  resolved "https://registry.yarnpkg.com/moment/-/moment-2.29.3.tgz#edd47411c322413999f7a5940d526de183c031f3"
-  integrity sha512-c6YRvhEo//6T2Jz/vVtYzqBzwvPT95JBQ+smCytzf7c50oMZRsR/a4w88aD34I+/QVSfnoAnSBFPJHItlOMJVw==
-
 monaco-editor-webpack-plugin@^6.0.0:
   version "6.0.0"
   resolved "https://registry.yarnpkg.com/monaco-editor-webpack-plugin/-/monaco-editor-webpack-plugin-6.0.0.tgz#628956ce1851afa2a5f6c88d0ecbb24e9a444898"
@@ -15813,6 +15822,11 @@ preceptor-core@~0.10.0:
     log4js "1.1.1"
     underscore "1.7.0"
 
+prefix-style@2.0.1:
+  version "2.0.1"
+  resolved "https://registry.yarnpkg.com/prefix-style/-/prefix-style-2.0.1.tgz#66bba9a870cfda308a5dc20e85e9120932c95a06"
+  integrity sha512-gdr1MBNVT0drzTq95CbSNdsrBDoHGlb2aDJP/FoY+1e+jSDPOb1Cv554gH2MGiSr2WTcXi/zu+NaFzfcHQkfBQ==
+
 prelude-ls@^1.2.1:
   version "1.2.1"
   resolved "https://registry.yarnpkg.com/prelude-ls/-/prelude-ls-1.2.1.tgz#debc6489d7a6e6b0e7611888cec880337d316396"
@@ -16814,6 +16828,15 @@ react-copy-to-clipboard@5.0.3:
     copy-to-clipboard "^3"
     prop-types "^15.5.8"
 
+react-custom-scrollbars@^4.2.1:
+  version "4.2.1"
+  resolved "https://registry.yarnpkg.com/react-custom-scrollbars/-/react-custom-scrollbars-4.2.1.tgz#830fd9502927e97e8a78c2086813899b2a8b66db"
+  integrity sha512-VtJTUvZ7kPh/auZWIbBRceGPkE30XBYe+HktFxuMWBR2eVQQ+Ur6yFJMoaYcNpyGq22uYJ9Wx4UAEcC0K+LNPQ==
+  dependencies:
+    dom-css "^2.0.0"
+    prop-types "^15.5.10"
+    raf "^3.1.0"
+
 react-debounce-input@^3.2.3:
   version "3.2.5"
   resolved "https://registry.yarnpkg.com/react-debounce-input/-/react-debounce-input-3.2.5.tgz#3a29682c4b9dcd62694d6e03f85d7bfa96cec433"
@@ -19755,6 +19778,13 @@ to-arraybuffer@^1.0.0:
   resolved "https://registry.yarnpkg.com/to-arraybuffer/-/to-arraybuffer-1.0.1.tgz#7d229b1fcc637e466ca081180836a7aabff83f43"
   integrity sha1-fSKbH8xjfkZsoIEYCDanqr/4P0M=
 
+to-camel-case@1.0.0:
+  version "1.0.0"
+  resolved "https://registry.yarnpkg.com/to-camel-case/-/to-camel-case-1.0.0.tgz#1a56054b2f9d696298ce66a60897322b6f423e46"
+  integrity sha512-nD8pQi5H34kyu1QDMFjzEIYqk0xa9Alt6ZfrdEMuHCFOfTLhDG5pgTu/aAM9Wt9lXILwlXmWP43b8sav0GNE8Q==
+  dependencies:
+    to-space-case "^1.0.0"
+
 to-fast-properties@^1.0.3:
   version "1.0.3"
   resolved "https://registry.yarnpkg.com/to-fast-properties/-/to-fast-properties-1.0.3.tgz#b83571fa4d8c25b82e231b06e3a3055de4ca1a47"
@@ -19765,6 +19795,11 @@ to-fast-properties@^2.0.0:
   resolved "https://registry.yarnpkg.com/to-fast-properties/-/to-fast-properties-2.0.0.tgz#dc5e698cbd079265bc73e0377681a4e4e83f616e"
   integrity sha1-3F5pjL0HkmW8c+A3doGk5Og/YW4=
 
+to-no-case@^1.0.0:
+  version "1.0.2"
+  resolved "https://registry.yarnpkg.com/to-no-case/-/to-no-case-1.0.2.tgz#c722907164ef6b178132c8e69930212d1b4aa16a"
+  integrity sha512-Z3g735FxuZY8rodxV4gH7LxClE4H0hTIyHNIHdk+vpQxjLm0cwnKXq/OFVZ76SOQmto7txVcwSCwkU5kqp+FKg==
+
 to-object-path@^0.3.0:
   version "0.3.0"
   resolved "https://registry.yarnpkg.com/to-object-path/-/to-object-path-0.3.0.tgz#297588b7b0e7e0ac08e04e672f85c1f4999e17af"
@@ -19802,6 +19837,13 @@ to-regex@^3.0.1, to-regex@^3.0.2:
     regex-not "^1.0.2"
     safe-regex "^1.1.0"
 
+to-space-case@^1.0.0:
+  version "1.0.0"
+  resolved "https://registry.yarnpkg.com/to-space-case/-/to-space-case-1.0.0.tgz#b052daafb1b2b29dc770cea0163e5ec0ebc9fc17"
+  integrity sha512-rLdvwXZ39VOn1IxGL3V6ZstoTbwLRckQmn/U8ZDLuWwIXNpuZDhQ3AiRUlhTbOXFVE9C+dR51wM0CBDhk31VcA==
+  dependencies:
+    to-no-case "^1.0.0"
+
 toggle-selection@^1.0.6:
   version "1.0.6"
   resolved "https://registry.npmmirror.com/toggle-selection/-/toggle-selection-1.0.6.tgz#6e45b1263f2017fa0acc7d89d78b15b8bf77da32"