Procházet zdrojové kódy

Merge xyh

Next xyh
XieYongHong před 3 roky
rodič
revize
87949e283b

+ 13 - 2
src/hooks/websocket/useSendWebsocketMessage.ts

@@ -16,6 +16,7 @@ enum MsgType {
 }
 
 const subscribeList: Record<string, { next: any; complete: any }[]> = {};
+const messageCache: Record<string, string> = {};
 
 export const useSendWebsocketMessage = () => {
   const messageHistory = useRef<any>([]);
@@ -41,12 +42,20 @@ export const useSendWebsocketMessage = () => {
       }
     }
   };
+
   const { sendMessage, latestMessage } = useWebSocket(url, {
-    reconnectInterval: 1000,
-    reconnectLimit: 1,
+    // reconnectInterval: 1000,
+    // reconnectLimit: 1,
     onClose: () => console.error('websocket 链接关闭'),
     onOpen: (event) => console.log('打开链接', event),
     onError: (event) => console.log('报错了', event),
+    onReconnect: () => {
+      if (Object.keys(messageCache).length && sendMessage) {
+        Object.values(messageCache).forEach((item) => {
+          sendMessage(item);
+        });
+      }
+    },
     onMessage: dispenseMessage,
   });
 
@@ -69,6 +78,7 @@ export const useSendWebsocketMessage = () => {
         complete: () => subscriber.complete(),
       });
       const message = JSON.stringify({ id, topic, parameter, type: MsgType.sub });
+      messageCache[id] = message;
       if (sendMessage) {
         sendMessage(message);
       } else {
@@ -77,6 +87,7 @@ export const useSendWebsocketMessage = () => {
       return () => {
         const unsub = JSON.stringify({ id, type: MsgType.unsub });
         delete subscribeList[id];
+        delete messageCache[id];
         sendMessage?.(unsub);
       };
     });

+ 46 - 20
src/hooks/websocket/useWebSocket.ts

