欢迎页

未登录访问项目,初始页会直接跳转到 欢迎 页. 从路由配置可以看到,如果我们访问 http://localhost:8000/ , 能匹配到的路由只有被 redirectWelcome 组建.

image.png

框架布局与核心组件

路由分别经过了 SecurityLayout ,BasicLayout 组件,以及顶部的BlankLayout 组件. 那么这三个组件是什么作用呢?
image.png

BlankLayout组件

组件位置: src/layouts/BlankLayout , 该组件是项目的顶级组件, 可以在开发模式下更好的显示错误提示. 当前组件可以理解为一个空白组件.

  1. import React from 'react';
  2. import { Inspector } from 'react-dev-inspector';
  3. const InspectorWrapper = process.env.NODE_ENV === 'development' ? Inspector : React.Fragment;
  4. const Layout: React.FC = ({ children }) => {
  5. // {children} 是dom内容,可以理解为 vue中的插槽
  6. // dom的内容会直接被解析到这里
  7. // InspectorWrapper 是根据是否为开发模式而替换为 Inspector 或正常的React虚拟组件
  8. // Inspector 可以使我们在开发阶段 代码报错 提示的更友好
  9. return <InspectorWrapper>{children}</InspectorWrapper>;
  10. };
  11. export default Layout;

image.png

SecurityLayout 组件

组件位置: src/layouts/SecurityLayout , 顾名思义,该组件应该是对安全访问方面做了一些控制.
这里实现了对是否登录的逻辑判断: 登录中,则显示 <PageLoading /> 组件; 如果未登录访问,则重定向至 /user/login ; 如果登录成功则返回子组件.

  1. render() {
  2. // 组件是否渲染完毕
  3. const { isReady } = this.state;
  4. // loading为获取user用户信息的网络请求状态 由dva-loading管控
  5. const { children, loading, currentUser } = this.props;
  6. // You can replace it to your authentication rule (such as check token exists)
  7. // 你可以把它替换成你自己的登录认证规则(比如判断 token 是否存在)
  8. const isLogin = currentUser && currentUser.userid;
  9. const queryString = stringify({
  10. redirect: window.location.href,
  11. });
  12. // 未登录 且在loading状态 或者组件未渲染完毕
  13. if ((!isLogin && loading) || !isReady) {
  14. return <PageLoading />;
  15. }
  16. // 未登录 且跳转未非登录页
  17. if (!isLogin && window.location.pathname !== '/user/login') {
  18. return <Redirect to={`/user/login?${queryString}`} />;
  19. }
  20. return children;
  21. }

BasicLayout 组件

