Explorar o código

feat(路由管理): 1.替换全局动态路由;

xieyonghong %!s(int64=3) %!d(string=hai) anos
pai
achega
940ddda3e4

+ 60 - 396
config/routes.ts

@@ -21,437 +21,101 @@
   //   icon: 'smile',
   //   component: './Analysis',
   // },
-  {
-    path: '/system',
-    name: 'system',
-    icon: 'crown',
-    routes: [
-      {
-        path: '/system',
-        redirect: '/system/user',
-      },
-      {
-        path: '/system/user',
-        name: 'user',
-        icon: 'smile',
-        access: 'user',
-        component: './system/User',
-      },
-      {
-        path: '/system/department',
-        name: 'department',
-        icon: 'smile',
-        component: './system/Department',
-      },
-      {
-        hideInMenu: true,
-        path: '/system/department/:id/assets',
-        name: 'assets',
-        icon: 'smile',
-        component: './system/Department/Assets',
-      },
-      {
-        hideInMenu: true,
-        path: '/system/department/:id/user',
-        name: 'member',
-        icon: 'smile',
-        component: './system/Department/Member',
-      },
-      {
-        path: '/system/role',
-        name: 'role',
-        icon: 'smile',
-        access: 'role',
-        component: './system/Role',
-      },
-      {
-        hideInMenu: true,
-        path: '/system/role/edit/:id',
-        name: 'role-edit',
-        icon: 'smile',
-        component: './system/Role/Edit',
-      },
-      {
-        path: '/system/permission',
-        name: 'permission',
-        icon: 'smile',
-        component: './system/Permission',
-      },
-      {
-        path: '/system/menu',
-        name: 'menu',
-        icon: 'smile',
-        component: './system/Menu',
-      },
-      {
-        path: '/system/menu/detail',
-        name: 'menuDetail',
-        icon: 'smile',
-        hideInMenu: true,
-        component: './system/Menu/Detail',
-      },
-      // {
-      //   path: '/system/open-api',
-      //   name: 'open-api',
-      //   icon: 'smile',
-      //   component: './system/OpenAPI',
-      // },
-      // {
-      //   path: '/system/tenant',
-      //   name: 'tenant',
-      //   icon: 'smile',
-      //   component: './system/Tenant',
-      // },
-      {
-        hideInMenu: true,
-        path: '/system/tenant/detail/:id',
-        name: 'tenant-detail',
-        icon: 'smile',
-        component: './system/Tenant/Detail',
-      },
-      // {
-      //   path: '/system/datasource',
-      //   name: 'datasource',
-      //   icon: 'smile',
-      //   component: './system/DataSource',
-      // },
-      //
-    ],
-  },
   // {
-  //   path: '/device',
-  //   name: 'device',
+  //   path: '/system',
+  //   name: 'system',
   //   icon: 'crown',
   //   routes: [
   //     {
-  //       path: '/device',
-  //       redirect: '/device/product',
-  //     },
-  //     {
-  //       path: '/device/product',
-  //       name: 'product',
+  //       path: '/system/user',
+  //       name: 'user',
   //       icon: 'smile',
-  //       component: './device/Product',
+  //       access: 'user',
+  //       component: './system/User',
   //     },
   //     {
-  //       path: '/device/category',
-  //       name: 'category',
+  //       path: '/system/department',
+  //       name: 'department',
   //       icon: 'smile',
-  //       component: './device/Category',
+  //       component: './system/Department',
   //     },
   //     {
   //       hideInMenu: true,
-  //       path: '/device/product/detail/:id',
-  //       name: 'product-detail',
+  //       path: '/system/department/:id/assets',
+  //       name: 'assets',
   //       icon: 'smile',
-  //       component: './device/Product/Detail',
-  //     },
-  //     {
-  //       path: '/device/instance',
-  //       name: 'instance',
-  //       icon: 'smile',
-  //       component: './device/Instance',
+  //       component: './system/Department/Assets',
   //     },
   //     {
   //       hideInMenu: true,
