wzyyy il y a 3 ans
Parent
commit
142c9d2f45
49 fichiers modifiés avec 550 ajouts et 160 suppressions
  1. 3 3
      src/components/AMapComponent/hooks/Distance.tsx
  2. 73 30
      src/components/FSelectDevices/index.tsx
  3. 1 1
      src/components/ProTableCard/CardItems/duerOs.tsx
  4. 1 0
      src/hooks/websocket/useSendWebsocketMessage.ts
  5. 16 8
      src/hooks/websocket/useWebSocket.ts
  6. 5 0
      src/pages/Northbound/DuerOS/types.d.ts
  7. 25 2
      src/pages/device/Firmware/Save/index.tsx
  8. 16 17
      src/pages/device/Firmware/Task/Save/index.tsx
  9. 229 0
      src/pages/device/Firmware/Task/Save/index1.tsx
  10. 19 15
      src/pages/device/Firmware/Task/index.tsx
  11. 5 0
      src/pages/device/Firmware/service.ts
  12. 4 4
      src/pages/device/Instance/Detail/Diagnose/Status/index.tsx
  13. 5 5
      src/pages/device/Instance/Detail/Diagnose/Status/model.ts
  14. 3 1
      src/pages/device/Product/Detail/Access/AccessConfig/index.tsx
  15. 5 1
      src/pages/device/Product/Detail/PropertyImport/index.tsx
  16. 6 0
      src/pages/device/Product/service.ts
  17. 5 0
      src/pages/device/components/Metadata/Base/Edit/index.tsx
  18. 2 2
      src/pages/device/components/Metadata/Base/index.tsx
  19. 12 0
      src/pages/device/components/Metadata/Import/index.tsx
  20. 25 18
      src/pages/link/Type/Detail/index.tsx
  21. 3 7
      src/pages/notice/Template/Detail/doc/DingTalk.tsx
  22. 1 0
      src/pages/rule-engine/Alarm/Configuration/Save/index.tsx
  23. 1 1
      src/pages/rule-engine/DashBoard/index.tsx
  24. 2 0
      src/pages/rule-engine/Scene/Save/action/VariableItems/builtIn.tsx
  25. 2 0
      src/pages/rule-engine/Scene/Save/action/VariableItems/org.tsx
  26. 2 0
      src/pages/rule-engine/Scene/Save/action/VariableItems/tag.tsx
  27. 2 0
      src/pages/rule-engine/Scene/Save/action/VariableItems/user.tsx
  28. 2 0
      src/pages/rule-engine/Scene/Save/action/device/WriteProperty/index.tsx
  29. 2 0
      src/pages/rule-engine/Scene/Save/action/device/deviceModal.tsx
  30. 2 0
      src/pages/rule-engine/Scene/Save/action/device/readProperty.tsx
  31. 2 0
      src/pages/rule-engine/Scene/Save/action/device/relationSelect.tsx
  32. 2 0
      src/pages/rule-engine/Scene/Save/action/device/tagModal.tsx
  33. 2 0
      src/pages/rule-engine/Scene/Save/components/InputUpload/index.tsx
  34. 2 1
      src/pages/rule-engine/Scene/Save/components/TimeSelect/index.tsx
  35. 2 0
      src/pages/rule-engine/Scene/Save/components/TimingTrigger/RangePicker.tsx
  36. 2 0
      src/pages/rule-engine/Scene/Save/components/TimingTrigger/TimePicker.tsx
  37. 8 1
      src/pages/rule-engine/Scene/Save/trigger/OrgTreeSelect.tsx
  38. 2 0
      src/pages/rule-engine/Scene/Save/trigger/operation.tsx
  39. 1 1
      src/pages/system/DataSource/Management/RemoveData.tsx
  40. 1 1
      src/pages/system/Department/Assets/product/index.tsx
  41. 1 8
      src/pages/system/Department/Member/bind.tsx
  42. 0 3
      src/pages/system/Department/Member/index.tsx
  43. 2 3
      src/pages/system/Menu/Detail/buttons.tsx
  44. 0 1
      src/pages/system/Menu/Setting/baseMenu.ts
  45. 4 4
      src/pages/system/Menu/Setting/index.tsx
  46. 26 17
      src/pages/system/Menu/Setting/tree.tsx
  47. 8 1
      src/pages/system/Menu/index.tsx
  48. 1 0
      src/pages/system/Role/Detail/UserManage/BindUser.tsx
  49. 5 4
      src/pages/user/Login/service.ts

+ 3 - 3
src/components/AMapComponent/hooks/Distance.tsx

