wzyyy 3 سال پیش
والد
کامیت
c58598216e
24فایلهای تغییر یافته به همراه1180 افزوده شده و 491 حذف شده
  1. 8 6
      src/components/BindParentDevice/index.tsx
  2. 33 0
      src/components/ProTableCard/CardItems/AccessConfig/index.less
  3. 6 0
      src/components/ProTableCard/CardItems/AccessConfig/index.tsx
  4. 6 1
      src/components/ProTableCard/CardItems/device.tsx
  5. 6 1
      src/components/ProTableCard/CardItems/product.tsx
  6. 33 0
      src/components/ProTableCard/index.less
  7. 13 2
      src/hooks/websocket/useSendWebsocketMessage.ts
  8. 46 20
      src/hooks/websocket/useWebSocket.ts
  9. 3 0
      src/pages/device/Instance/Detail/Diagnose/Message/index.tsx
  10. 6 7
      src/pages/device/Instance/Detail/Diagnose/Status/DiagnosticAdvice.tsx
  11. 771 345
      src/pages/device/Instance/Detail/Diagnose/Status/index.tsx
  12. 3 0
      src/pages/device/Instance/Detail/Diagnose/Status/model.ts
  13. 1 1
      src/pages/device/Instance/Detail/Diagnose/index.tsx
  14. 22 36
      src/pages/device/Instance/Detail/MetadataMap/EditableTable/index.tsx
  15. 1 0
      src/pages/device/Instance/Detail/Running/Property/index.tsx
  16. 31 5
      src/pages/device/Instance/index.tsx
  17. 1 1
      src/pages/device/Product/Save/index.tsx
  18. 38 0
      src/pages/link/AccessConfig/Detail/Access/index.less
  19. 41 22
      src/pages/link/AccessConfig/Detail/Access/index.tsx
  20. 6 1
      src/pages/link/DashBoard/index.tsx
  21. 4 1
      src/pages/link/Type/Detail/index.tsx
  22. 29 1
      src/pages/rule-engine/Scene/Save/action/action.tsx
  23. 9 1
      src/pages/rule-engine/Scene/Save/index.tsx
  24. 63 40
      src/pages/system/Menu/components/permission.tsx

+ 8 - 6
src/components/BindParentDevice/index.tsx

@@ -80,12 +80,14 @@ const BindParentDevice = (props: Props) => {
   ];
 
   const submitBtn = async () => {
-    // const resp = await service.bindDevice(InstanceModel.detail.id!, bindKeys);
-    // if (resp.status === 200) {
-    //   props.onCancel();
-    //   setBindKeys([]);
-    //   actionRef.current?.reset?.();
-    // }
+    if (props.data?.id) {
+      const resp = await service.bindDevice(bindKeys[0], [props.data?.id]);
+      if (resp.status === 200) {
+        props.onOk(bindKeys[0]);
+        setBindKeys([]);
+        actionRef.current?.reset?.();
+      }
+    }
   };
 
   return (

+ 33 - 0
src/components/ProTableCard/CardItems/AccessConfig/index.less

@@ -2,7 +2,13 @@
 
 .access-config-card-item {
   &.active {
+    position: relative;
+    color: @primary-color-active;
     border: 1px solid @primary-color-active;
+
+    .checked-icon {
+      display: block;
+    }
   }
 
   .tableCardDisabled {
@@ -96,6 +102,33 @@
       }
     }
   }
+
+  .checked-icon {
+    position: absolute;
+    right: -22px;
+    bottom: -22px;
+    z-index: 2;
+    display: none;
+    width: 44px;
+    height: 44px;
+    color: #fff;
+    background-color: red;
+    background-color: @primary-color-active;
+    transform: rotate(-45deg);
+
+    > div {
+      position: relative;
+      height: 100%;
+      transform: rotate(45deg);
+
+      > span {
+        position: absolute;
+        top: 6px;
+        left: 6px;
+        font-size: 12px;
+      }
+    }
+  }
 }
 
 :global {

+ 6 - 0
src/components/ProTableCard/CardItems/AccessConfig/index.tsx

@@ -7,6 +7,7 @@ import type { AccessItem } from '@/pages/link/AccessConfig/typings';
 import './index.less';
 import classNames from 'classnames';
 import { Store } from 'jetlinks-store';
+import { CheckOutlined } from '@ant-design/icons';
 
 export interface AccessConfigCardProps extends AccessItem {
   detail?: React.ReactNode;
@@ -108,6 +109,11 @@ export default (props: AccessConfigCardProps) => {
           </div>
         </div>
       </div>
+      <div className={'checked-icon'}>
+        <div>
+          <CheckOutlined />
+        </div>
+      </div>
     </TableCard>
   );
 };

+ 6 - 1
src/components/ProTableCard/CardItems/device.tsx

@@ -4,7 +4,7 @@ import { StatusColorEnum } from '@/components/BadgeStatus';
 import { TableCard } from '@/components';
 import '@/style/common.less';
 import '../index.less';
-import { DisconnectOutlined } from '@ant-design/icons';
+import { CheckOutlined, DisconnectOutlined } from '@ant-design/icons';
 import { Popconfirm, Tooltip } from 'antd';
 import { useIntl } from '@@/plugin-locale/localeExports';
 
@@ -120,6 +120,11 @@ export const ExtraDeviceCard = (props: DeviceCardProps) => {
           </div>
         </div>
       </div>
+      <div className={'checked-icon'}>
+        <div>
+          <CheckOutlined />
+        </div>
+      </div>
     </TableCard>
   );
 };