-  //       path: '/device/instance/detail/:id',
-  //       name: 'instance-detail',
-  //       icon: 'smile',
-  //       component: './device/Instance/Detail',
-  //     },
-  //     {
-  //       path: '/device/command',
-  //       name: 'command',
+  //       path: '/system/department/:id/user',
+  //       name: 'member',
   //       icon: 'smile',
-  //       component: './device/Command',
+  //       component: './system/Department/Member',
   //     },
   //     {
-  //       path: '/device/firmware',
-  //       name: 'firmware',
+  //       path: '/system/role',
+  //       name: 'role',
   //       icon: 'smile',
-  //       component: './device/Firmware',
+  //       access: 'role',
+  //       component: './system/Role',
   //     },
   //     {
   //       hideInMenu: true,
-  //       path: '/device/firmware/detail/:id',
-  //       name: 'firmware-detail',
-  //       icon: 'smile',
-  //       component: './device/Firmware/Detail',
-  //     },
-  //     {
-  //       path: '/device/alarm',
-  //       name: 'alarm',
-  //       icon: 'smile',
-  //       component: './device/Alarm',
-  //     },
-  //     {
-  //       path: '/device/location',
-  //       name: 'location',
-  //       icon: 'smile',
-  //       component: './device/Location',
-  //     },
-  //   ],
-  // },
-  // {
-  //   path: '/link',
-  //   name: 'link',
-  //   icon: 'crown',
-  //   routes: [
-  //     {
-  //       path: '/link',
-  //       redirect: '/link/certificate',
-  //     },
-  //     {
-  //       path: '/link/certificate',
-  //       name: 'certificate',
-  //       icon: 'smile',
-  //       component: './link/Certificate',
-  //     },
-  //     {
-  //       path: '/link/protocol',
-  //       name: 'protocol',
-  //       icon: 'smile',
-  //       component: './link/Protocol',
-  //     },
-  //     {
-  //       path: 'link/type',
-  //       name: 'type',
-  //       icon: 'smile',
-  //       component: './link/Type',
-  //     },
-  //     {
-  //       path: '/link/gateway',
-  //       name: 'gateway',
-  //       icon: 'smile',
-  //       component: './link/Gateway',
-  //     },
-  //     {
-  //       path: '/link/opcua',
-  //       name: 'opcua',
-  //       icon: 'smile',
-  //       component: './link/Opcua',
-  //     },
-  //   ],
-  // },
-  // {
-  //   path: '/notice',
-  //   name: 'notice',
-  //   icon: 'crown',
-  //   routes: [
-  //     {
-  //       path: '/notice',
-  //       redirect: '/notice/config',
-  //     },
-  //     {
-  //       path: '/notice/config',
-  //       name: 'config',
-  //       icon: 'smile',
-  //       component: './notice/Config',
-  //     },
-  //     {
-  //       path: '/notice/template',
-  //       name: 'template',
-  //       icon: 'smile',
-  //       component: './notice/Template',
-  //     },
-  //   ],
-  // },
-  // {
-  //   path: '/rule-engine',
-  //   name: 'rule-engine',
-  //   icon: 'crown',
-  //   routes: [
-  //     {
-  //       path: '/rule-engine',
-  //       redirect: '/rule-engine/instance',
-  //     },
-  //     {
-  //       path: '/rule-engine/instance',
-  //       name: 'instance',
-  //       icon: 'smile',
-  //       component: './rule-engine/Instance',
-  //     },
-  //     {
-  //       path: '/rule-engine/sqlRule',
-  //       name: 'sqlRule',
+  //       path: '/system/role/edit/:id',
+  //       name: 'role-edit',
   //       icon: 'smile',
-  //       component: './rule-engine/SQLRule',
+  //       component: './system/Role/Edit',
   //     },
   //     {
-  //       path: '/rule-engine/scene',
-  //       name: 'scene',
+  //       path: '/system/permission',
+  //       name: 'permission',
   //       icon: 'smile',
-  //       component: './rule-engine/Scene',
+  //       component: './system/Permission',
   //     },
