Просмотр исходного кода

fix(route): fix router

fix(菜单管理): 修改全局菜单路由
Lind 3 лет назад
Родитель
Сommit
c7a25d3166

+ 3 - 3
src/app.tsx

@@ -12,7 +12,7 @@ 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';
+import getRoutes, { getMenus, handleRoutes, saveMenusCache } from '@/utils/menu';
 
 const isDev = process.env.NODE_ENV === 'development';
 const loginPath = '/user/login';
@@ -239,8 +239,8 @@ export function render(oldRender: any) {
   if (history.location.pathname !== loginPath) {
     MenuService.queryMenuThree({ paging: false }).then((res) => {
       if (res.status === 200) {
-        extraRoutes = res.result;
-        saveMenusCache(res.result);
+        extraRoutes = handleRoutes(res.result);
+        saveMenusCache(extraRoutes);
       }
       oldRender();
     });

+ 15 - 18
src/pages/system/Department/Assets/index.tsx

@@ -1,5 +1,4 @@
 // 部门-资产分配
-import { PageContainer } from '@ant-design/pro-layout';
 import { Tabs } from 'antd';
 import { useIntl } from '@@/plugin-locale/localeExports';
 import ProductCategory from './productCategory';
@@ -32,23 +31,21 @@ const Assets = () => {
   const intl = useIntl();
 
   return (
-    <PageContainer>
-      <div style={{ background: '#fff', padding: 12 }}>
-        <Tabs tabPosition="left" defaultActiveKey="ProductCategory">
-          {TabsArray.map((item) => (
-            <Tabs.TabPane
-              tab={intl.formatMessage({
-                id: item.intlTitle,
-                defaultMessage: item.defaultMessage,
-              })}
-              key={item.key}
-            >
-              <item.components />
-            </Tabs.TabPane>
-          ))}
-        </Tabs>
-      </div>
-    </PageContainer>
+    <div style={{ background: '#fff', padding: 12 }}>
+      <Tabs tabPosition="left" defaultActiveKey="ProductCategory">
+        {TabsArray.map((item) => (
+          <Tabs.TabPane
+            tab={intl.formatMessage({
+              id: item.intlTitle,
+              defaultMessage: item.defaultMessage,
+            })}
+            key={item.key}
+          >
+            <item.components />
+          </Tabs.TabPane>
+        ))}
+      </Tabs>
+    </div>
   );
 };
 

+ 15 - 0
src/pages/system/Department/Detail/index.tsx

@@ -0,0 +1,15 @@
+import { PageContainer } from '@ant-design/pro-layout';
+import { useLocation } from 'umi';
+import Assets from '../Assets';
+import Member from '../Member';
+
+type LocationType = {
+  type: string;
+};
+
+export default () => {
+  const location = useLocation<LocationType>();
+  const params: any = new URLSearchParams(location.search);
+
+  return <PageContainer>{params.get('type') === 'assets' ? <Assets /> : <Member />}</PageContainer>;
+};

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

@@ -1,5 +1,4 @@
 // 部门-用户管理
-import { PageContainer } from '@ant-design/pro-layout';
 import type { ActionType, ProColumns } from '@jetlinks/pro-table';
 import ProTable from '@jetlinks/pro-table';
 import { useIntl } from '@@/plugin-locale/localeExports';
@@ -151,7 +150,7 @@ const Member = observer(() => {
   };
 
   return (
-    <PageContainer>
+    <>
       <Bind
         visible={MemberModel.bind}
         onCancel={closeModal}
@@ -216,7 +215,7 @@ const Member = observer(() => {
           </Popconfirm>,
         ]}
       />
-    </PageContainer>
+    </>
   );
 });
 

+ 12 - 2
src/pages/system/Department/index.tsx

@@ -20,6 +20,7 @@ import { observer } from '@formily/react';
 import { model } from '@formily/reactive';
 import Save from './save';
 import SearchComponent from '@/components/SearchComponent';
+import { getMenuPathByParams, MENUS_CODE } from '@/utils/menu';
 
 export const service = new Service('organization');
 
@@ -115,7 +116,13 @@ export default observer(() => {
             <PlusCircleOutlined />
           </Tooltip>
         </a>,
-        <Link key="assets" to={`/system/department/${record.id}/assets`}>
+        <Link
+          key="assets"
+          to={`${getMenuPathByParams(
+            MENUS_CODE['system/Department/Detail'],
+            record.id,
+          )}?type=assets`}
+        >
           <Tooltip
             title={intl.formatMessage({
               id: 'pages.data.option.assets',
@@ -125,7 +132,10 @@ export default observer(() => {
             <MedicineBoxOutlined />
           </Tooltip>
         </Link>,
-        <Link key="user" to={`/system/department/${record.id}/user`}>
+        <Link
+          key="user"
+          to={`${getMenuPathByParams(MENUS_CODE['system/Department/Detail'], record.id)}?type=user`}
+        >
           <Tooltip
             title={intl.formatMessage({
               id: 'pages.system.department.user',

+ 29 - 3
src/pages/system/Menu/Detail/edit.tsx

@@ -17,15 +17,17 @@ import Permission from '@/pages/system/Menu/components/permission';
 import { useIntl } from '@@/plugin-locale/localeExports';
 import { useEffect, useState } from 'react';
 import { service } from '@/pages/system/Menu';
-import { useRequest } from 'umi';
+import { useRequest, useHistory } from 'umi';
 import type { MenuItem } from '@/pages/system/Menu/typing';
 // import { debounce } from 'lodash';
 import Title from '../components/Title';
 import { UploadImage } from '@/components';
 import { QuestionCircleFilled } from '@ant-design/icons';
+import { getMenuPathByParams, MENUS_CODE } from '@/utils/menu';
 
 type EditProps = {
   data: MenuItem;
+  basePath?: string;
   onLoad: (id: string) => void;
 };
 
@@ -34,6 +36,7 @@ export default (props: EditProps) => {
   const [disabled, setDisabled] = useState(true);
   const [show, setShow] = useState(true);
   const [accessSupport, setAccessSupport] = useState('unsupported');
+  const history = useHistory();
 
   const [form] = Form.useForm();
 
@@ -52,20 +55,43 @@ export default (props: EditProps) => {
     formatResult: (response) => response.result,
   });
 
+  /**
+   * 跳转详情页
+   * @param id
+   */
+  const pageJump = (id?: string) => {
+    // 跳转详情
+    history.push(`${getMenuPathByParams(MENUS_CODE['system/Menu/Detail'], id)}`);
+  };
+
   const saveData = async () => {
     const formData = await form.validateFields();
     if (formData) {
-      const response: any = await service.update(formData);
+      const response: any = !props.data.id
+        ? await service.save(formData)
+        : await service.update(formData);
       if (response.status === 200) {
         message.success('操作成功!');
         setDisabled(true);
-        props.onLoad(response.result.id);
+        // 新增后刷新页面,编辑则不需要
+        if (!props.data.id) {
+          pageJump(response.result.id);
+        }
       } else {
         message.error('操作失败!');
       }
     }
   };
 
+  useEffect(() => {
+    console.log(props);
+    if (form && props.basePath) {
+      form.setFieldsValue({
+        url: props.basePath,
+      });
+    }
+  }, []);
+
   // const filterThree = (e: any) => {
   //   const _data: any = {
   //     paging: false,

+ 6 - 4
src/pages/system/Menu/Detail/index.tsx

@@ -4,7 +4,7 @@ import { useIntl } from '@@/plugin-locale/localeExports';
 import { useEffect, useState } from 'react';
 import BaseDetail from './edit';
 import Buttons from './buttons';
-import { useLocation, useRequest } from 'umi';
+import { useLocation, useParams, useRequest } from 'umi';
 import { service } from '@/pages/system/Menu';
 
 type LocationType = {
@@ -16,6 +16,8 @@ export default () => {
   const [tabKey, setTabKey] = useState('detail');
   const [pId, setPid] = useState<string | null>(null);
   const location = useLocation<LocationType>();
+  const params: any = new URLSearchParams(location.search);
+  const param = useParams<{ id?: string }>();
 
   const { data, run: queryData } = useRequest(service.queryDetail, {
     manual: true,
@@ -28,10 +30,9 @@ export default () => {
    * 获取当前菜单详情
    */
   const queryDetail = (editId?: string) => {
-    const params = new URLSearchParams(location.search);
-    const id = editId || params.get('id');
+    const id = editId || param.id;
     const _pId = params.get('pId');
-    if (id) {
+    if (id && id !== ':id') {
       queryData(id);
     }
     if (_pId) {
@@ -73,6 +74,7 @@ export default () => {
               ...data,
               parentId: pId,
             }}
+            basePath={params.get('basePath')}
             onLoad={queryDetail}
           />
         </div>

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

@@ -18,7 +18,7 @@ import SearchComponent from '@/components/SearchComponent';
 import Service from './service';
 import type { MenuItem } from './typing';
 import moment from 'moment';
-import { getMenuPathByCode, MENUS_CODE } from '@/utils/menu';
+import { getMenuPathByParams, MENUS_CODE } from '@/utils/menu';
 
 export const service = new Service('menu');
 
@@ -59,11 +59,14 @@ export default observer(() => {
    * 跳转详情页
    * @param id
    * @param pId
+   * @param basePath
    */
-  const pageJump = (id?: string, pId?: string) => {
+  const pageJump = (id?: string, pId?: string, basePath?: string) => {
     // 跳转详情
     history.push(
-      `${getMenuPathByCode(MENUS_CODE['system/Menu/Detail'])}?id=${id || ''}&pId=${pId || ''}`,
+      `${getMenuPathByParams(MENUS_CODE['system/Menu/Detail'], id)}?pId=${pId || ''}&basePath=${
+        basePath || ''
+      }`,
     );
   };
 
@@ -148,7 +151,7 @@ export default observer(() => {
               parentId: record.id,
             };
             // State.visible = true;
-            pageJump('', record.id);
+            pageJump('', record.id, record.url);
           }}
         >
           <Tooltip

+ 164 - 0
src/utils/menu/index.ts

@@ -0,0 +1,164 @@
+// 路由components映射
+import type { IRouteProps } from 'umi';
+import type { MenuItem } from '@/pages/system/Menu/typing';
+import { MENUS_CODE, getDetailNameByCode } from './router';
+
+/** localStorage key */
+export const MENUS_DATA_CACHE = 'MENUS_DATA_CACHE';
+
+const DetailCode = 'Detail';
+
+/**
+ * 根据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 baseCode
+ */
+const findDetailRoute = (baseCode: string): MenuItem | undefined => {
+  if (baseCode) {
+    const allComponents = findComponents(require.context('@/pages', true, /index(\.tsx)$/));
+    const code = `${baseCode}/${DetailCode}`;
+    const path = `/${code}/:id`;
+    const component = allComponents[code];
+    return component
+      ? ({ path: path, url: path, name: getDetailNameByCode[code], code } as MenuItem)
+      : undefined;
+  }
+  return undefined;
+};
+
+export const handleRoutes = (routes?: MenuItem[], level = 1): MenuItem[] => {
+  return routes
+    ? routes.map((item) => {
+        const detailComponent = findDetailRoute(item.code);
+        if (detailComponent) {
+          item.children = item.children ? [...item.children, detailComponent] : [detailComponent];
+        }
+        // eslint-disable-next-line @typescript-eslint/no-unused-vars
+        if (item.children) {
+          item.children = handleRoutes(item.children, level + 1);
+        }
+        item.level = level;
+        return item;
+      })
+    : [];
+};
+
+/**
+ * 处理为正确的路由格式
+ * @param extraRoutes 后端菜单数据
+ * @param level 路由层级
+ */
+const getRoutes = (extraRoutes: MenuItem[], level = 1): IRouteProps[] => {
+  const allComponents = findComponents(require.context('@/pages', true, /index(\.tsx)$/));
+  return extraRoutes.map((route) => {
+    const component = allComponents[route.code] || null;
+    const _route: IRouteProps = {
+      key: route.url,
+      name: route.name,
+      path: route.url,
+    };
+
+    if (route.children && route.children.length) {
+      const flatRoutes = getRoutes(flatRoute(route.children || []), level + 1);
+      const redirect = flatRoutes.filter((r) => r.component)[0]?.path;
+      _route.children = redirect
+        ? [
+            ...flatRoutes,
+            {
+              path: _route.path,
+              redirect: redirect,
+            },
+          ]
+        : flatRoutes;
+    } else if (component) {
+      _route.component = component;
+    }
+
+    if (level !== 1) {
+      _route.exact = true;
+    }
+
+    return _route;
+  });
+};
+
+export const getMenus = (extraRoutes: IRouteProps[]): any[] => {
+  return extraRoutes.map((route) => {
+    const children = route.children && route.children.length ? route.children : [];
+
+    return {
+      key: route.url,
+      name: route.name,
+      path: route.url,
+      icon: route.icon,
+      hideChildrenInMenu: children && children.some((item: any) => item.url.includes(DetailCode)),
+      exact: route.level !== 1,
+      children: getMenus(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 getMenuPathByCode = (code: string): string => {
+  const menusStr = localStorage.getItem(MENUS_DATA_CACHE) || '{}';
+  const menusData = JSON.parse(menusStr);
+  return menusData[code];
+};
+
+/**
+ * 通过缓存的数据取出相应的路由url
+ * @param code 路由Code
+ * @param id 路由携带参数
+ * @param regStr 路由参数code
+ */
+export const getMenuPathByParams = (code: string, id?: string, regStr: string = ':id') => {
+  const menusData = getMenuPathByCode(code);
+  return id ? menusData.replace(regStr, id) : menusData;
+};
+
+export default getRoutes;
+
+export { MENUS_CODE };

+ 28 - 147
src/utils/menu.ts

@@ -1,10 +1,3 @@
-// 路由components映射
-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',
@@ -22,27 +15,10 @@ export const MENUS_CODE = {
   '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',
@@ -64,7 +40,6 @@ export const MENUS_CODE = {
   'link/Type': 'link/Type',
   'link/Type/Save': 'link/Type/Save',
   'link/AccessConfig': 'link/AccessConfig',
-  'link/AccessConfig/Detail': 'link/AccessConfig/Detail',
   'log/Access': 'log/Access',
   'log/System': 'log/System',
   'media/Cascade': 'media/Cascade',
@@ -81,7 +56,6 @@ export const MENUS_CODE = {
   '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',
@@ -98,127 +72,34 @@ export const MENUS_CODE = {
   'visualization/Category': 'visualization/Category',
   'visualization/Configuration': 'visualization/Configuration',
   'visualization/Screen': 'visualization/Screen',
+  '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/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/Product/Detail/BaseInfo': 'device/Product/Detail/BaseInfo',
+  'device/Product/Detail': 'device/Product/Detail',
+  'link/AccessConfig/Detail': 'link/AccessConfig/Detail',
+  'system/Menu/Detail': 'system/Menu/Detail',
+  'system/Department/Detail': 'system/Department/Detail',
 };
 
-/**
- * 根据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 allComponents = findComponents(require.context('@/pages', true, /index(\.tsx)$/));
-const getRoutes = (extraRoutes: MenuItem[], level = 1): IRouteProps[] => {
-  return extraRoutes.map((route) => {
-    const component = allComponents[route.code] || null;
-    const _route: IRouteProps = {
-      key: route.url,
-      name: route.name,
-      path: route.url,
-    };
-
-    if (route.children && route.children.length) {
-      const flatRoutes = getRoutes(flatRoute(route.children || []), level + 1);
-      const redirect = flatRoutes.filter((r) => r.component)[0]?.path;
-
-      _route.children = redirect
-        ? [
-            ...flatRoutes,
-            {
-              path: _route.path,
-              redirect: redirect,
-            },
-          ]
-        : flatRoutes;
-    }
-
-    if (component) {
-      _route.component = component;
-    }
-
-    if (level !== 1) {
-      _route.exact = true;
-    }
-
-    return _route;
-  });
-};
-
-export const getMenus = (extraRoutes: IRouteProps[], level: number = 1): any[] => {
-  return extraRoutes.map((route) => {
-    const children =
-      route.children && route.children.length ? getMenus(route.children, level + 1) : [];
-    const _route = {
-      key: route.url,
-      name: route.name,
-      path: route.url,
-      icon: route.icon,
-      exact: level !== 1 ? true : false,
-      children: children,
-    };
-    return _route;
-  });
-};
-
-/** 缓存路由数据,格式为 [{ 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 getMenuPathByCode = (code: string): string => {
-  const menusStr = localStorage.getItem(MENUS_DATA_CACHE) || '{}';
-  const menusData = JSON.parse(menusStr);
-  return menusData[code];
-};
-
-/**
- * 通过缓存的数据取出相应的路由url
- * @param code 路由Code
- * @param id 路由携带参数
- * @param regStr 路由参数code
- */
-export const getMenuPathByParams = (code: string, id?: string, regStr: string = ':id') => {
-  const menusData = getMenuPathByCode(code);
-  return id ? menusData.replace(regStr, id) : menusData;
+export const getDetailNameByCode = {
+  'system/Menu/Detail': '菜单详情',
+  'device/Product/Detail': '产品详情',
+  'device/Instance/Detail': '设备详情',
+  'device/Firmware/Detail': '固件详情',
+  'system/Department/Detail': '部门详情',
+  'link/Type/Save': '网络组件详情',
+  'link/AccessConfig/Detail': '配置详情',
 };
-
-export default getRoutes;