+ 6 - 1
src/components/ProTableCard/CardItems/product.tsx

@@ -6,7 +6,7 @@ import { TableCard } from '@/components';
 import '@/style/common.less';
 import '../index.less';
 import { Popconfirm, Tooltip } from 'antd';
-import { DisconnectOutlined } from '@ant-design/icons';
+import { CheckOutlined, DisconnectOutlined } from '@ant-design/icons';
 
 export interface ProductCardProps extends Partial<ProductItem> {
   detail?: React.ReactNode;
@@ -124,6 +124,11 @@ export const ExtraProductCard = (props: ProductCardProps) => {
           </div>
         </div>
       </div>
+      <div className={'checked-icon'}>
+        <div>
+          <CheckOutlined />
+        </div>
+      </div>
     </TableCard>
   );
 };

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

@@ -144,7 +144,40 @@
   background-color: #fff;
 
   &.item-active {
+    position: relative;
+    color: @primary-color-active;
     border: 1px solid @primary-color-active;
+
+    .checked-icon {
+      display: block;
+    }
+  }
+
+  .checked-icon {
+    position: absolute;
+    right: -22px;
+    bottom: -22px;
+    z-index: 2;
+    display: none;
+    width: 44px;
+    height: 44px;
+    color: #fff;
+    background-color: red;
+    background-color: @primary-color-active;
+    transform: rotate(-45deg);
+
+    > div {
+      position: relative;
+      height: 100%;
+      transform: rotate(45deg);
+
+      > span {
+        position: absolute;
+        top: 6px;
+        left: 6px;
+        font-size: 12px;
+      }
+    }
   }
 
   &.hover {

+ 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();
   });
 

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

@@ -328,6 +328,9 @@ const Message = observer(() => {
                   type: 'string',
                   'x-decorator': 'FormItem',
                   'x-component': 'PreviewText.Input',
+                  // 'x-decorator-props': {
+                  //   tooltip: '使用固定的通知配置来发送此通知模版',
+                  // },
                 },
               },
             },

+ 6 - 7
src/pages/device/Instance/Detail/Diagnose/Status/DiagnosticAdvice.tsx

