布局是一个中后台应用必备的,一个布局 + ProTable + Form 即可获得一个 CRUD 页面。

Pro 中内置了 plugin-layout 来减少样板代码。简单的使用中我们只需要在 config.ts 中配置 layout 属性就可以实现通用的页面布局。

zero-admin-ui\config\config.ts
image.png

UI 配置

布局样式

layout 插件 与pro-layout 配置的配置形同。

推荐先使用 Pro 站点 的右侧抽屉来帮助你完成布局相关的整体风格、主题色、导航模式、内容区域宽度、固定 Header、固定侧边菜单、色弱模式等配置选择。然后将拷贝的配置粘贴与 layout 配置中。
布局 - 图2

  1. // https://umijs.org/config/
  2. import { defineConfig } from 'umi';
  3. import defaultSettings from './defaultSettings';
  4. import proxy from './proxy';
  5. import routes from './routes';
  6. const { REACT_APP_ENV } = process.env;
  7. export default defineConfig({
  8. hash: true,
  9. antd: {},
  10. dva: {
  11. hmr: true,
  12. },
  13. layout: {
  14. // https://umijs.org/zh-CN/plugins/plugin-layout
  15. locale: true,
  16. siderWidth: 208,
  17. ...defaultSettings,
  18. },
  19. // https://umijs.org/zh-CN/plugins/plugin-locale
  20. locale: {
  21. // default zh-CN
  22. default: 'zh-CN',
  23. antd: true,
  24. // default true, when it is true, will use `navigator.language` overwrite default
  25. baseNavigator: true,
  26. },
  27. dynamicImport: {
  28. loading: '@ant-design/pro-layout/es/PageLoading',
  29. },
  30. targets: {
  31. ie: 11,
  32. },
  33. // umi routes: https://umijs.org/docs/routing
  34. routes,
  35. // Theme for antd: https://ant.design/docs/react/customize-theme-cn
  36. theme: {
  37. 'primary-color': defaultSettings.primaryColor,
  38. },
  39. // esbuild is father build tools
  40. // https://umijs.org/plugins/plugin-esbuild
  41. esbuild: {},
  42. title: false,
  43. ignoreMomentLocale: true,
  44. proxy: proxy[REACT_APP_ENV || 'dev'],
  45. manifest: {
  46. basePath: '/',
  47. },
  48. // Fast Refresh 热更新
  49. //https://umijs.org/zh-CN/docs/fast-refresh
  50. fastRefresh: {},
  51. //https://umijs.org/guide/boost-compile-speed
  52. nodeModulesTransform: {
  53. type: 'none',
  54. },
  55. //https://umijs.org/zh-CN/docs/mfsu
  56. mfsu: {},
  57. //https://webpack.docschina.org/blog/2020-10-10-webpack-5-release/
  58. webpack5: {},
  59. exportStatic: {},
  60. });

菜单展示

我们可以在 route 中进行 menu 相关配置,来决定当前路由是否会被渲染在菜单中。详细配置说明

  • 当不需要展示在菜单中展示时,可以在路由上配置 hideInMenu 或者删除 menu 相关的配置;
  • 当不需要展示 children 时,可以在路由上配置 hideChildrenInMenu
  • 当不需要展示自己,只展示 children,可以在路由上配置 flatMenu
  • 如果没有配置 menu,没有配置 name 的话,则该路由不会在侧边栏中出现。
    1. // config/routes.ts
    2. export default [
    3. {
    4. path: '/overview',
    5. component: 'Overview/index',
    6. menu: {
    7. name: 'overview',
    8. icon: 'testicon',
    9. flatMenu: false,
    10. hideInMenu: false,
    11. hideChildrenInMenu: false,
    12. },
    13. },
    14. ];
    zero-admin-ui\config\routes.ts
    image.png

    菜单国际化

    通过 layout 配置的 locale 配置开启国际化。
    开启后路由里配置的菜单名会被当作菜单名国际化的 key,插件会去 locales 文件中查找 menu.[key] 对应的文案,默认值为改 key
    1. // locale/zh-CN.js
    2. export default {
    3. 'menu.overview': '总览',
    4. };
    zero-admin-ui\src\locales\zh-CN.ts
    image.png

导航右上角

布局 - 图5
用户名以及国际化可以通过配置拥有默认的 UI。国际化会通过检测 locale 目录下的文件来展示可供切换的语言种类。

用户名、头像信息可以通过配置全局初始化信息来提供数据。

  1. // src/app.ts
  2. export function getInitialState() {
  3. return {
  4. name: 'Serati Ma',
  5. avatar: 'https://gw.alipayobjects.com/zos/antfincdn/XAosXuNZyF/BiazfanxmamNRoxxVxka.png',
  6. };
  7. }

退出登陆的逻辑也可以通过配置来自定义。

  1. // src/app.ts
  2. export const layout = {
  3. logout: () => {
  4. alert('退出登录成功');
  5. },
  6. };

