布局是一个中后台应用必备的,一个布局 + ProTable + Form 即可获得一个 CRUD 页面。
Pro 中内置了 plugin-layout 来减少样板代码。简单的使用中我们只需要在 config.ts 中配置 layout 属性就可以实现通用的页面布局。
zero-admin-ui\config\config.ts
UI 配置
布局样式
layout 插件 与pro-layout 配置的配置形同。
推荐先使用 Pro 站点 的右侧抽屉来帮助你完成布局相关的整体风格、主题色、导航模式、内容区域宽度、固定 Header、固定侧边菜单、色弱模式等配置选择。然后将拷贝的配置粘贴与 layout 配置中。
// https://umijs.org/config/import { defineConfig } from 'umi';import defaultSettings from './defaultSettings';import proxy from './proxy';import routes from './routes';const { REACT_APP_ENV } = process.env;export default defineConfig({hash: true,antd: {},dva: {hmr: true,},layout: {// https://umijs.org/zh-CN/plugins/plugin-layoutlocale: true,siderWidth: 208,...defaultSettings,},// https://umijs.org/zh-CN/plugins/plugin-localelocale: {// default zh-CNdefault: 'zh-CN',antd: true,// default true, when it is true, will use `navigator.language` overwrite defaultbaseNavigator: true,},dynamicImport: {loading: '@ant-design/pro-layout/es/PageLoading',},targets: {ie: 11,},// umi routes: https://umijs.org/docs/routingroutes,// Theme for antd: https://ant.design/docs/react/customize-theme-cntheme: {'primary-color': defaultSettings.primaryColor,},// esbuild is father build tools// https://umijs.org/plugins/plugin-esbuildesbuild: {},title: false,ignoreMomentLocale: true,proxy: proxy[REACT_APP_ENV || 'dev'],manifest: {basePath: '/',},// Fast Refresh 热更新//https://umijs.org/zh-CN/docs/fast-refreshfastRefresh: {},//https://umijs.org/guide/boost-compile-speednodeModulesTransform: {type: 'none',},//https://umijs.org/zh-CN/docs/mfsumfsu: {},//https://webpack.docschina.org/blog/2020-10-10-webpack-5-release/webpack5: {},exportStatic: {},});
菜单展示
我们可以在 route 中进行 menu 相关配置,来决定当前路由是否会被渲染在菜单中。详细配置说明
- 当不需要展示在菜单中展示时,可以在路由上配置 hideInMenu 或者删除 menu 相关的配置;
- 当不需要展示 children 时,可以在路由上配置 hideChildrenInMenu;
- 当不需要展示自己,只展示 children,可以在路由上配置 flatMenu;
- 如果没有配置 menu,没有配置 name 的话,则该路由不会在侧边栏中出现。
// config/routes.tsexport default [{path: '/overview',component: 'Overview/index',menu: {name: 'overview',icon: 'testicon',flatMenu: false,hideInMenu: false,hideChildrenInMenu: false,},},];
zero-admin-ui\config\routes.ts
菜单国际化
通过 layout 配置的 locale 配置开启国际化。
开启后路由里配置的菜单名会被当作菜单名国际化的 key,插件会去 locales 文件中查找menu.[key]对应的文案,默认值为改 key。// locale/zh-CN.jsexport default {'menu.overview': '总览',};
zero-admin-ui\src\locales\zh-CN.ts
导航右上角

用户名以及国际化可以通过配置拥有默认的 UI。国际化会通过检测 locale 目录下的文件来展示可供切换的语言种类。
用户名、头像信息可以通过配置全局初始化信息来提供数据。
// src/app.tsexport function getInitialState() {return {name: 'Serati Ma',avatar: 'https://gw.alipayobjects.com/zos/antfincdn/XAosXuNZyF/BiazfanxmamNRoxxVxka.png',};}
退出登陆的逻辑也可以通过配置来自定义。
// src/app.tsexport const layout = {logout: () => {alert('退出登录成功');},};
如果以上满足不了需求,可以通过以下接口实现右上角 UI 完全的自定义。
// src/app.tsximport React from 'react';export const layout = {rightRender: (initialState, setInitialState) => {// xxxreturn 'xxx';},};
自定义 footer

