浏览代码

fix: merge next

wzyyy 3 年之前
父节点
当前提交
ddab09a84e
共有 38 个文件被更改,包括 593 次插入262 次删除
  1. 6 4
      src/components/FIndicators/index.tsx
  2. 38 4
      src/components/FMonacoEditor/index.tsx
  3. 11 4
      src/components/FRuleEditor/Advance/index.tsx
  4. 1 0
      src/components/FRuleEditor/Debug/index.tsx
  5. 44 26
      src/components/FRuleEditor/Editor/index.tsx
  6. 2 1
      src/components/FRuleEditor/Operator/index.tsx
  7. 12 8
      src/components/FRuleEditor/index.tsx
  8. 2 1
      src/components/Metadata/EditTable/index.tsx
  9. 53 22
      src/components/NoticeIcon/index.tsx
  10. 1 1
      src/components/ProTableCard/CardItems/Scene/index.tsx
  11. 1 1
      src/components/ProTableCard/CardItems/networkCard.tsx
  12. 1 0
      src/pages/DataCollect/Collector/components/Device/Save/index.tsx
  13. 2 2
      src/pages/DataCollect/Collector/components/Device/index.tsx
  14. 68 56
      src/pages/DataCollect/Collector/components/Point/CollectorCard/WritePoint.tsx
  15. 18 10
      src/pages/DataCollect/Collector/components/Tree/index.tsx
  16. 13 14
      src/pages/account/NotificationRecord/detail/index.tsx
  17. 10 1
      src/pages/account/NotificationRecord/index.tsx
  18. 1 0
      src/pages/account/NotificationRecord/typings.d.ts
  19. 4 9
      src/pages/device/Instance/Detail/Functions/AdvancedMode.tsx
  20. 1 1
      src/pages/device/Instance/Detail/Functions/form.tsx
  21. 9 7
      src/pages/device/Instance/Detail/MetadataLog/Property/index.tsx
  22. 2 2
      src/pages/device/Instance/Detail/Parsing/index.tsx
  23. 56 23
      src/pages/device/Instance/Detail/Running/Property/FileComponent/index.tsx
  24. 0 5
      src/pages/device/components/Metadata/Import/index.tsx
  25. 1 1
      src/pages/link/AccessConfig/service.ts
  26. 13 12
      src/pages/link/Type/Detail/index.tsx
  27. 48 2
      src/pages/media/Device/Save/SaveProduct.tsx
  28. 5 0
      src/pages/media/Device/service.ts
  29. 3 14
      src/pages/rule-engine/Scene/Save/action/DeviceOutput/actions/ObjModel.tsx
  30. 29 1
      src/pages/rule-engine/Scene/Save/action/ListItem/FilterCondition.tsx
  31. 30 3
      src/pages/rule-engine/Scene/Save/components/Buttons/ParamsDropdown.tsx
  32. 6 0
      src/pages/rule-engine/Scene/Save/device/index.tsx
  33. 31 5
      src/pages/rule-engine/Scene/Save/terms/paramsItem.tsx
  34. 6 7
      src/pages/system/Apply/Api/swagger-ui/debugging.tsx
  35. 4 4
      src/pages/system/Apply/Home/index.tsx
  36. 51 0
      src/pages/system/DataSource/Save/index.tsx
  37. 6 7
      src/pages/system/Platforms/Api/swagger-ui/debugging.tsx
  38. 4 4
      src/pages/system/Platforms/Home/index.tsx

+ 6 - 4
src/components/FIndicators/index.tsx

@@ -73,10 +73,12 @@ const FIndicators = (props: Props) => {
               ]
             }
             onChange={(_: any, date: string[]) => {
-              onChange({
-                ...value,
-                value: [...date],
-              });
+              if (date[0] !== date[1]) {
+                onChange({
+                  ...value,
+                  value: [...date],
+                });
+              }
             }}
           />
         );

+ 38 - 4
src/components/FMonacoEditor/index.tsx

@@ -1,11 +1,33 @@
 import MonacoEditor from 'react-monaco-editor';
 import { connect, mapProps } from '@formily/react';
-import { useRef, useState } from 'react';
+import { useCallback, useEffect, useRef, useState } from 'react';
+import type { MonacoEditorProps } from 'react-monaco-editor/lib/types';
 
-export const JMonacoEditor = (props: any) => {
+interface Props extends MonacoEditorProps {}
+
+export const JMonacoEditor = (props: Props) => {
   const [loading, setLoading] = useState(false);
+  const formatLock = useRef<boolean>(false);
   const monacoRef = useRef<any>();
-  console.log(props);
+  const monacoEditorRef = useRef<any>();
+
+  const editorFormat = (editor: any) => {
+    editor.getAction('editor.action.formatDocument').run();
+  };
+
+  useEffect(() => {
+    if (formatLock.current === false && monacoEditorRef.current) {
+      setTimeout(() => {
+        editorFormat(monacoEditorRef.current);
+      }, 300);
+      formatLock.current = true;
+    }
+  }, [props.value, loading, monacoEditorRef.current]);
+
+  const editorDidMountHandle = useCallback((editor: any, monaco: any) => {
+    monacoEditorRef.current = editor;
+    props.editorDidMount?.(editor, monaco);
+  }, []);
 
   return (
     <div
@@ -16,7 +38,19 @@ export const JMonacoEditor = (props: any) => {
       }}
       style={{ height: '100%', width: '100%' }}
     >
-      {loading && <MonacoEditor ref={monacoRef} {...props} options={{ wordWrap: 'on' }} />}
+      {loading && (
+        <MonacoEditor
+          ref={(r: any) => {
+            monacoRef.current = r;
+            if (r && formatLock.current === false) {
+              editorFormat(r.editor);
+            }
+          }}
+          {...props}
+          options={{ wordWrap: 'on', automaticLayout: true }}
+          editorDidMount={editorDidMountHandle}
+        />
+      )}
     </div>
   );
 };

+ 11 - 4
src/components/FRuleEditor/Advance/index.tsx

