1.Ant Design Pro介绍
Ant Design Pro 是一个企业级中后台前端/设计解决方案
特性
- 优雅美观:基于 Ant Design 体系精心设计
- 常见设计模式:提炼自中后台应用的典型页面和场景
- 最新技术栈:使用 React/dva/antd 等前端前沿技术开发
- 响应式:针对不同屏幕大小设计
- 主题:可配置的主题满足多样化的品牌诉求
- 国际化:内建业界通用的国际化方案
- 最佳实践:良好的工程实践助您持续产出高质量代码
- Mock 数据:实用的本地数据调试方案
- UI 测试:自动化测试保障前端产品质量
创建项目:
yarn create umi myprotest
yarn install
yarn start
注意:这里创建项目的时候选择V5版本
Pro V4+antd@4 版本权限验证不好用。Pro V4+antd@4 可以查看完整版的ant design pro项目。
Pro V4+antd@3 版本有区块不好用
2.目录结构和配置介绍
2.1 目录结构
2.2 配置文件
2.2.1 config.ts
该文件类似.umirc.ts文件
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124
| import { defineConfig } from 'umi';
import defaultSettings from './defaultSettings';
import proxy from './proxy'; let path = require('path');
const { REACT_APP_ENV } = process.env;
export default defineConfig({ hash: true, antd: {}, dva: { hmr: true, }, layout: { name: 'Ant Design Pro', locale: true, siderWidth: 208, }, locale: { default: 'zh-CN', antd: true, baseNavigator: false, }, dynamicImport: { loading: '@/components/PageLoading/index', }, targets: { ie: 11, }, routes: [ { path: '/user', layout: false, routes: [ { name: 'login', path: '/user/login', component: './user/login', }, ], }, { path: '/welcome', name: 'welcome', icon: 'smile', component: './Welcome', }, { path: '/admin', name: 'admin', icon: 'crown', access: 'canAdmin', component: './Admin', routes: [ { path: '/admin/sub-page', name: 'sub-page', icon: 'smile', component: './Welcome', }, ], }, { name: 'list.table-list', icon: 'table', path: '/list', component: './ListTableList', }, { path: '/', redirect: '/welcome', }, { component: './404', }, ], theme: { 'primary-color': defaultSettings.primaryColor, }, title: false, ignoreMomentLocale: true, proxy: proxy[REACT_APP_ENV || 'dev'], manifest: { basePath: '/', }, alias: { '@': path.resolve(__dirname, 'src') } });
|
2.2.2 defaultSettings.ts
页面样式的配置文件
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
| import { Settings as LayoutSettings } from '@ant-design/pro-layout';
export default { navTheme: 'dark', primaryColor: '#1890ff', layout: 'mix', contentWidth: 'Fluid', fixedHeader: false, fixSiderbar: true, colorWeak: false, menu: { locale: true, }, title: 'Ant Design Pro', pwa: false, iconfontUrl: '', } as LayoutSettings & { pwa: boolean; };
|
2.2.3 proxy.ts
该文件主要是代理的配置文件,仅在开发环境有效
2.2.4 app.tsx
app.tsx为运行时配置文件,导出的信息会使用对应的插件在运行时配置到程序中
2.3 配置详解
2.3.1 路由和菜单
ant design pro会根据配置的路由自动生成对应的菜单
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55
|
routes: [ { path: '/user', layout: false, routes: [ { name: 'login', path: '/user/login', component: './user/login', }, ], }, { path: '/welcome', name: 'welcome', icon: 'smile', component: './Welcome', }, { path: '/admin', name: 'admin', icon: 'crown', access: 'canAdmin', component: './Admin', routes: [ { path: '/admin/sub-page', name: 'sub-page', icon: 'smile', component: './Welcome', }, ], }, { name: 'list.table-list', icon: 'table', path: '/list', component: './ListTableList', }, { path: '/', redirect: '/welcome', }, { component: './404', }, ],
|
2.3.2 权限
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
| 1.当要设定某些菜单只有部分用户可见的时候,可以在菜单配置中写 access: 'canAdmin'
2.canAdmin的信息在src/access.ts中声明,该文件中导出的access函数会用插件@umijs/plugin-access进行运行时配置,函数返回的结果要是下面类型 * 返回结果: * { * canAdmin:true, * canUser:false * }
3.useAccess:可以在页面根据不同的登录用户显示不同的信息 import { useAccess, Access } from 'umi'; const access = useAccess(); if (access.canReadFoo) { } <Access accessible={access.canReadFoo} fallback={<div>Can not read foo content.</div>} > Foo content. </Access>
|
2.3.3 布局
1 2 3
| 当某个路由不需要布局文件的时候,只需要在config.ts的路由配置中设定layout: false即可,如果没有写这个,则会使用默认的布局文件
默认的布局文件在app.tsx中layout中声明,会使用@umijs/plugin-layout进行运行时配置
|
2.3.4 InitialState
在app.tsx中导出getInitialState方法,会使用@umijs/plugin-initial-state插件进行运行时配置,该方法会在整个应用最开始执行,返回值会作为全局共享的数据。(每次刷新页面的之后都会执行)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32
| #1. app.tsx使用getInitialState提供全局共享数据
export async function getInitialState(): Promise<{ currentUser?: API.CurrentUser; settings?: LayoutSettings; }> { if (history.location.pathname !== '/user/login') { try { const currentUser = await queryCurrent(); return { currentUser, settings: defaultSettings, }; } catch (error) { history.push('/user/login'); } } return { settings: defaultSettings, }; }
#2.在其他组件中可以通过useModel获取上面暴露的数据 import { useModel } from 'umi';
const { initialState,loading,refresh } = useModel('@@initialState');
|
2.3.5 国际化
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| #1.src/locales 是国际化的配置文件
#2.SelectLang是语言选择组件 import { SelectLang } from 'umi';
#3.useIntl国际化库 import { useIntl,FormattedMessage } from 'umi';
useIntl().formatMessage({ id: 'menu.home', defaultMessage: 'Home', }),
<button><FormattedMessage id="menu.admin" /></button>
|
3.知识点介绍
3.1 封装组件介绍
Footer 网页底部组件的封装
HeaderDropdown 鼠标移动的下拉组件的封装
PageLoading 页面加载组件的封装 需要在config.ts中配置
1 2 3 4 5 6
| import { PageLoading } from '@ant-design/pro-layout';
export default PageLoading;
|
1 2 3 4
| dynamicImport: { loading: '@/components/PageLoading/index', },
|
RightContent 登录后右上侧组件的封装(包含搜索、用户头像、语言选择等信息)
3.2 procomponents
https://procomponents.ant.design/components/layout#api
3.2.1 高级布局ProLayout
a) BasicLayout&&BasicLayoutProps
BasicLayout 基础页面布局,包含了头部导航,侧边栏和通知栏等等。
ProLayout 与 umi 配合使用会有最好的效果,umi 会把 config.ts 中的路由帮我们自动注入到配置的 layout 中,这样我们就可以免去手写菜单的烦恼。
BasicLayoutProps基本页面布局的属性
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77
|
export const layout = ({ initialState, }: { initialState: { settings?: LayoutSettings; currentUser?: API.CurrentUser }; }): BasicLayoutProps => { return { rightContentRender: () => <RightContent />, disableContentMargin: false, footerRender: () => <Footer />, onPageChange: () => { if (!initialState?.currentUser?.userid && history.location.pathname !== '/user/login') { history.push('/user/login'); } }, breadcrumbRender: (routers = []) => [ { path: '/', breadcrumbName: useIntl().formatMessage({ id: 'menu.home', defaultMessage: '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> ); }, menuHeaderRender: () => <div>测试菜单</div>, menuDataRender: (menuData) => { return menuData;
}, ...initialState?.settings, }; };
|
b) PageContainer
正文内容组件,封装了 ant design 的 PageHeader 组件,增加了 tabList 和 content。 根据当前的路由填入 title 和 breadcrumb。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
|
<PageContainer content=" 这个页面只有 admin 权限才能查看" title="管理员页面头部信息" pageHeaderRender={(props) => { return <div>测试</div>; }} footer={[<div>页面尾巴</div>]} tabList={ [{key:"张三",tab:<div>张三</div>},{key:"李四",tab:<div>李四</div>}] } > <p>正文内容</p> </PageContainer>
|
网页默认的底部组件
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
| <DefaultFooter copyright="2019 蚂蚁金服体验技术部出品" 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, }, ]} />
|
d) PageLoading
加载页面组件,需要在config.ts中配置。配置之后加载页面就默认会用该组件
1 2 3 4
| dynamicImport: { loading: '@/components/PageLoading/index', },
|
底部工具栏组件
1 2 3 4 5 6 7 8 9 10 11 12 13
| <FooterToolbar extra={ <div> 已选择项 <span>服务调用次数总计万</span> </div> } > {} <Button>批量删除</Button> {} <Button type="primary">批量审批</Button> </FooterToolbar>
|
3.2.2 高级表格ProTable
a) ProColumns
高级列,会嵌套在高级表格中使用
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94
| const columns: ProColumns<TableListItem>[] = [ { title: '规则名称', dataIndex: 'name', tip: '规则名称是唯一的 key', formItemProps: { rules: [ { required: true, message: '规则名称为必填项', }, ], }, render: (dom, entity) => { return <a onClick={() => setRow(entity)}>{dom}</a>; }, }, { title: '描述', dataIndex: 'desc', valueType: 'textarea', }, { title: '服务调用次数', dataIndex: 'callNo', sorter: true, hideInForm: true, renderText: (val: string) => `${val} 万`, }, { title: '状态', dataIndex: 'status', hideInForm: true, valueEnum: { 0: { text: '关闭', status: 'Default' }, 1: { text: '运行中', status: 'Processing' }, 2: { text: '已上线', status: 'Success' }, 3: { text: '异常', status: 'Error' }, }, }, { title: '上次调度时间', dataIndex: 'updatedAt', sorter: true, valueType: 'dateTime', renderFormItem: (item, { defaultRender, ...rest }, form) => { const status = form.getFieldValue('status'); if (`${status}` === '0') { return false; } if (`${status}` === '3') { return <Input {...rest} placeholder="请输入异常原因!" />; } return defaultRender(item); }, }, { title: '操作', dataIndex: 'option', valueType: 'option', render: (_, record) => ( <> {/**配置按钮 */} <a onClick={() => { //点击配置按钮让更新表单可见 handleUpdateModalVisible(true); //点击配置按钮设置更新表单的数据 setStepFormValues(record); }} > 配置 </a> <Divider type="vertical" /> {/**订阅警报按钮 */} <a href="">订阅警报</a> </> ), }, ];
|
b) ProTable&ActionType
ProTable 高级表格
ActionType 记录高级表格的常用事件 (reload reset reloadAndRest等事件)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26
| import ProTable, { ProColumns, ActionType } from '@ant-design/pro-table';
const actionRef = useRef<ActionType>();
<ProTable<TableListItem> headerTitle="查询表格" actionRef={actionRef} rowKey="key" search={{ labelWidth: 120, }} toolBarRender={() => [ <Button type="primary">新建</Button>, ]} request={(params, sorter, filter) => } columns={columns} rowSelection={{ }} />
|
3.2.3 高级描述ProDescriptions
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| import ProDescriptions from '@ant-design/pro-descriptions';
<ProDescriptions<TableListItem> column={2} title={row?.name} request={async () => ({ data: row || {}, })} params={{ id: row?.name, }} columns={columns} />
|
3.3 登录页面及流程
3.3.1 登录页面及子组件
user/login/index.tsx 登录页面
user/login/components/login/index.tsx 封装的login组件,同时把Tab、Submit、Username、Password、Mobile、Captcha等组件挂在到当前index.tsx中
LoginContext.tsx 上下文对象,主要通过上下文在user/login/components/login/index.tsx中给子组件共享和暴露数据
LoginItem.tsx 登录项,主要在这里通过map.tsx中的配置动态创建登录子组件,包括Username、Password、Mobile、Captcha等
LoginSumit.tsx 提交登录表单的组件
LoginTab.tsx 登录切换Tab组件
3.3.2 登录流程
提交登录表单
调用fakeAccountLogin这个service发送登录请求
登录成功之后跳回回调地址
注意:这边使用window.location.href=”xx”,会刷新页面,从而触发app.tsx中的getInitialState方法重新执行
根据当前url从路由配置中找寻相应的规则,然后根据getInitialState中的CurrentUser做权限验证,呈现登录后的用户菜单和页面
3.4 列表页流程
3.4.1 列表页面及子组件
pages/ListTableList/index.tsx 列表页面
pages/ListTableList/service.ts 列表页面的请求文件
pages/ListTableList/data.d.ts 列表页面的类型声明文件
pages/ListTableList/components/createForm.tsx 创建表单的组件
pages/ListTableList/components/updateForm.tsx 更新表单的组件
3.4.2 列表流程
a) 列表页面展示、查询、分页
1 2 3 4
| request={(params, sorter, filter) => queryRule({ ...params, sorter: { callNo: 'ascend' }, filter }) }
|
b) 规则新增
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28
| <Button type="primary" onClick={() => handleModalVisible(true)}> <PlusOutlined /> 新建 </Button>,
<CreateForm onCancel={() => handleModalVisible(false)} modalVisible={createModalVisible}> {/**嵌套一个ProTable */} <ProTable<TableListItem, TableListItem> //点击提交按钮 发送请求 让外部的ProTable刷新表格 onSubmit={async (value) => { const success = await handleAdd(value); if (success) { handleModalVisible(false); if (actionRef.current) { actionRef.current.reload(); } } }} //每一行的索引 rowKey="key" //表格的类型 type="form" //指定表格的列 此时columns中配置的hideInForm生效了 columns={columns} /> </CreateForm>
|
c) 规则更新
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28
| handleUpdateModalVisible(true);
setStepFormValues(record);
<UpdateForm //提交表单 发送请求 让外部的ProTable刷新表格 onSubmit={async (value) => { const success = await handleUpdate(value); if (success) { handleUpdateModalVisible(false); setStepFormValues({}); if (actionRef.current) { actionRef.current.reload(); } } }} //取消更新表单 onCancel={() => { handleUpdateModalVisible(false); setStepFormValues({}); }} //更新表单的显示隐藏状态 updateModalVisible={updateModalVisible} //更新表单的数据 values={stepFormValues} />
|
d) 规则选中
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32
| rowSelection={{ onChange: (_, selectedRows) => setSelectedRows(selectedRows), }}
{selectedRowsState?.length > 0 && ( <FooterToolbar extra={ <div> 已选择 <a style={{ fontWeight: 600 }}>{selectedRowsState.length}</a> 项 <span> 服务调用次数总计 {selectedRowsState.reduce((pre, item) => pre + item.callNo, 0)} 万 </span> </div> } > {/**批量删除按钮 */} <Button onClick={async () => { await handleRemove(selectedRowsState); setSelectedRows([]); actionRef.current?.reloadAndRest?.(); }} > 批量删除 </Button> {/**批量审批按钮 */} <Button type="primary">批量审批</Button> </FooterToolbar> )}
|
e) 查看规则名称
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36
| <a onClick={() => setRow(entity)}>{dom}</a>
<Drawer width={500} //当row数据存在的时候让抽屉组件显示 visible={!!row} //关闭抽屉组件的时候触发的方法 onClose={() => { setRow(undefined); }} //是否显示抽屉组件的关闭按钮 closable={false} > {/**如果row中有信息则显示规则的详细信息 */} {row?.name && ( <ProDescriptions<TableListItem> //几列显示 column={2} //标题 如果row存在则取name,如果row不存在则不取name,避免row不存在的时候报错 title={row?.name} //设置ProDescriptions的数据 request={async () => ({ data: row || {}, })} //传参,参数改变的时候会触发reload params={{ id: row?.name, }} //设置ProDescriptions的列数据 columns={columns} /> )} </Drawer>
|
4. ant design pro区块
https://pro.ant.design/docs/block-cn
区块是研发资产的一种,它是一系列快速搭建页面的代码片段,它可以帮助你快速的在项目中初始化好一个页面,帮助你更快速的开发代码。当前的区块都是页面级别的区块,你可以理解为它是一些项目中经常会用到的典型页面的模板,使用区块其实相当于从已有的项目中复制一些页面的代码到你当前的项目中。
- 以前开发一个页面:创建 JS -> 创建 CSS -> 创建 Model -> 创建 service -> 写页面组件。
- 现在开发一个页面:下载区块 -> 基于区块初始化好的页面组件修改代码。
在 Pro 中资产被分为了两种,区块和模板。区块可以类比为一个组件,而模板代表一个页面。区块现在支持所有 antd 中的 demo,可以更加快速的将 demo 导入到项目中去。
5.vue-antd-admin
https://github.com/iczer/vue-antd-admin