插件并没有提供默认的 footer UI。可以通过以下配置来完成自定义。想和 Pro 官网使用相同的样式可以参考:https://procomponents.ant.design/components/layout#footer
// src/app.tsximport React from 'react';export const layout = {footerRender: () => {// xxxreturn <xxx />;},};
路由配置
权限路由
当需要对某些路由做权限管控,能够搭配内置权限方案来方便的实现。当用户访问没有权限的路由时,layout 会提供默认的无权限页面。
详细的配置方案可:点击查看
通过全局初始化信息来请求权限相关的初始化信息
// src/app.tsexport async function getInitialState() {const data = await fetchXXX();return data;}
zero-admin-ui\src\app.tsx
新增权限定义文件 ```typescript // src/access.ts import { InitialState } from ‘umi’;
export default function accessFactory(initialState: InitialState) { return { readArticle: initialState.name === ‘haha’, }; }
3. 给路由配置权限```typescript// config/route.tsexport default [{path: '/overview',component: 'Overview/index',name: 'overview',icon: 'testicon',access: 'readArticle',},];
404 / 403
内置布局会对不存在的路由、无权访问的路由都会展示默认的 UI。无权访问如上所示。
嵌套布局
有时我们的页面可能会有一些全局的通用的处理逻辑或者 UI,会希望在页面加载前完成,通常会希望可以在内置布局内部再包一层 layout 来完成需求。
// config/routes.tsexport default [{path: '/',component: '../layout/index',menu: {flatMenu: true,},routes: [{path: '/',redirect: '/overview',},{path: '/overview',component: 'Overview/index',menu: {name: 'overview',icon: 'testicon',},},],},];// src/layout/index.tsxconst Layout = ({ children }) => children;export default Layout;
根据路由隐藏左侧菜单、隐藏导航头、footer
有时我们的页面可能存在一些沉浸式的设计,需要针对路由隐藏部分布局。可以通过添加扩展路由配置来实现。详细配置
// config/route.tsexport default [{path: '/overview',component: 'Overview/index',name: 'overview',icon: 'testicon',layout: {hideMenu: false,hideNav: false,hideFooter: false,},},];
菜单布局展示方式的修改
有时菜单可能需要于顶部显示,左侧显示,或者顶部显示一级菜单,左侧显示二三级菜单。我们可以修改 defaultSettings 中的 layout 的配置来决定菜单的展示方式。
- top 菜单于顶部展示
- side 菜单于左侧展示
- mix 菜单于顶部和左侧混合展示,需要注意,当 mix 模式时,需要添加splitMenus: true,顶部才可以正确展示一级菜单
同时,当使用 mix 模式后,点击一级菜单,并不会直接定位到第一个子级菜单页面,而是会呈现空白页面,需要于配置中设置一下 redirect 的地址// config/defaultSettings.tsexport default {layout: 'mix',splitMenus: true,};
[{"path": "/test/list","component": "./test/list"},{"path": "/test/list/testAdd","component": "./test/list/testAdd"},{"redirect": "./test/list"}]
自定义布局
有些时候我们不想使用自带的布局,想做更多的自定义,我们也提供了灵活的自定义方案。
布局本质上是一个特殊的组件,子页面将作为属性传递到布局组件中。 最简单的布局是这样的:
// 必须渲染 children,否则子级路由无法显示// 在这里您还可以设置全局提供const layout = ({ children }) => children;export default layout;
我们在 src/layouts/中创建一个新的 BaseLayout.tsx,复制上面的代码,并在 config/config.ts 添加如下代码:
defineConfig({// added configurationroutes: {path: '/',component: '.../layouts/BaseLayout',},});
我们可以对 children 进行修改或者包裹,ProLayout 组件就是通过这样的方案来注入菜单等配置。children 是什么与你当前的路径和 layout 在项目中的配置有关系,如果满足不了需求可以试试调整位置。
下面是默认的 ProLayout 的配置,我们可以复制默认代码然后再自定义:
/*** Ant Design Pro v4 use `@ant-design/pro-layout` to handle Layout.** @see You can view component api by: https://github.com/ant-design/ant-design-pro-layout*/import type {MenuDataItem,BasicLayoutProps as ProLayoutProps,Settings,} from '@ant-design/pro-layout';import ProLayout, { DefaultFooter } from '@ant-design/pro-layout';import React from 'react';import { Link } from 'umi';import { GithubOutlined } from '@ant-design/icons';import RightContent from '@/components/GlobalHeader/RightContent';import logo from '../assets/logo.svg';export type BasicLayoutProps = {breadcrumbNameMap: Record<string, MenuDataItem>;route: ProLayoutProps['route'] & {authority: string[];};settings: Settings;} & ProLayoutProps;export type BasicLayoutContext = { [K in 'location']: BasicLayoutProps[K] } & {breadcrumbNameMap: Record<string, MenuDataItem>;};const menuDataRender = (menuList: MenuDataItem[]): MenuDataItem[] =>menuList.map((item) => {return {...item,children: item.children ? menuDataRender(item.children) : undefined,};});const defaultFooterDom = (<DefaultFootercopyright={`${new Date().getFullYear()} 蚂蚁集团体验技术部出品`}links={[{key: 'Ant Design Pro',title: 'Ant Design Pro',href: 'https://pro.ant.design',blankTarget: true,},{key: 'github',title: <GithubOutlined />,href: 'https://github.com/ant-design/ant-design-pro',blankTarget: true,},{key: 'Ant Design',title: 'Ant Design',href: 'https://ant.design',blankTarget: true,},]}/>);const BasicLayout: React.FC<BasicLayoutProps> = (props) => {const {children,location = {pathname: '/',},} = props;const { formatMessage } = useIntl();return (<ProLayoutlogo={logo}formatMessage={formatMessage}{...props}onCollapse={handleMenuCollapse}onMenuHeaderClick={() => history.push('/')}menuItemRender={(menuItemProps, defaultDom) => {if (menuItemProps.isUrl ||!menuItemProps.path ||location.pathname === menuItemProps.path) {return defaultDom;}return <Link to={menuItemProps.path}>{defaultDom}</Link>;}}breadcrumbRender={(routers = []) => [{path: '/',breadcrumbName: formatMessage({ id: 'menu.home' }),},...routers,]}itemRender={(route, params, routes, paths) => {const first = routes.indexOf(route) === 0;return first ? (<Link to={paths.join('/')}>{route.breadcrumbName}</Link>) : (<span>{route.breadcrumbName}</span>);}}footerRender={() => defaultFooterDom}menuDataRender={menuDataRender}rightContentRender={() => <RightContent />}>{children}</ProLayout>);};export default BasicLayout;
更多
如果内置的 layout 插件满足不了您的需求,可以通过 issue 告诉我们,我们会尽快处理。
你也可以通过以下配置来关闭默认的功能。
关闭 Layout 插件
将 layout 配置设置成 false。
// config.jsimport { defineConfig } from 'umi';export const config = defineConfig({layout: false,});