@@ -14,13 +14,14 @@ interface Props {
 }
 
 const Advance = (props: Props) => {
-  const { model, onChange, virtualRule } = props;
+  const { onChange, virtualRule } = props;
   const [success, setSuccess] = useState(false);
-  const cacheRef = useRef();
+  const [editorValue, setEditorValue] = useState(virtualRule.script);
+  const cacheRef = useRef(virtualRule.script);
   return (
     <Modal
       maskClosable={false}
-      visible={model === 'advance'}
+      visible
       width="70vw"
       title="设置属性规则"
       onCancel={() => onChange('simple')}
@@ -36,13 +37,19 @@ const Advance = (props: Props) => {
         <div className={styles.left}>
           <Editor
             mode="advance"
+            key={'advance'}
+            value={virtualRule.script}
             onValueChange={(v) => {
               cacheRef.current = v;
+              setEditorValue(v);
               setSuccess(false);
             }}
           />
           <Debug
-            virtualRule={virtualRule}
+            virtualRule={{
+              ...virtualRule,
+              script: editorValue,
+            }}
             onSuccess={() => {
               setSuccess(true);
             }}

+ 1 - 0
src/components/FRuleEditor/Debug/index.tsx

@@ -160,6 +160,7 @@ const Debug = observer((props: Props) => {
     if (ws.current) {
       ws.current.unsubscribe();
     }
+    console.log(props.virtualRule);
     if (!props.virtualRule.script) {
       setIsBeginning(true);
       message.warning('请编辑规则');

+ 44 - 26
src/components/FRuleEditor/Editor/index.tsx

@@ -1,8 +1,9 @@
 import styles from '@/components/FRuleEditor/index.less';
 import { Dropdown, Menu, Tooltip } from 'antd';
 import { FullscreenOutlined, MoreOutlined } from '@ant-design/icons';
-import MonacoEditor, { monaco } from 'react-monaco-editor';
-import { useEffect, useRef, useState } from 'react';
+import { monaco } from 'react-monaco-editor';
+import { JMonacoEditor } from '@/components/FMonacoEditor';
+import { useCallback, useEffect, useRef, useState } from 'react';
 import type * as monacoEditor from 'monaco-editor';
 import { Store } from 'jetlinks-store';
 import { State } from '@/components/FRuleEditor';
@@ -87,38 +88,52 @@ interface Props {
   onChange?: (value: 'advance' | 'simple') => void;
   id?: string;
   onValueChange?: (v: any) => void;
+  value?: string;
 }
 
 const Editor = (props: Props) => {
   const [loading, setLoading] = useState(false);
+  const [value, setValue] = useState<string | undefined>(State.code);
+
   const editorRef = useRef<monacoEditor.editor.IStandaloneCodeEditor>();
   const editorDidMountHandle = (editor: monacoEditor.editor.IStandaloneCodeEditor) => {
     editor.getAction('editor.action.formatDocument').run();
     editorRef.current = editor;
   };
 
-  const handleInsertCode = (value: string) => {
-    const editor = editorRef.current;
-    if (!editor || !value) return;
-    const position = editor.getPosition()!;
-    editor?.executeEdits(State.code, [
-      {
-        range: new monaco.Range(
-          position?.lineNumber,
-          position?.column,
-          position?.lineNumber,
-          position?.column,
-        ),
-        text: value,
-      },
-    ]);
-    Store.set('add-operator-value', undefined);
-  };
+  useEffect(() => {
+    setValue(props.value);
+  }, [props.value]);
+
+  const handleInsertCode = useCallback(
+    (_value: string) => {
+      const editor = editorRef.current;
+      if (!editor || !_value) return;
+      const position = editor.getPosition()!;
+      editor?.executeEdits(value, [
+        {
+          range: new monaco.Range(
+            position?.lineNumber,
+            position?.column,
+            position?.lineNumber,
+            position?.column,
+          ),
+          text: _value,
+        },
+      ]);
+      // Store.set('add-operator-value', undefined);
+    },
+    [value],
+  );
 
   useEffect(() => {
-    const subscription = Store.subscribe('add-operator-value', handleInsertCode);
-    return () => subscription.unsubscribe();
+    let subscription: any = null;
+    if (props.mode === 'advance') {
+      subscription = Store.subscribe('add-operator-value', handleInsertCode);
+    }
+    return () => subscription?.unsubscribe();
   }, [props.mode]);
+
   return (
     <div
       className={styles.box}
@@ -185,16 +200,19 @@ const Editor = (props: Props) => {
         </div>
       </div>
       {loading && (
-        <MonacoEditor
-          editorDidMount={(editor) => editorDidMountHandle(editor)}
+        <JMonacoEditor
+          editorDidMount={editorDidMountHandle}
           language={'javascript'}
           height={300}
           onChange={(c) => {
-            State.code = c;
-            // Store.set('rule-editor-value', State.code);
+            setValue(c);
+            if (props.mode !== 'advance') {
+              State.code = c;
+              Store.set('rule-editor-value', State.code);
+            }
             props.onValueChange?.(c);
           }}
-          value={State.code}
+          value={value}
         />
       )}
     </div>

+ 2 - 1
src/components/FRuleEditor/Operator/index.tsx

@@ -77,9 +77,10 @@ const Operator = (props: Props) => {
             <div className={node.children?.length > 0 ? styles.parent : styles.add}>
               {node.type === 'property' ? (
                 <Popover
-                  visible={visible}
+                  open={visible}
                   placement="right"
                   title="请选择使用值"
+                  onOpenChange={setVisible}
                   content={
                     <Space direction="vertical">
                       <Tooltip

+ 12 - 8
src/components/FRuleEditor/index.tsx

@@ -42,19 +42,23 @@ const FRuleEditor = observer((props: Props) => {
   return (
     <>
       <Editor
+        key={'simple'}
         onChange={(v) => {
           State.model = v;
         }}
+        value={value}
         id={props.id}
       />
-      <Advance
-        model={State.model}
-        virtualRule={virtualRule}
-        id={props.id}
-        onChange={(v) => {
-          State.model = v;
-        }}
-      />
+      {State.model === 'advance' && (
+        <Advance
+          model={State.model}
+          virtualRule={virtualRule}
+          id={props.id}
+          onChange={(v) => {
+            State.model = v;
+          }}
+        />
+      )}
     </>
   );
 });

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

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

+ 53 - 22
src/components/NoticeIcon/index.tsx

@@ -1,4 +1,4 @@
-import { useEffect, useRef, useState } from 'react';
+import { useEffect, useRef } from 'react';
 import { Button, message, notification } from 'antd';
 import { groupBy } from 'lodash';
 import moment from 'moment';
@@ -8,8 +8,10 @@ import styles from './index.less';
 import encodeQuery from '@/utils/encodeQuery';
 import { getMenuPathByCode, MENUS_CODE } from '@/utils/menu';
 import useHistory from '@/hooks/route/useHistory';
-import { throttleTime } from 'rxjs/operators';
+// import { throttleTime } from 'rxjs/operators';
 import useSendWebsocketMessage from '@/hooks/websocket/useSendWebsocketMessage';
+import { observer } from '@formily/react';
+import { model } from '@formily/reactive';
 
 export type GlobalHeaderRightProps = {
   fetchingNotices?: boolean;
@@ -62,13 +64,25 @@ const getNoticeData = (notices: API.NoticeIconItem[]): Record<string, API.Notice
 
 export const service = new Service('notifications');
 
-const NoticeIconView = () => {
+export const NoticeIconViewModel = model<{
+  unreadCount: number;
+  notices: API.NoticeIconItem[];
+  visible: boolean;
+  loading: boolean;
+}>({
+  unreadCount: 0,
+  notices: [],
+  visible: false,
+  loading: true,
+});
+
+const NoticeIconView = observer(() => {
   // const { initialState } = useModel('@@initialState');
   // const { currentUser } = initialState || {};
-  const [notices, setNotices] = useState<API.NoticeIconItem[]>([]);
-  const [unreadCount, setUnreadCount] = useState<number>(0);
-  const [visible, setVisible] = useState<boolean>(false);
-  const [loading, setLoading] = useState<boolean>(true);
+  // const [notices, setNotices] = useState<API.NoticeIconItem[]>([]);
+  // const [unreadCount, setUnreadCount] = useState<number>(0);
+  // const [visible, setVisible] = useState<boolean>(false);
+  // const [loading, setLoading] = useState<boolean>(true);
   // const { data } = useRequest(getNotices);
 
   const history = useHistory();
@@ -78,7 +92,8 @@ const NoticeIconView = () => {
   const [subscribeTopic] = useSendWebsocketMessage();
 
   const getUnread = () => {
-    setLoading(true);
+    // setLoading(true);
+    NoticeIconViewModel.loading = true;
     service
       .fetchNotices(
         encodeQuery({
@@ -88,20 +103,26 @@ const NoticeIconView = () => {
       )
       .then((resp) => {
         if (resp.status === 200) {
-          setNotices(resp.result?.data || []);
-          setUnreadCount(resp.result?.total || 0);
+          NoticeIconViewModel.notices = resp.result?.data || [];
+          NoticeIconViewModel.unreadCount = resp.result?.total || 0;
+          // setNotices(resp.result?.data || []);
+          // setUnreadCount(resp.result?.total || 0);
         }
-        setLoading(false);
+        NoticeIconViewModel.loading = false;
+        // setLoading(false);
       });
   };
 
   const changeReadState = async (item: any, type?: 'notice' | 'icon') => {
+    console.log(item, type);
     const resp = await service.changeNoticeReadState(item.id);
     if (resp.status === 200) {
+      getUnread();
       const url = getMenuPathByCode(MENUS_CODE['account/NotificationRecord']);
       historyRef.current?.push(url, { ...item });
       if (type === 'icon') {
-        setVisible(false);
+        // setVisible(false);
+        NoticeIconViewModel.visible = false;
       } else {
         notification.close(item.id);
       }
@@ -147,9 +168,13 @@ const NoticeIconView = () => {
     const id = `notification`;
     const topic = `/notifications`;
     subscribeTopic!(id, topic, {})
-      ?.pipe(throttleTime(2000))
+      ?.pipe() // throttleTime(2000)
       .subscribe((resp: any) => {
-        getUnread();
+        // setUnreadCount(unreadCount + 1);
+        NoticeIconViewModel.unreadCount += 1;
+        // setTimeout(() => {
+        //   getUnread();
+        // }, 500)
         openNotification(resp);
       });
   };
@@ -159,11 +184,12 @@ const NoticeIconView = () => {
     subscribeNotice();
   }, []);
 
-  const noticeData = getNoticeData(notices);
+  const noticeData = getNoticeData(NoticeIconViewModel.notices);
   // const unreadMsg = getUnreadData(noticeData || {})
 
   const clearReadState = async (title: string) => {
-    const clearIds = (getNoticeData(notices).unread || []).map((item) => item.id) || [];
+    const clearIds =
+      (getNoticeData(NoticeIconViewModel.notices).unread || []).map((item) => item.id) || [];
     const resp = await service.clearNotices(clearIds);
     if (resp.status === 200) {
       message.success(`${'清空了'} ${title}`);
@@ -174,22 +200,27 @@ const NoticeIconView = () => {
   return (
     <NoticeIcon
       className={styles.action}
-      count={unreadCount}
+      count={NoticeIconViewModel.unreadCount}
       onItemClick={(item) => {
         changeReadState(item!, 'icon');
       }}
       onClear={(title: string) => clearReadState(title)}
-      loading={loading}
+      loading={NoticeIconViewModel.loading}
       clearText="当前标记为已读"
       viewMoreText="查看更多"
       onViewMore={() => {
         const url = getMenuPathByCode(MENUS_CODE['account/NotificationRecord']);
         history.push(url);
-        setVisible(false);
+        // setVisible(false);
+        NoticeIconViewModel.visible = false;
       }}
-      popupVisible={visible}
+      popupVisible={NoticeIconViewModel.visible}
       onPopupVisibleChange={(see: boolean) => {
-        setVisible(see);
+        // setVisible(see);
+        NoticeIconViewModel.visible = see;
+        if (see) {
+          getUnread();
+        }
       }}
       clearClose
     >
@@ -211,6 +242,6 @@ const NoticeIconView = () => {
       /> */}
     </NoticeIcon>
   );
-};
+});
 
 export default NoticeIconView;

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

@@ -235,7 +235,7 @@ const ContentRender = (data: SceneCardProps) => {
             <MyTooltip placement="topLeft" title={trigger?.when || ''}>
               <div
                 className={classNames(styles['card-item-content-trigger-item'], 'ellipsis')}
-                style={{ maxWidth: '15%' }}
+                style={{ maxWidth: '16%' }}
               >
                 {trigger?.when || ''}
               </div>

+ 1 - 1
src/components/ProTableCard/CardItems/networkCard.tsx

@@ -76,7 +76,7 @@ export default (props: NoticeCardProps) => {
             {/*</span>*/}
             <Ellipsis title={props.name} titleClassName={'card-item-header-name'} />
           </div>
-          <div className={'card-item-content'}>
+          <div className={'card-item-content'} style={{ minHeight: 66 }}>
             <div>
               <label>类型</label>
               <Ellipsis title={props?.type} />

+ 1 - 0
src/pages/DataCollect/Collector/components/Device/Save/index.tsx

@@ -127,6 +127,7 @@ export default (props: Props) => {
             'x-component-props': {
               placeholder: '请选择所属通道',
             },
+            'x-disabled': !!props.data?.id,
             'x-validator': [
               {
                 required: true,

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

@@ -415,8 +415,8 @@ export default observer((props: Props) => {
       {CollectorModel.visible && (
         <Save
           data={CollectorModel.current}
-          channelId={props.id}
-          provider={props.provider || CollectorModel.current?.provider}
+          // channelId={props.id}
+          // provider={props.provider || CollectorModel.current?.provider}
           close={() => {
             CollectorModel.visible = false;
           }}

+ 68 - 56
src/pages/DataCollect/Collector/components/Point/CollectorCard/WritePoint.tsx

@@ -34,62 +34,74 @@ const WritePoint = (props: Props) => {
       createForm({
         effects() {
           onFormInit((f) => {
-            let valueType: string =
-              props.data?.provider === 'OPC_UA'
-                ? props?.data?.configuration?.type || 'Number'
-                : props.data?.configuration?.codec?.provider || 'int8';
-            valueType = valueType.toLocaleLowerCase();
-            switch (valueType) {
-              case 'boolean':
-                f.setFieldState('propertyValue', async (state) => {
-                  state.dataSource = [
-                    {
-                      label: '是',
-                      value: true,
-                    },
-                    {
-                      label: '否',
-                      value: false,
-                    },
-                  ];
-                  state.componentProps = {
-                    placeholder: '请选择',
-                  };
-                  state.componentType = 'Select';
-                });
-                break;
-              case 'int8':
-              case 'int16':
-              case 'int32':
-              case 'int64':
-              case 'ieee754_float':
-              case 'ieee754_double':
-              case 'hex':
-              case 'number':
-                f.setFieldState('propertyValue', (state) => {
-                  state.componentType = 'NumberPicker';
-                  state.componentProps = {
-                    placeholder: '请输入',
-                  };
-                });
-                break;
-              case 'date':
-                f.setFieldState('propertyValue', (state) => {
-                  state.componentType = 'DatePicker';
-                  state.componentProps = {
-                    placeholder: '请选择',
-                    format: 'YYYY-MM-DD HH:mm:ss',
-                  };
-                });
-                break;
-              default:
-                f.setFieldState('propertyValue', (state) => {
-                  state.componentType = 'Input';
-                  state.componentProps = {
-                    placeholder: '请输入',
-                  };
-                });
-                break;
+            if (
+              props.data?.provider === 'MODBUS_TCP' &&
+              props.data?.configuration.function === 'Coils'
+            ) {
+              f.setFieldState('propertyValue', (state) => {
+                state.componentType = 'Input.TextArea';
+                state.componentProps = {
+                  placeholder: '请输入',
+                };
+              });
+            } else {
+              let valueType: string =
+                props.data?.provider === 'OPC_UA'
+                  ? props?.data?.configuration?.type || 'Number'
+                  : props.data?.configuration?.codec?.provider || 'int8';
+              valueType = valueType.toLocaleLowerCase();
+              switch (valueType) {
+                case 'boolean':
+                  f.setFieldState('propertyValue', async (state) => {
+                    state.dataSource = [
+                      {
+                        label: '是',
+                        value: true,
+                      },
+                      {
+                        label: '否',
+                        value: false,
+                      },
+                    ];
+                    state.componentProps = {
+                      placeholder: '请选择',
+                    };
+                    state.componentType = 'Select';
+                  });
+                  break;
+                case 'int8':
+                case 'int16':
+                case 'int32':
+                case 'int64':
+                case 'ieee754_float':
+                case 'ieee754_double':
+                case 'hex':
+                case 'number':
+                  f.setFieldState('propertyValue', (state) => {
+                    state.componentType = 'NumberPicker';
+                    state.componentProps = {
+                      placeholder: '请输入',
+                    };
+                  });
+                  break;
+                case 'date':
+                  f.setFieldState('propertyValue', (state) => {
+                    state.componentType = 'DatePicker';
+                    state.componentProps = {
+                      placeholder: '请选择',
+                      format: 'YYYY-MM-DD HH:mm:ss',
+                    };
+                  });
+                  break;
+                default:
+                  f.setFieldState('propertyValue', (state) => {
+                    state.componentType = 'Input';
+                    state.componentProps = {
+                      placeholder: '请输入',
+                    };
+                  });
+                  break;
+              }
             }
           });
         },

+ 18 - 10
src/pages/DataCollect/Collector/components/Tree/index.tsx

@@ -48,13 +48,17 @@ export default observer((props: Props) => {
       .queryCollector({ ...params, paging: false, sorts: [{ name: 'createTime', order: 'desc' }] })
       .then((resp) => {
         if (resp.status === 200) {
-          TreeModel.dataSource = [
-            {
-              id: '*',
-              name: '全部',
-              children: resp.result,
-            },
-          ];
+          if (params.terms) {
+            TreeModel.dataSource = resp.result;
+          } else {
+            TreeModel.dataSource = [
+              {
+                id: '*',
+                name: '全部',
+                children: resp.result,
+              },
+            ];
+          }
           props.change(TreeModel.dataSource[0]);
         }
         TreeModel.loading = false;
@@ -95,9 +99,13 @@ export default observer((props: Props) => {
           placeholder="请输入名称"
           allowClear
           onSearch={(val) => {
-            TreeModel.param = {
-              terms: [{ column: 'name', value: `%${val}%`, termType: 'like' }],
-            };
+            if (val) {
+              TreeModel.param = {
+                terms: [{ column: 'name', value: `%${val}%`, termType: 'like' }],
+              };
+            } else {
+              TreeModel.param = {};
+            }
           }}
           style={{ width: '100%' }}
         />

+ 13 - 14
src/pages/account/NotificationRecord/detail/index.tsx

@@ -1,7 +1,7 @@
 import { Descriptions, Modal } from 'antd';
 import { useEffect, useState } from 'react';
 import moment from 'moment';
-import { service } from '@/pages/account/NotificationRecord';
+// import { service } from '@/pages/account/NotificationRecord';
 import { service as service1 } from '@/pages/rule-engine/Alarm/Log';
 import { Store } from 'jetlinks-store';
 import ReactJson from 'react-json-view';
@@ -15,7 +15,7 @@ const Detail = (props: Props) => {
   const [data, setDada] = useState<any>({});
 
   useEffect(() => {
-    if (props.data.dataId) {
+    if (props.data?.dataId) {
       service1.queryDefaultLevel().then((resp) => {
         if (resp.status === 200) {
           Store.set('default-level', resp.result?.levels || []);
@@ -23,11 +23,7 @@ const Detail = (props: Props) => {
             const detailJson = props.data.detailJson || '{}';
             setDada(JSON.parse(detailJson));
           } else {
-            service.getAlarmList(props.data.dataId).then((res) => {
-              if (res.status === 200) {
-                setDada(res.result);
-              }
-            });
+            setDada(props.data?.detail || props.data);
           }
         }
       });
@@ -63,13 +59,16 @@ const Detail = (props: Props) => {
         </Descriptions.Item>
         <Descriptions.Item label="告警流水" span={2}>
           <div style={{ maxHeight: 400, overflowY: 'auto' }}>
-            <ReactJson
-              enableClipboard={false}
-              displayObjectSize={false}
-              displayDataTypes={false}
-              name={false}
-              src={JSON.parse(data?.alarmInfo || '{}')}
-            />
+            {
+              // @ts-ignore
+              <ReactJson
+                enableClipboard={false}
+                displayObjectSize={false}
+                displayDataTypes={false}
+                name={false}
+                src={JSON.parse(data?.alarmInfo || '{}')}
+              />
+            }
           </div>
         </Descriptions.Item>
       </Descriptions>

+ 10 - 1
src/pages/account/NotificationRecord/index.tsx

@@ -15,6 +15,7 @@ import { onlyMessage } from '@/utils/util';
 import type { CustomIconComponentProps } from '@ant-design/icons/lib/components/Icon';
 import useLocations from '@/hooks/route/useLocation';
 import { observer } from '@formily/reactive-react';
+import { NoticeIconViewModel } from '@/components/NoticeIcon';
 
 export const service = new Service('notifications');
 
@@ -40,7 +41,6 @@ const NotificationRecord = observer(() => {
   }, []);
 
   useEffect(() => {
-    console.log(location.state);
     if (location.state?.id) {
       setVisible(true);
       setCurrent(location.state);
@@ -145,6 +145,15 @@ const NotificationRecord = observer(() => {
               const state = record?.state?.value !== 'read' ? 'read' : 'unread';
               const resp = await service.saveData(state, [record.id]);
               if (resp.status === 200) {
+                if (state === 'read') {
+                  if (NoticeIconViewModel.unreadCount) {
+                    NoticeIconViewModel.unreadCount -= 1;
+                  } else {
+                    NoticeIconViewModel.unreadCount = 0;
+                  }
+                } else {
+                  NoticeIconViewModel.unreadCount += 1;
+                }
                 onlyMessage('操作成功');
                 actionRef.current?.reload();
               }

+ 1 - 0
src/pages/account/NotificationRecord/typings.d.ts

@@ -10,4 +10,5 @@ type NotifitionRecord = {
   topicName: string;
   dataId: string;
   detailJson: string;
+  detail?: any;
 };

+ 4 - 9
src/pages/device/Instance/Detail/Functions/AdvancedMode.tsx

@@ -82,18 +82,14 @@ export default (props: FunctionProps) => {
     }
   };
 
-  const editorDidMountHandle = (editor: any) => {
-    monacoRef.current = editor;
-    editor.getAction('editor.action.formatDocument').run();
-    editor.onDidContentSizeChange?.(() => {
-      editor.getAction('editor.action.formatDocument').run();
-    });
-  };
-
   useEffect(() => {
     handleData(props.data);
   }, [props.data]);
 
+  useEffect(() => {
+    monacoRef.current?.layout();
+  });
+
   return (
     <div className="device-function-content">
       <div className="left">
@@ -107,7 +103,6 @@ export default (props: FunctionProps) => {
             onChange={(newValue: any) => {
               setValue(newValue);
             }}
-            editorDidMount={editorDidMountHandle}
           />
         </div>
         <div className="button-tool">

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

@@ -65,7 +65,7 @@ export default (props: FunctionProps) => {
       case 'object':
         return <MetadataJsonInput json={record.json} />;
       case 'password':
-        return <Input type={'password'} />;
+        return <Input.Password />;
       case 'date':
         return (
           // <>

+ 9 - 7
src/pages/device/Instance/Detail/MetadataLog/Property/index.tsx

@@ -15,7 +15,7 @@ import type { PropertyMetadata } from '@/pages/device/Product/typings';
 import encodeQuery from '@/utils/encodeQuery';
 import { useEffect, useState } from 'react';
 import moment from 'moment';
-import FileComponent from '../../Running/Property/FileComponent';
+import FileComponent, { getType } from '../../Running/Property/FileComponent';
 import { DownloadOutlined, SearchOutlined } from '@ant-design/icons';
 import Detail from './Detail';
 import AMap from './AMap';
@@ -69,22 +69,24 @@ const PropertyLog = (props: Props) => {
       dataIndex: 'action',
       key: 'action',
       render: (text: any, record: any) => [
-        data.valueType?.type === 'file' ? (
+        (data.valueType?.type === 'file' && data?.valueType?.fileType === 'Binary(二进制)') ||
+        (!getType(record?.value) && data?.valueType?.fileType === 'base64') ? (
           <a style={{ marginRight: 20 }}>
             <ATooltip title="下载">
               <DownloadOutlined
                 onClick={() => {
-                  const type = (record?.value || '').split('.').pop();
-                  const downloadUrl = record.value;
                   const downNode = document.createElement('a');
-                  downNode.href = downloadUrl;
-                  downNode.target = '_blank';
                   downNode.download = `${InstanceModel.detail.name}-${data.name}${moment(
                     new Date().getTime(),
-                  ).format('YYYY-MM-DD-HH-mm-ss')}.${type}`;
+                  ).format('YYYY-MM-DD-HH-mm-ss')}.txt`;
                   downNode.style.display = 'none';
+                  //字符串内容转成Blob地址
+                  const blob = new Blob([record.value]);
+                  downNode.href = URL.createObjectURL(blob);
+                  //触发点击
                   document.body.appendChild(downNode);
                   downNode.click();
+                  //移除
                   document.body.removeChild(downNode);
                 }}
               />

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

@@ -2,7 +2,7 @@ import { useDomFullHeight } from '@/hooks';
 import { ExclamationCircleOutlined, ExpandOutlined } from '@ant-design/icons';
 import { Card, Tooltip, Select, Input, Button, AutoComplete, message } from 'antd';
 import { useState, useRef, useEffect } from 'react';
-import MonacoEditor from 'react-monaco-editor';
+import { JMonacoEditor } from '@/components/FMonacoEditor';
 import { useFullscreen, useSize } from 'ahooks';
 import Service from '@/pages/device/Instance/service';
 import { onlyMessage } from '@/utils/util';
@@ -244,7 +244,7 @@ const Parsing = (props: Props) => {
               }}
             ></div>
           )}
-          <MonacoEditor
+          <JMonacoEditor
             width={'100%'}
             height={'100%'}
             theme="vs"

+ 56 - 23
src/pages/device/Instance/Detail/Running/Property/FileComponent/index.tsx

@@ -25,6 +25,27 @@ imgMap.set('video', require('/public/images/running/video.png'));
 imgMap.set('other', require('/public/images/running/other.png'));
 imgMap.set('obj', require('/public/images/running/obj.png'));
 
+const imgList = ['.jpg', '.png', '.swf', '.tiff'];
+const videoList = ['.m3u8', '.flv', '.mp4', '.rmvb', '.mvb'];
+const fileList = ['.txt', '.doc', '.xls', '.pdf', '.ppt', '.docx', '.xlsx', '.pptx'];
+
+export const getType = (url: string) => {
+  let t: string = '';
+  [...imgList, ...videoList, ...fileList].map((item) => {
+    const str = item.slice(1, item.length);
+    if (url.indexOf(str) !== -1) {
+      if (imgList.includes(item)) {
+        t = 'img';
+      } else if (videoList.includes(item)) {
+        t = 'video';
+      } else {
+        t = str;
+      }
+    }
+  });
+  return t;
+};
+
 const FileComponent = (props: Props) => {
   const { data, value } = props;
   const [type, setType] = useState<string>('other');
@@ -33,7 +54,7 @@ const FileComponent = (props: Props) => {
   const [temp, setTemp] = useState<boolean>(false);
 
   const renderFile = () => {
-    if (['.jpg', '.png', '.swf', '.tiff'].some((item) => value?.formatValue.includes(item))) {
+    if (imgList.some((item) => value?.formatValue.includes(item))) {
       // 图片
       return (
         <div
@@ -61,9 +82,7 @@ const FileComponent = (props: Props) => {
         </div>
       );
     }
-    if (
-      ['.m3u8', '.flv', '.mp4', '.rmvb', '.mvb'].some((item) => value?.formatValue.includes(item))
-    ) {
+    if (videoList.some((item) => value?.formatValue.includes(item))) {
       return (
         <div
           className={props.type === 'card' ? styles.cardValue : styles.otherValue}
@@ -83,15 +102,8 @@ const FileComponent = (props: Props) => {
           <img src={imgMap.get('video')} />
         </div>
       );
-    } else if (
-      ['.txt', '.doc', '.xls', '.pdf', '.ppt', '.docx', '.xlsx', '.pptx'].some((item) =>
-        value?.formatValue.includes(item),
-      )
-    ) {
-      const flag =
-        ['.txt', '.doc', '.xls', '.pdf', '.ppt', '.docx', '.xlsx', '.pptx'].find((item) =>
-          value?.formatValue.includes(item),
-        ) || '--';
+    } else if (fileList.some((item) => value?.formatValue.includes(item))) {
+      const flag = fileList.find((item) => value?.formatValue.includes(item)) || '--';
       return (
         <div className={props.type === 'card' ? styles.cardValue : styles.otherValue}>
           <img src={imgMap.get(flag.slice(1))} />
@@ -106,6 +118,28 @@ const FileComponent = (props: Props) => {
     }
   };
 
+  const base64Render = () => {
+    const _type = getType(value?.formatValue);
+    if (!!_type) {
+      return (
+        <div className={props.type === 'card' ? styles.cardValue : styles.otherValue}>
+          <img
+            src={imgMap.get(_type)}
+            onError={(e: any) => {
+              e.target.src = imgMap.get('other');
+            }}
+          />
+        </div>
+      );
+    } else {
+      return (
+        <div className={props.type === 'card' ? styles.cardValue : styles.otherValue}>
+          <img src={imgMap.get('other')} />
+        </div>
+      );
+    }
+  };
+
   const renderValue = () => {
     if (value?.formatValue !== 0 && !value?.formatValue) {
       return (
@@ -116,16 +150,15 @@ const FileComponent = (props: Props) => {
         data?.valueType?.fileType === 'base64' ||
         data?.valueType?.fileType === 'Binary(二进制)'
       ) {
-        return (
-          <div className={props.type === 'card' ? styles.cardValue : styles.otherValue}>
-            <img
-              src={value?.formatValue}
-              onError={(e: any) => {
-                e.target.src = imgMap.get('other');
-              }}
-            />
-          </div>
-        );
+        if (data?.valueType?.fileType === 'base64') {
+          return base64Render();
+        } else {
+          return (
+            <div className={props.type === 'card' ? styles.cardValue : styles.otherValue}>
+              <img src={imgMap.get('other')} />
+            </div>
+          );
+        }
       } else {
         return renderFile();
       }

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

@@ -233,11 +233,6 @@ const Import = (props: Props) => {
           height: 350,
           theme: 'vs',
           language: 'json',
-          editorDidMount: (editor1: any) => {
-            editor1.onDidContentSizeChange?.(() => {
-              editor1.getAction('editor.action.formatDocument').run();
-            });
-          },
         },
         'x-visible': false,
         'x-validator': [

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

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

+ 13 - 12
src/pages/link/Type/Detail/index.tsx

@@ -669,10 +669,10 @@ const Save = observer(() => {
         },
         enum: [
           { value: 'DIRECT', label: '不处理' },
-          { value: 'delimited', label: '分隔符' },
-          { value: 'script', label: '自定义脚本' },
-          { value: 'fixed_length', label: '固定长度' },
-          { value: 'length', label: '长度' },
+          { value: 'DELIMITED', label: '分隔符' },
+          { value: 'SCRIPT', label: '自定义脚本' },
+          { value: 'FIXED_LENGTH', label: '固定长度' },
+          { value: 'LENGTH_FIELD', label: '长度字段' },
         ],
         'x-reactions': {
           dependencies: ['type'],
@@ -701,7 +701,8 @@ const Save = observer(() => {
             dependencies: ['.parserType'],
             fulfill: {
               state: {
-                visible: '{{["delimited", "script", "fixed_length", "length"].includes($deps[0])}}',
+                visible:
+                  '{{["DELIMITED", "SCRIPT", "FIXED_LENGTH", "LENGTH_FIELD"].includes($deps[0])}}',
               },
             },
           },
@@ -731,7 +732,7 @@ const Save = observer(() => {
                 dependencies: ['..parserType'],
                 fulfill: {
                   state: {
-                    visible: '{{$deps[0] === "delimited"}}',
+                    visible: '{{$deps[0] === "DELIMITED"}}',
                   },
                 },
               },
@@ -761,7 +762,7 @@ const Save = observer(() => {
                 fulfill: {
                   state: {
                     // visible: '{{$deps[0] === "script"}}',
-                    value: '{{$deps[0] === "script" ? "javascript" : ""}}',
+                    value: '{{$deps[0] === "SCRIPT" ? "javascript" : ""}}',
                   },
                 },
               },
@@ -797,7 +798,7 @@ const Save = observer(() => {
                 dependencies: ['..parserType'],
                 fulfill: {
                   state: {
-                    visible: '{{$deps[0] === "script"}}',
+                    visible: '{{$deps[0] === "SCRIPT"}}',
                   },
                 },
               },
@@ -827,7 +828,7 @@ const Save = observer(() => {
                 dependencies: ['..parserType'],
                 fulfill: {
                   state: {
-                    visible: '{{$deps[0] === "fixed_length"}}',
+                    visible: '{{$deps[0] === "FIXED_LENGTH"}}',
                   },
                 },
               },
@@ -858,7 +859,7 @@ const Save = observer(() => {
                 dependencies: ['..parserType'],
                 fulfill: {
                   state: {
-                    visible: '{{$deps[0] === "length"}}',
+                    visible: '{{$deps[0] === "LENGTH_FIELD"}}',
                   },
                 },
               },
@@ -892,7 +893,7 @@ const Save = observer(() => {
                 dependencies: ['..parserType'],
                 fulfill: {
                   state: {
-                    visible: '{{$deps[0] === "length"}}',
+                    visible: '{{$deps[0] === "LENGTH_FIELD"}}',
                   },
                 },
               },
@@ -921,7 +922,7 @@ const Save = observer(() => {
                 dependencies: ['..parserType'],
                 fulfill: {
                   state: {
-                    visible: '{{$deps[0] === "length"}}',
+                    visible: '{{$deps[0] === "LENGTH_FIELD"}}',
                   },
                 },
               },

+ 48 - 2
src/pages/media/Device/Save/SaveProduct.tsx

@@ -1,7 +1,7 @@
 import { useEffect, useState } from 'react';
 import { service } from '../index';
 import { useRequest } from 'umi';
-import { Form, Input, message, Modal } from 'antd';
+import { Form, Input, message, Modal, Select } from 'antd';
 import ProviderItem from './ProviderSelect';
 
 interface SaveProps {
@@ -14,6 +14,7 @@ interface SaveProps {
 export default (props: SaveProps) => {
   const { visible, close, reload } = props;
   const [loading, setLoading] = useState(false);
+  const [extendFormItem, setExtendFormItem] = useState([]);
   const [form] = Form.useForm();
 
   const { data: providerList, run: getProviderList } = useRequest(service.queryProvider, {
@@ -69,6 +70,17 @@ export default (props: SaveProps) => {
     }
   };
 
+  const handlerItem = (type: string, label: string, options: any[]) => {
+    switch (type) {
+      case 'enum':
+        return <Select placeholder={`请选择${label}`} options={options} />;
+      case 'password':
+        return <Input.Password placeholder={`请输入${label}`} />;
+      default:
+        return <Input placeholder={`请输入${label}`} />;
+    }
+  };
+
   return (
     <Modal
       maskClosable={false}
@@ -98,6 +110,23 @@ export default (props: SaveProps) => {
         >
           <Input placeholder={'请输入产品名称'} />
         </Form.Item>
+        {extendFormItem.map((item: any) => {
+          let messageType = '请输入';
+          if (item.type === 'enum') {
+            messageType = '请选择';
+          }
+          return (
+            <Form.Item
+              name={item.name}
+              label={item.label}
+              required={item.required}
+              rules={[{ required: true, message: `${messageType}${item.label}` }]}
+              initialValue={item.value}
+            >
+              {handlerItem(item.type, item.label, item.options)}
+            </Form.Item>
+          );
+        })}
         <Form.Item
           name={'accessId'}
           label={'接入网关'}
@@ -107,7 +136,7 @@ export default (props: SaveProps) => {
           <ProviderItem
             options={providerList}
             type={props.type}
-            onSelect={(_, rowData) => {
+            onSelect={async (_, rowData) => {
               form.setFieldsValue({
                 accessName: rowData.name,
                 protocolName: rowData.protocolDetail.name,
@@ -115,6 +144,23 @@ export default (props: SaveProps) => {
                 transportProtocol: rowData.transportDetail.id,
                 accessProvider: rowData.provider,
               });
+              service.getConfiguration(rowData.protocol, rowData.transport).then((res: any) => {
+                console.log(res);
+                const arr = res.result.properties.map((item: any) => {
+                  return {
+                    name: ['configuration', item.property],
+                    label: item.name,
+                    type: item.type?.type,
+                    value: item.type.expands?.defaultValue,
+                    options: item.type.elements?.map((e: any) => ({
+                      label: e.text,
+                      value: e.value,
+                    })),
+                    required: !!item.type.expands?.required,
+                  };
+                });
+                setExtendFormItem(arr);
+              });
             }}
           />
         </Form.Item>

+ 5 - 0
src/pages/media/Device/service.ts

@@ -42,6 +42,11 @@ class Service extends BaseService<DeviceItem> {
       method: 'GET',
       params,
     });
+
+  getConfiguration = (id: string, transport: string) =>
+    request(`/${SystemConst.API_BASE}/protocol/${id}/${transport}/configuration`, {
+      method: 'GET',
+    });
 }
 
 export default Service;

+ 3 - 14
src/pages/rule-engine/Scene/Save/action/DeviceOutput/actions/ObjModel.tsx

@@ -1,6 +1,6 @@
 import { Modal } from 'antd';
-import { useEffect, useRef, useState } from 'react';
-import MonacoEditor from 'react-monaco-editor';
+import { useEffect, useState } from 'react';
+import { JMonacoEditor } from '@/components/FMonacoEditor';
 
 interface Props {
   value: any;
@@ -9,19 +9,9 @@ interface Props {
 }
 
 export default (props: Props) => {
-  const monacoRef = useRef<any>();
-
   const [value, setValue] = useState<any>(props.value);
   const [loading, setLoading] = useState(false);
 
-  const editorDidMountHandle = (editor: any) => {
-    monacoRef.current = editor;
-    editor.getAction('editor.action.formatDocument').run();
-    editor.onDidContentSizeChange?.(() => {
-      editor.getAction('editor.action.formatDocument').run();
-    });
-  };
-
   useEffect(() => {
     setValue(props?.value || '');
   }, [props.value]);
@@ -46,7 +36,7 @@ export default (props: Props) => {
         }}
       >
         {loading && (
-          <MonacoEditor
+          <JMonacoEditor
             width={'100%'}
             height={400}
             theme="vs-dark"
@@ -55,7 +45,6 @@ export default (props: Props) => {
             onChange={(newValue) => {
               setValue(newValue);
             }}
-            editorDidMount={editorDidMountHandle}
           />
         )}
       </div>

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

@@ -4,7 +4,7 @@ import { CloseOutlined, PlusOutlined } from '@ant-design/icons';
 import { useEffect, useState, useCallback, useRef } from 'react';
 import classNames from 'classnames';
 import { observer } from '@formily/react';
-import { Popconfirm, Space } from 'antd';
+import { message, Popconfirm, Space } from 'antd';
 import './index.less';
 import { AIcon } from '@/components';
 
@@ -49,6 +49,7 @@ export default observer((props: FilterProps) => {
   const [valueOptions] = useState<any[]>([]);
   const [valueType, setValueType] = useState('');
   const labelCache = useRef<any[]>([undefined, undefined, {}, 'and']);
+  const [paramsOpen, setParamsOpen] = useState(false);
 
   const [deleteVisible, setDeleteVisible] = useState(false);
   const columnsRef = useRef<string[]>([]);
@@ -259,6 +260,15 @@ export default observer((props: FilterProps) => {
               type="value"
               placeholder="参数值"
               valueType={valueType}
+              open={paramsOpen}
+              openChange={(_open) => {
+                if (ValueRef.current.column) {
+                  setParamsOpen(_open);
+                } else {
+                  setParamsOpen(false);
+                  message.warning('请先选择参数');
+                }
+              }}
               value={value}
               BuiltInOptions={BuiltInOptions}
               showLabelKey="fullName"
@@ -293,6 +303,15 @@ export default observer((props: FilterProps) => {
               type="value"
               placeholder="参数值"
               valueType={valueType}
+              open={paramsOpen}
+              openChange={(_open) => {
+                if (ValueRef.current.column) {
+                  setParamsOpen(_open);
+                } else {
+                  setParamsOpen(false);
+                  message.warning('请先选择参数');
+                }
+              }}
               value={value}
               BuiltInOptions={BuiltInOptions}
               showLabelKey="fullName"
@@ -329,6 +348,15 @@ export default observer((props: FilterProps) => {
             type="value"
             placeholder="参数值"
             valueType={valueType}
+            open={paramsOpen}
+            openChange={(_open) => {
+              if (ValueRef.current.column) {
+                setParamsOpen(_open);
+              } else {
+                setParamsOpen(false);
+                message.warning('请先选择参数');
+              }
+            }}
             value={value}
             BuiltInOptions={BuiltInOptions}
             showLabelKey="fullName"

+ 30 - 3
src/pages/rule-engine/Scene/Save/components/Buttons/ParamsDropdown.tsx

@@ -22,6 +22,8 @@ export interface ParamsDropdownProps {
   valueType: string;
   showLabelKey?: string;
   icon?: ReactNode;
+  open?: boolean;
+  openChange?: (open: boolean) => void;
 }
 
 interface MenusProps {
@@ -64,6 +66,10 @@ export default (props: ParamsDropdownProps) => {
   const [activeKey, setActiveKey] = useState(props.BuiltInOptions ? 'fixed' : 'manual');
   const [open, setOpen] = useState(false);
 
+  useEffect(() => {
+    setOpen(!!props.open);
+  }, [props.open]);
+
   const onValueChange = useCallback(
     (value: any, _label: any, item?: any) => {
       setMyValue(value);
@@ -99,6 +105,18 @@ export default (props: ParamsDropdownProps) => {
               placeholder={'请输入'}
             />
           );
+        case 'metric':
+          return (
+            <Menus
+              value={_value}
+              options={props.metricsOptions}
+              onChange={(v, l) => {
+                console.log(l);
+                onValueChange(v, l);
+                setOpen(false);
+              }}
+            />
+          );
         case 'enum':
           return (
             <Menus
@@ -173,11 +191,14 @@ export default (props: ParamsDropdownProps) => {
     [props, activeKey],
   );
 
-  const findLabel = (value: string, data: any[]): boolean => {
+  const findLabel = (value: string, data: any[], titleName?: string): boolean => {
     let isLabel = false;
     return data.some((item) => {
       if (item.key === value) {
         let titleKey = 'title';
+        if (titleName) {
+          titleKey = titleName;
+        }
         if (props.showLabelKey) {
           titleKey = props.showLabelKey;
         }
@@ -196,6 +217,9 @@ export default (props: ParamsDropdownProps) => {
         case 'boolean':
           setLabel(v ? '是' : '否');
           break;
+        case 'metric':
+          findLabel(v, props.metricsOptions || []);
+          break;
         case 'enum':
         case 'object':
           findLabel(v, props.options || []);
@@ -274,7 +298,7 @@ export default (props: ParamsDropdownProps) => {
       _itemList.push({
         key: 'metric',
         label: '指标值',
-        content: renderNode('enum', myValue, props),
+        content: renderNode('metric', myValue, props),
       });
     }
   }
@@ -299,7 +323,10 @@ export default (props: ParamsDropdownProps) => {
       bodyStyle={{ width: 'auto' }}
       type={props.valueType}
       open={open}
-      openChange={setOpen}
+      openChange={(_open) => {
+        setOpen(_open);
+        props.openChange?.(_open);
+      }}
     >
       <div
         className={classNames(styles['dropdown-button'], styles.value)}

+ 6 - 0
src/pages/rule-engine/Scene/Save/device/index.tsx

@@ -29,6 +29,12 @@ export default observer((props: Props) => {
   const [visible, setVisible] = useState(false);
 
   useEffect(() => {
+    return () => {
+      set(FormModel.current, ['options', 'trigger'], {});
+    };
+  }, []);
+
+  useEffect(() => {
     if (FormModel.current.trigger!.device?.productId) {
       service.detail(FormModel.current.trigger!.device?.productId).then((res) => {
         if (res.status === 200) {

+ 31 - 5
src/pages/rule-engine/Scene/Save/terms/paramsItem.tsx

@@ -154,14 +154,18 @@ const ParamsItem = observer((props: ParamsItemProps) => {
           if (labelOptions.metrics) {
             // 指标值
             const _metrics = labelOptions.metrics.map((mItem: any) => ({
-              title: mItem.name,
-              key: mItem.id,
+              ...mItem,
+              label: mItem.name,
+              value: mItem.value,
             }));
             setMetricsOptions(_metrics);
+          } else {
+            setMetricsOptions([]);
           }
         } else {
           props.onChange?.({});
           setTtOptions([]);
+          setMetricsOptions([]);
           setValueType('');
         }
       }
@@ -293,7 +297,14 @@ const ParamsItem = observer((props: ParamsItemProps) => {
           <>
             <ParamsDropdown
               options={valueOptions}
-              metricsOptions={metricsOptions}
+              metricsOptions={metricsOptions.filter((mItem) => {
+                if (ValueRef.current.termType && DoubleFilter.includes(ValueRef.current.termType)) {
+                  return mItem.range;
+                } else {
+                  return !mItem.range;
+                }
+              })}
+              isMetric={!!metricsOptions.length}
               type="value"
               placeholder="参数值"
               valueType={valueType}
@@ -315,7 +326,14 @@ const ParamsItem = observer((props: ParamsItemProps) => {
             />
             <ParamsDropdown
               options={valueOptions}
-              metricsOptions={metricsOptions}
+              metricsOptions={metricsOptions.filter((mItem) => {
+                if (ValueRef.current.termType && DoubleFilter.includes(ValueRef.current.termType)) {
+                  return mItem.range;
+                } else {
+                  return !mItem.range;
+                }
+              })}
+              isMetric={!!metricsOptions.length}
               type="value"
               placeholder="参数值"
               valueType={valueType}
@@ -339,7 +357,15 @@ const ParamsItem = observer((props: ParamsItemProps) => {
         ) : (
           <ParamsDropdown
             options={valueOptions}
-            metricsOptions={metricsOptions}
+            metricsOptions={metricsOptions.filter((mItem) => {
+              console.log(mItem);
+              if (ValueRef.current.termType && DoubleFilter.includes(ValueRef.current.termType)) {
+                return mItem.range;
+              } else {
+                return !mItem.range;
+              }
+            })}
+            isMetric={!!metricsOptions.length}
             type="value"
             placeholder="参数值"
             valueType={valueType}

+ 6 - 7
src/pages/system/Apply/Api/swagger-ui/debugging.tsx

@@ -1,7 +1,7 @@
 import { TitleComponent } from '@/components';
 import ReactJson from 'react-json-view';
 import { request } from 'umi';
-import MonacoEditor from 'react-monaco-editor';
+import { JMonacoEditor } from '@/components/FMonacoEditor';
 import { Button, Input } from 'antd';
 import { useCallback, useEffect, useMemo, useRef, useState } from 'react';
 import { createSchemaField, FormProvider, observer } from '@formily/react';
@@ -20,13 +20,12 @@ export default observer(() => {
 
   useEffect(() => {
     if (ApiModel.debugger.body && editor.current) {
-      const { editor: MEditor } = editor.current;
-      MEditor.setValue(JSON.stringify(ApiModel.debugger.body));
+      editor.current.setValue(JSON.stringify(ApiModel.debugger.body));
       setTimeout(() => {
-        MEditor.getAction('editor.action.formatDocument').run();
+        editor.current.getAction('editor.action.formatDocument').run();
       }, 300);
       // MEditor.trigger('anyString', 'editor.action.formatDocument');//自动格式化代码
-      MEditor.setValue(MEditor.getValue());
+      editor.current.setValue(editor.current.getValue());
     }
   }, [ApiModel.debugger, editor.current]);
 
@@ -215,11 +214,10 @@ export default observer(() => {
             </FormProvider>
           )}
           {ApiModel.debugger.body && (
-            <MonacoEditor
+            <JMonacoEditor
               height={200}
               language={'json'}
               theme={'dark'}
-              ref={editor}
               onChange={(value) => {
                 try {
                   setBody(JSON.parse(value));
@@ -228,6 +226,7 @@ export default observer(() => {
                 }
               }}
               editorDidMount={(_editor) => {
+                editor.current = _editor;
                 _editor.getAction('editor.action.formatDocument').run();
               }}
             />

+ 4 - 4
src/pages/system/Apply/Home/index.tsx

@@ -1,5 +1,5 @@
 import { Table } from 'antd';
-import MonacoEditor from 'react-monaco-editor';
+import { JMonacoEditor } from '@/components/FMonacoEditor';
 import './index.less';
 const Home = () => {
   const data = [
@@ -126,7 +126,7 @@ const Home = () => {
           <div>
             <p>使用和签名相同的算法(不需要对响应结果排序)</p>
             <div>
-              <MonacoEditor
+              <JMonacoEditor
                 width={'100%'}
                 height={620}
                 theme="vs-dark"
@@ -147,7 +147,7 @@ const Home = () => {
         <h3>添加 SDK 依赖</h3>
         <div className="h3-text">将以下Maven依赖加入到pom.xml文件中</div>
         <div>
-          <MonacoEditor
+          <JMonacoEditor
             width={'100%'}
             height={100}
             theme="vs-dark"
@@ -159,7 +159,7 @@ const Home = () => {
         </div>
         <h3>SDK 客户端的初始化和请求方式</h3>
         <div>
-          <MonacoEditor
+          <JMonacoEditor
             width={'100%'}
             height={370}
             theme="vs-dark"

+ 51 - 0
src/pages/system/DataSource/Save/index.tsx

@@ -120,6 +120,23 @@ const Save = (props: Props) => {
                 required: true,
                 message: '请输入URL',
               },
+              {
+                triggerType: 'onBlur',
+                validator: (value: string) => {
+                  return new Promise((resolve) => {
+                    if (!value) {
+                      resolve('');
+                    } else {
+                      const arr = value.split(':');
+                      if ((arr?.[0] === 'jdbc' || arr?.[0] === 'r2dbc') && arr?.[1] === 'mysql') {
+                        resolve('');
+                      } else {
+                        resolve('请输入正确的URL');
+                      }
+                    }
+                  });
+                },
+              },
             ],
             required: true,
             'x-reactions': {
@@ -152,6 +169,23 @@ const Save = (props: Props) => {
                 required: true,
                 message: '请输入管理地址',
               },
+              // {
+              //   triggerType: 'onBlur',
+              //   validator: (value: string) => {
+              //     return new Promise((resolve) => {
+              //       if (!value) {
+              //         resolve('');
+              //       } else {
+              //         const arr = value.split('://')
+              //         if (arr[0] === 'http' || arr[0] === 'https') {
+              //           resolve('');
+              //         } else {
+              //           resolve('请输入正确的管理地址')
+              //         }
+              //       }
+              //     });
+              //   },
+              // },
             ],
             required: true,
             'x-reactions': {
@@ -184,6 +218,23 @@ const Save = (props: Props) => {
                 required: true,
                 message: '请输入链接地址',
               },
+              // {
+              //   triggerType: 'onBlur',
+              //   validator: (value: string) => {
+              //     return new Promise((resolve) => {
+              //       if (!value) {
+              //         resolve('');
+              //       } else {
+              //         const reg = /(http|ftp|https):\/\/[\w\-_]+(\.[\w\-_]+)+([\w\-\.,@?^=%&:/~\+#]*[\w\-\@?^=%&/~\+#])?/;
+              //         if (reg.test(value)) {
+              //           resolve('');
+              //         } else {
+              //           resolve('请输入正确的链接地址')
+              //         }
+              //       }
+              //     });
+              //   },
+              // },
             ],
             required: true,
             'x-reactions': {

+ 6 - 7
src/pages/system/Platforms/Api/swagger-ui/debugging.tsx

@@ -1,7 +1,7 @@
 import { TitleComponent } from '@/components';
 import ReactJson from 'react-json-view';
 import { request } from 'umi';
-import MonacoEditor from 'react-monaco-editor';
+import { JMonacoEditor } from '@/components/FMonacoEditor';
 import { Button, Input, Popconfirm } from 'antd';
 import { useCallback, useEffect, useMemo, useRef, useState } from 'react';
 import { createSchemaField, FormProvider, observer } from '@formily/react';
@@ -50,13 +50,12 @@ export default observer(() => {
   useEffect(() => {
     console.log('ApiModel.debugger.body', ApiModel.debugger.body);
     if (ApiModel.debugger.body && editor.current) {
-      const { editor: MEditor } = editor.current;
-      MEditor.setValue(JSON.stringify(ApiModel.debugger.body));
+      editor.current.setValue(JSON.stringify(ApiModel.debugger.body));
       setTimeout(() => {
-        MEditor.getAction('editor.action.formatDocument').run();
+        editor.current.getAction('editor.action.formatDocument').run();
       }, 300);
       // MEditor.trigger('anyString', 'editor.action.formatDocument');//自动格式化代码
-      MEditor.setValue(MEditor.getValue());
+      editor.current.setValue(editor.current.getValue());
     }
   }, [ApiModel.debugger, editor.current]);
 
@@ -248,11 +247,10 @@ export default observer(() => {
             </FormProvider>
           )}
           {isBody() && (
-            <MonacoEditor
+            <JMonacoEditor
               height={200}
               language={'json'}
               theme={'dark'}
-              ref={editor}
               onChange={(value) => {
                 try {
                   setBody(JSON.parse(value));
@@ -261,6 +259,7 @@ export default observer(() => {
                 }
               }}
               editorDidMount={(_editor) => {
+                editor.current = _editor;
                 _editor.getAction('editor.action.formatDocument').run();
               }}
             />

+ 4 - 4
src/pages/system/Platforms/Home/index.tsx

@@ -1,5 +1,5 @@
 import { Table } from 'antd';
-import MonacoEditor from 'react-monaco-editor';
+import { JMonacoEditor } from '@/components/FMonacoEditor';
 import './index.less';
 const Home = () => {
   const data = [
@@ -126,7 +126,7 @@ const Home = () => {
           <div>
             <p>使用和签名相同的算法(不需要对响应结果排序)</p>
             <div>
-              <MonacoEditor
+              <JMonacoEditor
                 width={'100%'}
                 height={620}
                 theme="vs-dark"
@@ -147,7 +147,7 @@ const Home = () => {
         <h3>添加 SDK 依赖</h3>
         <div className="h3-text">将以下Maven依赖加入到pom.xml文件中</div>
         <div>
-          <MonacoEditor
+          <JMonacoEditor
             width={'100%'}
             height={100}
             theme="vs-dark"
@@ -159,7 +159,7 @@ const Home = () => {
         </div>
         <h3>SDK 客户端的初始化和请求方式</h3>
         <div>
-          <MonacoEditor
+          <JMonacoEditor
             width={'100%'}
             height={370}
             theme="vs-dark"