@@ -11,13 +11,14 @@ export enum ReadyState {
 }
 
 export interface Options {
-  reconnectLimit?: number;
-  reconnectInterval?: number;
+  // reconnectLimit?: number;
+  // reconnectInterval?: number;
   manual?: boolean;
   onOpen?: (event: WebSocketEventMap['open']) => void;
   onClose?: (event: WebSocketEventMap['close']) => void;
   onMessage?: (message: WebSocketEventMap['message']) => void;
   onError?: (event: WebSocketEventMap['error']) => void;
+  onReconnect?: () => void;
 }
 
 export interface Result {
@@ -31,18 +32,21 @@ export interface Result {
 
 export default function useWebSocket(socketUrl: string, options: Options = {}): Result {
   const {
-    reconnectLimit = 3,
-    reconnectInterval = 3 * 1000,
+    // reconnectLimit = 3,
+    // reconnectInterval = 3 * 1000,
     manual = false,
     onOpen,
     onClose,
     onMessage,
+    onReconnect,
     onError,
   } = options;
 
-  const reconnectTimesRef = useRef(0);
-  const reconnectTimerRef = useRef<NodeJS.Timeout>();
+  const reconnectTimesRef = useRef(0); // 重连次数
+  const reconnectTimerRef = useRef<NodeJS.Timeout>(); // 计时器
   const websocketRef = useRef<WebSocket>();
+  const lockReconnect = useRef(false); // 避免重复连接
+  const isReconnect = useRef(false);
 
   const [latestMessage, setLatestMessage] = useState<WebSocketEventMap['message']>();
   const [readyState, setReadyState] = useState<ReadyState>(ReadyState.Closed);
@@ -52,7 +56,7 @@ export default function useWebSocket(socketUrl: string, options: Options = {}):
     if (ws) {
       setReadyState(ws?.readyState);
     } else {
-      if (reconnectTimerRef.current) clearTimeout(reconnectTimerRef.current);
+      // if (reconnectTimerRef.current) clearTimeout(reconnectTimerRef.current);
 
       // if (websocketRef.current) {
       //   // 此处应考虑状态。
@@ -60,16 +64,21 @@ export default function useWebSocket(socketUrl: string, options: Options = {}):
       // }
 
       try {
-        console.log(websocketRef.current, 'current');
+        // console.log(websocketRef.current, 'current');
         websocketRef.current = new WebSocket(socketUrl);
 
         websocketRef.current.onerror = (event) => {
+          isReconnect.current = true; // 开启重连
           // eslint-disable-next-line @typescript-eslint/no-use-before-define
           reconnect();
           onError?.(event);
           setReadyState(websocketRef.current?.readyState || ReadyState.Closed);
         };
         websocketRef.current.onopen = (event) => {
+          if (isReconnect.current && onReconnect) {
+            // 是否为重连
+            onReconnect();
+          }
           onOpen?.(event);
           reconnectTimesRef.current = 0;
           setReadyState(websocketRef.current?.readyState || ReadyState.Closed);
@@ -79,10 +88,12 @@ export default function useWebSocket(socketUrl: string, options: Options = {}):
           setLatestMessage(message);
         };
         websocketRef.current.onclose = (event) => {
+          isReconnect.current = true; // 开启重连
           // eslint-disable-next-line @typescript-eslint/no-use-before-define
           reconnect();
           onClose?.(event);
           setReadyState(websocketRef.current?.readyState || ReadyState.Closed);
+          Store.set(SystemConst.GLOBAL_WEBSOCKET, null);
         };
         Store.set(SystemConst.GLOBAL_WEBSOCKET, websocketRef.current);
       } catch (error) {
@@ -91,22 +102,37 @@ export default function useWebSocket(socketUrl: string, options: Options = {}):
     }
   });
 
+  const getTime = (time: number): number => {
+    const m = 60 * 1000;
+    if (time <= 5) {
+      return 3000;
+    } else if (time > 5 && time <= 10) {
+      return 10000;
+    } else if (time > 10 && time <= 20) {
+      return m;
+    }
+    return 5 * m;
+  };
+
   /**
    * 重连
    */
   const reconnect = usePersistFn(() => {
-    if (
-      reconnectTimesRef.current < reconnectLimit &&
-      websocketRef.current?.readyState !== ReadyState.Open
-    ) {
-      if (reconnectTimerRef.current) {
-        clearTimeout(reconnectTimerRef.current);
-      }
-      reconnectTimerRef.current = setTimeout(() => {
-        connectWs();
-        reconnectTimesRef.current += 1;
-      }, reconnectInterval);
+    if (lockReconnect.current) {
+      return;
     }
+
+    if (reconnectTimerRef.current) {
+      clearTimeout(reconnectTimerRef.current);
+    }
+
+    lockReconnect.current = true;
+    const _time = getTime(reconnectTimesRef.current);
+    reconnectTimerRef.current = setTimeout(() => {
+      lockReconnect.current = false;
+      reconnectTimesRef.current += 1;
+      connectWs();
+    }, _time);
   });
 
   /**
@@ -142,7 +168,7 @@ export default function useWebSocket(socketUrl: string, options: Options = {}):
   const disconnect = usePersistFn(() => {
     if (reconnectTimerRef.current) clearTimeout(reconnectTimerRef.current);
 
-    reconnectTimesRef.current = reconnectLimit;
+    reconnectTimesRef.current = 0;
     websocketRef.current?.close();
   });
 

+ 31 - 5
src/pages/device/Instance/index.tsx

@@ -519,12 +519,38 @@ const Instance = () => {
         onSearch={(data) => {
           actionRef.current?.reset?.();
           setSearchParams(data);
+          const terms1 = data.terms[0]?.terms?.map((e) => {
+            if (e.column === 'classifiedId') {
+              return {
+                column: 'productId$product-info',
+                value: [e],
+              };
+            } else {
+              return e;
+            }
+          });
+          if (data.terms && data.terms.length === 2) {
+            const terms2 = data.terms[1]?.terms?.map((e) => {
+              if (e.column === 'classifiedId') {
+                return {
+                  column: 'productId$product-info',
+                  value: [e],
+                };
+              } else {
+                return e;
+              }
+            });
+            setSearchParams({
+              ...searchParams,
+              terms: [{ terms: terms1 }, { terms: terms2, type: data.terms[1].type }],
+            });
+          } else {
+            setSearchParams({
+              ...searchParams,
+              terms: [{ terms: terms1 }],
+            });
+          }
         }}
-        // onReset={() => {
-        //   // 重置分页及搜索参数
-        //   actionRef.current?.reset?.();
-        //   setSearchParams({});
-        // }}
       />
       <ProTableCard<DeviceInstance>
         columns={columns}

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

@@ -227,6 +227,7 @@ export default () => {
       },
       tooltip: {
         trigger: 'axis',
+        valueFormatter: (value) => `${value}M`,
       },
       yAxis: {
         type: 'value',
@@ -238,7 +239,7 @@ export default () => {
       color: ['#979AFF'],
       series: Object.keys(data).length
         ? Object.keys(data).map((key) => ({
-            data: data[key]._data,
+            data: data[key]._data.map((item: number) => Number((item / 1024 / 1024).toFixed(2))),
             name: key,
             type: 'line',
             smooth: true,
@@ -273,6 +274,7 @@ export default () => {
       },
       tooltip: {
         trigger: 'axis',
+        valueFormatter: (value) => `${value}%`,
       },
       yAxis: {
         type: 'value',
@@ -299,6 +301,7 @@ export default () => {
             name: key,
             type: 'line',
             smooth: true,
+            symbol: 'none',
             areaStyle: {
               color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [
                 {
@@ -330,6 +333,7 @@ export default () => {
       },
       tooltip: {
         trigger: 'axis',
+        valueFormatter: (value) => `${value}%`,
       },
       yAxis: {
         type: 'value',
@@ -356,6 +360,7 @@ export default () => {
             name: key,
             type: 'line',
             smooth: true,
+            symbol: 'none',
             areaStyle: {
               color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [
                 {

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

@@ -14,7 +14,7 @@ import WriteProperty from './device/WriteProperty';
 import ReadProperty from './device/readProperty';
 import FunctionCall from './device/functionCall';
 import { InputNumber } from '../components';
-import { DeleteOutlined } from '@ant-design/icons';
+import { ArrowUpOutlined, DeleteOutlined, ArrowDownOutlined } from '@ant-design/icons';
 import { observer } from '@formily/reactive-react';
 import ConditionalFiltering from './device/ConditionalFiltering';
 
@@ -25,6 +25,8 @@ interface ActionProps {
   title?: string;
   triggerType: string;
   onRemove: () => void;
+  onMove: (type: 'up' | 'down') => void;
+  isLast?: boolean;
   actionItemData?: any;
   trigger?: any;
   parallel?: boolean;
@@ -32,6 +34,8 @@ interface ActionProps {
 
 export default observer((props: ActionProps) => {
   const { name } = props;
+  console.log('name', name);
+  console.log('isLast', props.isLast);
   const [type1, setType1] = useState('');
   // 消息通知
   const [notifyType, setNotifyType] = useState('');
@@ -239,6 +243,30 @@ export default observer((props: ActionProps) => {
         >
           <DeleteOutlined />
         </Button>
+        <Button
+          onClick={() => {
+            props.onMove?.('up');
+          }}
+          disabled={name === 0}
+          style={{
+            padding: '0 8px',
+            margin: '0 0 12px 12px',
+          }}
+        >
+          <ArrowUpOutlined />
+        </Button>
+        <Button
+          onClick={() => {
+            props.onMove?.('down');
+          }}
+          disabled={props.isLast}
+          style={{
+            padding: '0 8px',
+            margin: '0 0 12px 12px',
+          }}
+        >
+          <ArrowDownOutlined />
+        </Button>
       </div>
       <Row gutter={24}>
         <Col span={4}>

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

@@ -346,7 +346,7 @@ export default () => {
                 }
               >
                 <Form.List name="actions">
-                  {(fields, { add, remove }, { errors }) => (
+                  {(fields, { add, remove, move }, { errors }) => (
                     <>
                       <div className={'scene-actions'} style={{ paddingBottom: 24 }}>
                         {fields.map(({ key, name, ...restField }) => (
@@ -358,7 +358,15 @@ export default () => {
                             trigger={actionParams}
                             triggerType={triggerType}
                             onRemove={() => remove(name)}
+                            onMove={(type) => {
+                              if (type === 'up') {
+                                move(name, name - 1);
+                              } else {
+                                move(name, name + 1);
+                              }
+                            }}
                             actionItemData={actionsData.length && actionsData[name]}
+                            isLast={!actionsData.length || actionsData.length - 1 === name}
                             parallel={parallel}
                           />
                         ))}

+ 63 - 40
src/pages/system/Menu/components/permission.tsx

@@ -13,11 +13,13 @@ type PermissionDataType = {
   actions: PermissionDataType[];
 };
 
+type PermissionValueType = {
+  permission: string;
+  actions: string[];
+};
+
 type PermissionType = {
-  value?: {
-    permission: string;
-    actions: string[];
-  }[];
+  value?: PermissionValueType[];
   data: PermissionDataType[];
   title?: React.ReactNode | string;
   onChange?: (data: PermissionInfo[]) => void;
@@ -120,6 +122,8 @@ const ParentNode = (props: ParentNodeType) => {
   );
 };
 
+const checkKeysRef = new Map<string, PermissionValueType>();
+
 export default (props: PermissionType) => {
   // const [indeterminate, setIndeterminate] = useState(false);
   // const [checkAll, setCheckAll] = useState(false);
@@ -127,18 +131,26 @@ export default (props: PermissionType) => {
   const checkListRef = useRef<CheckItem[]>([]);
   // const intl = useIntl();
 
-  const onChange = (list: CheckItem[]) => {
+  const onChange = () => {
     if (props.onChange) {
-      const _list = list
-        .filter((a) => a.checked || a.actions.filter((b) => b.checked).length)
-        .map((item) => ({
-          permission: item.id,
-          actions: item.actions.filter((b) => b.checked).map((b) => b.action),
-        }));
-      props.onChange(_list);
+      // list
+      //   .filter((a) => a.checked || a.actions.filter((b) => b.checked).length)
+      //   .map((item) => {
+      //     const value = {
+      //       permission: item.id,
+      //       actions: item.actions.filter((b) => b.checked).map((b) => b.action),
+      //     }
+      //     checkKeysRef.current?.set(item.id, value)
+      //     console.log([...checkKeysRef.current!.values()])
+      //   });
+      props.onChange([...checkKeysRef.values()]);
     }
   };
 
+  useEffect(() => {
+    checkKeysRef.clear();
+  }, []);
+
   /**
    * 全选或者全部取消
    * @param e
@@ -163,37 +175,46 @@ export default (props: PermissionType) => {
   const parentChange = (value: ParentNodeChange) => {
     // let indeterminateCount = 0;
     // let _checkAll = 0;
-    const list = checkListRef.current.map((item) => {
-      const _checked = item.id === value.id ? value.checkedAll : item.checked;
-      const _state = item.id === value.id ? value.state : item.state;
-      const actions =
-        item.id === value.id
-          ? item.actions.map((a) => ({ ...a, checked: value.list.includes(a.action) }))
-          : item.actions;
-      if (_checked) {
-        // 父checkbox为全选或者有子节点被选中
-        // _checkAll += 1;
-        // indeterminateCount += 1;
-      } else if (_state) {
-        // 父checkbox下
-        // indeterminateCount += 1;
-      }
-
-      return {
-        ...item,
-        actions,
-        state: _state,
-        checked: _checked,
+    if (value.list.length) {
+      const _value = {
+        permission: value.id,
+        actions: value.list,
       };
-    });
+      checkKeysRef.set(value.id, _value);
+    } else if (checkKeysRef.has(value.id)) {
+      checkKeysRef.delete(value.id);
+    }
+    // const list = checkListRef.current.map((item) => {
+    //   const _checked = item.id === value.id ? value.checkedAll : item.checked;
+    //   const _state = item.id === value.id ? value.state : item.state;
+    //   const actions =
+    //     item.id === value.id
+    //       ? item.actions.map((a) => ({ ...a, checked: value.list.includes(a.action) }))
+    //       : item.actions;
+    //   if (_checked) {
+    //     // 父checkbox为全选或者有子节点被选中
+    //     // _checkAll += 1;
+    //     // indeterminateCount += 1;
+    //   } else if (_state) {
+    //     // 父checkbox下
+    //     // indeterminateCount += 1;
+    //   }
+    //
+    //   return {
+    //     ...item,
+    //     actions,
+    //     state: _state,
+    //     checked: _checked,
+    //   };
+    // });
     // 如果全部选中,则取消半选状态
     // const isIndeterminate =
     //   _checkAll === list.length && _checkAll !== 0 ? false : !!indeterminateCount;
     // setIndeterminate(isIndeterminate);
     // setCheckAll(_checkAll === list.length && _checkAll !== 0);
     // setCheckedList(list)
-    checkListRef.current = list;
-    onChange(list);
+    // checkListRef.current = list;
+    onChange();
   };
 
   /**
@@ -224,12 +245,14 @@ export default (props: PermissionType) => {
    * @param data
    */
   const initialState = (data: PermissionDataType[]) => {
+    props.value?.forEach((item) => {
+      checkKeysRef.set(item.permission, item);
+    });
     const _list = data.map((item) => {
-      const propsPermission =
-        props.value && props.value.length
-          ? props.value.find((p) => p.permission === item.id)
-          : undefined;
+      let propsPermission = checkKeysRef.get(item.id);
+
       const propsActions = propsPermission ? propsPermission.actions : [];
+
       return {
         ...item,
         actions: item.actions