-  //   ],
-  // },
-  // {
-  //   path: '/visualization',
-  //   name: 'visualization',
-  //   icon: 'crown',
-  //   routes: [
-  //     {
-  //       path: '/visualization',
-  //       redirect: '/visualization/category',
-  //     },
-  //     {
-  //       path: '/visualization/category',
-  //       name: 'category',
-  //       icon: 'smile',
-  //       component: './visualization/Category',
-  //     },
-  //     {
-  //       path: '/visualization/screen',
-  //       name: 'screen',
-  //       icon: 'smile',
-  //       component: './visualization/Screen',
-  //     },
-  //     {
-  //       path: '/visualization/configuration',
-  //       name: 'configuration',
-  //       icon: 'smile',
-  //       component: './visualization/Configuration',
-  //     },
-  //   ],
-  // },
-  // {
-  //   path: '/simulator',
-  //   name: 'simulator',
-  //   icon: 'crown',
-  //   routes: [
-  //     {
-  //       path: '/simulator',
-  //       redirect: '/simulator/device',
-  //     },
-  //     {
-  //       path: '/simulator/device',
-  //       name: 'device',
-  //       icon: 'smile',
-  //       component: './simulator/Device',
-  //     },
-  //   ],
-  // },
-  // {
-  //   path: '/log',
-  //   name: 'log',
-  //   icon: 'crown',
-  //   routes: [
-  //     {
-  //       path: '/log',
-  //       redirect: '/log/access',
-  //     },
-  //     {
-  //       path: '/log/access',
-  //       name: 'access',
-  //       icon: 'smile',
-  //       component: './log/Access',
-  //     },
-  //     {
-  //       path: '/log/system',
-  //       name: 'system',
-  //       icon: 'smile',
-  //       component: './log/System',
-  //     },
-  //   ],
-  // },
-  // {
-  //   path: '/cloud',
-  //   name: 'cloud',
-  //   icon: 'crown',
-  //   routes: [
   //     {
-  //       path: '/cloud',
-  //       redirect: '/cloud/duer',
-  //     },
-  //     {
-  //       path: '/cloud/dueros',
-  //       name: 'DuerOS',
-  //       icon: 'smile',
-  //       component: './cloud/DuerOS',
-  //     },
-  //     {
-  //       path: '/cloud/aliyun',
-  //       name: 'aliyun',
-  //       icon: 'smile',
-  //       component: './cloud/Aliyun',
-  //     },
-  //     {
-  //       path: '/cloud/onenet',
-  //       name: 'onenet',
-  //       icon: 'smile',
-  //       component: './cloud/Onenet',
-  //     },
-  //     {
-  //       path: '/cloud/ctwing',
-  //       name: 'ctwing',
-  //       icon: 'smile',
-  //       component: './cloud/Ctwing',
-  //     },
-  //   ],
-  // },
-  // {
-  //   path: '/media',
-  //   name: 'media',
-  //   icon: 'crown',
-  //   routes: [
-  //     {
-  //       path: '/media',
-  //       redirect: '/media/config',
-  //     },
-  //     {
-  //       path: '/media/config',
-  //       name: 'config',
-  //       icon: 'smile',
-  //       component: './media/Config',
-  //     },
-  //     {
-  //       path: '/media/device',
-  //       name: 'device',
+  //       path: '/system/menu',
+  //       name: 'menu',
   //       icon: 'smile',
-  //       component: './media/Device',
+  //       component: './system/Menu',
   //     },
   //     {
-  //       path: '/media/reveal',
-  //       name: 'reveal',
+  //       path: '/system/menu/detail',
+  //       name: 'menuDetail',
   //       icon: 'smile',
-  //       component: './media/Reveal',
-  //     },
+  //       hideInMenu: true,
+  //       component: './system/Menu/Detail',
+  //     },
+  //     // {
+  //     //   path: '/system/open-api',
+  //     //   name: 'open-api',
+  //     //   icon: 'smile',
+  //     //   component: './system/OpenAPI',
+  //     // },
+  //     // {
+  //     //   path: '/system/tenant',
+  //     //   name: 'tenant',
+  //     //   icon: 'smile',
+  //     //   component: './system/Tenant',
+  //     // },
   //     {