如果以上满足不了需求,可以通过以下接口实现右上角 UI 完全的自定义。

  1. // src/app.tsx
  2. import React from 'react';
  3. export const layout = {
  4. rightRender: (initialState, setInitialState) => {
  5. // xxx
  6. return 'xxx';
  7. },
  8. };

自定义 footer

布局 - 图6
插件并没有提供默认的 footer UI。可以通过以下配置来完成自定义。想和 Pro 官网使用相同的样式可以参考:https://procomponents.ant.design/components/layout#footer

  1. // src/app.tsx
  2. import React from 'react';
  3. export const layout = {
  4. footerRender: () => {
  5. // xxx
  6. return <xxx />;
  7. },
  8. };

路由配置

权限路由

当需要对某些路由做权限管控,能够搭配内置权限方案来方便的实现。当用户访问没有权限的路由时,layout 会提供默认的无权限页面。
布局 - 图7
详细的配置方案可:点击查看

  1. 通过全局初始化信息来请求权限相关的初始化信息

    1. // src/app.ts
    2. export async function getInitialState() {
    3. const data = await fetchXXX();
    4. return data;
    5. }

    zero-admin-ui\src\app.tsx
    image.png

  2. 新增权限定义文件 ```typescript // src/access.ts import { InitialState } from ‘umi’;

export default function accessFactory(initialState: InitialState) { return { readArticle: initialState.name === ‘haha’, }; }

  1. 3. 给路由配置权限
  2. ```typescript
  3. // config/route.ts
  4. export default [
  5. {
  6. path: '/overview',
  7. component: 'Overview/index',
  8. name: 'overview',
  9. icon: 'testicon',
  10. access: 'readArticle',
  11. },
  12. ];

404 / 403

内置布局会对不存在的路由、无权访问的路由都会展示默认的 UI。无权访问如上所示。

访问不存在的 UI 时,默认 UI 如下 布局 - 图9

嵌套布局

有时我们的页面可能会有一些全局的通用的处理逻辑或者 UI,会希望在页面加载前完成,通常会希望可以在内置布局内部再包一层 layout 来完成需求。

  1. // config/routes.ts
  2. export default [
  3. {
  4. path: '/',
  5. component: '../layout/index',
  6. menu: {
  7. flatMenu: true,
  8. },
  9. routes: [
  10. {
  11. path: '/',
  12. redirect: '/overview',
  13. },
  14. {
  15. path: '/overview',
  16. component: 'Overview/index',
  17. menu: {
  18. name: 'overview',
  19. icon: 'testicon',
  20. },
  21. },
  22. ],
  23. },
  24. ];
  25. // src/layout/index.tsx
  26. const Layout = ({ children }) => children;
  27. export default Layout;

根据路由隐藏左侧菜单、隐藏导航头、footer

有时我们的页面可能存在一些沉浸式的设计,需要针对路由隐藏部分布局。可以通过添加扩展路由配置来实现。详细配置

  1. // config/route.ts
  2. export default [
  3. {
  4. path: '/overview',
  5. component: 'Overview/index',
  6. name: 'overview',
  7. icon: 'testicon',
  8. layout: {
  9. hideMenu: false,
  10. hideNav: false,
  11. hideFooter: false,
  12. },
  13. },
  14. ];

菜单布局展示方式的修改

有时菜单可能需要于顶部显示,左侧显示,或者顶部显示一级菜单,左侧显示二三级菜单。我们可以修改 defaultSettings 中的 layout 的配置来决定菜单的展示方式。

  • top 菜单于顶部展示
  • side 菜单于左侧展示
  • mix 菜单于顶部和左侧混合展示,需要注意,当 mix 模式时,需要添加splitMenus: true,顶部才可以正确展示一级菜单
    1. // config/defaultSettings.ts
    2. export default {
    3. layout: 'mix',
    4. splitMenus: true,
    5. };
    同时,当使用 mix 模式后,点击一级菜单,并不会直接定位到第一个子级菜单页面,而是会呈现空白页面,需要于配置中设置一下 redirect 的地址
    1. [
    2. {
    3. "path": "/test/list",
    4. "component": "./test/list"
    5. },
    6. {
    7. "path": "/test/list/testAdd",
    8. "component": "./test/list/testAdd"
    9. },
    10. {
    11. "redirect": "./test/list"
    12. }
    13. ]

    自定义布局

    有些时候我们不想使用自带的布局,想做更多的自定义,我们也提供了灵活的自定义方案。

布局本质上是一个特殊的组件,子页面将作为属性传递到布局组件中。 最简单的布局是这样的:

  1. // 必须渲染 children,否则子级路由无法显示
  2. // 在这里您还可以设置全局提供
  3. const layout = ({ children }) => children;
  4. export default layout;

