后台管理系统用的非常多,如果比较复杂点的后台可能会多人开发,这时候可能导致各种样式不统一之类的问题。
antdpro用起来有一定学习成本,所以需要梳理一下。
快速上手
npx create-umi your-project
1
选择最新v5,antd4
启动
npm run start
1
即可看见页面。
登录页默认用户名密码是admin ant.design 。输入进去即可进入后台。
进入后右下角有个米饭一样的图标,那个就是umi-ui,可以非常方便的导入代码或者模板进行修改。
注意!尽量别用umi-ui导入模板,容易引起各种问题。而且是一个不可逆的过程!同时,很多模板没有人维护,使用的是老的api或者写法,也容易导致各种bug!
读者可以玩一下,这玩意就是我最初学前端非常想要的功能。不仅仅是复制代码,包括路由什么的都整好了。
概念
使用antdpro,首先默认你已经会了react dva ts umi antd(里面很多组件和antd关系比较大,没熟练用过antd的要花更多时间上手)
配置
config文件夹下3个文件,相当于原umirc的配置:
路由参考:https://beta-pro.ant.design/docs/layout-cn
defaultSettings主要用来配置主题
proxy主要就是devserver的代理。
antdpro整了react环境变量,你可以看见在proxy里分别配了3个环境,当然都是开发环境,对应着3个start脚本。
主题
优先使用官方提供的工具先看一下哪种主题好:https://preview.pro.ant.design/dashboard/analysis/
然后复制粘贴到defaultSettings中。
当然你也可以在自己的站点里使用它,需要将PageContainer, SettingDrawer, ProSettings从pro-layout里导出后自己实现。
具体参考:https://pro.ant.design/blog/new-pro-use-cn#settingdrawer
布局
一般小项目不会用这玩意的,如果用了antdpro可能对样式什么的有需求,所以antdpro还整了个可视化配置样式插件。
这个也是为啥在antd的基础上封了个proxxx组件,而且这些组件把常用的逻辑都写差不多了,只要修改对应的地方就行了,没人用的主要原因就是又大又重,他们称之为重型组件,有些地方想定制比较难下手,还是需要把所有代码以及配置都搞懂。
参考官网:https://procomponents.ant.design/components/
请求
里面的很多loading都是使用umihook的useRequest:
import React from 'react';
import { useRequest } from 'umi';
import services from '@/services/afs2demo';
const YourComponent: React.FC = () => {
const { data, loading } = useRequest(() => {
return services.AccountController.addAccount();
});
if (loading) {
return <>Loading...</>;
}
return <div>{data?.name}</div>;
};
export default YourComponent;
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
这样避免写太多loading,同时可以配合table使用。useRequest的参数的函数的返回值是一个promise,一般是每个页面的serverice.ts下定义的请求:
import { request } from 'umi';
import { TableListParams, TableListItem } from './data.d';
export async function queryRule(params?: TableListParams) {
return request('/api/rule', {
params,
});
}
1
2
3
4
5
6
7
8
umi-request在app.tsx下有个errorHandler已经写好了个异常处理。
中间件处理需要加上配置:
export const request = {
middlewares: [
async function middlewareA(ctx, next) {
console.log('A before');
await next();
console.log('A after');
},
async function middlewareB(ctx, next) {
console.log('B before');
await next();
console.log('B after');
}
]
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
拦截器处理也是一样:
requestInterceptors?: RequestInterceptor[];
responseInterceptors?: ResponseInterceptor[];
1
2
MOCK
约定mock文件夹下的为mock数据,具体可以参考:https://umijs.org/zh-CN/docs/mock
如果你不想用mock,可以配置关闭:
export default {
mock: false,
};
1
2
3
或者使用命令不开:
MOCK=none umi dev
1
数据流
antdpro5中使用了新的数据流可以代替dva,这样在纯hooks组件下更加轻量好理解,也容易方便管理并且没有学习成本。
这个是通过plugin-model实现的功能,约定model下的文件为一个id,然后就像使用自定义hook一样使用它,但是这个状态是全局的,另外状态更新各个组件都可以更新 。
dva学习成本还是略高的,就算你学会了,别人不一定能很快学会,而这个就不一样了。
初始数据:
export async function getInitialState() {
return {
userName: 'xxx',
};
}
1
2
3
4
5
app中有个getInitialState方法用来做全局的初始数据,依靠 @umijs/plugin-initial-state插件完成。
同样,这个也是个model,只是它自带的:
import React from 'react';
import { useModel } from 'umi';
import { Spin } from 'antd';
export default () => {
const { initialState, loading, refresh, setInitialState } = useModel('@@initialState');
if (loading) {
return <Spin />;
}
return <div>{initialState.userName}</div>;
};
1
2
3
4
5
6
7
8
9
10
11
12
13
14
DVA
5版本的model同样也支持dva,这是因为plugin-dva的获取文件后对导出进行了校验。
/* eslint-disable no-param-reassign */
import { Effect, ImmerReducer, Subscription } from 'umi';
export interface IndexModelState {
name: string;
}
export interface IndexModelType {
namespace: 'index';
state: IndexModelState;
effects: {
query: Effect;
};
reducers: {
save: ImmerReducer<IndexModelState>;
};
subscriptions: { setup: Subscription };
}
const IndexModel: IndexModelType = {
namespace: 'index',
state: {
name: '',
},
effects: {
*query({ payload }, { put }) {
yield put({
type: 'save',
payload,
});
},
},
reducers: {
save(state, action) {
state.name = action.payload;
},
},
subscriptions: {
setup({ dispatch, history }) {
return history.listen(({ pathname }) => {
if (pathname === '/testdemo') {
dispatch({
type: 'query',
payload: 'yehuozhili',
});
}
});
},
},
};
export default IndexModel;
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
页面中使用需要connect后props获取:
import React, { FC } from 'react';
import { IndexModelState, ConnectProps, Loading, connect } from 'umi';
interface PageProps extends ConnectProps {
index: IndexModelState;
loading: boolean;
}
const IndexPage: FC<PageProps> = ({ index, dispatch, loading }) => {
const { name } = index;
return <div>Hello {name}</div>;
};
export default connect(({ index, loading }: { index: IndexModelState; loading: Loading }) => ({
index,
loading: loading.models.index,
}))(IndexPage);
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
权限
权限参考资料:https://beta-pro.ant.design/docs/authority-management-cn
权限实质就是个高阶组件,思想有点类似于守卫的意思,src下有个access的文件,里面设置了权限以及判定。具体可以根据业务需要来,主要就是业务返回某个权限值,然后基于权限值判断:
// src/access.ts
export default function access(initialState: { currentUser?: API.CurrentUser | undefined }) {
const { currentUser } = initialState || {};
return {
canAdmin: currentUser && currentUser.access === 'admin',
canGuset: currentUser && currentUser.access === 'guest',
};
}
1
2
3
4
5
6
7
8
比如我加一个canGuest表明某个页面只能由guest进行访问。
在mock文件夹里模拟着返回值,其中有句:
'POST /api/login/account': (req: Request, res: Response) => {
const { password, username, type } = req.body;
if (password === 'ant.design' && username === 'admin') {
res.send({
status: 'ok',
type,
currentAuthority: 'admin',
});
access = 'admin';
return;
}
if (password === 'ant.design' && username === 'user') {
res.send({
status: 'ok',
type,
currentAuthority: 'user',
});
access = 'user';
return;
}
if (type === 'mobile') {
res.send({
status: 'ok',
type,
currentAuthority: 'admin',
});
return;
}
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
也就是以admin登录,是admin权限。
可以试着在路由配置中对权限进行控制:
{
name: 'test',
icon: 'smile',
path: '/testdemo',
access: 'canGuset',
component: './testdemo',
},
1
2
3
4
5
6
7
此时,以admin身份登录/testdemo则会出现401页面。
将mock文件的那句改为guest再次访问,即可出现想要的页面。
组件中使用:
const access = useAccess();
1
即可拿到access.ts所定义的权限。
国际化
国际化参考文档:
https://beta-pro.ant.design/docs/i18n-cn
https://umijs.org/zh-CN/plugins/plugin-locale#%E4%BB%8B%E7%BB%8D
约定 在 src/locales 中引入 相应的 js,例如 en-US.ts 和 zh-CN.ts,并且在 config/config.ts 中做如下配置:
plugins:[
...,
locale: {
enable: true,
default: 'zh-CN',
baseNavigator: true,
},
...,
]
1
2
3
4
5
6
7
8
9
它使用的是react-intl :https://formatjs.io/docs/getting-started/installation/
这个库有很多高级功能,比如动态导入什么的,所以还是比较大的。
layout中若设置了国际化,默认配置的route的name就是国际化的key值。
如果你写了对应的zh-CN.ts,默认会
重型组件PRO
了解了上面的设定,基本可以玩了,下面还需要了解antdpro特有的重型组件,来使得样式保持统一。
ProLayout - 高级布局
prolayout是高级布局,是一开始就自带的。
可以通过context拿到布局上下文:
import { RouteContext, RouteContextType } from '@ant-design/pro-layout';
const Page = () => (
<RouteContext.Consumer>
{(value: RouteContextType) => {
return value.title;
}}
</RouteContext.Consumer>
);
1
2
3
4
5
6
7
8
routerContext里面还有个很常用的就是isMobile,如果需要对手机端支持,拿此参数判断。
plugin-layout将layout的代码写入.umi,所以用户可以完全不用管路由,我个人觉得这玩意相当好。其实确实这东西不需要怎么自定义。如果想自定义的可以关掉layout,手动引入ProLayout或者自己写layout。
https://procomponents.ant.design/components/layout#api
PageContainer是一个壳,它可以显示嵌入页面的底色,也可以显示白色,另外提供面包屑功能,这个面包屑,1级菜单是不显示的,如果想显示,需要修改app.tsx。
ProTable - 高级表格
https://procomponents.ant.design/components/table
这玩意配置项太多,因为不只是表格,会带着别的东西,所以还需要耐下心来先挑选满足自己要求的表格,然后自定义的地方再仔细看配置。
ProForm - 高级表单
https://procomponents.ant.design/components/form
这个主要是登录注册,或者是让用户填表单的地方,同样是一个组件一个页面。
ProCard - 高级卡片
https://procomponents.ant.design/components/card
如果你想让padding固定,有个比较美观的样式,那就需要使用它。
卡片不仅仅是那种小的,它可以去承载别的之类。在一开始布局时可以调整好。
ProDescriptions - 高级定义列表
这玩意是按固定格式的白板,类似于antd的descriptions
https://procomponents.ant.design/components/descriptions
ProList - 高级列表
这组件是自带padding的,需要列表时可以使用。
https://procomponents.ant.design/components/list
————————————————