feat: 动态路由

This commit is contained in:
2025-09-12 15:37:07 +08:00
parent 2bb11b49fe
commit e42e1c01fb
14 changed files with 321 additions and 118 deletions

View File

@@ -1,9 +1,9 @@
import { LinkOutlined } from "@ant-design/icons";
import React, { Children, Component, JSX, Suspense } from "react";
import { Spin } from "antd";
import type { Settings as LayoutSettings } from "@ant-design/pro-components";
import { SettingDrawer } from "@ant-design/pro-components";
import type { RequestConfig, RunTimeLayoutConfig } from "@umijs/max";
import { history, Link } from "@umijs/max";
import React from "react";
import { history, Link, Navigate } from "@umijs/max";
import {
AvatarDropdown,
AvatarName,
@@ -16,13 +16,20 @@ import type { UserVO, TokenType, UserInfoVO } from "@/services/login/types";
import defaultSettings from "../config/defaultSettings";
import { errorConfig } from "./requestErrorConfig";
import "@ant-design/v5-patch-for-react-19";
import { getAccessToken, getRefreshToken, getTenantId } from "./utils/auth";
import { getAccessToken, getRefreshToken, getTenantId } from "@/utils/auth";
import { CACHE_KEY, useCache } from "./hooks/web/useCache";
import { MenuVO } from "./services/system/menu";
import {
transformBackendMenuToFlatRoutes,
transformMenuToRoutes,
} from "@/utils/menuUtils";
const isDev = process.env.NODE_ENV === "development";
const isDevOrTest = isDev || process.env.CI;
const loginPath = "/user/login";
// 全局存储菜单数据和路由映射
/**
* @see https://umijs.org/docs/api/runtime-config#getinitialstate
* */
@@ -43,6 +50,9 @@ export async function getInitialState(): Promise<{
const { data } = await getInfo();
wsCache.set(CACHE_KEY.USER, data);
wsCache.set(CACHE_KEY.ROLE_ROUTERS, data.menus);
// 转换菜单格式
return data;
} catch (_error) {
history.push(loginPath);
@@ -51,12 +61,17 @@ export async function getInitialState(): Promise<{
};
// 如果不是登录页面,执行
const { location } = history;
if (
![loginPath, "/user/register", "/user/register-result"].includes(
location.pathname
)
) {
const currentUser = await fetchUserInfo();
const currentUser = wsCache.get(CACHE_KEY.USER);
if (getAccessToken() && !currentUser) {
fetchUserInfo();
}
return {
fetchUserInfo,
currentUser,
@@ -79,6 +94,10 @@ export const layout: RunTimeLayoutConfig = ({
<Question key="doc" />,
<SelectLang key="SelectLang" />,
],
menu: {
locale: false,
// 关闭国际化-
},
avatarProps: {
src: initialState?.currentUser?.user.avatar,
title: <AvatarName />,
@@ -117,17 +136,9 @@ export const layout: RunTimeLayoutConfig = ({
width: "331px",
},
],
links: isDevOrTest
? [
<Link key="openapi" to="/umi/plugin/openapi" target="_blank">
<LinkOutlined />
<span>OpenAPI </span>
</Link>,
]
: [],
menuHeaderRender: undefined,
// 自定义 403 页面
// unAccessible: <div>unAccessible</div>,
unAccessible: <div>unAccessible</div>,
// 增加一个 loading 的状态
childrenRender: (children) => {
// if (initialState?.loading) return <PageLoading />;
@@ -187,3 +198,65 @@ export const request: RequestConfig = {
},
],
};
// umi 4 使用 modifyRoutes
export function patchClientRoutes({ routes }: { routes: any }) {
const { wsCache } = useCache();
const globalMenus = wsCache.get(CACHE_KEY.ROLE_ROUTERS);
const routerIndex = routes.findIndex((item: any) => item.path === "/");
const parentId = routes[routerIndex].id;
if (globalMenus) {
routes[routerIndex]["routes"].push(...loopMenuItem(globalMenus, parentId));
}
}
const loopMenuItem = (menus: any[], pId: number | string): any[] => {
return menus.flatMap((item) => {
let Component: React.ComponentType<any> | null = null;
if (item.component && item.component.length > 0) {
// 防止配置了路由,但本地暂未添加对应的页面,产生的错误
console.log(item.component);
Component = React.lazy(() => {
const importComponent = () => import(`@/pages/${item.component}`);
const import404 = () => import("@/pages/404");
return importComponent().catch(import404);
});
}
if (item.children && item.children.length > 0) {
return [
{
path: item.path,
name: item.name,
icon: item.icon,
id: item.id,
parentId: pId,
children: [
{
path: item.url,
element: <Navigate to={item.children[0].url} replace />,
},
...loopMenuItem(item.children, item.menuID),
],
},
];
} else {
return [
{
path: item.path,
name: item.name,
icon: item.icon,
id: item.menuID,
parentId: pId,
element: (
<React.Suspense
fallback={<Spin style={{ width: "100%", height: "100%" }} />}
>
{Component && <Component />}
</React.Suspense>
),
children: [], // 添加缺失的 children 属性
},
];
}
});
};