当前组件是后台页面布局的主要组件,使用了 ProLayout 进行实现.
ProLayout 是在 ant-design 基础之上封装的一个高级组件. ProLayout 可以提供一个标准又不失灵活的中后台标准布局,同时提供一键切换布局形态,自动生成菜单等功能。与 PageContainer 配合使用可以自动生成面包屑,页面标题,并且提供低成本方案接入页脚工具栏。
页面权限相关功能通过 Authorized 组件和route配置结合控制,大致思路是通过当前用户的 角色 , 和组件Authorized 属性 authority 所具有的权限做对比,从而控制组件内容是否渲染或者渲染为 noMatch 对应组件.这种实现思路和我们在 vue-element-admin 模板中的思路大致相同, 但和我们课程中的动态权限思路不同. 关于权限部分后期会针对性说明,暂时可以跨过.

  1. /**
  2. * Ant Design Pro v4 use `@ant-design/pro-layout` to handle Layout.
  3. * @see You can view component api by: https://github.com/ant-design/ant-design-pro-layout
  4. */
  5. ...
  6. return (
  7. <ProLayout
  8. logo={logo}
  9. formatMessage={formatMessage}
  10. {...props}
  11. {...settings}
  12. onCollapse={handleMenuCollapse}
  13. onMenuHeaderClick={() => history.push('/')}
  14. menuItemRender={(menuItemProps, defaultDom) => {
  15. if (
  16. menuItemProps.isUrl ||
  17. !menuItemProps.path ||
  18. location.pathname === menuItemProps.path
  19. ) {
  20. return defaultDom;
  21. }
  22. return <Link to={menuItemProps.path}>{defaultDom}</Link>;
  23. }}
  24. breadcrumbRender={(routers = []) => [
  25. {
  26. path: '/',
  27. breadcrumbName: formatMessage({ id: 'menu.home' }),
  28. },
  29. ...routers,
  30. ]}
  31. itemRender={(route, params, routes, paths) => {
  32. const first = routes.indexOf(route) === 0;
  33. return first ? (
  34. <Link to={paths.join('/')}>{route.breadcrumbName}</Link>
  35. ) : (
  36. <span>{route.breadcrumbName}</span>
  37. );
  38. }}
  39. footerRender={() => {
  40. if (settings.footerRender || settings.footerRender === undefined) {
  41. return defaultFooterDom;
  42. }
  43. return null;
  44. }}
  45. menuDataRender={menuDataRender}
  46. rightContentRender={() => <RightContent />}
  47. postMenuData={(menuData) => {
  48. menuDataRef.current = menuData || [];
  49. return menuData || [];
  50. }}
  51. >
  52. // 这里是权限控制 可以通过当前用户的角色 和authority 的角色列表做对比
  53. // 当前项目不用考虑页面权限, 具体实现会在后续的教程里体现
  54. <Authorized authority={authorized!.authority} noMatch={noMatch}>
  55. {children}
  56. </Authorized>
  57. </ProLayout>
  58. );
  59. };
  60. 比如 config/route.ts 路由部分权限配置
  61. ...
  62. routes: [
  63. {
  64. path: '/admin/sub-page',
  65. name: 'sub-page',
  66. icon: 'smile',
  67. component: './Welcome',
  68. authority: ['admin'],
  69. },
  70. ],

UserLayout 组件

当前组件是包装了用户登录页的容器组件. 可以通过当前组件设置网页 title , meta , 国际化选择, 登录页logo , 页脚等功能.
image.png

  1. return (
  2. <HelmetProvider>
  3. <Helmet>
  4. <title>{title}</title>
  5. <meta name="description" content={title} />
  6. </Helmet>
  7. <div className={styles.container}>
  8. <div className={styles.lang}>
  9. <SelectLang />
  10. </div>
  11. <div className={styles.content}>
  12. <div className={styles.top}>
  13. <div className={styles.header}>
  14. <Link to="/">
  15. <img alt="logo" className={styles.logo} src={logo} />
  16. <span className={styles.title}>Ant Design</span>
  17. </Link>
  18. </div>
  19. <div className={styles.desc}>
  20. <FormattedMessage
  21. id="pages.layouts.userLayout.title"
  22. defaultMessage="Ant Design 是西湖区最具影响力的 Web 设计规范"
  23. />
  24. </div>
  25. </div>
  26. // Login组件在这里渲染
  27. {children}
  28. </div>
  29. <DefaultFooter />
  30. </div>
  31. </HelmetProvider>
  32. );

登录页

我们可以直接访问 http://localhost:8000/user/login 来跳转登录页,或者在后台点击推出跳转登录页. 登录页对应组件 pages/User/login/index.tsx
登陆后sideBar多了一个 管理页 功能, 该页面是登陆后,而且用户 角色admin 才会出现的.
image.png
image.png
src/layouts/BasicLayout.tsx 权限控制的代码部分:

  1. /** Use Authorized check all menu item */
  2. const menuDataRender = (menuList: MenuDataItem[]): MenuDataItem[] =>
  3. menuList.map((item) => {
  4. const localItem = {
  5. ...item,
  6. children: item.children ? menuDataRender(item.children) : undefined,
  7. };
  8. // 使用Authorized鉴权判断当前sideItem是否被渲染
  9. return Authorized.check(item.authority, localItem, null) as MenuDataItem;
  10. });

总结

我们基本上已经搞清楚了 ant design pro 后台框架模板的基本结构,接下来我们需要按照自己的需求进行开发.
因为默认模板的登录页相对复杂,也不符合我们需求,我们可以自行替换需要的登录模板. 我们将在下一节开始具体开发.
image.png