@@ -4,9 +4,9 @@ const useDistance = () => {
   const [distance, setDistance] = useState(0);
 
   const onDistance = (data: number[][]) => {
-    if (window.AMap && data && data.length >= 2) {
-      const pointArr = data.map((point) => new window.AMap.LngLat(point[0], point[1]));
-      const distanceOfLine = AMap.GeometryUtil.distanceOfLine(pointArr);
+    if ((window as any).AMap && data && data.length >= 2) {
+      const pointArr = data.map((point) => new (window as any).AMap.LngLat(point[0], point[1]));
+      const distanceOfLine = (window as any).AMap.GeometryUtil.distanceOfLine(pointArr);
       setDistance(Math.round(distanceOfLine));
     }
   };

+ 73 - 30
src/components/FSelectDevices/index.tsx

@@ -1,28 +1,38 @@
-import { Input, Modal } from 'antd';
+import { Badge, Input, Modal } from 'antd';
 import { EditOutlined } from '@ant-design/icons';
 import type { Key } from 'react';
 import { useRef, useState } from 'react';
-import { connect } from '@formily/react';
 import type { ActionType, ProColumns } from '@jetlinks/pro-table';
 import ProTable from '@jetlinks/pro-table';
 import type { DeviceInstance } from '@/pages/device/Instance/typings';
 import { useIntl } from '@@/plugin-locale/localeExports';
 import Service from '@/pages/device/Instance/service';
 import SearchComponent from '../SearchComponent';
-import _ from 'lodash';
+import { observer } from '@formily/react';
+import { model } from '@formily/reactive';
 
 interface Props {
-  value: Partial<DeviceInstance>[];
-  onChange: (data: Partial<DeviceInstance>[]) => void;
+  value?: Partial<DeviceInstance>[];
+  onChange?: (data: Partial<DeviceInstance>[]) => void;
   productId?: string;
 }
 
-export const service = new Service('device/instance');
-const FSelectDevices = connect((props: Props) => {
+const deviceStatus = new Map();
+deviceStatus.set('online', <Badge status="success" text={'在线'} />);
+deviceStatus.set('offline', <Badge status="error" text={'离线'} />);
+deviceStatus.set('notActive', <Badge status="processing" text={'禁用'} />);
+
+const service = new Service('device/instance');
+const State = model<{
+  visible: boolean;
+}>({
+  visible: false,
+});
+
+const FSelectDevices = observer((props: Props) => {
   // todo 考虑与单选设备合并
-  const [visible, setVisible] = useState<boolean>(false);
   const intl = useIntl();
-  const actionRef = useRef<ActionType>();
+  const actionRef1 = useRef<ActionType>();
   const [searchParam, setSearchParam] = useState({});
   const columns: ProColumns<DeviceInstance>[] = [
     {
@@ -53,50 +63,87 @@ const FSelectDevices = connect((props: Props) => {
       width: '200px',
       valueType: 'dateTime',
     },
+    {
+      title: '状态',
+      dataIndex: 'state',
+      render: (text: any, record: any) =>
+        record?.state?.value ? deviceStatus.get(record?.state?.value) : '',
+      ellipsis: true,
+    },
   ];
 
   const [data, setData] = useState<Partial<DeviceInstance>[]>(props?.value || []);
   const rowSelection = {
-    onChange: (selectedRowKeys: Key[], selectedRows: DeviceInstance[]) => {
-      const list = [...data];
-      selectedRows.map((item) => {
-        if (!_.map(data, 'id').includes(item.id)) {
-          list.push(item);
-        }
-      });
-      setData(list);
+    onSelect: (selectedRow: any, selected: any) => {
+      let newSelectKeys = [...data];
+      if (selected) {
+        newSelectKeys.push({ ...selectedRow });
+      } else {
+        newSelectKeys = newSelectKeys.filter((item) => item.id !== selectedRow.id);
+      }
+      setData(newSelectKeys);
+    },
+    onSelectAll: (selected: boolean, _: any, changeRows: any) => {
+      let newSelectKeys = [...data];
+      if (selected) {
+        changeRows.forEach((item: any) => {
+          newSelectKeys.push({ ...item });
+        });
+      } else {
+        newSelectKeys = newSelectKeys.filter((a) => {
+          return !changeRows.some((b: any) => b.id === a.id);
+        });
+      }
+      setData(newSelectKeys);
     },
     selectedRowKeys: data?.map((item) => item.id) as Key[],
   };
 
+  const reload = () => {
+    actionRef1.current?.reset?.();
+    setSearchParam({});
+  };
+
   return (
     <>
       <Input
         disabled
         value={props.value?.map((item) => item.name).join(',')}
-        addonAfter={<EditOutlined onClick={() => setVisible(true)} />}
+        addonAfter={
+          <EditOutlined
+            onClick={() => {
+              State.visible = true;
+            }}
+          />
+        }
       />
-      {visible && (
+      {State.visible && (
         <Modal
           maskClosable={false}
           visible
           title="选择设备"
           width="80vw"
-          onCancel={() => setVisible(false)}
+          onCancel={() => {
+            State.visible = false;
+            reload();
+          }}
           onOk={() => {
-            setVisible(false);
-            props.onChange(data);
+            State.visible = false;
+            reload();
+            if (props.onChange) {
+              props.onChange(data);
+            }
           }}
         >
           <SearchComponent<DeviceInstance>
             field={columns}
             enableSave={false}
             model="simple"
-            onSearch={async (data1) => {
+            onSearch={(data1) => {
+              actionRef1.current?.reset?.();
               setSearchParam(data1);
-              actionRef.current?.reset?.();
             }}
-            target="choose-device"
+            target="choose-devices"
           />
           <ProTable<DeviceInstance>
             tableAlertRender={false}
@@ -106,14 +153,10 @@ const FSelectDevices = connect((props: Props) => {
             }}
             search={false}
             columnEmptyText={''}
-            toolBarRender={false}
             rowKey="id"
-            pagination={{
-              pageSize: 10,
-            }}
             params={searchParam}
             columns={columns}
-            actionRef={actionRef}
+            actionRef={actionRef1}
             request={async (params) => {
               return service.queryDetailList({
                 context: {

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

@@ -2,7 +2,7 @@ import React from 'react';
 import { Ellipsis, TableCard } from '@/components';
 import '@/style/common.less';
 import '../index.less';
-import { DuerOSItem } from '@/pages/cloud/DuerOS/typings';
+import { DuerOSItem } from '@/pages/Northbound/DuerOS/types';
 import { StatusColorEnum } from '@/components/BadgeStatus';
 
 export interface DuerOSProps extends DuerOSItem {

+ 1 - 0
src/hooks/websocket/useSendWebsocketMessage.ts

@@ -30,6 +30,7 @@ export const useSendWebsocketMessage = () => {
     if (data.type === 'error') {
       notification.error({ key: 'websocket-error', message: data.message });
     }
+
     if (subscribeList[data.requestId]) {
       if (data.type === 'complete') {
         subscribeList[data.requestId].forEach((element: any) => {

+ 16 - 8
src/hooks/websocket/useWebSocket.ts

@@ -46,7 +46,8 @@ export default function useWebSocket(socketUrl: string, options: Options = {}):
   const reconnectTimerRef = useRef<NodeJS.Timeout>(); // 计时器
   const websocketRef = useRef<WebSocket>();
   const lockReconnect = useRef(false); // 避免重复连接
-  const isReconnect = useRef(false);
+  const isReconnect = useRef(false); //
+  const readyStateRef = useRef<number>(ReadyState.Closed);
 
   const [latestMessage, setLatestMessage] = useState<WebSocketEventMap['message']>();
   const [readyState, setReadyState] = useState<ReadyState>(ReadyState.Closed);
@@ -55,6 +56,7 @@ export default function useWebSocket(socketUrl: string, options: Options = {}):
     const ws = Store.get(SystemConst.GLOBAL_WEBSOCKET) as WebSocket;
     if (ws) {
       setReadyState(ws?.readyState);
+      readyStateRef.current = ws?.readyState;
     } else {
       // if (reconnectTimerRef.current) clearTimeout(reconnectTimerRef.current);
 
@@ -73,16 +75,18 @@ export default function useWebSocket(socketUrl: string, options: Options = {}):
           reconnect();
           onError?.(event);
           setReadyState(websocketRef.current?.readyState || ReadyState.Closed);
+          readyStateRef.current = websocketRef.current?.readyState || ReadyState.Closed;
         };
         websocketRef.current.onopen = (event) => {
           if (isReconnect.current && onReconnect) {
             // 是否为重连
             onReconnect();
-            return
+            return;
           }
           onOpen?.(event);
           reconnectTimesRef.current = 0;
           setReadyState(websocketRef.current?.readyState || ReadyState.Closed);
+          readyStateRef.current = websocketRef.current?.readyState || ReadyState.Closed;
         };
         websocketRef.current.onmessage = (message: WebSocketEventMap['message']) => {
           onMessage?.(message);
@@ -94,6 +98,7 @@ export default function useWebSocket(socketUrl: string, options: Options = {}):
           reconnect();
           onClose?.(event);
           setReadyState(websocketRef.current?.readyState || ReadyState.Closed);
+          readyStateRef.current = websocketRef.current?.readyState || ReadyState.Closed;
           Store.set(SystemConst.GLOBAL_WEBSOCKET, null);
         };
         Store.set(SystemConst.GLOBAL_WEBSOCKET, websocketRef.current);
@@ -143,16 +148,19 @@ export default function useWebSocket(socketUrl: string, options: Options = {}):
   const sendMessage: WebSocket['send'] = usePersistFn((message) => {
     const ws = Store.get(SystemConst.GLOBAL_WEBSOCKET) as WebSocket;
     setReadyState(ws?.readyState);
-    if (readyState === ReadyState.Open) {
+    readyStateRef.current = ws?.readyState;
+
+    if (readyStateRef.current === ReadyState.Open) {
       ws.send(message);
     } else if (ws) {
+      isReconnect.current = true;
       connectWs();
       // todo 考虑重写
-      setTimeout(() => {
-        if (readyState === ReadyState.Open) {
-          ws.send(message);
-        }
-      }, 3000);
+      // setTimeout(() => {
+      //   if (readyStateRef.current === ReadyState.Open) {
+      //     ws.send(message);
+      //   }
+      // }, 3000);
       // throw new Error('WebSocket disconnected');
     }
   });

+ 5 - 0
src/pages/Northbound/DuerOS/types.d.ts

@@ -17,6 +17,11 @@ type DuerOSItem = {
   version: number;
   manufacturerName: string;
   autoReportProperty: boolean;
+  state?: {
+    text: string;
+    value: string | number;
+  };
+  productName?: string;
   applianceType: {
     text: string;
     value: string;

+ 25 - 2
src/pages/device/Firmware/Save/index.tsx

@@ -13,6 +13,7 @@ import { useRef } from 'react';
 import type { ProductItem } from '@/pages/device/Product/typings';
 import { onlyMessage } from '@/utils/util';
 import RemoveData from './RemoveData';
+import _ from 'lodash';
 
 interface Props {
   data?: FirmwareItem;
@@ -41,6 +42,25 @@ const Save = (props: Props) => {
         const value = (field as Field).value;
         fileInfo.current = value;
       });
+      onFieldValueChange('productId', (field, form1) => {
+        if (field.modified) {
+          form1.setFieldState('versionOrder', (state) => {
+            state.value = undefined;
+          });
+        }
+      });
+      onFieldValueChange('versionOrder', async (field, f1) => {
+        const value = (field as Field).value;
+        const productId = (field.query('.productId').take() as Field).value;
+        if (field.modified && productId && value) {
+          const resp = await service.validateVersion(productId, value);
+          if (resp.status === 200) {
+            f1.setFieldState('versionOrder', (state) => {
+              state.selfErrors = resp.result ? ['版本序号已存在'] : undefined;
+            });
+          }
+        }
+      });
     },
   });
 
@@ -50,7 +70,10 @@ const Save = (props: Props) => {
     field.loading = true;
     services(field).then(
       action.bound!((list: any) => {
-        field.dataSource = list.result.map((item: any) => ({ label: item.name, value: item.id }));
+        const _data = list.result.filter((it: any) => {
+          return _.map(it?.features || [], 'id').includes('supportFirmware');
+        });
+        field.dataSource = _data.map((item: any) => ({ label: item.name, value: item.id }));
         products.current = list.result;
         field.loading = false;
       }),
@@ -65,7 +88,7 @@ const Save = (props: Props) => {
           value: 1,
         },
       ],
-      sorts: [{ name: 'name', order: 'desc' }],
+      sorts: [{ name: 'createTime', order: 'desc' }],
     });
 
   const SchemaField = createSchemaField({

+ 16 - 17
src/pages/device/Firmware/Task/Save/index.tsx

@@ -2,13 +2,12 @@ import { Modal } from 'antd';
 import type { FirmwareItem } from '@/pages/device/Firmware/typings';
 import { createSchemaField } from '@formily/react';
 import { Form, FormGrid, FormItem, Input, Select, NumberPicker, Radio } from '@formily/antd';
-import { createForm, onFieldValueChange, onFormInit } from '@formily/core';
+import { createForm, onFieldValueChange } from '@formily/core';
 import type { ISchema } from '@formily/json-schema';
 import { service } from '@/pages/device/Firmware';
-import { useEffect, useMemo, useRef } from 'react';
+import { useEffect, useMemo } from 'react';
 import { onlyMessage } from '@/utils/util';
 import FSelectDevices from '@/components/FSelectDevices';
-import type { DeviceInstance } from '@/pages/device/Instance/typings';
 
 interface Props {
   ids: { id: string; productId: string };
@@ -27,10 +26,10 @@ const Save = (props: Props) => {
         validateFirst: true,
         initialValues: {},
         effects() {
-          onFormInit(async (form1) => {
-            if (!data?.id) return;
-            form1.setInitialValues({ ...data, upload: { url: data?.url } });
-          });
+          // onFormInit(async (form1) => {
+          //   if (!data?.id) return;
+          //   form1.setInitialValues({ ...data, upload: { url: data?.url } });
+          // });
           onFieldValueChange('mode', async (field) => {
             field
               .query('timeoutSeconds')
@@ -55,7 +54,7 @@ const Save = (props: Props) => {
     [],
   );
 
-  const devices = useRef<DeviceInstance[]>([]);
+  // const devices = useRef<DeviceInstance[]>([]);
 
   const SchemaField = createSchemaField({
     components: {
@@ -70,19 +69,19 @@ const Save = (props: Props) => {
   });
 
   useEffect(() => {
-    if (visible) {
-      service.queryDevice().then((resp) => {
-        if (resp.status === 200) {
-          devices.current = resp.result;
-        }
-      });
-    }
+    // if (visible) {
+    //   service.queryDevice().then((resp) => {
+    //     if (resp.status === 200) {
+    //       devices.current = resp.result;
+    //     }
+    //   });
+    // }
   }, [visible]);
 
   const save = async () => {
     const values: any = await form.submit();
     if (values?.releaseType !== 'all') {
-      values.deviceId = devices.current.map((item) => item.id);
+      // values.deviceId = devices.current.map((item) => item.id);
     } else {
       values.deviceId = undefined;
     }
@@ -211,7 +210,7 @@ const Save = (props: Props) => {
             },
           },
           releaseType: {
-            type: 'number',
+            type: 'string',
             title: '升级设备',
             default: 'all',
             'x-visible': false,

+ 229 - 0
src/pages/device/Firmware/Task/Save/index1.tsx

@@ -0,0 +1,229 @@
+import { Col, Form, Input, InputNumber, message, Modal, Radio, Row, Select } from 'antd';
+import type { FirmwareItem } from '@/pages/device/Firmware/typings';
+import FSelectDevices from '@/components/FSelectDevices';
+import { useEffect, useRef, useState } from 'react';
+import type { DeviceInstance } from '@/pages/device/Instance/typings';
+import { service } from '@/pages/device/Firmware';
+import { onlyMessage } from '@/utils/util';
+
+interface Props {
+  ids: { id: string; productId: string };
+  data?: FirmwareItem;
+  close: () => void;
+  save: () => void;
+}
+
+const Save = (props: Props) => {
+  const { data, close, ids } = props;
+  const [mode, setMode] = useState<'push' | 'pull' | undefined>(undefined);
+  const [releaseType, setReleaseType] = useState<'all' | 'part' | undefined>(undefined);
+
+  const [form] = Form.useForm();
+
+  const devices = useRef<DeviceInstance[]>([]);
+
+  useEffect(() => {
+    service.queryDevice().then((resp) => {
+      if (resp.status === 200) {
+        devices.current = resp.result;
+      }
+    });
+  }, []);
+
+  const save = async () => {
+    const values = await form.validateFields();
+    if (values?.releaseType !== 'all') {
+      values.deviceId = devices.current.map((item) => item.id);
+    } else {
+      values.deviceId = undefined;
+    }
+    const resp = await service.saveTask({
+      ...values,
+      firmwareId: ids?.id,
+      productId: ids?.productId,
+    });
+    if (resp.status === 200) {
+      onlyMessage('保存成功!');
+      props.save();
+      form.resetFields();
+      setMode(undefined);
+      setReleaseType(undefined);
+    } else {
+      message.error('保存失败!');
+    }
+  };
+
+  return (
+    <Modal
+      maskClosable={false}
+      width="50vw"
+      title={data?.id ? '编辑任务' : '新增任务'}
+      onCancel={() => {
+        form.resetFields();
+        close();
+        setMode(undefined);
+        setReleaseType(undefined);
+      }}
+      onOk={() => save()}
+      visible
+    >
+      <Form form={form} name="basic" layout="vertical">
+        <Row gutter={24}>
+          <Col span={24}>
+            <Form.Item
+              label="任务名称"
+              name="name"
+              rules={[
+                {
+                  required: true,
+                  message: '请输入任务名称',
+                },
+                {
+                  max: 64,
+                  message: '最多可输入64个字符',
+                },
+              ]}
+            >
+              <Input placeholder="请输入任务名称" />
+            </Form.Item>
+          </Col>
+          <Col span={24}>
+            <Form.Item
+              label="推送方式"
+              name="mode"
+              rules={[
+                {
+                  required: true,
+                  message: '请选择推送方式',
+                },
+              ]}
+            >
+              <Select
+                placeholder="请选择推送方式"
+                onChange={(value) => {
+                  setMode(value);
+                }}
+              >
+                <Select.Option value="push">平台推送</Select.Option>
+                <Select.Option value="pull">设备拉取</Select.Option>
+              </Select>
+            </Form.Item>
+          </Col>
+          {mode === 'push' && (
+            <>
+              <Col span={12}>
+                <Form.Item
+                  label="响应超时时间"
+                  name="responseTimeoutSeconds"
+                  rules={[
+                    {
+                      required: true,
+                      message: '请输入响应超时时间',
+                    },
+                    {
+                      type: 'number',
+                      max: 99999,
+                      min: 1,
+                      message: '请输入1~99999之间的数字',
+                    },
+                  ]}
+                >
+                  <InputNumber style={{ width: '100%' }} placeholder="请输入响应超时时间(秒)" />
+                </Form.Item>
+              </Col>
+              <Col span={12}>
+                <Form.Item
+                  label="升级超时时间"
+                  name="timeoutSeconds"
+                  rules={[
+                    {
+                      required: true,
+                      message: '请输入升级超时时间',
+                    },
+                    {
+                      type: 'number',
+                      max: 99999,
+                      min: 1,
+                      message: '请输入1~99999之间的数字',
+                    },
+                  ]}
+                >
+                  <InputNumber style={{ width: '100%' }} placeholder="请请输入升级超时时间(秒)" />
+                </Form.Item>
+              </Col>
+            </>
+          )}
+          {mode === 'pull' && (
+            <Col span={24}>
+              <Form.Item
+                label="升级超时时间"
+                name="timeoutSeconds"
+                rules={[
+                  {
+                    required: true,
+                    message: '请输入升级超时时间',
+                  },
+                  {
+                    type: 'number',
+                    max: 99999,
+                    min: 1,
+                    message: '请输入1~99999之间的数字',
+                  },
+                ]}
+              >
+                <InputNumber style={{ width: '100%' }} placeholder="请请输入升级超时时间(秒)" />
+              </Form.Item>
+            </Col>
+          )}
+          {!!mode && (
+            <>
+              <Col span={12}>
+                <Form.Item
+                  label="升级设备"
+                  name="releaseType"
+                  rules={[
+                    {
+                      required: true,
+                      message: '请选择升级设备',
+                    },
+                  ]}
+                >
+                  <Radio.Group
+                    onChange={(e) => {
+                      setReleaseType(e.target.value);
+                    }}
+                  >
+                    <Radio value="all"> 所有设备 </Radio>
+                    <Radio value="part"> 选择设备 </Radio>
+                  </Radio.Group>
+                </Form.Item>
+              </Col>
+              {releaseType === 'part' && (
+                <Col span={12}>
+                  <Form.Item
+                    label="选择设备"
+                    name="deviceId"
+                    rules={[
+                      {
+                        required: true,
+                        message: '请选择设备',
+                      },
+                    ]}
+                  >
+                    <FSelectDevices productId={ids?.productId || ''} />
+                  </Form.Item>
+                </Col>
+              )}
+            </>
+          )}
+          <Col span={24}>
+            <Form.Item label="说明" name="description">
+              <Input.TextArea rows={3} maxLength={200} showCount={true} placeholder="请输入说明" />
+            </Form.Item>
+          </Col>
+        </Row>
+      </Form>
+    </Modal>
+  );
+};
+export default Save;

+ 19 - 15
src/pages/device/Firmware/Task/index.tsx

@@ -15,7 +15,7 @@ import { useHistory, useLocation } from 'umi';
 import { model } from '@formily/reactive';
 import { observer } from '@formily/react';
 import type { FirmwareItem } from '@/pages/device/Firmware/typings';
-import Save from './Save';
+import Save from './Save/index1';
 import { onlyMessage } from '@/utils/util';
 import { PermissionButton } from '@/components';
 import useDomFullHeight from '@/hooks/document/useDomFullHeight';
@@ -26,6 +26,9 @@ import { service } from '@/pages/device/Firmware';
 
 const UpgradeBtn = (props: { data: any; actions: any }) => {
   const { data, actions } = props;
+  if (data.progress === 100) {
+    return null;
+  }
   return (
     <a>
       <Tooltip title={data.waiting ? '停止' : '继续升级'}>
@@ -106,9 +109,9 @@ const Task = observer(() => {
     {
       title: '完成比例',
       ellipsis: true,
-      // hideInSearch: true,
+      hideInSearch: true,
       dataIndex: 'progress',
-      valueType: 'digit',
+      // valueType: 'digit',
     },
     {
       title: intl.formatMessage({
@@ -223,18 +226,19 @@ const Task = observer(() => {
         columns={columns}
         actionRef={actionRef}
       />
-      <Save
-        data={state.current}
-        ids={{ id: id, productId: productId }}
-        visible={state.visible}
-        save={() => {
-          state.visible = false;
-          actionRef.current?.reload?.();
-        }}
-        close={() => {
-          state.visible = false;
-        }}
-      />
+      {state.visible && (
+        <Save
+          data={state.current}
+          ids={{ id: id, productId: productId }}
+          save={() => {
+            state.visible = false;
+            actionRef.current?.reload?.();
+          }}
+          close={() => {
+            state.visible = false;
+          }}
+        />
+      )}
     </PageContainer>
   );
 });

+ 5 - 0
src/pages/device/Firmware/service.ts

@@ -63,6 +63,11 @@ class Service extends BaseService<FirmwareItem> {
 
   queryDevice = () =>
     request(`/${SystemConst.API_BASE}/device/instance/_query/no-paging?paging=false`);
+
+  validateVersion = (productId: string, versionOrder: number) =>
+    request(`/${SystemConst.API_BASE}/firmware/${productId}/${versionOrder}/exists`, {
+      method: 'GET',
+    });
 }
 
 export default Service;

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

@@ -3,9 +3,6 @@ import { Badge, Button, message, Popconfirm, Space } from 'antd';
 import styles from './index.less';
 import { observer } from '@formily/reactive-react';
 import type { ListProps } from './model';
-import { urlMap } from './model';
-import { gatewayList } from './model';
-import { textColorMap } from './model';
 import {
   DiagnoseStatusModel,
   StatusMap,
@@ -13,6 +10,9 @@ import {
   childInitList,
   cloudInitList,
   mediaInitList,
+  TextColorMap,
+  gatewayList,
+  urlMap,
 } from './model';
 import type { ReactNode } from 'react';
 import { useEffect, useState } from 'react';
@@ -2006,7 +2006,7 @@ const Status = observer((props: Props) => {
                 <div className={styles.info}>{item?.info}</div>
               </div>
             </div>
-            <div className={styles.statusRight} style={{ color: textColorMap.get(item.status) }}>
+            <div className={styles.statusRight} style={{ color: TextColorMap.get(item.status) }}>
               {item?.text}
             </div>
           </div>

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

@@ -9,11 +9,11 @@ StatusMap.set('success', require('/public/images/diagnose/status/success.png'));
 StatusMap.set('warning', require('/public/images/diagnose/status/warning.png'));
 StatusMap.set('loading', require('/public/images/diagnose/status/loading.png'));
 
-export const textColorMap = new Map();
-textColorMap.set('loading', 'black');
-textColorMap.set('error', 'red');
-textColorMap.set('success', 'green');
-textColorMap.set('warning', 'red');
+export const TextColorMap = new Map();
+TextColorMap.set('loading', 'black');
+TextColorMap.set('error', 'red');
+TextColorMap.set('success', 'green');
+TextColorMap.set('warning', 'red');
 
 export type ListProps = {
   key: string;

+ 3 - 1
src/pages/device/Product/Detail/Access/AccessConfig/index.tsx

@@ -136,7 +136,9 @@ const AccessConfig = (props: Props) => {
         oldData.push(item);
       }
     });
-    return oldData;
+    return oldData.map((item, index) => {
+      return { ...item, sortsIndex: index };
+    });
   };
 
   return (

+ 5 - 1
src/pages/device/Product/Detail/PropertyImport/index.tsx

@@ -31,7 +31,11 @@ const NormalUpload = (props: any) => {
 
     const target = typeMap.get(props.type);
 
-    const _data = updateMetadata('properties', _metadata.properties, target) as ProductItem;
+    const properties = (_metadata?.properties || []).map((item, index) => {
+      return { ...item, sortsIndex: index };
+    });
+
+    const _data = updateMetadata('properties', properties, target) as ProductItem;
     // const resp = await service.update(_product);
     const resp = await asyncUpdateMedata(props.type, _data);
     if (resp.status === 200) {

+ 6 - 0
src/pages/device/Product/service.ts

@@ -157,6 +157,12 @@ class Service extends BaseService<ProductItem> {
       method: 'POST',
       data: {
         paging: false,
+        sorts: [
+          {
+            name: 'createTime',
+            order: 'desc',
+          },
+        ],
       },
     });
   //获取协议详情

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

@@ -1112,9 +1112,14 @@ const Edit = observer((props: Props) => {
 
     if (!typeMap.get(props.type)) return;
 
+    const list = await DB.getDB().table(`${type}`).toArray();
+
     const updateDB = (t: 'add' | 'update', item: MetadataItem) => {
       switch (t) {
         case 'add':
+          const dt = list.sort((a, b) => b?.sortsIndex - a?.sortsIndex) || [];
+          item.sortsIndex =
+            dt.length > 0 && dt[0]?.sortsIndex !== undefined ? dt[0].sortsIndex + 1 : 0;
           DB.getDB().table(`${type}`).add(item, item.id);
           return;
         case 'update':

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

@@ -121,7 +121,7 @@ const BaseMetadata = observer((props: Props) => {
 
   const initData = useCallback(async () => {
     const result = await DB.getDB().table(`${type}`).toArray();
-    setData(result);
+    setData(result.sort((a, b) => b?.sortsIndex - a?.sortsIndex));
   }, [param.id, type]);
 
   useEffect(() => {
@@ -146,7 +146,7 @@ const BaseMetadata = observer((props: Props) => {
         .where('id')
         .startsWithAnyOfIgnoreCase(name)
         .toArray();
-      setData(result);
+      setData(result.sort((a, b) => b?.sortsIndex - a?.sortsIndex));
     } else {
       await initData();
     }

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

@@ -197,6 +197,18 @@ const Import = (props: Props) => {
     if (fid.includes('propertyNotModifiable')) {
       obj.properties = old?.properties || [];
     }
+    obj.events.map((item, index) => {
+      return { ...item, sortsIndex: index };
+    });
+    obj.properties.map((item, index) => {
+      return { ...item, sortsIndex: index };
+    });
+    obj.functions.map((item, index) => {
+      return { ...item, sortsIndex: index };
+    });
+    obj.tags.map((item, index) => {
+      return { ...item, sortsIndex: index };
+    });
     return obj;
   };
 

+ 25 - 18
src/pages/link/Type/Detail/index.tsx

@@ -124,7 +124,14 @@ const Save = observer(() => {
           onFormInit(async (form1) => {
             if (param?.id && param.id !== ':id') {
               const resp = await service.detail(param.id);
-              form1.setInitialValues({ ...resp?.result });
+              const data = resp?.result || {};
+              if (data?.shareCluster === false) {
+                data.cluster = data.cluster?.map((item: any) => ({
+                  ...item.configuration,
+                  configuration: item,
+                }));
+              }
+              form1.setInitialValues({ ...data });
             }
           });
           onFieldValueChange('type', (field, f) => {
@@ -208,23 +215,23 @@ const Save = observer(() => {
     [],
   );
 
-  useEffect(() => {
-    console.log(Store.get('current-network-data'));
-    // const subscription = Store.subscribe('current-network-data', (data) => {
-    //   if (!data) return;
-    //   // form.readPretty = true;
-    //   const _data = _.cloneDeep(data);
-    //   // 处理一下集群模式数据
-    //   if (!_data.shareCluster) {
-    //     _data.cluster = _data.cluster?.map((item: any) => ({ ...item.configuration }));
-    //   }
-    //   form.setValues({ ..._data });
-    // });
-    // return () => {
-    //   subscription.unsubscribe();
-    //   // Store.set('current-network-data', undefined);
-    // };
-  }, []);
+  // useEffect(() => {
+  //   console.log(Store.get('current-network-data'));
+  //   // const subscription = Store.subscribe('current-network-data', (data) => {
+  //   //   if (!data) return;
+  //   //   // form.readPretty = true;
+  //   //   const _data = _.cloneDeep(data);
+  //   //   // 处理一下集群模式数据
+  //   //   if (!_data.shareCluster) {
+  //   //     _data.cluster = _data.cluster?.map((item: any) => ({ ...item.configuration }));
+  //   //   }
+  //   //   form.setValues({ ..._data });
+  //   // });
+  //   // return () => {
+  //   //   subscription.unsubscribe();
+  //   //   // Store.set('current-network-data', undefined);
+  //   // };
+  // }, []);
 
   const SchemaField = createSchemaField({
     components: {

+ 3 - 7
src/pages/notice/Template/Detail/doc/DingTalk.tsx

@@ -22,9 +22,7 @@ const DingTalk = () => {
       <h1>1. 概述</h1>
       <div>
         通知模板结合通知配置为告警消息通知提供支撑。通知模板只能调用同一类型的通知配置服务。
-        <div>
-          使用钉钉消息通知时需在钉钉开放平台中创建好对应的应用
-        </div>
+        <div>使用钉钉消息通知时需在钉钉开放平台中创建好对应的应用</div>
       </div>
       <h1> 2.模板配置说明</h1>
       <h2> 1、绑定配置</h2>
@@ -35,10 +33,8 @@ const DingTalk = () => {
       <div className="image">
         <Image width="100%" src={agentId} />
       </div>
-      <h2> 3、收信人ID、收信部门ID</h2>
-      <div>
-        若不填写收信人,则在模板调试和配置告警通知时手动填写。
-      </div>
+      <h2> 3、收信人、收信部门</h2>
+      <div>若不填写收信人,则在模板调试和配置告警通知时手动填写。</div>
       {/*<div> 收信人ID获取路径:“钉钉管理后台”--“通讯录”--“查看用户”</div>*/}
       {/*<div> 收信部门ID获取路径:“钉钉管理后台”--“通讯录”--“编辑部门”</div>*/}
       {/*<div className="image">*/}

+ 1 - 0
src/pages/rule-engine/Alarm/Configuration/Save/index.tsx

@@ -96,6 +96,7 @@ const Save = (props: Props) => {
     return service
       .getScene(
         encodeQuery({
+          sorts: { createTime: 'desc' },
           terms: {
             triggerType: map[form.getValuesIn('targetType')],
           },

+ 1 - 1
src/pages/rule-engine/DashBoard/index.tsx

@@ -126,7 +126,7 @@ const Dashboard = observer(() => {
         series: [
           {
             name: '告警数',
-            data: fifteenData.sort((a,b) => b.timestamp - a.timestamp).map((item) => item.value),
+            data: fifteenData.sort((a, b) => b.timestamp - a.timestamp).map((item) => item.value),
             type: 'bar',
             itemStyle: {
               color: '#2F54EB',

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

@@ -24,6 +24,7 @@ interface BuiltInProps {
   form: FormInstance;
   name: number;
   isEdit?: boolean;
+  id?: string;
 }
 
 export default (props: BuiltInProps) => {
@@ -110,6 +111,7 @@ export default (props: BuiltInProps) => {
     <ItemGroup compact>
       <Select
         value={source}
+        id={props.id}
         options={[
           { label: '手动输入', value: 'fixed' },
           { label: '内置参数', value: 'upper' },

+ 2 - 0
src/pages/rule-engine/Scene/Save/action/VariableItems/org.tsx

@@ -16,6 +16,7 @@ interface OrgProps {
   notifyType: string;
   configId: string;
   onChange?: (value: ChangeType) => void;
+  id?: string;
 }
 
 export default (props: OrgProps) => {
@@ -47,6 +48,7 @@ export default (props: OrgProps) => {
   return (
     <TreeSelect
       value={keys}
+      id={props.id}
       listHeight={200}
       treeData={departmentTree}
       fieldNames={{

+ 2 - 0
src/pages/rule-engine/Scene/Save/action/VariableItems/tag.tsx

@@ -6,6 +6,7 @@ interface TagSelectProps {
   configId?: string;
   value?: string;
   onChange?: (value: string) => void;
+  id?: string;
 }
 
 export default (props: TagSelectProps) => {
@@ -32,6 +33,7 @@ export default (props: TagSelectProps) => {
 
   return (
     <Select
+      id={props.id}
       value={value}
       placeholder={'请选择标签'}
       options={options}

+ 2 - 0
src/pages/rule-engine/Scene/Save/action/VariableItems/user.tsx

@@ -21,6 +21,7 @@ interface UserProps {
   value?: ChangeType;
   type?: string;
   onChange?: (value: ChangeType) => void;
+  id?: string;
 }
 
 export default (props: UserProps) => {
@@ -308,6 +309,7 @@ export default (props: UserProps) => {
   return (
     <ItemGroup compact>
       <Select
+        id={props.id}
         value={source}
         options={
           props.notifyType && ['dingTalk', 'weixin'].includes(props.notifyType)

+ 2 - 0
src/pages/rule-engine/Scene/Save/action/device/WriteProperty/index.tsx

@@ -18,6 +18,7 @@ interface WritePropertyProps {
   trigger?: any;
   productId: string;
   isEdit?: boolean;
+  id?: string;
 }
 
 export default (props: WritePropertyProps) => {
@@ -249,6 +250,7 @@ export default (props: WritePropertyProps) => {
       <Col span={6}>
         <Select
           value={propertiesKey}
+          id={props.id}
           options={props.properties.filter((item) => {
             if (item.expands && item.expands.type) {
               return item.expands.type.includes('write');

+ 2 - 0
src/pages/rule-engine/Scene/Save/action/device/deviceModal.tsx

@@ -11,6 +11,7 @@ interface DeviceModelProps {
   value?: ChangeValueType[];
   onChange?: (value: ChangeValueType[]) => void;
   productId?: string;
+  id?: string;
 }
 
 type DeviceBadgeProps = {
@@ -200,6 +201,7 @@ export default (props: DeviceModelProps) => {
         </Modal>
       )}
       <Input
+        id={props.id}
         placeholder={'请选择设备'}
         onClick={onClick}
         addonAfter={<AIcon type={'icon-shebei'} onClick={onClick} />}

+ 2 - 0
src/pages/rule-engine/Scene/Save/action/device/readProperty.tsx

@@ -4,11 +4,13 @@ interface ReadPropertyProps {
   properties: any[];
   value?: any;
   onChange?: (value?: any) => void;
+  id?: string;
 }
 
 export default (props: ReadPropertyProps) => {
   return (
     <Select
+      id={props.id}
       value={props.value ? props.value[0] : undefined}
       options={props.properties.filter((item) => {
         if (item.expands && item.expands.type) {

+ 2 - 0
src/pages/rule-engine/Scene/Save/action/device/relationSelect.tsx

@@ -5,6 +5,7 @@ import { getRelations } from '@/pages/rule-engine/Scene/Save/action/service';
 interface RelationProps {
   value?: any;
   onChange?: (value?: any) => void;
+  id?: string;
 }
 
 export default (props: RelationProps) => {
@@ -28,6 +29,7 @@ export default (props: RelationProps) => {
 
   return (
     <Select
+      id={props.id}
       options={options}
       style={{ width: '100%' }}
       value={props.value ? props.value[0]?.value?.relation : undefined}

+ 2 - 0
src/pages/rule-engine/Scene/Save/action/device/tagModal.tsx

@@ -7,6 +7,7 @@ interface TagModalProps {
   tagData: any[];
   value?: any[];
   onChange?: (value: any[]) => void;
+  id?: string;
 }
 
 /**
@@ -251,6 +252,7 @@ export default (props: TagModalProps) => {
         </div>
       </Modal>
       <Input
+        id={props.id}
         value={nameList.length ? nameList.toString() : undefined}
         readOnly
         style={{ width: 300 }}

+ 2 - 0
src/pages/rule-engine/Scene/Save/components/InputUpload/index.tsx

@@ -8,6 +8,7 @@ import { LoadingOutlined, PlusOutlined } from '@ant-design/icons';
 interface InputUploadProps {
   value?: string;
   onChange?: (value?: string) => void;
+  id?: string;
 }
 
 export default (props: InputUploadProps) => {
@@ -68,6 +69,7 @@ export default (props: InputUploadProps) => {
   return (
     <Input
       value={url}
+      id={props.id}
       onChange={(e) => {
         if (onChange) {
           onChange(e.target.value);

+ 2 - 1
src/pages/rule-engine/Scene/Save/components/TimeSelect/index.tsx

@@ -15,6 +15,7 @@ interface TimeSelect {
   options?: OptionItemType[];
   className?: string;
   style?: React.CSSProperties;
+  id?: string;
 }
 
 export default (props: TimeSelect) => {
@@ -104,7 +105,7 @@ export default (props: TimeSelect) => {
         }}
         id={'timeSelect'}
       >
-        <div className={'time-select-content ellipsis'}>
+        <div className={'time-select-content ellipsis'} id={props.id}>
           {checkedNames.length ? (
             checkedKeys.includes('null') ? (
               '每天'

+ 2 - 0
src/pages/rule-engine/Scene/Save/components/TimingTrigger/RangePicker.tsx

@@ -9,10 +9,12 @@ type RangePickerValue = {
 interface RangePickerProps {
   value?: RangePickerValue;
   onChange?: (value: RangePickerValue) => void;
+  id?: string;
 }
 export default (props: RangePickerProps) => {
   return (
     <TimePicker.RangePicker
+      id={props.id}
       style={{ width: '100%' }}
       format={'HH:mm:ss'}
       value={[

+ 2 - 0
src/pages/rule-engine/Scene/Save/components/TimingTrigger/TimePicker.tsx

@@ -8,10 +8,12 @@ type TimePickerValue = {
 interface TimePickerProps {
   value?: TimePickerValue;
   onChange?: (value: TimePickerValue) => void;
+  id?: string;
 }
 export default (props: TimePickerProps) => {
   return (
     <TimePicker
+      id={props.id}
       style={{ width: '100%' }}
       format={'HH:mm:ss'}
       value={moment(props.value?.time || new Date(), 'HH:mm:ss')}

+ 8 - 1
src/pages/rule-engine/Scene/Save/trigger/OrgTreeSelect.tsx

@@ -7,6 +7,7 @@ interface OrgTreeSelect extends Omit<TreeSelectProps, 'onChange' | 'value'> {
   onChange?: (value: any[]) => void;
   value?: any;
   productId?: string;
+  id?: string;
 }
 
 export default (props: OrgTreeSelect) => {
@@ -43,6 +44,12 @@ export default (props: OrgTreeSelect) => {
   }, [props.value]);
 
   return (
-    <TreeSelect<string> value={myValue} treeData={orgTree} onChange={onchange} {...extraProps} />
+    <TreeSelect<string>
+      id={props.id}
+      value={myValue}
+      treeData={orgTree}
+      onChange={onchange}
+      {...extraProps}
+    />
   );
 };

+ 2 - 0
src/pages/rule-engine/Scene/Save/trigger/operation.tsx

@@ -7,6 +7,7 @@ interface OperatorProps {
   propertiesList?: any[];
   value?: any;
   onChange?: (value: any) => void;
+  id?: string;
 }
 
 export default (props: OperatorProps) => {
@@ -81,6 +82,7 @@ export default (props: OperatorProps) => {
       <Col span={6}>
         <Select
           mode="multiple"
+          id={props.id}
           options={props.propertiesList || []}
           value={key}
           fieldNames={{

+ 1 - 1
src/pages/system/DataSource/Management/RemoveData.tsx

@@ -12,7 +12,7 @@ interface Props {
 
 const RemoveData = (props: Props) => {
   const { type } = props;
-  const row = ArrayItems.useRecord!();
+  const row = ArrayItems.useRecord!()();
 
   const index = ArrayItems.useIndex!();
   const self = useField();

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

@@ -149,7 +149,7 @@ export default observer((props: { parentId: string }) => {
             <Tooltip
               title={intl.formatMessage({
                 id: 'pages.system.role.option.unBindUser',
-                defaultMessage: '删除',
+                defaultMessage: '解绑',
               })}
             >
               <DisconnectOutlined />

+ 1 - 8
src/pages/system/Department/Member/bind.tsx

@@ -35,9 +35,6 @@ const Bind = observer((props: Props) => {
         id: 'pages.system.name',
         defaultMessage: '姓名',
       }),
-      search: {
-        transform: (value) => ({ name$LIKE: value }),
-      },
     },
     {
       dataIndex: 'username',
@@ -73,11 +70,7 @@ const Bind = observer((props: Props) => {
       visible={props.visible}
       onOk={handleBind}
       onCancel={props.onCancel}
-      width={'800'}
-      bodyStyle={{
-        height: 'calc(100vh - 240px);',
-        overflowY: 'auto',
-      }}
+      width={'75vw'}
       title="绑定"
     >
       <SearchComponent<UserItem>

+ 0 - 3
src/pages/system/Department/Member/index.tsx

@@ -49,9 +49,6 @@ const Member = observer((props: { parentId: string }) => {
         id: 'pages.system.name',
         defaultMessage: '姓名',
       }),
-      search: {
-        transform: (value) => ({ name$LIKE: value }),
-      },
       width: 120,
       fixed: 'left',
     },

+ 2 - 3
src/pages/system/Menu/Detail/buttons.tsx

@@ -272,8 +272,7 @@ export default (props: ButtonsProps) => {
           </PermissionButton>,
         ]}
       />
-      {
-        visible &&
+      {visible && (
         <Modal
           maskClosable={false}
           width={660}
@@ -370,7 +369,7 @@ export default (props: ButtonsProps) => {
             </Form.Item>
           </Form>
         </Modal>
-      }
+      )}
     </>
   );
 };

+ 0 - 1
src/pages/system/Menu/Setting/baseMenu.ts

@@ -1267,7 +1267,6 @@ export default [
         url: '/system/Department',
         icon: 'icon-bumenguanli',
         permissions: [
-
           { permission: 'assets-bind', actions: ['bind', 'unbind', 'query', 'permission'] },
           { permission: 'role', actions: ['query', 'save', 'delete'] },
           { permission: 'device-category', actions: ['query', 'save', 'delete'] },

+ 4 - 4
src/pages/system/Menu/Setting/index.tsx

@@ -7,7 +7,7 @@ import {
 } from '@ant-design/icons';
 import Tree from './tree';
 import './index.less';
-import {Button, message, Modal, Tooltip} from 'antd';
+import { Button, message, Modal, Tooltip } from 'antd';
 import BaseTreeData from './baseMenu';
 import { useEffect, useState } from 'react';
 import { HTML5Backend } from 'react-dnd-html5-backend';
@@ -186,10 +186,10 @@ export default observer(() => {
         visible={visible}
         onOk={() => {
           MenuSettingModel.menuData = cloneDeep(baseMenu);
-          setVisible(false)
+          setVisible(false);
         }}
         onCancel={() => {
-          setVisible(false)
+          setVisible(false);
         }}
       >
         源数据将会覆盖当前的系统菜单数据,确定要一键拷贝吗?
@@ -213,7 +213,7 @@ export default observer(() => {
                   type={'primary'}
                   ghost
                   onClick={() => {
-                    setVisible(true)
+                    setVisible(true);
                   }}
                 >
                   一键拷贝

+ 26 - 17
src/pages/system/Menu/Setting/tree.tsx

@@ -2,7 +2,7 @@ import { Input, Tree } from 'antd';
 import { SearchOutlined } from '@ant-design/icons';
 import DragItem from '@/pages/system/Menu/Setting/dragItem';
 import { useDrop } from 'react-dnd';
-import {useEffect, useState} from 'react';
+import { useEffect, useState } from 'react';
 import type { TreeProps } from 'antd';
 import { cloneDeep, debounce } from 'lodash';
 import './DragItem.less';
@@ -21,7 +21,17 @@ export const DragType = 'DragBox';
 
 const { TreeNode } = Tree;
 
-const defaultExpandedKeys = ['iot', 'media', 'system', 'device', 'link', 'link/Channel', 'rule-engine/Alarm', 'Northbound', 'rule-engine']
+const defaultExpandedKeys = [
+  'iot',
+  'media',
+  'system',
+  'device',
+  'link',
+  'link/Channel',
+  'rule-engine/Alarm',
+  'Northbound',
+  'rule-engine',
+];
 
 export default (props: TreeBodyProps) => {
   const [newData, setNewData] = useState(props.treeData);
@@ -35,9 +45,9 @@ export default (props: TreeBodyProps) => {
 
   useEffect(() => {
     setTimeout(() => {
-      setExpandedKeys(defaultExpandedKeys)
-    }, 300)
-  }, [])
+      setExpandedKeys(defaultExpandedKeys);
+    }, 300);
+  }, []);
 
   const [, drop] = useDrop(() => ({
     accept: DragType,
@@ -105,15 +115,15 @@ export default (props: TreeBodyProps) => {
     data.forEach((item: any) => {
       if (item.children) {
         if (item.children.some((cItem: any) => cItem.code === key)) {
-          parentKey = item.code
+          parentKey = item.code;
         } else if (!!getParentKey(key, item.children)) {
-          parentKey = getParentKey(key, item.children)
+          parentKey = getParentKey(key, item.children);
         }
       }
-    })
+    });
     // @ts-ignore
-    return parentKey
-  }
+    return parentKey;
+  };
 
   const findAllItem = (data: any[], value: string): string[] => {
     return data.reduce((pre, next) => {
@@ -128,17 +138,16 @@ export default (props: TreeBodyProps) => {
     const value = e.target.value;
 
     if (value) {
-
       const newKeys = findAllItem(props.treeData, value);
-      const newExpandedKeys = newKeys.map(key => {
-        return getParentKey(key, props.treeData)
-      })
+      const newExpandedKeys = newKeys.map((key) => {
+        return getParentKey(key, props.treeData);
+      });
       setSearchKeys(newKeys);
       setExpandedKeys(newExpandedKeys);
     } else {
       setSearchKeys([]);
     }
-    setAutoExpandParent(true)
+    setAutoExpandParent(true);
   };
 
   return (
@@ -157,7 +166,7 @@ export default (props: TreeBodyProps) => {
             expandedKeys={expandedKeys}
             onExpand={(_expandedKeys) => {
               setExpandedKeys(_expandedKeys);
-              setAutoExpandParent(false)
+              setAutoExpandParent(false);
             }}
             autoExpandParent={autoExpandParent}
           >
@@ -170,7 +179,7 @@ export default (props: TreeBodyProps) => {
             expandedKeys={expandedKeys}
             onExpand={(_expandedKeys) => {
               setExpandedKeys(_expandedKeys);
-              setAutoExpandParent(false)
+              setAutoExpandParent(false);
             }}
             autoExpandParent={autoExpandParent}
             draggable={{

+ 8 - 1
src/pages/system/Menu/index.tsx

@@ -108,6 +108,9 @@ export default observer(() => {
       width: 80,
       dataIndex: 'sortIndex',
       valueType: 'digit',
+      render: (_, record) => {
+        return record.sortIndex;
+      },
     },
     {
       title: intl.formatMessage({
@@ -261,7 +264,11 @@ export default observer(() => {
         search={false}
         params={param}
         request={async (params) => {
-          const response = await service.queryMenuThree({ ...params, sorts: [{ name: 'sortIndex', order: 'asc'}], paging: false });
+          const response = await service.queryMenuThree({
+            ...params,
+            sorts: [{ name: 'sortIndex', order: 'asc' }],
+            paging: false,
+          });
           return {
             code: response.message,
             result: {

+ 1 - 0
src/pages/system/Role/Detail/UserManage/BindUser.tsx

@@ -119,6 +119,7 @@ const BindUser = (props: Props) => {
                 ],
               },
             ],
+            sorts: [{ name: 'createTime', order: 'desc' }],
           });
           return {
             result: {

+ 5 - 4
src/pages/user/Login/service.ts

@@ -37,10 +37,11 @@ const Service = {
         map((resp) => resp.result),
       ),
     ),
-  login2: (data: LoginParam) => request(`/${SystemConst.API_BASE}/authorize/login`, {
-    method: 'POST',
-    data,
-  }),
+  login2: (data: LoginParam) =>
+    request(`/${SystemConst.API_BASE}/authorize/login`, {
+      method: 'POST',
+      data,
+    }),
 
   queryCurrent: () =>
     request(`/${SystemConst.API_BASE}/authorize/me`, {