@@ -21,7 +21,7 @@ const DiagnosticAdvice = (props: Props) => {
       onOk={() => {
         props.close();
       }}
-      width={900}
+      width={1000}
       visible
     >
       <div>
@@ -53,7 +53,7 @@ const DiagnosticAdvice = (props: Props) => {
                 title={
                   <div className="serverItem">
                     {(data?.info?.address || []).map((i: any) => (
-                      <div key={i.address}>
+                      <div key={i.address} className="eellipsiss">
                         <Badge color={i.health === -1 ? 'red' : 'green'} />
                         {i.address}
                       </div>
@@ -63,11 +63,10 @@ const DiagnosticAdvice = (props: Props) => {
               >
                 <div className="serverItem">
                   {(data?.info?.address || []).slice(0, 1).map((i: any) => (
-                    <Badge
-                      key={i.address}
-                      color={i.health === -1 ? 'red' : 'green'}
-                      text={i.address}
-                    />
+                    <div key={i.address} className="eellipsiss">
+                      <Badge color={i.health === -1 ? 'red' : 'green'} />
+                      {i.address}
+                    </div>
                   ))}
                 </div>
               </Tooltip>

تفاوت فایلی نمایش داده نمی شود زیرا این فایل بسیار بزرگ است
+ 771 - 345
src/pages/device/Instance/Detail/Diagnose/Status/index.tsx


+ 3 - 0
src/pages/device/Instance/Detail/Diagnose/Status/model.ts

@@ -1,6 +1,7 @@
 import type { ProductItem } from '@/pages/device/Product/typings';
 import { model } from '@formily/reactive';
 import type { ReactNode } from 'react';
+import { DeviceInstance } from '../../../typings';
 
 export const StatusMap = new Map();
 StatusMap.set('error', require('/public/images/diagnose/status/error.png'));
@@ -231,6 +232,7 @@ export const DiagnoseStatusModel = model<{
   list: ListProps[];
   product: Partial<ProductItem>;
   gateway: any;
+  parent: Partial<DeviceInstance>;
   configuration: {
     product: any[];
     device: any[];
@@ -255,6 +257,7 @@ export const DiagnoseStatusModel = model<{
 }>({
   list: [],
   product: {},
+  parent: {},
   gateway: {},
   configuration: {
     product: [],

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

@@ -35,7 +35,7 @@ const Diagnose = observer(() => {
     if (provider === 'fixed-media' || provider === 'gb28181-2016') {
       setProviderType('media');
     } else if (provider === 'OneNet' || provider === 'Ctwing') {
-      setProviderType('media');
+      setProviderType('cloud');
     } else if (provider === 'modbus-tcp' || provider === 'opc-ua') {
       setProviderType('channel');
     } else if (provider === 'child-device') {

+ 22 - 36
src/pages/device/Instance/Detail/MetadataMap/EditableTable/index.tsx

@@ -1,10 +1,10 @@
 import React, { useContext, useEffect, useState } from 'react';
-import { Badge, Col, Form, Input, Pagination, Row, Select, Table, Tooltip } from 'antd';
+import { AutoComplete, Badge, Col, Form, Input, Pagination, Row, Table, Tooltip } from 'antd';
 import { service } from '@/pages/device/Instance';
 import './index.less';
 import { QuestionCircleOutlined } from '@ant-design/icons';
 import { onlyMessage } from '@/utils/util';
-// import { throttle } from 'lodash';
+// import { debounce } from 'lodash';
 
 const defaultImage = require('/public/images/metadata-map.png');
 
@@ -46,15 +46,17 @@ const EditableCell = ({
 }: EditableCellProps) => {
   const form: any = useContext(EditableContext);
   const [temp, setTemp] = useState<any>({});
+  const uuid = '使用物模型属性';
 
   const save = async () => {
     try {
       const values = await form.validateFields();
-      handleSave({
+      const dt = {
         ...record,
-        originalId: !!values?.originalId ? values?.originalId : record.metadataId,
-        customMapping: values?.originalId !== '',
-      });
+        originalId: values?.originalId === uuid ? '' : values?.originalId,
+        customMapping: values?.originalId !== uuid,
+      };
+      handleSave(dt);
     } catch (errInfo) {
       console.log('Save failed:', errInfo);
     }
@@ -62,7 +64,7 @@ const EditableCell = ({
 
   useEffect(() => {
     if (record) {
-      form.setFieldsValue({ [dataIndex]: record.customMapping ? record[dataIndex] : '' });
+      form.setFieldsValue({ [dataIndex]: record.customMapping ? record[dataIndex] : uuid });
       setTemp(properties.find((i) => i.id === record.originalId));
     }
   }, [record]);
@@ -72,41 +74,25 @@ const EditableCell = ({
   if (editable) {
     childNode = (
       <Form.Item style={{ margin: 0 }} name={dataIndex}>
-        {/* <AutoComplete onChange={throttle(save, 5000)}>
-          <AutoComplete.Option value={''}>使用物模型属性</AutoComplete.Option>
+        <AutoComplete
+          allowClear
+          onBlur={() => {
+            save();
+          }}
+        >
+          <AutoComplete.Option value={uuid}>使用物模型属性</AutoComplete.Option>
           {record.customMapping && temp && (
             <AutoComplete.Option value={record.originalId}>
               {temp?.name}({temp?.id})
             </AutoComplete.Option>
           )}
-          {tempList.length > 0 &&
-            tempList.map((item: any) => (
-              <AutoComplete.Option key={item?.id} value={item?.id}>
-                {item?.name}({item?.id})
-              </AutoComplete.Option>
-            ))}
-        </AutoComplete> */}
-        <Select
-          onChange={save}
-          showSearch
-          optionFilterProp="children"
-          filterOption={(input: string, option: any) =>
-            (option?.children || '').toLowerCase()?.indexOf(input.toLowerCase()) >= 0
-          }
-        >
-          <Select.Option value={''}>使用物模型属性</Select.Option>
-          {record.customMapping && (
-            <Select.Option value={record.originalId}>
-              {temp?.name}({temp?.id})
-            </Select.Option>
-          )}
           {list.length > 0 &&
             list.map((item: any) => (
-              <Select.Option key={item?.id} value={item?.id}>
+              <AutoComplete.Option key={item?.id} value={item?.id}>
                 {item?.name}({item?.id})
-              </Select.Option>
+              </AutoComplete.Option>
             ))}
-        </Select>
+        </AutoComplete>
       </Form.Item>
     );
   }
@@ -119,7 +105,7 @@ interface Props {
 }
 
 const EditableTable = (props: Props) => {
-  const baseColumns = [
+  const baseColumns: any = [
     {
       title: '物模型属性',
       dataIndex: 'name',
@@ -203,7 +189,7 @@ const EditableTable = (props: Props) => {
           const t = data.find((item: any) => item?.originalId === i?.id);
           return !t || (t && !t.customMapping);
         });
-        setPmList(arr);
+        setPmList([...arr]);
       } else {
         setPmList([]);
       }
@@ -295,7 +281,7 @@ const EditableTable = (props: Props) => {
     }
   };
 
-  const columns = baseColumns.map((col) => {
+  const columns = baseColumns.map((col: any) => {
     if (!col.editable) {
       return col;
     }

+ 1 - 0
src/pages/device/Instance/Detail/Running/Property/index.tsx

@@ -237,6 +237,7 @@ const Property = (props: Props) => {
 
   useEffect(() => {
     if (dataSource.data.length > 0) {
+      setLoading1(true);
       getDashboard();
     } else {
       setLoading(false);

+ 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}

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

@@ -282,7 +282,7 @@ const Save = (props: Props) => {
                   });
                 }}
                 filterTreeNode={(input, treeNode) => treeNode.name.includes(input)}
-                placeholder={`${intlFormat('pages.form.tip.select', '请选择')}分类`}
+                placeholder={`${intlFormat('pages.form.tip.select', '请选择')}产品分类`}
                 fieldNames={{
                   label: 'name',
                   value: 'id',

+ 38 - 0
src/pages/link/AccessConfig/Detail/Access/index.less

@@ -1,3 +1,4 @@
+@import '~antd/es/style/themes/default.less';
 .box {
   display: flex;
   flex-direction: column;
@@ -103,6 +104,43 @@
 
 .cardRender {
   width: 100%;
+  overflow: hidden;
   background: url('/images/access.png') no-repeat;
   background-size: 100% 100%;
+
+  .checkedIcon {
+    position: absolute;
+    right: -22px;
+    bottom: -22px;
+    z-index: 2;
+    display: none;
+    width: 44px;
+    height: 44px;
+    color: #fff;
+    background-color: red;
+    background-color: @primary-color-active;
+    transform: rotate(-45deg);
+
+    > div {
+      position: relative;
+      height: 100%;
+      transform: rotate(45deg);
+
+      > span {
+        position: absolute;
+        top: 6px;
+        left: 6px;
+        font-size: 12px;
+      }
+    }
+  }
+  &.checked {
+    position: relative;
+    color: @primary-color-active;
+    border-color: @primary-color-active;
+
+    .checkedIcon {
+      display: block;
+    }
+  }
 }

+ 41 - 22
src/pages/link/AccessConfig/Detail/Access/index.tsx

@@ -6,12 +6,13 @@ import encodeQuery from '@/utils/encodeQuery';
 import { useHistory } from 'umi';
 import ReactMarkdown from 'react-markdown';
 import { getButtonPermission, getMenuPathByCode, MENUS_CODE } from '@/utils/menu';
-import { InfoCircleOutlined } from '@ant-design/icons';
+import { CheckOutlined, InfoCircleOutlined } from '@ant-design/icons';
 import TitleComponent from '@/components/TitleComponent';
 import { PermissionButton } from '@/components';
 import { useDomFullHeight } from '@/hooks';
 import { onlyMessage } from '@/utils/util';
 import { descriptionList, MetworkTypeMapping, ProcotoleMapping } from './data';
+import classNames from 'classnames';
 
 interface Props {
   change: () => void;
@@ -27,6 +28,7 @@ const Access = (props: Props) => {
   const [current, setCurrent] = useState<number>(0);
   const [networkList, setNetworkList] = useState<any[]>([]);
   const [procotolList, setProcotolList] = useState<any[]>([]);
+  const [allProcotolList, setAllProcotolList] = useState<any[]>([]);
   const [procotolCurrent, setProcotolCurrent] = useState<string>('');
   const [networkCurrent, setNetworkCurrent] = useState<string>('');
   const [config, setConfig] = useState<any>();
@@ -46,6 +48,7 @@ const Access = (props: Props) => {
     service.getProtocolList(ProcotoleMapping.get(id), params).then((resp) => {
       if (resp.status === 200) {
         setProcotolList(resp.result);
+        setAllProcotolList(resp.result);
       }
     });
   };
@@ -279,7 +282,9 @@ const Access = (props: Props) => {
                 isPermission={networkPermission.add}
                 onClick={() => {
                   const url = getMenuPathByCode(MENUS_CODE['link/Type/Detail']);
-                  const tab: any = window.open(`${origin}/#${url}`);
+                  const tab: any = window.open(
+                    `${origin}/#${url}?type=${MetworkTypeMapping.get(props.provider?.id) || ''}`,
+                  );
                   tab!.onTabSaveSuccess = (value: any) => {
                     if (value.status === 200) {
                       queryNetworkList(props.provider?.id, {
@@ -299,12 +304,10 @@ const Access = (props: Props) => {
                 {networkList.map((item) => (
                   <Col key={item.id} span={8}>
                     <Card
-                      className={styles.cardRender}
-                      style={{
-                        width: '100%',
-                        borderColor:
-                          networkCurrent === item.id ? 'var(--ant-primary-color-active)' : '',
-                      }}
+                      className={classNames(
+                        styles.cardRender,
+                        networkCurrent === item.id ? styles.checked : '',
+                      )}
                       hoverable
                       onClick={() => {
                         setNetworkCurrent(item.id);
@@ -360,6 +363,11 @@ const Access = (props: Props) => {
                           </Tooltip>
                         </div>
                       </div>
+                      <div className={styles.checkedIcon}>
+                        <div>
+                          <CheckOutlined />
+                        </div>
+                      </div>
                     </Card>
                   </Col>
                 ))}
@@ -409,14 +417,16 @@ const Access = (props: Props) => {
                 allowClear
                 placeholder="请输入名称"
                 onSearch={(value: string) => {
-                  queryProcotolList(
-                    props.provider?.id,
-                    encodeQuery({
-                      terms: {
-                        name$LIKE: `%${value}%`,
-                      },
-                    }),
-                  );
+                  if (value) {
+                    const list = allProcotolList.filter((i) => {
+                      return (
+                        i?.name && i.name.toLocaleLowerCase().includes(value.toLocaleLowerCase())
+                      );
+                    });
+                    setProcotolList(list);
+                  } else {
+                    setProcotolList(allProcotolList);
+                  }
                 }}
                 style={{ width: 500, margin: '20px 0' }}
               />
@@ -442,12 +452,16 @@ const Access = (props: Props) => {
                 {procotolList.map((item) => (
                   <Col key={item.id} span={8}>
                     <Card
-                      className={styles.cardRender}
-                      style={{
-                        width: '100%',
-                        borderColor:
-                          procotolCurrent === item.id ? 'var(--ant-primary-color-active)' : '',
-                      }}
+                      // className={styles.cardRender}
+                      className={classNames(
+                        styles.cardRender,
+                        procotolCurrent === item.id ? styles.checked : '',
+                      )}
+                      // style={{
+                      //   width: '100%',
+                      //   borderColor:
+                      //     procotolCurrent === item.id ? 'var(--ant-primary-color-active)' : '',
+                      // }}
                       hoverable
                       onClick={() => {
                         setProcotolCurrent(item.id);
@@ -463,6 +477,11 @@ const Access = (props: Props) => {
                           </Tooltip>
                         </div>
                       </div>
+                      <div className={styles.checkedIcon}>
+                        <div>
+                          <CheckOutlined />
+                        </div>
+                      </div>
                     </Card>
                   </Col>
                 ))}

+ 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, [
                 {

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

@@ -70,8 +70,11 @@ const Save = observer(() => {
     field.loading = true;
     services(field).then(
       action.bound!((resp: any) => {
+        const type = location.href.split('?')?.pop()?.split('=')?.pop() || '';
         const save = location.href.split('/');
-        if (save[save.length - 1] === ':id') {
+        if (location.href.includes('type=') && !!type) {
+          field.value = type;
+        } else if (save[save.length - 1] === ':id') {
           field.value = resp[0].value;
         }
         field.dataSource = resp;

+ 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