我们在 src/layouts/中创建一个新的 BaseLayout.tsx,复制上面的代码,并在 config/config.ts 添加如下代码:

  1. defineConfig({
  2. // added configuration
  3. routes: {
  4. path: '/',
  5. component: '.../layouts/BaseLayout',
  6. },
  7. });

我们可以对 children 进行修改或者包裹,ProLayout 组件就是通过这样的方案来注入菜单等配置。children 是什么与你当前的路径和 layout 在项目中的配置有关系,如果满足不了需求可以试试调整位置。

下面是默认的 ProLayout 的配置,我们可以复制默认代码然后再自定义:

  1. /**
  2. * Ant Design Pro v4 use `@ant-design/pro-layout` to handle Layout.
  3. *
  4. * @see You can view component api by: https://github.com/ant-design/ant-design-pro-layout
  5. */
  6. import type {
  7. MenuDataItem,
  8. BasicLayoutProps as ProLayoutProps,
  9. Settings,
  10. } from '@ant-design/pro-layout';
  11. import ProLayout, { DefaultFooter } from '@ant-design/pro-layout';
  12. import React from 'react';
  13. import { Link } from 'umi';
  14. import { GithubOutlined } from '@ant-design/icons';
  15. import RightContent from '@/components/GlobalHeader/RightContent';
  16. import logo from '../assets/logo.svg';
  17. export type BasicLayoutProps = {
  18. breadcrumbNameMap: Record<string, MenuDataItem>;
  19. route: ProLayoutProps['route'] & {
  20. authority: string[];
  21. };
  22. settings: Settings;
  23. } & ProLayoutProps;
  24. export type BasicLayoutContext = { [K in 'location']: BasicLayoutProps[K] } & {
  25. breadcrumbNameMap: Record<string, MenuDataItem>;
  26. };
  27. const menuDataRender = (menuList: MenuDataItem[]): MenuDataItem[] =>
  28. menuList.map((item) => {
  29. return {
  30. ...item,
  31. children: item.children ? menuDataRender(item.children) : undefined,
  32. };
  33. });
  34. const defaultFooterDom = (
  35. <DefaultFooter
  36. copyright={`${new Date().getFullYear()} 蚂蚁集团体验技术部出品`}
  37. links={[
  38. {
  39. key: 'Ant Design Pro',
  40. title: 'Ant Design Pro',
  41. href: 'https://pro.ant.design',
  42. blankTarget: true,
  43. },
  44. {
  45. key: 'github',
  46. title: <GithubOutlined />,
  47. href: 'https://github.com/ant-design/ant-design-pro',
  48. blankTarget: true,
  49. },
  50. {
  51. key: 'Ant Design',
  52. title: 'Ant Design',
  53. href: 'https://ant.design',
  54. blankTarget: true,
  55. },
  56. ]}
  57. />
  58. );
  59. const BasicLayout: React.FC<BasicLayoutProps> = (props) => {
  60. const {
  61. children,
  62. location = {
  63. pathname: '/',
  64. },
  65. } = props;
  66. const { formatMessage } = useIntl();
  67. return (
  68. <ProLayout
  69. logo={logo}
  70. formatMessage={formatMessage}
  71. {...props}
  72. onCollapse={handleMenuCollapse}
  73. onMenuHeaderClick={() => history.push('/')}
  74. menuItemRender={(menuItemProps, defaultDom) => {
  75. if (
  76. menuItemProps.isUrl ||
  77. !menuItemProps.path ||
  78. location.pathname === menuItemProps.path
  79. ) {
  80. return defaultDom;
  81. }
  82. return <Link to={menuItemProps.path}>{defaultDom}</Link>;
  83. }}
  84. breadcrumbRender={(routers = []) => [
  85. {
  86. path: '/',
  87. breadcrumbName: formatMessage({ id: 'menu.home' }),
  88. },
  89. ...routers,
  90. ]}
  91. itemRender={(route, params, routes, paths) => {
  92. const first = routes.indexOf(route) === 0;
  93. return first ? (
  94. <Link to={paths.join('/')}>{route.breadcrumbName}</Link>
  95. ) : (
  96. <span>{route.breadcrumbName}</span>
  97. );
  98. }}
  99. footerRender={() => defaultFooterDom}
  100. menuDataRender={menuDataRender}
  101. rightContentRender={() => <RightContent />}
  102. >
  103. {children}
  104. </ProLayout>
  105. );
  106. };
  107. export default BasicLayout;

更多

如果内置的 layout 插件满足不了您的需求,可以通过 issue 告诉我们,我们会尽快处理。

你也可以通过以下配置来关闭默认的功能。

关闭 Layout 插件

layout 配置设置成 false

  1. // config.js
  2. import { defineConfig } from 'umi';
  3. export const config = defineConfig({
  4. layout: false,
  5. });

原文链接

https://pro.ant.design/zh-CN/docs/layout/