xieyonghong 3 anos atrás
pai
commit
bedbc89c0f
52 arquivos alterados com 701 adições e 274 exclusões
  1. 1 1
      src/components/FMonacoEditor/index.tsx
  2. 9 8
      src/components/Metadata/ArrayParam/index.tsx
  3. 2 2
      src/components/Metadata/EditTable/index.tsx
  4. 74 21
      src/components/Metadata/JsonParam/index.tsx
  5. 1 0
      src/components/ProTableCard/CardItems/DataCollect/index.less
  6. 1 0
      src/components/ProTableCard/index.less
  7. 6 2
      src/pages/DataCollect/Channel/index.tsx
  8. 1 1
      src/pages/DataCollect/Collector/components/Point/Save/BatchUpdate.tsx
  9. 30 3
      src/pages/DataCollect/Collector/components/Point/Save/modbus.tsx
  10. 1 1
      src/pages/DataCollect/Collector/components/Point/Save/opc-ua.tsx
  11. 40 33
      src/pages/DataCollect/Collector/components/Tree/index.tsx
  12. 5 0
      src/pages/DataCollect/service.ts
  13. 1 1
      src/pages/device/Category/Save/index.tsx
  14. 1 27
      src/pages/device/Category/index.tsx
  15. 8 1
      src/pages/device/Firmware/Save/index.tsx
  16. 1 1
      src/pages/device/Firmware/Task/Save/SelectDevices.tsx
  17. 18 3
      src/pages/device/Instance/Detail/Diagnose/Status/index.tsx
  18. 1 1
      src/pages/device/Instance/Detail/EdgeMap/mapTable/index.tsx
  19. 1 5
      src/pages/device/Instance/Detail/Functions/form.tsx
  20. 3 0
      src/pages/device/Instance/Detail/Info/index.tsx
  21. 2 1
      src/pages/device/Instance/Import/index.tsx
  22. 3 0
      src/pages/device/Instance/typings.d.ts
  23. 32 0
      src/pages/device/components/InputSelect/index.tsx
  24. 103 29
      src/pages/device/components/Metadata/Base/Edit/index.tsx
  25. 10 1
      src/pages/device/components/Metadata/Base/index.tsx
  26. 4 0
      src/pages/device/components/Metadata/index.tsx
  27. 2 2
      src/pages/device/data.ts
  28. 1 1
      src/pages/edge/Resource/Issue/Result.tsx
  29. 13 13
      src/pages/iot-card/CardManagement/SaveModal.tsx
  30. 1 1
      src/pages/link/Channel/Modbus/savePoint.tsx
  31. 1 1
      src/pages/link/DashBoard/index.tsx
  32. 45 43
      src/pages/media/Device/Channel/index.tsx
  33. 1 1
      src/pages/notice/Config/Detail/index.tsx
  34. 18 8
      src/pages/rule-engine/Scene/Save/action/DeviceOutput/actions/index.tsx
  35. 12 2
      src/pages/rule-engine/Scene/Save/action/DeviceOutput/device/Tag.tsx
  36. 5 3
      src/pages/rule-engine/Scene/Save/action/DeviceOutput/device/index.tsx
  37. 7 4
      src/pages/rule-engine/Scene/Save/action/DeviceOutput/index.tsx
  38. 2 0
      src/pages/rule-engine/Scene/Save/action/DeviceOutput/model.ts
  39. 2 3
      src/pages/rule-engine/Scene/Save/action/ListItem/Item.tsx
  40. 50 0
      src/pages/rule-engine/Scene/Save/action/device/ObjInput.tsx
  41. 59 0
      src/pages/rule-engine/Scene/Save/action/device/TypeTime.tsx
  42. 34 9
      src/pages/rule-engine/Scene/Save/action/device/functionCall.tsx
  43. 16 1
      src/pages/rule-engine/Scene/Save/device/addModel.tsx
  44. 7 20
      src/pages/rule-engine/Scene/index.tsx
  45. 20 4
      src/pages/system/Apply/Save/index.tsx
  46. 6 2
      src/pages/system/DataSource/Management/EditTable.tsx
  47. 2 2
      src/pages/system/Department/Assets/deivce/index.tsx
  48. 6 1
      src/pages/system/Menu/Detail/buttons.tsx
  49. 21 3
      src/pages/system/Menu/Setting/baseMenu.ts
  50. 6 5
      src/pages/system/Role/Detail/Permission/Allocate/index.tsx
  51. 4 2
      src/pages/system/User/Save/index.tsx
  52. 1 1
      src/utils/util.ts

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

@@ -12,7 +12,7 @@ export const JMonacoEditor = (props: Props) => {
   const monacoEditorRef = useRef<any>();
   const monacoEditorRef = useRef<any>();
 
 
   const editorFormat = (editor: any) => {
   const editorFormat = (editor: any) => {
-    editor.getAction('editor.action.formatDocument').run();
+    editor.getAction('editor.action.formatDocument')?.run();
   };
   };
 
 
   useEffect(() => {
   useEffect(() => {

+ 9 - 8
src/components/Metadata/ArrayParam/index.tsx

@@ -126,14 +126,15 @@ const ArrayParam = (props: Props) => {
                 message: '请选择时间格式',
                 message: '请选择时间格式',
               },
               },
             ],
             ],
-            'x-reactions': {
-              dependencies: ['.type'],
-              fulfill: {
-                state: {
-                  visible: "{{['date'].includes($deps[0])}}",
-                },
-              },
-            },
+            'x-visible': false,
+            // 'x-reactions': {
+            //   dependencies: ['.type'],
+            //   fulfill: {
+            //     state: {
+            //       visible: "{{['date'].includes($deps[0])}}",
+            //     },
+            //   },
+            // },
           },
           },
           expands: {
           expands: {
             type: 'object',
             type: 'object',

+ 2 - 2
src/components/Metadata/EditTable/index.tsx

@@ -168,8 +168,8 @@ Editable.Popover = observer((props) => {
         </div>
         </div>
         <CloseOutlined
         <CloseOutlined
           onClick={() => {
           onClick={() => {
-            // setVisible(false);
-            closePopover();
+            setVisible(false);
+            // closePopover();
           }}
           }}
         />
         />
       </div>
       </div>

+ 74 - 21
src/components/Metadata/JsonParam/index.tsx

@@ -45,16 +45,66 @@ const JsonParam = observer((props: Props) => {
       return _data;
       return _data;
     });
     });
 
 
-  const checkArray: any = (arr: any) => {
+  // const checkArray: any = (arr: any) => {
+  //   if (Array.isArray(arr) && arr.length) {
+  //     return arr.every((item: any) => {
+  //       if (item.valueType?.type === 'object') {
+  //         return item.id && item.name && checkArray(item.json?.properties);
+  //       }
+  //       return item.id && item.name && item.valueType;
+  //     });
+  //   }
+  //   return false;
+  // };
+
+  const checkArrayFormat: any = (arr: any) => {
+    const reg = new RegExp('^[0-9a-zA-Z_\\\\-]+$');
+    let str: string = '';
     if (Array.isArray(arr) && arr.length) {
     if (Array.isArray(arr) && arr.length) {
-      return arr.every((item: any) => {
-        if (item.valueType?.type === 'object') {
-          return item.id && item.name && checkArray(item.json?.properties);
+      arr.every((item: any) => {
+        if (!item.id) {
+          str = '请输入标识';
+          return false;
+        }
+        if (!item.name) {
+          str = '请输入名称';
+          return false;
+        }
+        if (!item.valueType?.type) {
+          str = '请选择数据类型';
+          return false;
+        }
+        if (!reg.exec(item.id)) {
+          str = '标识只能由数字、字母、下划线、中划线组成';
+          return false;
+        }
+        if (item.id.length > 64 && item.name.length > 64) {
+          str = '标识最多可输入64个字符';
+          return false;
+        }
+        if (item.name.length > 64) {
+          str = '名称最多可输入64个字符';
+          return false;
         }
         }
-        return item.id && item.name && item.valueType;
+        if (item.valueType?.type === 'boolean') {
+          if (!(item.valueType.falseText && item.valueType.trueText)) {
+            str = '请输入布尔值';
+            return false;
+          }
+          if (
+            item.valueType.falseValue === '' ||
+            item.valueType.trueValue === '' ||
+            item.valueType.falseValue === undefined ||
+            item.valueType.trueValue === undefined
+          ) {
+            str = '请输入布尔值';
+            return false;
+          }
+        }
+        return item.id && item.name && item.valueType?.type;
       });
       });
     }
     }
-    return false;
+    return str;
   };
   };
 
 
   const schema: ISchema = {
   const schema: ISchema = {
@@ -80,12 +130,8 @@ const JsonParam = observer((props: Props) => {
                 if (props.keys === 'inputs' && value.length === 0) {
                 if (props.keys === 'inputs' && value.length === 0) {
                   resolve('');
                   resolve('');
                 }
                 }
-                const flag = checkArray(value);
-                if (!!flag) {
-                  resolve('');
-                } else {
-                  resolve('请配置参数');
-                }
+                const str = checkArrayFormat(value);
+                resolve(str);
               });
               });
             },
             },
           },
           },
@@ -162,6 +208,12 @@ const JsonParam = observer((props: Props) => {
                           item.value,
                           item.value,
                         ),
                         ),
                       ),
                       ),
