1. 顶部菜单栏 伸缩展开按钮
需求:默认的菜单栏伸缩按钮是在侧面想换成顶部。
添加文件:/src/components/HeaderContent/index.tsx
import React from 'react';
import useMergedState from 'rc-util/es/hooks/useMergedState';
import { MenuFoldOutlined, MenuUnfoldOutlined } from '@ant-design/icons';
import { Button } from 'antd';
export type HeaderContent = {
collapse?: boolean;
onCollapse?: (collapsed: boolean) => void;
};
// eslint-disable-next-line @typescript-eslint/no-redeclare
const HeaderContent: React.FC<HeaderContent> = (props: any) => {
const [collapsed, setCollapsed] = useMergedState<boolean>(props.collapse ?? false, {
value: props.collapse,
onChange: props.onCollapse,
});
const urlParams = new URL(window.location.href);
return (
<>
<div>
<Button
onClick={() => setCollapsed(!collapsed)}
style={{
cursor: 'pointer',
fontSize: '16px',
border:0,
}}
>
{collapsed ? <MenuUnfoldOutlined /> : <MenuFoldOutlined />}
</Button>
Welcome
</div>
</>
);
};
export default HeaderContent;
修改文件:/src/app.tsx (完整文件 文章底部 ⬇)
完整文件:/src/app.tsx
import type { MenuDataItem, Settings as LayoutSettings } from '@ant-design/pro-layout';
import { notification } from 'antd';
import { SettingDrawer } from '@ant-design/pro-layout';
import { PageLoading } from '@ant-design/pro-layout';
import { dynamic, RunTimeLayoutConfig } from 'umi';
import { history, Link } from 'umi';
import HeaderContent from '@/components/HeaderContent';
import RightContent from '@/components/RightContent';
import Footer from '@/components/Footer';
import { message } from 'antd';
import { mergeRoutes } from './utils/roles';
import { oauthInfo as queryCurrentUser } from './services/api/oauth';
import { getMenu, getAuthMenu } from './services/api/menu';
import { BookOutlined, LinkOutlined } from '@ant-design/icons';
import Icon from '@ant-design/icons';
import * as icons from '@ant-design/icons';
import fixMenuItemIcon from '@/utils/fixMenuItemIcon';
import defaultSettings from '../config/defaultSettings';
const isDev = process.env.NODE_ENV === 'development';
const loginPath = '/user/login';
/** 获取用户信息比较慢的时候会展示一个 loading */
export const initialStateConfig = {
loading: <PageLoading />,
};
let extraRoutes;
export function patchRoutes({ routes }) {
if (extraRoutes) {
// extraRoutes.forEach((element: any) => {
routes.forEach((route: any) => {
if (route.path == "/") {
route.routes = mergeRoutes(extraRoutes);
}
});
// });
}
console.log("--------------------路由-------------------------", routes);
}
// export function render(oldRender) {
// getMenu().then((res: any) => {
// if (res.code == 200) {
// extraRoutes = res.result;
// oldRender();
// } else {
// history.push('/login');
// oldRender()
// }
// });
// }
// /**
// * 映射菜单对应的图标
// * */
// const loopMenuItem = (menus: MenuDataItem[]): MenuDataItem[] =>
// menus.map(({ icon, routes, ...item }) => ({
// ...item,
// icon: icon && <Icon component={icons[icon]} />,
// routes: routes && loopMenuItem(routes),
// })
// );
/**
* @see https://umijs.org/zh-CN/plugins/plugin-initial-state
* */
export async function getInitialState(): Promise<{
currentUser?: API.CurrentUser;
loading?: boolean;
collapsed?: boolean;
settings?: Partial<LayoutSettings>;
// menuData?: MenuDataItem[] | undefined;
fetchUserInfo?: () => Promise<API.CurrentUser | undefined>;
}> {
const fetchUserInfo = async () => {
try {
const msg = await queryCurrentUser();
return msg.data;
} catch (error) {
// 跳转到指定路由
history.push(loginPath);
}
return undefined;
};
// 如果不是登录页面,执行
if (history.location.pathname !== loginPath) {
const currentUser = await fetchUserInfo();
return {
fetchUserInfo,
currentUser,
settings: defaultSettings,
};
}
return {
fetchUserInfo,
settings: defaultSettings,
};
}
// ProLayout 支持的api https://procomponents.ant.design/components/layout
export const layout: RunTimeLayoutConfig = ({ initialState, setInitialState }) => {
const onCollapse = (collapsed: boolean): void => {
setInitialState({ ...initialState, collapsed }).then();
};
const onSettings = (settings: any): void => {
setInitialState({ ...initialState, settings }).then();
};
return {
// 自定义头内容的方法 我把 自定义侧边栏收缩按钮位置 方在这里
headerContentRender: () => (
<HeaderContent collapse={initialState?.collapsed} onCollapse={onCollapse} />
),
rightContentRender: () => <RightContent onSettings={onSettings} />,
disableContentMargin: false,
waterMarkProps: {
content: initialState?.currentUser?.name,
},
// 去掉系统自带
collapsedButtonRender: false,
// 指定配置collapsed
collapsed: initialState?.collapsed,
footerRender: () => <Footer />,
onPageChange: () => {
const { location } = history;
// 如果没有登录,重定向到 login
if (!initialState?.currentUser && location.pathname !== loginPath) {
history.push(loginPath);
}
},
// menuDataRender: () => { return fixMenuItemIcon(initialState.menuData) },
menu: {
locale: false,
params: {
userId: initialState?.currentUser?.userid,//引起菜单请求的参数
},
request: async (params, defaultMenuData) => {
// const msg = await getMenu();
return fixMenuItemIcon(initialState?.currentUser?.menu);
},
},
links: isDev
? [
<Link key="openapi" to="/umi/plugin/openapi" target="_blank">
<LinkOutlined />
<span>OpenAPI 文档</span>
</Link>,
<Link to="/~docs" key="docs">
<BookOutlined />
<span>业务组件文档</span>
</Link>,
]
: [],
menuHeaderRender: undefined,
// 自定义 403 页面
// unAccessible: <div>unAccessible</div>,
// 增加一个 loading 的状态
childrenRender: (children, props) => {
// if (initialState?.loading) return <PageLoading />;
const { location, route } = props;
if (history.location.pathname !== loginPath && initialState?.currentUser?.menu) {
// 路由守卫
extraRoutes = initialState?.currentUser?.menu;
console.log("--------------------路由01-------------------------", extraRoutes);
patchRoutes(route);
}
if (history.location.pathname === "/") {
// 默认跳转
history.push('/welcome');
}
return (
<>
{children}
{/* {!props.location?.pathname?.includes('/login') && (
<SettingDrawer
disableUrlParams
enableDarkTheme
settings={initialState?.settings}
onSettingChange={(settings) => {
setInitialState((preInitialState) => ({
...preInitialState,
settings,
}));
}}
/>
)} */}
</>
);
},
...initialState?.settings,
};
};
// ---------------------------------------------------------
//拦截器-响应后拦截
const demoResponseInterceptors = async (response: any) => {
const res = await response.clone().json(); //这里是关键,获取所有接口请求成功之后的数据
// console.log(res);
if (res?.code === 401) {
message.error('登录过期');
history.push(loginPath);
}
if (res?.code != 200) {
message.error(res?.message, 10);
}
return response
};
const codeMessage = {
200: '服务器成功返回请求的数据。',
201: '新建或修改数据成功。',
202: '一个请求已经进入后台排队(异步任务)。',
204: '删除数据成功。',
400: '发出的请求有错误,服务器没有进行新建或修改数据的操作。',
401: '用户没有权限(令牌、用户名、密码错误)。',
403: '用户得到授权,但是访问是被禁止的。',
404: '发出的请求针对的是不存在的记录,服务器没有进行操作。',
405: '请求方法不被允许。',
406: '请求的格式不可得。',
410: '请求的资源被永久删除,且不会再得到的。',
422: '当创建一个对象时,发生一个验证错误。',
500: '服务器发生错误,请检查服务器。',
502: '网关错误。',
503: '服务不可用,服务器暂时过载或维护。',
504: '网关超时。',
};
/**
* 异常处理程序
200: '服务器成功返回请求的数据。',
201: '新建或修改数据成功。',
202: '一个请求已经进入后台排队(异步任务)。',
204: '删除数据成功。',
400: '发出的请求有错误,服务器没有进行新建或修改数据的操作。',
401: '用户没有权限(令牌、用户名、密码错误)。',
403: '用户得到授权,但是访问是被禁止的。',
404: '发出的请求针对的是不存在的记录,服务器没有进行操作。',
405: '请求方法不被允许。',
406: '请求的格式不可得。',
410: '请求的资源被永久删除,且不会再得到的。',
422: '当创建一个对象时,发生一个验证错误。',
500: '服务器发生错误,请检查服务器。',
502: '网关错误。',
503: '服务不可用,服务器暂时过载或维护。',
504: '网关超时。',
//-----English
200: The server successfully returned the requested data. ',
201: New or modified data is successful. ',
202: A request has entered the background queue (asynchronous task). ',
204: Data deleted successfully. ',
400: 'There was an error in the request sent, and the server did not create or modify data. ',
401: The user does not have permission (token, username, password error). ',
403: The user is authorized, but access is forbidden. ',
404: The request sent was for a record that did not exist. ',
405: The request method is not allowed. ',
406: The requested format is not available. ',
410':
'The requested resource is permanently deleted and will no longer be available. ',
422: When creating an object, a validation error occurred. ',
500: An error occurred on the server, please check the server. ',
502: Gateway error. ',
503: The service is unavailable. ',
504: The gateway timed out. ',
* @see https://beta-pro.ant.design/docs/request-cn
*/
export const request: RequestConfig = {
errorHandler: (error: ResponseError) => {
const { response } = error;
if (response && response.status) {
const errorText = codeMessage[response.status] || response.statusText;
const { status, url } = response;
notification.error({
message: `请求错误 ${status}: ${url}`,
description: errorText,
});
}
if (!response) {
notification.error({
description: '您的网络发生异常,无法连接服务器',
message: '网络异常',
});
}
throw error;
},
// 拦截器-请求前拦截
// requestInterceptors: [authHeaderInterceptor],
//拦截器-响应后拦截
responseInterceptors: [demoResponseInterceptors]
};