-  //       path: '/media/cascade',
-  //       name: 'cascade',
-  //       icon: 'smile',
-  //       component: './media/Cascade',
-  //     },
+  //       hideInMenu: true,
+  //       path: '/system/tenant/detail/:id',
+  //       name: 'tenant-detail',
+  //       icon: 'smile',
+  //       component: './system/Tenant/Detail',
+  //     },
+  //     // {
+  //     //   path: '/system/datasource',
+  //     //   name: 'datasource',
+  //     //   icon: 'smile',
+  //     //   component: './system/DataSource',
+  //     // },
+  //     //
   //   ],
   // },
   // {
-  //   path: '/edge',
-  //   name: 'edge',
-  //   icon: 'crown',
-  //   routes: [
-  //     {
-  //       path: '/edge',
-  //       redirect: '/edge/product',
-  //     },
-  //     {
-  //       path: '/edge/product',
-  //       name: 'product',
-  //       icon: 'smile',
-  //       component: './edge/Product',
-  //     },
-  //     {
-  //       path: '/edge/device',
-  //       name: 'device',
-  //       icon: 'smile',
-  //       component: './edge/Device',
-  //     },
-  //   ],
+  //   path: '/',
+  //   redirect: '/system',
   // },
-  {
-    path: '/',
-    redirect: '/system',
-  },
-  {
-    component: './404',
-  },
 ];

+ 1 - 0
package.json

@@ -104,6 +104,7 @@
     "@types/react": "^17.0.0",
     "@types/react-dom": "^17.0.0",
     "@types/react-helmet": "^6.1.0",
+    "@types/webpack-env": "^1.16.3",
     "@umijs/fabric": "^2.6.2",
     "@umijs/openapi": "^1.1.14",
     "@umijs/plugin-blocks": "^2.0.5",

+ 29 - 2
src/app.tsx

@@ -11,9 +11,12 @@ import Token from '@/utils/token';
 import type { RequestOptionsInit } from 'umi-request';
 import ReconnectingWebSocket from 'reconnecting-websocket';
 import SystemConst from '@/utils/const';
+import { service as MenuService } from '@/pages/system/Menu';
+import getRoutes, { getMenus, saveMenusCache } from '@/utils/menu';
 
 const isDev = process.env.NODE_ENV === 'development';
 const loginPath = '/user/login';
+let extraRoutes: any[] = [];
 
 /** 获取用户信息比较慢的时候会展示一个 loading */
 export const initialStateConfig = {
@@ -183,13 +186,16 @@ export const layout: RunTimeLayoutConfig = ({ initialState }) => {
         history.push(loginPath);
       }
     },
+    menuDataRender: () => {
+      return getMenus(extraRoutes);
+    },
     links: isDev
       ? [
-          <Link to="/umi/plugin/openapi" target="_blank">
+          <Link key={1} to="/umi/plugin/openapi" target="_blank">
             <LinkOutlined />
             <span>OpenAPI 文档</span>
           </Link>,
-          <Link to="/~docs">
+          <Link key={2} to="/~docs">
             <BookOutlined />
             <span>业务组件文档</span>
           </Link>,
@@ -201,3 +207,24 @@ export const layout: RunTimeLayoutConfig = ({ initialState }) => {
     ...initialState?.settings,
   };
 };
+
+export function patchRoutes(routes: any) {
+  if (extraRoutes && extraRoutes.length) {
+    routes.routes[1].routes = [...routes.routes[1].routes, ...getRoutes(extraRoutes)];
+  }
+}
+
+export function render(oldRender: any) {
+  console.log(history.location.pathname !== loginPath && history.location.pathname !== '/');
+  if (history.location.pathname !== loginPath && history.location.pathname !== '/') {
+    MenuService.queryMenuThree({ paging: false }).then((res) => {
+      if (res.status === 200) {
+        extraRoutes = res.result;
+        saveMenusCache(res.result);
+      }
+      oldRender();
+    });
+  } else {
+    oldRender();
+  }
+}

+ 1 - 0
src/locales/zh-CN/pages.ts