+                      'x-validator': [
+                        {
+                          required: true,
+                          message: '请选择数据类型',
+                        },
+                      ],
                     },
                     },
                     booleanConfig: {
                     booleanConfig: {
                       title: '布尔值',
                       title: '布尔值',
@@ -264,21 +316,22 @@ const JsonParam = observer((props: Props) => {
                       'x-component': 'Select',
                       'x-component': 'Select',
                       enum: DateTypeList,
                       enum: DateTypeList,
                       'x-visible': false,
                       'x-visible': false,
-                      default: 'string',
+                      default: 'yyyy-MM-DD HH:mm:ss',
                       'x-validator': [
                       'x-validator': [
                         {
                         {
                           required: true,
                           required: true,
                           message: '请选择时间格式',
                           message: '请选择时间格式',
                         },
                         },
                       ],
                       ],
-                      'x-reactions': {
-                        dependencies: ['..valueType.type'],
-                        fulfill: {
-                          state: {
-                            visible: "{{['date'].includes($deps[0])}}",
-                          },
-                        },
-                      },
+                      // "x-hidden":true,
+                      // 'x-reactions': {
+                      //   dependencies: ['..valueType.type'],
+                      //   fulfill: {
+                      //     state: {
+                      //       visible: "{{['date'].includes($deps[0])}}",
+                      //     },
+                      //   },
+                      // },
                     },
                     },
                     expands: {
                     expands: {
                       type: 'object',
                       type: 'object',

+ 1 - 0
src/components/ProTableCard/CardItems/DataCollect/index.less

@@ -2,6 +2,7 @@
 
 
 .pro-table-card-item {
 .pro-table-card-item {
   display: flex;
   display: flex;
+  cursor: pointer;
 
 
   .card-item-avatar {
   .card-item-avatar {
     margin-right: 16px;
     margin-right: 16px;

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

@@ -29,6 +29,7 @@
 
 
     .pro-table-card-item {
     .pro-table-card-item {
       display: flex;
       display: flex;
+      cursor: pointer;
 
 
       .card-item-avatar {
       .card-item-avatar {
         margin-right: 16px;
         margin-right: 16px;

+ 6 - 2
src/pages/DataCollect/Channel/index.tsx

@@ -285,8 +285,12 @@ export default observer(() => {
                       </PermissionButton>,
                       </PermissionButton>,
                     ]}
                     ]}
                     onClick={() => {
                     onClick={() => {
-                      const url = getMenuPathByCode(MENUS_CODE['DataCollect/Collector']);
-                      history.push(url, { channelId: record.id });
+                      if (permission.view) {
+                        const url = getMenuPathByCode(MENUS_CODE['DataCollect/Collector']);
+                        history.push(url, { channelId: record.id });
+                      } else {
+                        onlyMessage('暂无权限,请联系管理员', 'error');
+                      }
                     }}
                     }}
                   />
                   />
                 </Col>
                 </Col>

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

@@ -40,7 +40,7 @@ export default (props: Props) => {
       if (!(Number(value) % 1 === 0) || Number(value) <= 0) {
       if (!(Number(value) % 1 === 0) || Number(value) <= 0) {
         return {
         return {
           type: 'error',
           type: 'error',
-          message: '请输入非0正整数',
+          message: '请输入正整数',
         };
         };
       }
       }
       return '';
       return '';

+ 30 - 3
src/pages/DataCollect/Collector/components/Point/Save/modbus.tsx

@@ -103,7 +103,7 @@ export default (props: Props) => {
       if (!(Number(value) % 1 === 0) || Number(value) < 0) {
       if (!(Number(value) % 1 === 0) || Number(value) < 0) {
         return {
         return {
           type: 'error',
           type: 'error',
-          message: '请输入0或正整数',
+          message: '请输入正整数',
         };
         };
       }
       }
       return '';
       return '';
@@ -209,6 +209,33 @@ export default (props: Props) => {
               {
               {
                 checkAddressLength: true,
                 checkAddressLength: true,
               },
               },
+              {
+                triggerType: 'onBlur',
+                validator: (value: string) => {
+                  return new Promise((resolve) => {
+                    if (props.data?.id && props.data?.configuration?.parameter?.address === value) {
+                      resolve('');
+                    }
+                    service
+                      ._validateField(props.collector?.id || '', {
+                        pointKey: value,
+                      })
+                      .then((resp) => {
+                        if (resp.status === 200) {
+                          if (resp.result.passed) {
+                            resolve('');
+                          } else {
+                            resolve('改地址已存在');
+                          }
+                        }
+                        resolve('');
+                      })
+                      .catch(() => {
+                        return '验证失败!';
+                      });
+                  });
+                },
+              },
             ],
             ],
           },
           },
           'configuration.parameter.quantity': {
           'configuration.parameter.quantity': {
@@ -240,7 +267,7 @@ export default (props: Props) => {
               },
               },
               {
               {
                 min: 1,
                 min: 1,
-                message: '请输入非0正整数',
+                message: '请输入正整数',
               },
               },
               {
               {
                 checkLength: true,
                 checkLength: true,
@@ -461,7 +488,7 @@ export default (props: Props) => {
               },
               },
               {
               {
                 min: 1,
                 min: 1,
-                message: '请输入非0正整数',
+                message: '请输入正整数',
               },
               },
               {
               {
                 checkLength: true,
                 checkLength: true,

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

@@ -89,7 +89,7 @@ export default (props: Props) => {
       if (!(Number(value) % 1 === 0) || Number(value) < 0) {
       if (!(Number(value) % 1 === 0) || Number(value) < 0) {
         return {
         return {
           type: 'error',
           type: 'error',
-          message: '请输入0或正整数',
+          message: '请输入正整数',
         };
         };
       }
       }
       return '';
       return '';

+ 40 - 33
src/pages/DataCollect/Collector/components/Tree/index.tsx

@@ -135,18 +135,21 @@ export default observer((props: Props) => {
         />
         />
       </div>
       </div>
       <div style={{ margin: '16px 0' }}>
       <div style={{ margin: '16px 0' }}>
-        <Button
-          type="primary"
-          ghost
-          style={{ width: '100%' }}
-          icon={<PlusOutlined />}
-          onClick={() => {
-            TreeModel.visible = true;
-            TreeModel.current = {};
-          }}
-        >
-          新增采集器
-        </Button>
+        <Tooltip title={!permission.add ? '暂无权限,请联系管理员' : ''}>
+          <Button
+            type="primary"
+            ghost
+            disabled={!permission.add}
+            style={{ width: '100%' }}
+            icon={<PlusOutlined />}
+            onClick={() => {
+              TreeModel.visible = true;
+              TreeModel.current = {};
+            }}
+          >
+            新增采集器
+          </Button>
+        </Tooltip>
       </div>
       </div>
       <div>
       <div>
         {TreeModel.dataSource.length ? (
         {TreeModel.dataSource.length ? (
@@ -210,22 +213,24 @@ export default observer((props: Props) => {
                           defaultMessage: '确认禁用?',
                           defaultMessage: '确认禁用?',
                         })}
                         })}
                         onConfirm={async () => {
                         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 = { terms: [] };
-                            handleSearch(TreeModel.param);
-                            onlyMessage('操作成功');
-                          } else {
-                            onlyMessage('操作失败!', 'error');
+                          if (permission.action) {
+                            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 = { terms: [] };
+                              handleSearch(TreeModel.param);
+                              onlyMessage('操作成功');
+                            } else {
+                              onlyMessage('操作失败!', 'error');
+                            }
                           }
                           }
                         }}
                         }}
                       >
                       >
@@ -241,11 +246,13 @@ export default observer((props: Props) => {
                         title={'该操作将会删除下属点位,确定删除?'}
                         title={'该操作将会删除下属点位,确定删除?'}
                         disabled={i?.state?.value !== 'disabled'}
                         disabled={i?.state?.value !== 'disabled'}
                         onConfirm={async () => {
                         onConfirm={async () => {
-                          const resp = await service.removeCollector(i.id);
-                          if (resp.status === 200) {
-                            TreeModel.param = { terms: [] };
-                            handleSearch(TreeModel.param);
-                            onlyMessage('操作成功');
+                          if (permission.delete) {
+                            const resp = await service.removeCollector(i.id);
+                            if (resp.status === 200) {
+                              TreeModel.param = { terms: [] };
+                              handleSearch(TreeModel.param);
+                              onlyMessage('操作成功');
+                            }
                           }
                           }
                         }}
                         }}
                       >
                       >

+ 5 - 0
src/pages/DataCollect/service.ts

@@ -169,6 +169,11 @@ class Service {
       method: 'POST',
       method: 'POST',
       data: params,
       data: params,
     });
     });
+  public _validateField = (id: string, data?: any) =>
+    request(`/${SystemConst.API_BASE}/data-collect/point/${id}/_validate`, {
+      method: 'GET',
+      params: data,
+    });
 }
 }
 
 
 const service = new Service();
 const service = new Service();

+ 1 - 1
src/pages/device/Category/Save/index.tsx

@@ -136,7 +136,7 @@ const Save = (props: Props) => {
         'x-validator': [
         'x-validator': [
           {
           {
             format: 'integer',
             format: 'integer',
-            message: '请输入非0正整数',
+            message: '请输入正整数',
           },
           },
         ],
         ],
       },
       },

+ 1 - 27
src/pages/device/Category/index.tsx

@@ -13,8 +13,6 @@ import SearchComponent from '@/components/SearchComponent';
 import { PermissionButton } from '@/components';
 import { PermissionButton } from '@/components';
 import { useDomFullHeight } from '@/hooks';
 import { useDomFullHeight } from '@/hooks';
 import { onlyMessage } from '@/utils/util';
 import { onlyMessage } from '@/utils/util';
-import { service as api } from '@/pages/device/Product';
-import { Spin } from 'antd';
 
 
 export const service = new Service('device/category');
 export const service = new Service('device/category');
 
 
@@ -57,8 +55,6 @@ const Category = observer(() => {
   const permissionCode = 'device/Category';
   const permissionCode = 'device/Category';
   const { permission } = PermissionButton.usePermission(permissionCode);
   const { permission } = PermissionButton.usePermission(permissionCode);
   const { minHeight } = useDomFullHeight(`.device-category`, 24);
   const { minHeight } = useDomFullHeight(`.device-category`, 24);
-  const [loading, setLoading] = useState<boolean>(true);
-  const [title, setTitle] = useState<string>('');
 
 
   const intl = useIntl();
   const intl = useIntl();
 
 
@@ -139,30 +135,8 @@ const Category = observer(() => {
           type="link"
           type="link"
           key="delete"
           key="delete"
           style={{ padding: 0 }}
           style={{ padding: 0 }}
-          onClick={async () => {
-            const res: any = await api.queryNoPagingPost({
-              terms: [{ terms: [{ column: 'classifiedId', value: record.id }] }],
-            });
-            if (res.status === 200) {
-              if (res.result.length === 0) {
-                setTitle('确定删除?');
-              } else {
-                setTitle('该数据已被产品引用,确定删除?');
-              }
-              setLoading(false);
-            } else {
-              setLoading(false);
-            }
-          }}
           popConfirm={{
           popConfirm={{
-            title: <>{loading ? <Spin /> : title}</>,
-            okButtonProps: {
-              loading: loading,
-            },
-            onCancel: () => {
-              setTitle('');
-              setLoading(true);
-            },
+            title: '确定删除?',
             onConfirm: async () => {
             onConfirm: async () => {
               const resp = (await service.remove(record.id)) as Response<any>;
               const resp = (await service.remove(record.id)) as Response<any>;
               if (resp.status === 200) {
               if (resp.status === 200) {

+ 8 - 1
src/pages/device/Firmware/Save/index.tsx

@@ -24,6 +24,7 @@ interface Props {
 const Save = (props: Props) => {
 const Save = (props: Props) => {
   const { data, close, visible } = props;
   const { data, close, visible } = props;
   const fileInfo = useRef<any>({});
   const fileInfo = useRef<any>({});
+  // const disabled = useRef<boolean>(false);
   const signMethod = useRef<'md5' | 'sha256'>('md5');
   const signMethod = useRef<'md5' | 'sha256'>('md5');
 
 
   const form = createForm({
   const form = createForm({
@@ -32,6 +33,12 @@ const Save = (props: Props) => {
     effects: () => {
     effects: () => {
       onFormInit(async (form1) => {
       onFormInit(async (form1) => {
         if (!data?.id) return;
         if (!data?.id) return;
+        // const resp = await service.task({terms: [{ column: 'firmwareId', value: data?.id }]})
+        // if(resp.status === 200 && resp.result?.total){
+        //   disabled.current = true
+        // } else {
+        //   disabled.current = false
+        // }
         form1.setInitialValues({ ...data, upload: { url: data?.url } });
         form1.setInitialValues({ ...data, upload: { url: data?.url } });
       });
       });
       onFieldValueChange('signMethod', (field, f) => {
       onFieldValueChange('signMethod', (field, f) => {
@@ -424,7 +431,7 @@ const Save = (props: Props) => {
             terms: [{ terms: [{ column: 'firmwareId', value: data.id }] }],
             terms: [{ terms: [{ column: 'firmwareId', value: data.id }] }],
           });
           });
           if (res.status === 200 && res.result.data && res.result.data.length !== 0) {
           if (res.status === 200 && res.result.data && res.result.data.length !== 0) {
-            onlyMessage('该固件有升级任务,不可编辑', 'warning');
+            onlyMessage('该固件有升级任务,不可编辑', 'error');
           } else {
           } else {
             save();
             save();
           }
           }

+ 1 - 1
src/pages/device/Firmware/Task/Save/SelectDevices.tsx

@@ -60,7 +60,7 @@ const SelectDevices = observer((props: Props) => {
       title: '固件版本',
       title: '固件版本',
       dataIndex: 'firmwareInfo',
       dataIndex: 'firmwareInfo',
       ellipsis: true,
       ellipsis: true,
-      render: (text: any, record: any) => record?.version || '',
+      render: (text: any, record: any) => record?.firmwareInfo?.version || '',
     },
     },
     {
     {
       title: intl.formatMessage({
       title: intl.formatMessage({

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

@@ -82,6 +82,7 @@ const Status = observer((props: Props) => {
   const modifyArrayList = (oldList: ListProps[], item: ListProps, index?: number) => {
   const modifyArrayList = (oldList: ListProps[], item: ListProps, index?: number) => {
     let newList: ListProps[] = [];
     let newList: ListProps[] = [];
     if (index !== 0 && !index) {
     if (index !== 0 && !index) {
+      // 添加
       for (let i = 0; i < oldList.length; i++) {
       for (let i = 0; i < oldList.length; i++) {
         const dt = oldList[i];
         const dt = oldList[i];
         if (item.key === dt.key) {
         if (item.key === dt.key) {
@@ -91,6 +92,7 @@ const Status = observer((props: Props) => {
         }
         }
       }
       }
     } else {
     } else {
+      // 修改
       oldList.splice(index, 0, item);
       oldList.splice(index, 0, item);
       newList = [...oldList];
       newList = [...oldList];
     }
     }
@@ -875,9 +877,9 @@ const Status = observer((props: Props) => {
         const response = await queryProductConfigRun(device.productId);
         const response = await queryProductConfigRun(device.productId);
         if (response.status === 200 && response.result.length > 0) {
         if (response.status === 200 && response.result.length > 0) {
           DiagnoseStatusModel.configuration.product = response.result;
           DiagnoseStatusModel.configuration.product = response.result;
-          const list = [...DiagnoseStatusModel.list];
           const configuration = DiagnoseStatusModel.product?.configuration || {};
           const configuration = DiagnoseStatusModel.product?.configuration || {};
           response.result.map((item: any, i: number) => {
           response.result.map((item: any, i: number) => {
+            const list = [...DiagnoseStatusModel.list];
             if (!_.map(list, 'key').includes(`product-auth${i}`)) {
             if (!_.map(list, 'key').includes(`product-auth${i}`)) {
               DiagnoseStatusModel.list = modifyArrayList(
               DiagnoseStatusModel.list = modifyArrayList(
                 DiagnoseStatusModel.list,
                 DiagnoseStatusModel.list,
@@ -1039,9 +1041,9 @@ const Status = observer((props: Props) => {
         const response = await queryDeviceConfigRun(device.id);
         const response = await queryDeviceConfigRun(device.id);
         if (response.status === 200 && response.result.length > 0) {
         if (response.status === 200 && response.result.length > 0) {
           DiagnoseStatusModel.configuration.device = response.result;
           DiagnoseStatusModel.configuration.device = response.result;
-          const list = [...DiagnoseStatusModel.list];
           const configuration = device?.configuration || {};
           const configuration = device?.configuration || {};
           response.result.map((item: any, i: number) => {
           response.result.map((item: any, i: number) => {
+            const list = [...DiagnoseStatusModel.list];
             if (!_.map(list, 'key').includes(`device-auth${i}`)) {
             if (!_.map(list, 'key').includes(`device-auth${i}`)) {
               DiagnoseStatusModel.list = modifyArrayList(
               DiagnoseStatusModel.list = modifyArrayList(
                 DiagnoseStatusModel.list,
                 DiagnoseStatusModel.list,
@@ -1899,6 +1901,7 @@ const Status = observer((props: Props) => {
             <Button
             <Button
               type="primary"
               type="primary"
               onClick={async () => {
               onClick={async () => {
+                let flag: boolean = true;
                 if (
                 if (
                   Object.keys(DiagnoseStatusModel.gateway).length > 0 &&
                   Object.keys(DiagnoseStatusModel.gateway).length > 0 &&
                   DiagnoseStatusModel.gateway?.state?.value !== 'enabled'
                   DiagnoseStatusModel.gateway?.state?.value !== 'enabled'
@@ -1913,6 +1916,8 @@ const Status = observer((props: Props) => {
                       text: '正常',
                       text: '正常',
                       info: null,
                       info: null,
                     });
                     });
+                  } else {
+                    flag = false;
                   }
                   }
                 }
                 }
                 if (DiagnoseStatusModel.product?.state !== 1) {
                 if (DiagnoseStatusModel.product?.state !== 1) {
@@ -1926,6 +1931,8 @@ const Status = observer((props: Props) => {
                       text: '正常',
                       text: '正常',
                       info: null,
                       info: null,
                     });
                     });
+                  } else {
+                    flag = false;
                   }
                   }
                 }
                 }
                 if (device?.state?.value === 'notActive') {
                 if (device?.state?.value === 'notActive') {
@@ -1940,6 +1947,8 @@ const Status = observer((props: Props) => {
                       text: '正常',
                       text: '正常',
                       info: null,
                       info: null,
                     });
                     });
+                  } else {
+                    flag = false;
                   }
                   }
                 }
                 }
                 if (providerType === 'network' || providerType === 'child-device') {
                 if (providerType === 'network' || providerType === 'child-device') {
@@ -1958,6 +1967,8 @@ const Status = observer((props: Props) => {
                         text: '正常',
                         text: '正常',
                         info: null,
                         info: null,
                       });
                       });
+                    } else {
+                      flag = false;
                     }
                     }
                   }
                   }
                 }
                 }
@@ -1973,10 +1984,14 @@ const Status = observer((props: Props) => {
                         text: '正常',
                         text: '正常',
                         info: null,
                         info: null,
                       });
                       });
+                    } else {
+                      flag = false;
                     }
                     }
                   }
                   }
                 }
                 }
-                onlyMessage('操作成功!');
+                if (flag) {
+                  onlyMessage('操作成功!');
+                }
               }}
               }}
             >
             >
               一键修复
               一键修复

+ 1 - 1
src/pages/device/Instance/Detail/EdgeMap/mapTable/index.tsx

@@ -446,8 +446,8 @@ const MapTable = (props: Props) => {
           批量映射
           批量映射
         </PermissionButton>
         </PermissionButton>
         <PermissionButton
         <PermissionButton
-          isPermission={permission.update}
           type="primary"
           type="primary"
+          isPermission={permission.update}
           onClick={async () => {
           onClick={async () => {
             if (props.formRef) {
             if (props.formRef) {
               add();
               add();

+ 1 - 5
src/pages/device/Instance/Detail/Functions/form.tsx

@@ -78,11 +78,7 @@ export default (props: FunctionProps) => {
           //   }
           //   }
           // </>
           // </>
           // @ts-ignore
           // @ts-ignore
-          <DatePicker
-            format={record.format || 'YYYY-MM-DD HH:mm:ss'}
-            style={{ width: '100%' }}
-            showTime
-          />
+          <DatePicker format={'YYYY-MM-DD HH:mm:ss'} style={{ width: '100%' }} showTime />
         );
         );
       default:
       default:
         return <Input placeholder={'请输入' + name} />;
         return <Input placeholder={'请输入' + name} />;

+ 3 - 0
src/pages/device/Instance/Detail/Info/index.tsx

@@ -114,6 +114,9 @@ const Info = observer(() => {
           >
           >
             {InstanceModel.detail?.deviceType?.text}
             {InstanceModel.detail?.deviceType?.text}
           </Descriptions.Item>
           </Descriptions.Item>
+          <Descriptions.Item label={'固件版本'}>
+            {InstanceModel.detail?.firmwareInfo?.version || ''}
+          </Descriptions.Item>
           <Descriptions.Item
           <Descriptions.Item
             label={intl.formatMessage({
             label={intl.formatMessage({
               id: 'pages.device.instanceDetail.transportProtocol',
               id: 'pages.device.instanceDetail.transportProtocol',

+ 2 - 1
src/pages/device/Instance/Import/index.tsx

@@ -93,7 +93,8 @@ const NormalUpload = (props: any) => {
           setErrMessage(res.message || '失败');
           setErrMessage(res.message || '失败');
         }
         }
       };
       };
-      source.onerror = () => {
+      source.onerror = (e) => {
+        if (e.status === 403) setErrMessage('暂无权限,请联系管理员');
         setFlag(false);
         setFlag(false);
         source.close();
         source.close();
       };
       };

+ 3 - 0
src/pages/device/Instance/typings.d.ts

@@ -45,6 +45,9 @@ export type DeviceInstance = {
   features?: any[];
   features?: any[];
   parentId?: string;
   parentId?: string;
   classifiedName?: string;
   classifiedName?: string;
+  firmwareInfo?: {
+    version?: string;
+  };
 };
 };
 
 
 type Unit = {
 type Unit = {

+ 32 - 0
src/pages/device/components/InputSelect/index.tsx

@@ -0,0 +1,32 @@
+import { Select } from 'antd';
+import { connect, mapProps } from '@formily/react';
+import { useState } from 'react';
+import { LoadingOutlined } from '@ant-design/icons';
+
+const InputSelect = connect(
+  Select,
+  mapProps(
+    {
+      dataSource: 'options',
+      loading: true,
+    },
+    (props, field) => {
+      const [value, setValue] = useState(props.value);
+      return {
+        ...props,
+        value: value,
+        onChange: (item: any) => {
+          if (item.length > 1) {
+            setValue(item.slice(item.length - 1));
+          } else {
+            setValue(item);
+          }
+        },
+        suffixIcon:
+          field?.['loading'] || field?.['validating'] ? <LoadingOutlined /> : props.suffixIcon,
+      };
+    },
+  ),
+  // mapReadPretty(PreviewText.Input),
+);
+export default InputSelect;

+ 103 - 29
src/pages/device/components/Metadata/Base/Edit/index.tsx

@@ -22,6 +22,7 @@ import {
   Radio,
   Radio,
   Select,
   Select,
   Space,
   Space,
+  Switch,
 } from '@formily/antd';
 } from '@formily/antd';
 import type { ISchema } from '@formily/json-schema';
 import type { ISchema } from '@formily/json-schema';
 import {
 import {
@@ -46,13 +47,16 @@ import { lastValueFrom } from 'rxjs';
 import SystemConst from '@/utils/const';
 import SystemConst from '@/utils/const';
 import DB from '@/db';
 import DB from '@/db';
 import _ from 'lodash';
 import _ from 'lodash';
-import { InstanceModel } from '@/pages/device/Instance';
+// import { InstanceModel } from '@/pages/device/Instance';
 import FRuleEditor from '@/components/FRuleEditor';
 import FRuleEditor from '@/components/FRuleEditor';
 import FIndicators from '@/components/FIndicators';
 import FIndicators from '@/components/FIndicators';
 import { action } from '@formily/reactive';
 import { action } from '@formily/reactive';
 import { asyncUpdateMedata, updateMetadata } from '../../metadata';
 import { asyncUpdateMedata, updateMetadata } from '../../metadata';
 import { onlyMessage } from '@/utils/util';
 import { onlyMessage } from '@/utils/util';
 import Editable from '@/components/Metadata/EditTable';
 import Editable from '@/components/Metadata/EditTable';
+import InputSelect from '../../../InputSelect';
+import { InstanceModel, service as instanceService } from '@/pages/device/Instance';
+import { useParams } from 'umi';
 
 
 interface Props {
 interface Props {
   type: 'product' | 'device';
   type: 'product' | 'device';
@@ -62,6 +66,7 @@ interface Props {
 const Edit = observer((props: Props) => {
 const Edit = observer((props: Props) => {
   const intl = useIntl();
   const intl = useIntl();
   const [loading, setLoading] = useState<boolean>(false);
   const [loading, setLoading] = useState<boolean>(false);
+  const param = useParams<{ id: string }>();
   const form = useMemo(
   const form = useMemo(
     () =>
     () =>
       createForm({
       createForm({
@@ -170,7 +175,9 @@ const Edit = observer((props: Props) => {
       Checkbox,
       Checkbox,
       FormGrid,
       FormGrid,
       DatePicker,
       DatePicker,
+      Switch,
       FIndicators,
       FIndicators,
+      InputSelect,
     },
     },
     scope: {
     scope: {
       async asyncOtherConfig(field: Field) {
       async asyncOtherConfig(field: Field) {
@@ -247,12 +254,13 @@ const Edit = observer((props: Props) => {
           defaultMessage: '单位',
           defaultMessage: '单位',
         }),
         }),
         'x-decorator': 'FormItem',
         'x-decorator': 'FormItem',
-        'x-component': 'Select',
+        'x-component': 'InputSelect',
         'x-visible': false,
         'x-visible': false,
         'x-component-props': {
         'x-component-props': {
           showSearch: true,
           showSearch: true,
           showArrow: true,
           showArrow: true,
           allowClear: true,
           allowClear: true,
+          mode: 'tags',
           filterOption: (input: string, option: any) =>
           filterOption: (input: string, option: any) =>
             option.label.toLowerCase().indexOf(input.toLowerCase()) >= 0,
             option.label.toLowerCase().indexOf(input.toLowerCase()) >= 0,
         },
         },
@@ -324,21 +332,22 @@ const Edit = observer((props: Props) => {
         'x-decorator': 'FormItem',
         'x-decorator': 'FormItem',
         'x-component': 'Select',
         'x-component': 'Select',
         enum: DateTypeList,
         enum: DateTypeList,
-        default: 'string',
+        // default: 'yyyy-MM-DD HH:mm:ss',
+        'x-visible': false,
         'x-validator': [
         'x-validator': [
           {
           {
             required: true,
             required: true,
             message: '请选择时间格式',
             message: '请选择时间格式',
           },
           },
         ],
         ],
-        'x-reactions': {
-          dependencies: ['.type'],
-          fulfill: {
-            state: {
-              visible: "{{['date'].includes($deps[0])}}",
-            },
-          },
-        },
+        // 'x-reactions': {
+        //   dependencies: ['.type'],
+        //   fulfill: {
+        //     state: {
+        //       visible: "{{['date'].includes($deps[0])}}",
+        //     },
+        //   },
+        // },
       },
       },
       enumConfig: {
       enumConfig: {
         title: intl.formatMessage({
         title: intl.formatMessage({
@@ -608,11 +617,12 @@ const Edit = observer((props: Props) => {
             type: 'object',
             type: 'object',
             title: '规则配置',
             title: '规则配置',
             'x-visible': false,
             'x-visible': false,
-            'x-component': 'Editable.Popover',
+            // 'x-component': 'Editable.Popover',
             'x-reactions': {
             'x-reactions': {
               dependencies: ['.source'],
               dependencies: ['.source'],
               fulfill: {
               fulfill: {
                 state: {
                 state: {
+                  // visible: '{{$deps[0]}}',
                   visible: '{{$deps[0]==="rule"}}',
                   visible: '{{$deps[0]==="rule"}}',
                 },
                 },
               },
               },
@@ -636,16 +646,37 @@ const Edit = observer((props: Props) => {
               //     },
               //     },
               //   ],
               //   ],
               // },
               // },
-
+              isVirtualRule: {
+                type: 'boolean',
+                title: '规则配置',
+                'x-decorator': 'FormItem',
+                'x-component': 'Switch',
+              },
               windowType: {
               windowType: {
                 type: 'string',
                 type: 'string',
                 title: '窗口',
                 title: '窗口',
                 'x-decorator': 'FormItem',
                 'x-decorator': 'FormItem',
                 'x-component': 'Select',
                 'x-component': 'Select',
+                required: true,
+                'x-validator': [
+                  {
+                    required: true,
+                    message: `请选择窗口`,
+                  },
+                ],
                 enum: [
                 enum: [
                   { label: '时间窗口', value: 'time' },
                   { label: '时间窗口', value: 'time' },
                   { label: '次数窗口', value: 'num' },
                   { label: '次数窗口', value: 'num' },
                 ],
                 ],
+                'x-visible': false,
+                'x-reactions': {
+                  dependencies: ['.isVirtualRule'],
+                  fulfill: {
+                    state: {
+                      visible: '{{$deps[0]}}',
+                    },
+                  },
+                },
                 'x-component-props': {
                 'x-component-props': {
                   allowClear: true,
                   allowClear: true,
                 },
                 },
@@ -668,29 +699,60 @@ const Edit = observer((props: Props) => {
                 title: '聚合函数',
                 title: '聚合函数',
                 'x-decorator': 'FormItem',
                 'x-decorator': 'FormItem',
                 'x-component': 'Select',
                 'x-component': 'Select',
-                'x-reactions': '{{useAsyncDataSource(getStreamingAggType)}}',
+                required: true,
+                'x-visible': false,
+                'x-validator': [
+                  {
+                    required: true,
+                    message: `请选择聚合函数`,
+                  },
+                ],
+                'x-reactions': [
+                  {
+                    dependencies: ['.isVirtualRule'],
+                    fulfill: {
+                      state: {
+                        visible: '{{$deps[0]}}',
+                      },
+                    },
+                  },
+                  '{{useAsyncDataSource(getStreamingAggType)}}',
+                ],
               },
               },
               window: {
               window: {
                 type: 'object',
                 type: 'object',
+                'x-visible': false,
+                'x-reactions': {
+                  dependencies: ['.isVirtualRule'],
+                  fulfill: {
+                    state: {
+                      visible: '{{$deps[0]}}',
+                    },
+                  },
+                },
                 properties: {
                 properties: {
                   span: {
                   span: {
                     title: '窗口长度',
                     title: '窗口长度',
-                    'x-component': 'Input',
+                    'x-component': 'NumberPicker',
                     'x-decorator': 'FormItem',
                     'x-decorator': 'FormItem',
-                    format: 'number',
+                    required: true,
+                    'x-component-props': {
+                      style: {
+                        width: '100%',
+                      },
+                    },
                     'x-validator': [
                     'x-validator': [
                       {
                       {
-                        // triggerType: 'onBlur',
-                        validator: (value: any[]) => {
-                          return new Promise((resolve) => {
-                            const number = Number(value);
-                            if (number <= 0 || value.length > 64 || /[.]/.test(value)) {
-                              resolve('请输入非0正整数,最多可输入64个字符');
-                            } else {
-                              resolve('');
-                            }
-                          });
-                        },
+                        required: true,
+                        message: `请输入窗口长度`,
+                      },
+                      {
+                        format: 'integer',
+                        message: '请输入正整数',
+                      },
+                      {
+                        min: 1,
+                        message: '请输入正整数',
                       },
                       },
                     ],
                     ],
                     'x-reactions': [
                     'x-reactions': [
@@ -725,9 +787,14 @@ const Edit = observer((props: Props) => {
                   },
                   },
                   every: {
                   every: {
                     title: '步长',
                     title: '步长',
-                    'x-component': 'Input',
+                    'x-component': 'NumberPicker',
                     'x-decorator': 'FormItem',
                     'x-decorator': 'FormItem',
-                    format: 'number',
+                    required: true,
+                    'x-component-props': {
+                      style: {
+                        width: '100%',
+                      },
+                    },
                     'x-validator': [
                     'x-validator': [
                       {
                       {
                         // triggerType: 'onBlur',
                         // triggerType: 'onBlur',
@@ -1271,6 +1338,12 @@ const Edit = observer((props: Props) => {
   typeMap.set('device', InstanceModel.detail);
   typeMap.set('device', InstanceModel.detail);
   const { type } = MetadataModel;
   const { type } = MetadataModel;
 
 
+  const resetMetadata = async () => {
+    const resp = await instanceService.detail(param.id);
+    if (resp.status === 200) {
+      InstanceModel.detail = resp?.result || [];
+    }
+  };
   const saveMetadata = async (deploy?: boolean) => {
   const saveMetadata = async (deploy?: boolean) => {
     setLoading(true);
     setLoading(true);
     let params;
     let params;
@@ -1322,6 +1395,7 @@ const Edit = observer((props: Props) => {
         if (deploy) {
         if (deploy) {
           Store.set('product-deploy', deploy);
           Store.set('product-deploy', deploy);
         } else {
         } else {
+          resetMetadata();
           message.success({
           message.success({
             key: 'metadata',
             key: 'metadata',
             content: '操作成功!',
             content: '操作成功!',

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

@@ -14,12 +14,13 @@ import SystemConst from '@/utils/const';
 import { useIntl } from '@@/plugin-locale/localeExports';
 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 { productModel } from '@/pages/device/Product';
-import { InstanceModel } from '@/pages/device/Instance';
+// import { InstanceModel } from '@/pages/device/Instance';
 import { asyncUpdateMedata, removeMetadata } from '../metadata';
 import { asyncUpdateMedata, removeMetadata } from '../metadata';
 import type { permissionType } from '@/hooks/permission';
 import type { permissionType } from '@/hooks/permission';
 import { PermissionButton } from '@/components';
 import { PermissionButton } from '@/components';
 import { onlyMessage } from '@/utils/util';
 import { onlyMessage } from '@/utils/util';
 import { message } from 'antd';
 import { message } from 'antd';
+import { InstanceModel, service as instanceService } from '@/pages/device/Instance';
 
 
 interface Props {
 interface Props {
   type: MetadataType;
   type: MetadataType;
@@ -39,6 +40,13 @@ const BaseMetadata = observer((props: Props) => {
   typeMap.set('product', productModel.current);
   typeMap.set('product', productModel.current);
   typeMap.set('device', InstanceModel.detail);
   typeMap.set('device', InstanceModel.detail);
 
 
+  const resetMetadata = async () => {
+    const resp = await instanceService.detail(param.id);
+    if (resp.status === 200) {
+      InstanceModel.detail = resp?.result || [];
+    }
+  };
+
   const removeItem = async (record: MetadataItem) => {
   const removeItem = async (record: MetadataItem) => {
     const removeDB = () => {
     const removeDB = () => {
       return DB.getDB().table(`${type}`).delete(record.id!);
       return DB.getDB().table(`${type}`).delete(record.id!);
@@ -50,6 +58,7 @@ const BaseMetadata = observer((props: Props) => {
       Store.set(SystemConst.REFRESH_METADATA_TABLE, true);
       Store.set(SystemConst.REFRESH_METADATA_TABLE, true);
       MetadataModel.edit = false;
       MetadataModel.edit = false;
       MetadataModel.item = {};
       MetadataModel.item = {};
+      resetMetadata();
     } else {
     } else {
       onlyMessage('操作失败!', 'error');
       onlyMessage('操作失败!', 'error');
     }
     }

+ 4 - 0
src/pages/device/components/Metadata/index.tsx

@@ -36,6 +36,10 @@ const Metadata = observer((props: Props) => {
   const resetMetadata = async () => {
   const resetMetadata = async () => {
     const resp = await instanceService.deleteMetadata(params.id);
     const resp = await instanceService.deleteMetadata(params.id);
     if (resp.status === 200) {
     if (resp.status === 200) {
+      const res = await instanceService.detail(params.id);
+      if (res.status === 200) {
+        InstanceModel.detail = res?.result || [];
+      }
       onlyMessage('操作成功');
       onlyMessage('操作成功');
       Store.set(SystemConst.REFRESH_DEVICE, true);
       Store.set(SystemConst.REFRESH_DEVICE, true);
       setTimeout(() => {
       setTimeout(() => {

+ 2 - 2
src/pages/device/data.ts

@@ -121,8 +121,8 @@ export const DateTypeList = [
     value: 'yyyy-MM-dd',
     value: 'yyyy-MM-dd',
   },
   },
   {
   {
-    label: 'yyyy-MM-dd HH:mm:ss',
-    value: 'yyyy-MM-dd HH:mm:ss',
+    label: 'yyyy-MM-DD HH:mm:ss',
+    value: 'yyyy-MM-DD HH:mm:ss',
   },
   },
   // {
   // {
   //   label: 'yyyy-MM-dd HH:mm:ss EE',
   //   label: 'yyyy-MM-dd HH:mm:ss EE',

+ 1 - 1
src/pages/edge/Resource/Issue/Result.tsx

@@ -29,7 +29,7 @@ const Publish = (props: Props) => {
         targetId: props.data.targetId,
         targetId: props.data.targetId,
         targetType: props.data.targetType,
         targetType: props.data.targetType,
         category: props.data.category,
         category: props.data.category,
-        metadata: encodeURIComponent(props.data?.metadata || ''),
+        metadata: JSON.parse(props.data?.metadata || '{}'),
       }),
       }),
     };
     };
     const url = new URLSearchParams();
     const url = new URLSearchParams();

+ 13 - 13
src/pages/iot-card/CardManagement/SaveModal.tsx

@@ -83,18 +83,7 @@ const Save = (props: SaveType) => {
           name={'id'}
           name={'id'}
           required
           required
           rules={[
           rules={[
-            { required: true, message: '请输入卡号' },
-            { max: 64, message: '最多可输入64个字符' },
-          ]}
-        >
-          <Input placeholder={'请输入卡号'} disabled={props.type === 'edit'} />
-        </Form.Item>
-        <Form.Item
-          label={'ICCID'}
-          name={'iccId'}
-          required
-          rules={[
-            { required: true, message: '请输入ICCID' },
+            // { required: true, message: '请输入卡号' },
             { max: 64, message: '最多可输入64个字符' },
             { max: 64, message: '最多可输入64个字符' },
             () => ({
             () => ({
               async validator(_, value) {
               async validator(_, value) {
@@ -106,12 +95,23 @@ const Save = (props: SaveType) => {
                     return Promise.reject(new Error(`${validateId}`));
                     return Promise.reject(new Error(`${validateId}`));
                   }
                   }
                 } else {
                 } else {
-                  return Promise.reject(new Error('请输入输入正确的ICCID'));
+                  return Promise.reject(new Error('请输入卡号'));
                 }
                 }
               },
               },
             }),
             }),
           ]}
           ]}
         >
         >
+          <Input placeholder={'请输入卡号'} disabled={props.type === 'edit'} />
+        </Form.Item>
+        <Form.Item
+          label={'ICCID'}
+          name={'iccId'}
+          required
+          rules={[
+            { required: true, message: '请输入ICCID' },
+            { max: 64, message: '最多可输入64个字符' },
+          ]}
+        >
           <Input placeholder={'请输入ICCID'} disabled={props.type === 'edit'} />
           <Input placeholder={'请输入ICCID'} disabled={props.type === 'edit'} />
         </Form.Item>
         </Form.Item>
         <Form.Item
         <Form.Item

+ 1 - 1
src/pages/link/Channel/Modbus/savePoint.tsx

@@ -102,7 +102,7 @@ const SavePoint = (props: Props) => {
                     if (value !== 0 || /(^[1-9]\d*$)/.test(value)) {
                     if (value !== 0 || /(^[1-9]\d*$)/.test(value)) {
                       return Promise.resolve();
                       return Promise.resolve();
                     }
                     }
-                    return Promise.reject(new Error('请输入非0正整数'));
+                    return Promise.reject(new Error('请输入正整数'));
                   },
                   },
                 }),
                 }),
               ]}
               ]}

+ 1 - 1
src/pages/link/DashBoard/index.tsx

@@ -260,7 +260,7 @@ export default () => {
         type: 'value',
         type: 'value',
       },
       },
       grid: {
       grid: {
-        left: 50,
+        left: 60,
         right: 0,
         right: 0,
         top: 10,
         top: 10,
         bottom: 20,
         bottom: 20,

+ 45 - 43
src/pages/media/Device/Channel/index.tsx

@@ -8,7 +8,7 @@ import { ChannelItem } from '@/pages/media/Device/Channel/typings';
 import { useHistory, useIntl, useLocation } from 'umi';
 import { useHistory, useIntl, useLocation } from 'umi';
 import { AIcon, BadgeStatus } from '@/components';
 import { AIcon, BadgeStatus } from '@/components';
 import { StatusColorEnum } from '@/components/BadgeStatus';
 import { StatusColorEnum } from '@/components/BadgeStatus';
-import { Button, message, Popconfirm, Tooltip } from 'antd';
+import { Button, message, Tooltip } from 'antd';
 import {
 import {
   DeleteOutlined,
   DeleteOutlined,
   EditOutlined,
   EditOutlined,
@@ -20,11 +20,11 @@ import Save from './Save';
 import Service from './service';
 import Service from './service';
 import { ProviderValue } from '../index';
 import { ProviderValue } from '../index';
 import Live from './Live';
 import Live from './Live';
-import { getButtonPermission, getMenuPathByCode, MENUS_CODE } from '@/utils/menu';
+import { getMenuPathByCode, MENUS_CODE } from '@/utils/menu';
 import Tree from './Tree';
 import Tree from './Tree';
 import { useDomFullHeight } from '@/hooks';
 import { useDomFullHeight } from '@/hooks';
 import classnames from 'classnames';
 import classnames from 'classnames';
-
+import { PermissionButton } from '@/components';
 export const service = new Service('media');
 export const service = new Service('media');
 
 
 export default () => {
 export default () => {
@@ -39,7 +39,8 @@ export default () => {
   const [type, setType] = useState('');
   const [type, setType] = useState('');
   const { minHeight } = useDomFullHeight(`.channelDevice`, 24);
   const { minHeight } = useDomFullHeight(`.channelDevice`, 24);
   const [show, setShow] = useState(false);
   const [show, setShow] = useState(false);
-
+  const permissionCode = 'media/Device';
+  const { permission } = PermissionButton.usePermission(permissionCode);
   const location = useLocation();
   const location = useLocation();
   const history = useHistory();
   const history = useHistory();
 
 
@@ -140,25 +141,24 @@ export default () => {
       align: 'left',
       align: 'left',
       width: 160,
       width: 160,
       render: (_, record) => [
       render: (_, record) => [
-        <Tooltip
-          key="edit"
-          title={intl.formatMessage({
-            id: 'pages.data.option.edit',
-            defaultMessage: '编辑',
-          })}
+        <PermissionButton
+          key="editable"
+          tooltip={{
+            title: intl.formatMessage({
+              id: 'pages.data.option.edit',
+              defaultMessage: '编辑',
+            }),
+          }}
+          isPermission={permission.update}
+          style={{ padding: 0 }}
+          type="link"
+          onClick={() => {
+            setCurrent(record);
+            setVisible(true);
+          }}
         >
         >
-          <Button
-            style={{ padding: 0 }}
-            type="link"
-            onClick={() => {
-              setCurrent(record);
-              setVisible(true);
-            }}
-            disabled={getButtonPermission('media/Device', 'update')}
-          >
-            <EditOutlined />
-          </Button>
-        </Tooltip>,
+          <EditOutlined />
+        </PermissionButton>,
         <Tooltip key={'live'} title={'播放'}>
         <Tooltip key={'live'} title={'播放'}>
           <a
           <a
             onClick={() => {
             onClick={() => {
@@ -185,27 +185,29 @@ export default () => {
           </a>
           </a>
         </Tooltip>,
         </Tooltip>,
         type === ProviderValue.FIXED ? (
         type === ProviderValue.FIXED ? (
-          <Popconfirm
+          <PermissionButton
+            type="link"
             key="delete"
             key="delete"
-            title={intl.formatMessage({
-              id: 'page.table.isDelete',
-              defaultMessage: '是否删除?',
-            })}
-            onConfirm={async () => {
-              deleteItem(record.id);
+            style={{ padding: 0 }}
+            popConfirm={{
+              title: intl.formatMessage({
+                id: 'pages.system.role.option.delete',
+                defaultMessage: '确定要删除吗',
+              }),
+              onConfirm: () => {
+                deleteItem(record.id);
+              },
+            }}
+            tooltip={{
+              title: intl.formatMessage({
+                id: 'pages.data.option.delete',
+                defaultMessage: '删除',
+              }),
             }}
             }}
-            disabled={getButtonPermission('media/Device', 'delete')}
+            isPermission={permission.delete || permission.update}
           >
           >
-            <Tooltip title="删除">
-              <Button
-                type={'link'}
-                style={{ padding: 0 }}
-                disabled={getButtonPermission('media/Device', 'delete')}
-              >
-                <DeleteOutlined />
-              </Button>
-            </Tooltip>
-          </Popconfirm>
+            <DeleteOutlined />
+          </PermissionButton>
         ) : null,
         ) : null,
       ],
       ],
     },
     },
@@ -289,13 +291,13 @@ export default () => {
                   </Button>
                   </Button>
                 </Tooltip>
                 </Tooltip>
               ) : (
               ) : (
-                <Button
+                <PermissionButton
                   onClick={() => {
                   onClick={() => {
                     setCurrent(undefined);
                     setCurrent(undefined);
                     setVisible(true);
                     setVisible(true);
                   }}
                   }}
+                  isPermission={permission.add || permission.update}
                   key="button"
                   key="button"
-                  disabled={getButtonPermission('media/Device', 'add')}
                   icon={<PlusOutlined />}
                   icon={<PlusOutlined />}
                   type="primary"
                   type="primary"
                 >
                 >
@@ -303,7 +305,7 @@ export default () => {
                     id: 'pages.data.option.add',
                     id: 'pages.data.option.add',
                     defaultMessage: '新增',
                     defaultMessage: '新增',
                   })}
                   })}
-                </Button>
+                </PermissionButton>
               ),
               ),
             ]}
             ]}
           />
           />

+ 1 - 1
src/pages/notice/Config/Detail/index.tsx

@@ -201,7 +201,7 @@ const Detail = observer(() => {
           dependencies: ['type'],
           dependencies: ['type'],
           fulfill: {
           fulfill: {
             state: {
             state: {
-              visible: '{{!!$deps[0] && $deps[0] !== "email"}}',
+              hidden: '{{!(!!$deps[0] && $deps[0] !== "email")}}',
             },
             },
           },
           },
         },
         },

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

@@ -27,29 +27,38 @@ export default observer((props: Props) => {
       label: '功能调用',
       label: '功能调用',
       value: 'INVOKE_FUNCTION',
       value: 'INVOKE_FUNCTION',
       image: require('/public/images/scene/invoke-function.png'),
       image: require('/public/images/scene/invoke-function.png'),
-      tip: '-',
+      tip: '',
     },
     },
     {
     {
       label: '读取属性',
       label: '读取属性',
       value: 'READ_PROPERTY',
       value: 'READ_PROPERTY',
       image: require('/public/images/scene/read-property.png'),
       image: require('/public/images/scene/read-property.png'),
-      tip: '-',
+      tip: '',
     },
     },
     {
     {
       label: '设置属性',
       label: '设置属性',
       value: 'WRITE_PROPERTY',
       value: 'WRITE_PROPERTY',
       image: require('/public/images/scene/write-property.png'),
       image: require('/public/images/scene/write-property.png'),
-      tip: '-',
+      tip: '',
     },
     },
   ];
   ];
 
 
   useEffect(() => {
   useEffect(() => {
+    // console.log('-----------',DeviceModel.deviceDetail)
     if (DeviceModel.productDetail) {
     if (DeviceModel.productDetail) {
-      const metadata = JSON.parse(DeviceModel.productDetail?.metadata || '{}');
-      setProperties(metadata.properties);
-      setFunctions(metadata.functions);
+      if (DeviceModel.selector === 'fixed') {
+        const metadata = JSON.parse(
+          DeviceModel.deviceDetail?.metadata || DeviceModel.deviceDetail?.deriveMetadata || '{}',
+        );
+        setProperties(metadata.properties);
+        setFunctions(metadata.functions);
+      } else {
+        const metadata = JSON.parse(DeviceModel.productDetail?.metadata || '{}');
+        setProperties(metadata.properties);
+        setFunctions(metadata.functions);
+      }
     }
     }
-  }, [DeviceModel.productDetail]);
+  }, [DeviceModel.productDetail, DeviceModel.deviceDetail]);
 
 
   useEffect(() => {
   useEffect(() => {
     if (functionId && functions.length !== 0) {
     if (functionId && functions.length !== 0) {
@@ -199,11 +208,12 @@ export default observer((props: Props) => {
               }}
               }}
               onChange={(value, text, valueLable) => {
               onChange={(value, text, valueLable) => {
                 const item = value[Object.keys(value)?.[0]]?.value;
                 const item = value[Object.keys(value)?.[0]]?.value;
+                console.log(item);
                 DeviceModel.propertiesName = text;
                 DeviceModel.propertiesName = text;
                 if (valueLable) {
                 if (valueLable) {
                   DeviceModel.propertiesValue = valueLable;
                   DeviceModel.propertiesValue = valueLable;
                 } else {
                 } else {
-                  DeviceModel.propertiesValue = `${item}`;
+                  DeviceModel.propertiesValue = item;
                 }
                 }
               }}
               }}
               onRest={(value: any) => {
               onRest={(value: any) => {

+ 12 - 2
src/pages/rule-engine/Scene/Save/action/DeviceOutput/device/Tag.tsx

@@ -7,7 +7,7 @@ import './index.less';
 interface TagModalProps {
 interface TagModalProps {
   tagData: any[];
   tagData: any[];
   value?: any[];
   value?: any[];
-  onChange?: (value: any[]) => void;
+  onChange?: (value: any[], OptionTag?: any[]) => void;
   id?: string;
   id?: string;
 }
 }
 
 
@@ -39,8 +39,18 @@ export default (props: TagModalProps) => {
           value: item.value,
           value: item.value,
         };
         };
       });
       });
+    const arr = tagList
+      .filter((item) => !!item.value)
+      .map((item: any) => {
+        return {
+          column: item.name,
+          type: item.type,
+          value: item.value,
+        };
+      });
+    console.log('----taglist---', tagList);
     if (props.onChange) {
     if (props.onChange) {
-      props.onChange([{ value: newValue, name: '标签' }]);
+      props.onChange([{ value: newValue, name: '标签' }], arr);
     }
     }
   };
   };
 
 

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

@@ -408,13 +408,16 @@ export default observer((props: Props) => {
           <Form.Item name="selectorValues" rules={[{ required: true, message: '请选择标签' }]}>
           <Form.Item name="selectorValues" rules={[{ required: true, message: '请选择标签' }]}>
             <Tag
             <Tag
               tagData={tagList}
               tagData={tagList}
-              onChange={(value) => {
-                console.log(value);
+              onChange={(value, arr) => {
+                console.log(value, arr);
                 if (value) {
                 if (value) {
                   DeviceModel.deviceId = 'deviceId';
                   DeviceModel.deviceId = 'deviceId';
                   DeviceModel.source = 'fixed';
                   DeviceModel.source = 'fixed';
                   DeviceModel.selectorValues = value;
                   DeviceModel.selectorValues = value;
                 }
                 }
+                if (arr) {
+                  DeviceModel.tagList = arr;
+                }
               }}
               }}
             />
             />
           </Form.Item>
           </Form.Item>
@@ -442,7 +445,6 @@ export default observer((props: Props) => {
   };
   };
 
 
   useEffect(() => {
   useEffect(() => {
-    console.log(DeviceModel.selectorValues, '--------');
     if (form) {
     if (form) {
       form.setFieldsValue({ selector: DeviceModel.selector });
       form.setFieldsValue({ selector: DeviceModel.selector });
       if (DeviceModel.selectorValues) {
       if (DeviceModel.selectorValues) {

+ 7 - 4
src/pages/rule-engine/Scene/Save/action/DeviceOutput/index.tsx

@@ -71,7 +71,7 @@ export default observer((props: Props) => {
       (DeviceModel.current === 0 && DeviceModel.productId) ||
       (DeviceModel.current === 0 && DeviceModel.productId) ||
       (DeviceModel.current === 1 && (DeviceModel.deviceId || DeviceModel.selector === 'tag'))
       (DeviceModel.current === 1 && (DeviceModel.deviceId || DeviceModel.selector === 'tag'))
     ) {
     ) {
-      if (DeviceModel.selector === 'tag') {
+      if (DeviceModel.selector === 'tag' && DeviceModel.current === 1) {
         const value = await tagFormRef.current?.validateFields();
         const value = await tagFormRef.current?.validateFields();
         if (value) {
         if (value) {
           return (DeviceModel.current += 1);
           return (DeviceModel.current += 1);
@@ -103,7 +103,7 @@ export default observer((props: Props) => {
   const save = async () => {
   const save = async () => {
     const value = await formRef.current?.validateFields();
     const value = await formRef.current?.validateFields();
 
 
-    const item = {
+    const item: any = {
       selector: DeviceModel.selector,
       selector: DeviceModel.selector,
       source: DeviceModel.source,
       source: DeviceModel.source,
       selectorValues: DeviceModel.selectorValues,
       selectorValues: DeviceModel.selectorValues,
@@ -114,6 +114,9 @@ export default observer((props: Props) => {
     if (DeviceModel.selector === 'variable') {
     if (DeviceModel.selector === 'variable') {
       item.selector = 'fixed';
       item.selector = 'fixed';
     }
     }
+    if (DeviceModel.selector === 'relation') {
+      item.upperKey = 'deviceId';
+    }
     // console.log(item, value);
     // console.log(item, value);
 
 
     const _options: any = {
     const _options: any = {
@@ -154,7 +157,7 @@ export default observer((props: Props) => {
       }
       }
     }
     }
     if (_options.selector === 'tag') {
     if (_options.selector === 'tag') {
-      _options.taglist = DeviceModel.selectorValues?.[0]?.value.map((it: any) => ({
+      _options.taglist = DeviceModel.tagList.map((it) => ({
         name: it.column || it.name,
         name: it.column || it.name,
         type: it.type ? (it.type === 'and' ? '并且' : '或者') : '',
         type: it.type ? (it.type === 'and' ? '并且' : '或者') : '',
         value: it.value,
         value: it.value,
@@ -163,7 +166,7 @@ export default observer((props: Props) => {
     if (_options.selector === 'variable') {
     if (_options.selector === 'variable') {
       _options.name = DeviceModel.selectorValues?.[0]?.name;
       _options.name = DeviceModel.selectorValues?.[0]?.name;
     }
     }
-    // console.log("----------",item,_options)
+    console.log('----------', item, _options, DeviceModel.propertiesValue);
     props.save(item, _options);
     props.save(item, _options);
     init();
     init();
   };
   };

+ 2 - 0
src/pages/rule-engine/Scene/Save/action/DeviceOutput/model.ts

@@ -26,6 +26,7 @@ type ModelType = {
   propertiesValue: string | any;
   propertiesValue: string | any;
   columns: string[];
   columns: string[];
   actionName: string;
   actionName: string;
+  tagList: any[];
 };
 };
 
 
 const DeviceModel = model<ModelType>({
 const DeviceModel = model<ModelType>({
@@ -47,6 +48,7 @@ const DeviceModel = model<ModelType>({
   propertiesValue: '',
   propertiesValue: '',
   columns: [],
   columns: [],
   actionName: '',
   actionName: '',
+  tagList: [],
 });
 });
 
 
 export default DeviceModel;
 export default DeviceModel;

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

@@ -249,11 +249,10 @@ export default (props: ItemProps) => {
             {data.options?.taglist?.map((item: any) => (
             {data.options?.taglist?.map((item: any) => (
               <span>
               <span>
                 {item.type}
                 {item.type}
-                {item.name}
-                {item.value}
+                {item.name}为{item.value}
               </span>
               </span>
             ))}
             ))}
-            {data?.options?.productName}
+            {data?.options?.productName}
             {data?.options?.properties}
             {data?.options?.properties}
           </div>
           </div>
         );
         );

+ 50 - 0
src/pages/rule-engine/Scene/Save/action/device/ObjInput.tsx

@@ -0,0 +1,50 @@
+import { EditOutlined } from '@ant-design/icons';
+import { Input } from 'antd';
+import { useState } from 'react';
+import ObjModel from '../DeviceOutput/actions/ObjModel';
+
+interface Props {
+  value: any;
+  onChange?: (data: any) => void;
+}
+
+export default (props: Props) => {
+  const [objVisiable, setObjVisable] = useState<boolean>(false);
+  const [value, setValue] = useState<any>(JSON.stringify(props.value) || undefined);
+  return (
+    <>
+      <Input
+        value={value}
+        style={{ width: '100%', textAlign: 'left' }}
+        readOnly
+        onClick={() => {
+          setObjVisable(true);
+        }}
+        addonAfter={
+          <EditOutlined
+            onClick={() => {
+              setObjVisable(true);
+            }}
+          />
+        }
+        placeholder={'请选择'}
+      />
+      {objVisiable && (
+        <ObjModel
+          value={value}
+          close={() => {
+            setObjVisable(false);
+          }}
+          ok={(param) => {
+            // console.log('------', param);
+            if (props.onChange) {
+              props.onChange(JSON.parse(param));
+            }
+            setValue(param);
+            setObjVisable(false);
+          }}
+        />
+      )}
+    </>
+  );
+};

+ 59 - 0
src/pages/rule-engine/Scene/Save/action/device/TypeTime.tsx

@@ -0,0 +1,59 @@
+import { TimePicker, DatePicker } from 'antd';
+import type { TimePickerProps } from 'antd/lib/time-picker';
+import moment from 'moment';
+import { useEffect, useState } from 'react';
+
+interface DatePickerFormat extends Omit<TimePickerProps, 'onChange'> {
+  onChange?: (dateString: string, date: moment.Moment | null) => void;
+}
+type Props = DatePickerFormat & {
+  type?: string;
+};
+
+export default (props: Props) => {
+  const [myValue, setMyValue] = useState<any>(props.value || undefined);
+
+  useEffect(() => {
+    setMyValue(props.value);
+    console.log('moment', props.value);
+  }, [props.value]);
+
+  return (
+    <div>
+      {props.type === 'time' ? (
+        <div>
+          <TimePicker
+            // {...props}
+            // value={myValue}
+            value={myValue ? moment(myValue, 'HH:mm:ss') : null}
+            onChange={(value, timeString) => {
+              setMyValue(value);
+              props.onChange?.(timeString, value);
+            }}
+            // @ts-ignore
+            getPopupContainer={(trigger) => {
+              return trigger && trigger?.parentNode ? trigger.parentNode : document.body;
+            }}
+          />
+        </div>
+      ) : (
+        <div>
+          {/* @ts-ignore */}
+          <DatePicker
+            showTime
+            // @ts-ignore
+            getPopupContainer={(trigger) => {
+              return trigger && trigger?.parentNode ? trigger.parentNode : document.body;
+            }}
+            // value={myValue}
+            value={myValue ? moment(myValue, 'yyyy-MM-DD HH:mm:ss') : ''}
+            onChange={(value, timeString) => {
+              setMyValue(value);
+              props.onChange?.(timeString, value);
+            }}
+          />
+        </div>
+      )}
+    </div>
+  );
+};

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

@@ -4,7 +4,9 @@ import { Input, InputNumber, Select } from 'antd';
 import React, { useEffect, useRef, useState } from 'react';
 import React, { useEffect, useRef, useState } from 'react';
 import type { ProFormInstance } from '@ant-design/pro-form';
 import type { ProFormInstance } from '@ant-design/pro-form';
 import ProForm from '@ant-design/pro-form';
 import ProForm from '@ant-design/pro-form';
-import { DatePickerFormat } from '@/pages/rule-engine/Scene/Save/components';
+import ObjModel from '../DeviceOutput/actions/ObjModel';
+import ObjInput from './ObjInput';
+import TypeTime from './TypeTime';
 
 
 type FunctionTableDataType = {
 type FunctionTableDataType = {
   id: string;
   id: string;
@@ -23,6 +25,8 @@ interface FunctionCallProps {
 export default (props: FunctionCallProps) => {
 export default (props: FunctionCallProps) => {
   const [editableKeys, setEditableRowKeys] = useState<React.Key[]>([]);
   const [editableKeys, setEditableRowKeys] = useState<React.Key[]>([]);
   const formRef = useRef<ProFormInstance<any>>();
   const formRef = useRef<ProFormInstance<any>>();
+  const [objVisiable, setObjVisable] = useState<boolean>(false);
+  const [value, setValue] = useState<any>();
 
 
   useEffect(() => {
   useEffect(() => {
     if (props.functionData && props.functionData.length) {
     if (props.functionData && props.functionData.length) {
@@ -59,7 +63,7 @@ export default (props: FunctionCallProps) => {
     }
     }
   }, [props.productId]);
   }, [props.productId]);
 
 
-  const getItemNode = (record: any, config: any) => {
+  const getItemNode = (record: any) => {
     const type = record.type;
     const type = record.type;
     const name = record.name;
     const name = record.name;
 
 
@@ -99,13 +103,16 @@ export default (props: FunctionCallProps) => {
         );
         );
       case 'date':
       case 'date':
         return (
         return (
-          <DatePickerFormat
-            {...config}
-            value={record.value}
-            format={record.format || 'YYYY-MM-DD HH:mm:ss'}
-            style={{ width: '100%' }}
-          />
+          // <DatePickerFormat
+          //   {...config}
+          //   value={record.value}
+          //   format={record.format || 'YYYY-MM-DD HH:mm:ss'}
+          //   style={{ width: '100%' }}
+          // />
+          <TypeTime type={'date'} value={record.value} />
         );
         );
+      case 'object':
+        return <ObjInput value={record.value} />;
       default:
       default:
         return <Input value={record.value} placeholder={'请输入' + name} />;
         return <Input value={record.value} placeholder={'请输入' + name} />;
     }
     }
@@ -130,11 +137,15 @@ export default (props: FunctionCallProps) => {
       align: 'center',
       align: 'center',
       width: 260,
       width: 260,
       renderFormItem: (_, row) => {
       renderFormItem: (_, row) => {
-        return getItemNode(row.record, row);
+        return getItemNode(row.record);
       },
       },
     },
     },
   ];
   ];
 
 
+  useEffect(() => {
+    console.log(value);
+  }, [value]);
+
   return (
   return (
     <ProForm<{ table: FunctionTableDataType[] }>
     <ProForm<{ table: FunctionTableDataType[] }>
       formRef={formRef}
       formRef={formRef}
@@ -142,6 +153,7 @@ export default (props: FunctionCallProps) => {
       submitter={false}
       submitter={false}
       onValuesChange={() => {
       onValuesChange={() => {
         const values = formRef.current?.getFieldsValue();
         const values = formRef.current?.getFieldsValue();
+        console.log('---values---', values);
         if (props.onChange) {
         if (props.onChange) {
           props.onChange(
           props.onChange(
             values.table.map((item: any) => ({
             values.table.map((item: any) => ({
@@ -164,6 +176,19 @@ export default (props: FunctionCallProps) => {
           onChange: setEditableRowKeys,
           onChange: setEditableRowKeys,
         }}
         }}
       />
       />
+      {objVisiable && (
+        <ObjModel
+          value={value}
+          close={() => {
+            setObjVisable(false);
+          }}
+          ok={(param) => {
+            console.log('------', param);
+            setValue(param);
+            setObjVisable(false);
+          }}
+        />
+      )}
     </ProForm>
     </ProForm>
   );
   );
 };
 };

+ 16 - 1
src/pages/rule-engine/Scene/Save/device/addModel.tsx

@@ -4,7 +4,7 @@ import { observable } from '@formily/reactive';
 import { useEffect, useRef, useState } from 'react';
 import { useEffect, useRef, useState } from 'react';
 import { onlyMessage } from '@/utils/util';
 import { onlyMessage } from '@/utils/util';
 import type { TriggerDevice, TriggerDeviceOptions } from '@/pages/rule-engine/Scene/typings';
 import type { TriggerDevice, TriggerDeviceOptions } from '@/pages/rule-engine/Scene/typings';
-import Product from './product';
+import Product, { handleMetadata } from './product';
 import Device from './device';
 import Device from './device';
 import Type from './type';
 import Type from './type';
 import { timeUnitEnum } from '../components/TimingTrigger';
 import { timeUnitEnum } from '../components/TimingTrigger';
@@ -12,6 +12,7 @@ import { Store } from 'jetlinks-store';
 import { FormModel } from '@/pages/rule-engine/Scene/Save';
 import { FormModel } from '@/pages/rule-engine/Scene/Save';
 import { isEqual } from 'lodash';
 import { isEqual } from 'lodash';
 import { continuousValue } from '@/pages/rule-engine/Scene/Save/timer/TimerTrigger';
 import { continuousValue } from '@/pages/rule-engine/Scene/Save/timer/TimerTrigger';
+import { service as api } from '@/pages/device/Instance/index';
 
 
 interface AddProps {
 interface AddProps {
   options?: any;
   options?: any;
@@ -227,6 +228,7 @@ export default observer((props: AddProps) => {
       }
       }
     } else if (TriggerDeviceModel.stepNumber === 1) {
     } else if (TriggerDeviceModel.stepNumber === 1) {
       if (TriggerDeviceModel.selector === 'fixed' && !TriggerDeviceModel.selectorValues?.length) {
       if (TriggerDeviceModel.selector === 'fixed' && !TriggerDeviceModel.selectorValues?.length) {
+        // handleMetadata(TriggerDeviceModel.productDetail?.metadata);
         onlyMessage('请选择设备', 'error');
         onlyMessage('请选择设备', 'error');
         return;
         return;
       } else if (
       } else if (
@@ -236,6 +238,18 @@ export default observer((props: AddProps) => {
         onlyMessage('请选择部门', 'error');
         onlyMessage('请选择部门', 'error');
         return;
         return;
       }
       }
+      if (
+        TriggerDeviceModel.selector === 'fixed' &&
+        TriggerDeviceModel.selectorValues?.length === 1
+      ) {
+        console.log('---TriggerDeviceModel.selectorValues----', TriggerDeviceModel.selectorValues);
+        const res = await api.detail(TriggerDeviceModel.selectorValues?.[0]?.value);
+        if (res.status === 200) {
+          // console.log(res.result.metadata)
+          handleMetadata(res.result.metadata);
+        }
+      }
+
       TriggerDeviceModel.stepNumber = 2;
       TriggerDeviceModel.stepNumber = 2;
     } else if (TriggerDeviceModel.stepNumber === 2) {
     } else if (TriggerDeviceModel.stepNumber === 2) {
       // TODO 验证类型数据
       // TODO 验证类型数据
@@ -256,6 +270,7 @@ export default observer((props: AddProps) => {
         Store.set('TriggerDeviceModel', {
         Store.set('TriggerDeviceModel', {
           update: !isUpdate,
           update: !isUpdate,
         });
         });
+        console.log('-----------', operationData);
         props.onSave?.(saveData, _options);
         props.onSave?.(saveData, _options);
       }
       }
     }
     }

+ 7 - 20
src/pages/rule-engine/Scene/index.tsx

@@ -1,5 +1,5 @@
 import { PageContainer } from '@ant-design/pro-layout';
 import { PageContainer } from '@ant-design/pro-layout';
-import React, { useEffect, useRef, useState } from 'react';
+import React, { useRef, useState } from 'react';
 import type { ActionType, ProColumns } from '@jetlinks/pro-table';
 import type { ActionType, ProColumns } from '@jetlinks/pro-table';
 import type { SceneItem } from '@/pages/rule-engine/Scene/typings';
 import type { SceneItem } from '@/pages/rule-engine/Scene/typings';
 import {
 import {
@@ -31,8 +31,6 @@ const Scene = () => {
   const [visible, setVisible] = useState<boolean>(false);
   const [visible, setVisible] = useState<boolean>(false);
   const [current, setCurrent] = useState<Partial<SceneItem>>({});
   const [current, setCurrent] = useState<Partial<SceneItem>>({});
   const history = useHistory();
   const history = useHistory();
-  const [loading, setLoading] = useState<boolean>(true);
-  const [title, setTitle] = useState<string>('');
 
 
   const deleteById = async (id: string) => {
   const deleteById = async (id: string) => {
     // const alarmResp = await service.sceneByAlarm(id);
     // const alarmResp = await service.sceneByAlarm(id);
@@ -46,13 +44,6 @@ const Scene = () => {
     // }
     // }
   };
   };
 
 
-  useEffect(() => {
-    console.log('----------', title, loading);
-    if (title) {
-      setLoading(false);
-    }
-  }, [title]);
-
   const Tools = (record: SceneItem): React.ReactNode[] => {
   const Tools = (record: SceneItem): React.ReactNode[] => {
     return [
     return [
       <PermissionButton
       <PermissionButton
@@ -157,14 +148,6 @@ const Scene = () => {
         style={{ padding: 0 }}
         style={{ padding: 0 }}
         isPermission={permission.delete}
         isPermission={permission.delete}
         disabled={record.state.value === 'started'}
         disabled={record.state.value === 'started'}
-        onClick={async () => {
-          const res = await service.sceneByAlarm(record.id);
-          if (res.status === 200 && res.result.data.length !== 0) {
-            setTitle('该场景已绑定告警,确定删除?');
-          } else {
-            setTitle('确定删除?');
-          }
-        }}
         popConfirm={{
         popConfirm={{
           // title: loading ? <Spin /> : title,
           // title: loading ? <Spin /> : title,
           // okButtonProps: {
           // okButtonProps: {
@@ -298,8 +281,12 @@ const Scene = () => {
           <SceneCard
           <SceneCard
             {...record}
             {...record}
             onClick={() => {
             onClick={() => {
-              const url = getMenuPathByCode('rule-engine/Scene/Save');
-              history.push(`${url}?triggerType=${record.trigger?.type}&id=${record?.id}`);
+              if (permission.view) {
+                const url = getMenuPathByCode('rule-engine/Scene/Save');
+                history.push(`${url}?triggerType=${record.trigger?.type}&id=${record?.id}`);
+              } else {
+                onlyMessage('暂无权限,请联系管理员', 'error');
+              }
             }}
             }}
             tools={Tools(record)}
             tools={Tools(record)}
           />
           />

+ 20 - 4
src/pages/system/Apply/Save/index.tsx

@@ -573,7 +573,7 @@ const Save = () => {
         gridSpan: 2,
         gridSpan: 2,
         layout: 'vertical',
         layout: 'vertical',
         labelAlign: 'left',
         labelAlign: 'left',
-        tooltip: '限制应用程序对用户账号的访问',
+        tooltip: '限制用户访问应用程序的权限',
       },
       },
       'x-component': 'Input',
       'x-component': 'Input',
       'x-component-props': {
       'x-component-props': {
@@ -818,6 +818,22 @@ const Save = () => {
         placeholder: '请输入授权地址',
         placeholder: '请输入授权地址',
       },
       },
     },
     },
+    'apiClient.authConfig.oauth2.tokenUrl': {
+      type: 'string',
+      title: 'token地址',
+      'x-decorator': 'FormItem',
+      'x-decorator-props': {
+        gridSpan: 2,
+        layout: 'vertical',
+        labelAlign: 'left',
+        tooltip: '设置token令牌的地址',
+      },
+      required: true,
+      'x-component': 'Input',
+      'x-component-props': {
+        placeholder: '请输入token地址',
+      },
+    },
     'apiClient.authConfig.oauth2.redirectUri': {
     'apiClient.authConfig.oauth2.redirectUri': {
       type: 'string',
       type: 'string',
       title: '回调地址',
       title: '回调地址',
@@ -1238,7 +1254,7 @@ const Save = () => {
                   gridSpan: 2,
                   gridSpan: 2,
                   layout: 'vertical',
                   layout: 'vertical',
                   labelAlign: 'left',
                   labelAlign: 'left',
-                  tooltip: '授权知道后跳转到具体页面的回调地址',
+                  tooltip: '授权完成后跳转到具体页面的回调地址',
                 },
                 },
                 // required: true,
                 // required: true,
                 'x-component': 'Input',
                 'x-component': 'Input',
@@ -1271,7 +1287,7 @@ const Save = () => {
                   gridSpan: 2,
                   gridSpan: 2,
                   layout: 'vertical',
                   layout: 'vertical',
                   labelAlign: 'left',
                   labelAlign: 'left',
-                  tooltip: '为第三方应用用户分配角色,根据绑定的角色,进行系统菜单赋权',
+                  tooltip: '为应用用户分配角色,根据绑定的角色,进行系统菜单赋权',
                   addonAfter: (
                   addonAfter: (
                     <PermissionButton
                     <PermissionButton
                       type="link"
                       type="link"
@@ -1319,7 +1335,7 @@ const Save = () => {
                   gridSpan: 2,
                   gridSpan: 2,
                   layout: 'vertical',
                   layout: 'vertical',
                   labelAlign: 'left',
                   labelAlign: 'left',
-                  tooltip: '为第三方应用用户分配所属组织,根据绑定的组织,进行数据隔离',
+                  tooltip: '为应用用户分配所属组织,根据绑定的组织,进行数据隔离',
                   addonAfter: (
                   addonAfter: (
                     <PermissionButton
                     <PermissionButton
                       type="link"
                       type="link"

+ 6 - 2
src/pages/system/DataSource/Management/EditTable.tsx

@@ -1,4 +1,4 @@
-import { randomString } from '@/utils/util';
+import { onlyMessage, randomString } from '@/utils/util';
 import { ArrayTable, Editable, Form, FormItem, Input, NumberPicker, Radio } from '@formily/antd';
 import { ArrayTable, Editable, Form, FormItem, Input, NumberPicker, Radio } from '@formily/antd';
 import { createForm } from '@formily/core';
 import { createForm } from '@formily/core';
 import { createSchemaField } from '@formily/react';
 import { createSchemaField } from '@formily/react';
@@ -267,7 +267,11 @@ const EditTable = (props: Props) => {
               const { old_id, ...extra } = i;
               const { old_id, ...extra } = i;
               return { ...extra };
               return { ...extra };
             });
             });
-            props.onChange({ array: list });
+            if (list.length) {
+              props.onChange({ array: list });
+            } else {
+              onlyMessage('请配置数据源字段', 'error');
+            }
           }}
           }}
         >
         >
           保存
           保存

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

@@ -258,8 +258,8 @@ export default observer((props: { parentId: string }) => {
     Models.unBindKeys = [];
     Models.unBindKeys = [];
     AssetsModel.params = {};
     AssetsModel.params = {};
   };
   };
-  const getSelectedRowsKey = (selectedRows) => {
-    return selectedRows.map((item) => item?.id).filter((item2) => !!item2 !== false);
+  const getSelectedRowsKey = (selectedRows: any) => {
+    return selectedRows.map((item: any) => item?.id).filter((item2: any) => !!item2 !== false);
   };
   };
 
 
   const getData = (params: any, parentId: string) => {
   const getData = (params: any, parentId: string) => {

+ 6 - 1
src/pages/system/Menu/Detail/buttons.tsx

@@ -369,7 +369,12 @@ export default (props: ButtonsProps) => {
                 defaultMessage: '说明',
                 defaultMessage: '说明',
               })}
               })}
             >
             >
-              <Input.TextArea disabled={disabled} placeholder={'请输入说明'} />
+              <Input.TextArea
+                disabled={disabled}
+                placeholder={'请输入说明'}
+                maxLength={200}
+                showCount={true}
+              />
             </Form.Item>
             </Form.Item>
           </Form>
           </Form>
         </Modal>
         </Modal>

+ 21 - 3
src/pages/system/Menu/Setting/baseMenu.ts

@@ -2533,7 +2533,7 @@ export default [
                 name: '编辑',
                 name: '编辑',
                 permissions: [
                 permissions: [
                   {
                   {
-                    permission: 'rule-instance',
+                    permission: 'device-instance',
                     actions: ['query', 'save'],
                     actions: ['query', 'save'],
                   },
                   },
                 ],
                 ],
@@ -2543,8 +2543,26 @@ export default [
                 name: '下发',
                 name: '下发',
                 permissions: [
                 permissions: [
                   {
                   {
-                    permission: 'rule-instance',
-                    actions: ['query', 'save'],
+                    permission: 'edge-operations',
+                    actions: ['invoke'],
+                  },
+                  {
+                    permission: 'device-instance',
+                    actions: ['query'],
+                  },
+                ],
+              },
+              {
+                id: 'view',
+                name: '查看',
+                permissions: [
+                  {
+                    permission: 'edge-operations',
+                    actions: ['invoke'],
+                  },
+                  {
+                    permission: 'device-instance',
+                    actions: ['query'],
                   },
                   },
                 ],
                 ],
               },
               },

+ 6 - 5
src/pages/system/Role/Detail/Permission/Allocate/index.tsx

@@ -37,7 +37,6 @@ const Allocate = (props: Props) => {
         } else {
         } else {
           check = 2;
           check = 2;
         }
         }
-
         return {
         return {
           ...item,
           ...item,
           check,
           check,
@@ -80,14 +79,16 @@ const Allocate = (props: Props) => {
         } else {
         } else {
           check = 2;
           check = 2;
         }
         }
-        setDataSource({
+        const dt = {
           // 重新初始化
           // 重新初始化
           id: 'menu-permission',
           id: 'menu-permission',
           buttons: [],
           buttons: [],
           check,
           check,
           name: '菜单权限',
           name: '菜单权限',
           children,
           children,
-        });
+        };
+        setDataSource(dt);
+        setDataOldSource(dt);
       } else {
       } else {
         setDataSource(props.value);
         setDataSource(props.value);
       }
       }
@@ -98,7 +99,7 @@ const Allocate = (props: Props) => {
     if (Array.isArray(arr) && arr.length > 0) {
     if (Array.isArray(arr) && arr.length > 0) {
       return arr.map((item) => {
       return arr.map((item) => {
         let li: any[] = [];
         let li: any[] = [];
-        if (item?.assetAccesses.length > 0) {
+        if (item?.assetAccesses.length) {
           if (_.map(item.assetAccesses, 'supportId').includes(str)) {
           if (_.map(item.assetAccesses, 'supportId').includes(str)) {
             li = item.assetAccesses.map((i: any) => {
             li = item.assetAccesses.map((i: any) => {
               return {
               return {
@@ -148,8 +149,8 @@ const Allocate = (props: Props) => {
           change={(data: any) => {
           change={(data: any) => {
             setDataSource(data);
             setDataSource(data);
             if (props.onChange) {
             if (props.onChange) {
-              setDataOldSource(data);
               props.onChange(data);
               props.onChange(data);
+              setDataOldSource(data);
             }
             }
           }}
           }}
         />
         />

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

@@ -331,9 +331,10 @@ const Save = (props: Props) => {
             'x-decorator': 'FormItem',
             'x-decorator': 'FormItem',
             'x-component': 'Select',
             'x-component': 'Select',
             'x-component-props': {
             'x-component-props': {
+              disabled: !rolePermission.view,
               mode: 'multiple',
               mode: 'multiple',
               showArrow: true,
               showArrow: true,
-              placeholder: '请选择角色',
+              placeholder: !rolePermission.view ? '暂无权限,请联系管理员' : '请选择角色',
               filterOption: (input: string, option: any) =>
               filterOption: (input: string, option: any) =>
                 option.label.toLowerCase().indexOf(input.toLowerCase()) >= 0,
                 option.label.toLowerCase().indexOf(input.toLowerCase()) >= 0,
             },
             },
@@ -373,9 +374,10 @@ const Save = (props: Props) => {
             'x-decorator': 'FormItem',
             'x-decorator': 'FormItem',
             'x-component': 'TreeSelect',
             'x-component': 'TreeSelect',
             'x-component-props': {
             'x-component-props': {
+              disabled: !deptPermission.view,
               multiple: true,
               multiple: true,
               showArrow: true,
               showArrow: true,
-              placeholder: '请选择组织',
+              placeholder: !deptPermission.view ? '暂无权限,请联系管理员' : '请选择组织',
               showCheckedStrategy: ATreeSelect.SHOW_ALL,
               showCheckedStrategy: ATreeSelect.SHOW_ALL,
               filterOption: (input: string, option: any) =>
               filterOption: (input: string, option: any) =>
                 option.label.toLowerCase().indexOf(input.toLowerCase()) >= 0,
                 option.label.toLowerCase().indexOf(input.toLowerCase()) >= 0,

+ 1 - 1
src/utils/util.ts

@@ -89,7 +89,7 @@ export const getDateFormat = (
  * 扁平化树数组
  * 扁平化树数组
  */
  */
 export const flattenArray: any = (arr: any[]) => {
 export const flattenArray: any = (arr: any[]) => {
-  return arr.reduce((result, item) => {
+  return (arr || []).reduce((result, item) => {
     return result.concat(item, Array.isArray(item.children) ? flattenArray(item.children) : []);
     return result.concat(item, Array.isArray(item.children) ? flattenArray(item.children) : []);
   }, []);
   }, []);
 };
 };