@@ -135,6 +135,7 @@ export default {
   'pages.system.menu.option.addChildren': '新增子菜单',
   'pages.system.menu.detail': '基本信息',
   'pages.system.menu.buttons': '按钮管理',
+  'pages.system.menu.root': '菜单权限',
   // 系统设置-第三方平台
   'pages.system.openApi': '第三方平台',
   'pages.system.openApi.username': '用户名',

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

@@ -42,7 +42,7 @@ import DB from '@/db';
 import _ from 'lodash';
 import { useParams } from 'umi';
 import { InstanceModel } from '@/pages/device/Instance';
-import FRuleEditor from '@/components/FRuleEditor';
+// import FRuleEditor from '@/components/FRuleEditor';
 
 interface Props {
   type: 'product' | 'device';
@@ -97,7 +97,7 @@ const Edit = (props: Props) => {
       EnumParam,
       BooleanEnum,
       ConfigParam,
-      FRuleEditor,
+      // FRuleEditor,
     },
     scope: {
       async asyncOtherConfig(field: Field) {

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

@@ -8,6 +8,7 @@ import { SearchOutlined, PlusOutlined, EditOutlined, DeleteOutlined } from '@ant
 import type { MenuButtonInfo, MenuItem } from '@/pages/system/Menu/typing';
 import Permission from '@/pages/system/Menu/components/permission';
 import { useRequest } from '@@/plugin-request/request';
+import { debounce } from 'lodash';
 
 type ButtonsProps = {
   data: MenuItem;
@@ -47,6 +48,17 @@ export default (props: ButtonsProps) => {
     setId('');
     setDisabled(false);
   };
+
+  const filterThree = (e: any) => {
+    const _data: any = {
+      paging: false,
+    };
+    if (e.target.value) {
+      _data.terms = [{ column: 'name', value: e.target.value }];
+    }
+    queryPermissions(_data);
+  };
+
   /**
    * 更新菜单信息
    * @param data
@@ -297,7 +309,7 @@ export default (props: ButtonsProps) => {
             })}
             required={true}
           >
-            <Input disabled={disabled} />
+            <Input disabled={disabled} onChange={debounce(filterThree, 300)} />
             <Form.Item name="permissions" rules={[{ required: true, message: '请选择权限' }]}>
               <Permission
                 title={intl.formatMessage({

+ 13 - 2
src/pages/system/Menu/Detail/edit.tsx

@@ -5,6 +5,7 @@ import { useEffect, useState } from 'react';
 import { service } from '@/pages/system/Menu';
 import { useRequest } from 'umi';
 import type { MenuItem } from '@/pages/system/Menu/typing';
+import { debounce } from 'lodash';
 
 type EditProps = {
   data: MenuItem;
@@ -35,6 +36,16 @@ export default (props: EditProps) => {
     }
   };
 
+  const filterThree = (e: any) => {
+    const _data: any = {
+      paging: false,
+    };
+    if (e.target.value) {
+      _data.terms = [{ column: 'name', value: e.target.value }];
+    }
+    queryPermissions(_data);
+  };
+
   useEffect(() => {
     queryPermissions({ paging: false });
     /* eslint-disable */
@@ -59,7 +70,7 @@ export default (props: EditProps) => {
           required={true}
           rules={[{ required: true, message: '该字段是必填字段' }]}
         >
-          <Input disabled={true} />
+          <Input disabled={disabled} />
         </Form.Item>
         <Form.Item
           name="name"
@@ -89,7 +100,7 @@ export default (props: EditProps) => {
             defaultMessage: '权限',
           })}
         >
-          <Input disabled={disabled} />
+          <Input disabled={disabled} onChange={debounce(filterThree, 300)} />
           <Form.Item name="permissions">
             <Permission
               title={intl.formatMessage({

+ 7 - 2
src/pages/system/Menu/components/permission.tsx

@@ -3,6 +3,7 @@ import { Checkbox } from 'antd';
 import './permission.less';
 import type { CheckboxChangeEvent } from 'antd/es/checkbox';
 import type { PermissionInfo } from '../typing';
+import { useIntl } from 'umi';
 
 type PermissionDataType = {
   action: string;
@@ -122,6 +123,7 @@ export default (props: PermissionType) => {
   const [checkAll, setCheckAll] = useState(false);
   const [nodes, setNodes] = useState<React.ReactNode>([]);
   const checkListRef = useRef<CheckItem[]>([]);
+  const intl = useIntl();
 
   const onChange = (list: CheckItem[]) => {
     if (props.onChange) {
@@ -239,7 +241,7 @@ export default (props: PermissionType) => {
   };
 
   useEffect(() => {
-    if (props.data && props.data.length) {
+    if (props.data) {
       initialState(props.data);
     }
     /* eslint-disable */
@@ -257,7 +259,10 @@ export default (props: PermissionType) => {
               checked={checkAll}
               disabled={props.disabled}
             >
-              菜单权限
+              {intl.formatMessage({
+                id: 'pages.system.menu.root',
+                defaultMessage: '菜单权限',
+              })}
             </Checkbox>
           </div>
         </div>

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

@@ -18,6 +18,7 @@ import SearchComponent from '@/components/SearchComponent';
 import Service from './service';
 import type { MenuItem } from './typing';
 import moment from 'moment';
+import { getMenuPathBuCode, MENUS_CODE } from '@/utils/menu';
 
 export const service = new Service('menu');
 
@@ -54,9 +55,13 @@ export default observer(() => {
     actionRef.current?.reload();
   };
 
+  /**
+   * 跳转详情页
+   * @param id
+   */
   const pageJump = (id: string) => {
     // 跳转详情
-    history.push(`/system/menu/detail?id=${id}`);
+    history.push(`${getMenuPathBuCode(MENUS_CODE['system/Menu/Detail'])}?id=${id}`);
   };
 
   const columns: ProColumns<MenuItem>[] = [
@@ -65,7 +70,7 @@ export default observer(() => {
         id: 'page.system.menu.encoding',
         defaultMessage: '编码',
       }),
-      width: 220,
+      width: 300,
       dataIndex: 'code',
     },
     {
@@ -73,7 +78,7 @@ export default observer(() => {
         id: 'page.system.menu.name',
         defaultMessage: '名称',
       }),
-      width: 300,
+      width: 220,
       dataIndex: 'name',
     },
     {

+ 2 - 1
src/pages/system/Menu/typing.d.ts

@@ -57,7 +57,8 @@ export type MenuItem = {
    */
   level: number;
   createTime: number;
-  children: MenuItem;
+  redirect?: string;
+  children?: MenuItem[];
 };
 
 /**

+ 204 - 26
src/utils/menu.ts

@@ -1,27 +1,205 @@
 // 路由components映射
-// const findComponents = (files: __WebpackModuleApi.RequireContext) => {
-//   const modules = {};
-//   files.keys().forEach((key) => {
-//     // 删除路径开头的./ 以及结尾的 /index;
-//     const str = key.replace(/(\.\/|\.tsx)/g, '').replace('/index', '');
-//     modules[str] = files(key).default;
-//   });
-//   return modules;
-// };
-//
-// /**
-//  * 处理为正确的路由格式
-//  * @param extraRoutes 后端菜单数据
-//  */
-// const getRoutes = (extraRoutes: any[]) => {
-//   const allComponents = findComponents(require.context('@/pages', true, /index(\.tsx)$/));
-//   return extraRoutes.map((route) => {
-//     const component = allComponents[route.key];
-//     return {
-//       ...route,
-//       component,
-//     };
-//   });
-// };
-//
-// export default getRoutes;
+import type { IRouteProps } from 'umi';
+import type { MenuItem } from '@/pages/system/Menu/typing';
+
+/** localStorage key */
+export const MENUS_DATA_CACHE = 'MENUS_DATA_CACHE';
+
+/** 路由Code */
+export const MENUS_CODE = {
+  'Analysis/CPU': 'Analysis/CPU',
+  'Analysis/DeviceChart': 'Analysis/DeviceChart',
+  'Analysis/DeviceMessage': 'Analysis/DeviceMessage',
+  'Analysis/Jvm': 'Analysis/Jvm',
+  'Analysis/MessageChart': 'Analysis/MessageChart',
+  Analysis: 'Analysis',
+  'cloud/Aliyun': 'cloud/Aliyun',
+  'cloud/Ctwing': 'cloud/Ctwing',
+  'cloud/DuerOS': 'cloud/DuerOS',
+  'cloud/Onenet': 'cloud/Onenet',
+  'device/Alarm': 'device/Alarm',
+  'device/Category/Save': 'device/Category/Save',
+  'device/Category': 'device/Category',
+  'device/Command': 'device/Command',
+  'device/DataSource': 'device/DataSource',
+  'device/Firmware/Detail/History': 'device/Firmware/Detail/History',
+  'device/Firmware/Detail/Task/Detail': 'device/Firmware/Detail/Task/Detail',
+  'device/Firmware/Detail/Task/Release': 'device/Firmware/Detail/Task/Release',
+  'device/Firmware/Detail/Task/Save': 'device/Firmware/Detail/Task/Save',
+  'device/Firmware/Detail/Task': 'device/Firmware/Detail/Task',
+  'device/Firmware/Detail': 'device/Firmware/Detail',
+  'device/Firmware/Save': 'device/Firmware/Save',
+  'device/Firmware': 'device/Firmware',
+  'device/Instance/Detail/Config/Tags': 'device/Instance/Detail/Config/Tags',
+  'device/Instance/Detail/Config': 'device/Instance/Detail/Config',
+  'device/Instance/Detail/Functions': 'device/Instance/Detail/Functions',
+  'device/Instance/Detail/Info': 'device/Instance/Detail/Info',
+  'device/Instance/Detail/Log': 'device/Instance/Detail/Log',
+  'device/Instance/Detail/MetadataLog/Event': 'device/Instance/Detail/MetadataLog/Event',
+  'device/Instance/Detail/MetadataLog/Property': 'device/Instance/Detail/MetadataLog/Property',
+  'device/Instance/Detail/Running': 'device/Instance/Detail/Running',
+  'device/Instance/Detail': 'device/Instance/Detail',
+  'device/Instance': 'device/Instance',
+  'device/Location': 'device/Location',
+  'device/Product/Detail/BaseInfo': 'device/Product/Detail/BaseInfo',
+  'device/Product/Detail': 'device/Product/Detail',
+  'device/Product/Save': 'device/Product/Save',
+  'device/Product': 'device/Product',
+  'device/components/Alarm/Edit': 'device/components/Alarm/Edit',
+  'device/components/Alarm/Record': 'device/components/Alarm/Record',
+  'device/components/Alarm/Setting': 'device/components/Alarm/Setting',
+  'device/components/Alarm': 'device/components/Alarm',
+  'device/components/Metadata/Base/Edit': 'device/components/Metadata/Base/Edit',
+  'device/components/Metadata/Base': 'device/components/Metadata/Base',
+  'device/components/Metadata/Cat': 'device/components/Metadata/Cat',
+  'device/components/Metadata/Import': 'device/components/Metadata/Import',
+  'device/components/Metadata': 'device/components/Metadata',
+  'edge/Device': 'edge/Device',
+  'edge/Product': 'edge/Product',
+  'link/Certificate': 'link/Certificate',
+  'link/Gateway': 'link/Gateway',
+  'link/Opcua': 'link/Opcua',
+  'link/Protocol/Debug': 'link/Protocol/Debug',
+  'link/Protocol': 'link/Protocol',
+  'link/Type': 'link/Type',
+  'log/Access': 'log/Access',
+  'log/System': 'log/System',
+  'media/Cascade': 'media/Cascade',
+  'media/Config': 'media/Config',
+  'media/Device': 'media/Device',
+  'media/Reveal': 'media/Reveal',
+  'notice/Config': 'notice/Config',
+  'notice/Template': 'notice/Template',
+  'rule-engine/Instance': 'rule-engine/Instance',
+  'rule-engine/SQLRule': 'rule-engine/SQLRule',
+  'rule-engine/Scene': 'rule-engine/Scene',
+  'simulator/Device': 'simulator/Device',
+  'system/DataSource': 'system/DataSource',
+  'system/Department/Assets': 'system/Department/Assets',
+  'system/Department/Member': 'system/Department/Member',
+  'system/Department': 'system/Department',
+  'system/Menu/Detail': 'system/Menu/Detail',
+  'system/Menu': 'system/Menu',
+  'system/OpenAPI': 'system/OpenAPI',
+  'system/Permission': 'system/Permission',
+  'system/Role/Edit/Info': 'system/Role/Edit/Info',
+  'system/Role/Edit/UserManage': 'system/Role/Edit/UserManage',
+  'system/Role/Edit': 'system/Role/Edit',
+  'system/Role': 'system/Role',
+  'system/Tenant/Detail/Assets': 'system/Tenant/Detail/Assets',
+  'system/Tenant/Detail/Info': 'system/Tenant/Detail/Info',
+  'system/Tenant/Detail/Member': 'system/Tenant/Detail/Member',
+  'system/Tenant/Detail/Permission': 'system/Tenant/Detail/Permission',
+  'system/Tenant/Detail': 'system/Tenant/Detail',
+  'system/Tenant': 'system/Tenant',
+  'system/User/Save': 'system/User/Save',
+  'system/User': 'system/User',
+  'user/Login': 'user/Login',
+  'visualization/Category': 'visualization/Category',
+  'visualization/Configuration': 'visualization/Configuration',
+  'visualization/Screen': 'visualization/Screen',
+};
+
+/**
+ * 根据url获取映射的组件
+ * @param files
+ */
+const findComponents = (files: __WebpackModuleApi.RequireContext) => {
+  const modules = {};
+  files.keys().forEach((key) => {
+    // 删除路径开头的./ 以及结尾的 /index;
+    const str = key.replace(/(\.\/|\.tsx)/g, '').replace('/index', '');
+    modules[str] = files(key).default;
+  });
+  return modules;
+};
+
+/**
+ * 扁平化路由树
+ * @param routes
+ */
+export const flatRoute = (routes: MenuItem[]): MenuItem[] => {
+  return routes.reduce<MenuItem[]>((pValue, currValue) => {
+    const menu: MenuItem[] = [];
+    const { children, ...extraRoute } = currValue;
+    menu.push(extraRoute);
+    return [...pValue, ...menu, ...flatRoute(children || [])];
+  }, []);
+};
+
+/**
+ * 处理为正确的路由格式
+ * @param extraRoutes 后端菜单数据
+ * @param level 路由层级
+ */
+const getRoutes = (extraRoutes: MenuItem[], level: number = 1) => {
+  const allComponents = findComponents(require.context('@/pages', true, /index(\.tsx)$/));
+  const Menus: IRouteProps[] = [];
+  extraRoutes.forEach((route) => {
+    const component = allComponents[route.code] || null;
+    if (level === 1) {
+      Menus.push({
+        key: route.url,
+        name: route.name,
+        path: route.url,
+        children: getRoutes(flatRoute(route.children || []), level + 1),
+      });
+    }
+    if (component) {
+      Menus.push({
+        key: route.url,
+        name: route.name,
+        path: route.url,
+        exact: true,
+        component,
+      });
+    }
+  });
+  return Menus;
+};
+
+export const getMenus = (extraRoutes: MenuItem[], level: number = 1): any[] => {
+  return extraRoutes.map((route) => {
+    const children =
+      route.children && route.children.length ? getMenus(route.children, level + 1) : [];
+    return {
+      key: route.url,
+      name: route.name,
+      path: route.url,
+      hideChildrenInMenu: level === 2,
+      exact: true,
+      state: {
+        params: {
+          id: 123,
+        },
+      },
+      children: children,
+    };
+  });
+};
+
+/** 缓存路由数据,格式为 [{ code: url }] */
+export const saveMenusCache = (routes: MenuItem[]) => {
+  const list: MenuItem[] = flatRoute(routes);
+  const listObject = {};
+  list.forEach((route) => {
+    listObject[route.code] = route.url;
+  });
+  try {
+    localStorage.setItem(MENUS_DATA_CACHE, JSON.stringify(listObject));
+  } catch (e) {
+    console.log(e);
+  }
+};
+
+/**
+ * 通过缓存的数据取出相应的路由url
+ * @param code
+ */
+export const getMenuPathBuCode = (code: string): string => {
+  const menusStr = localStorage.getItem(MENUS_DATA_CACHE) || '{}';
+  const menusData = JSON.parse(menusStr);
+  return menusData[code];
+};
+
+export default getRoutes;