修改端口号
根目录下的 .env
文件:
PORT=8888
目录结构 (umijs.org)
新增页面
在 umirc.ts 中进行配置。
新增页面 - Ant Design Pro
这里有一个配置 icon:string
,可以在菜单加 icon 图标,默认使用 antd 的 icon 名,默认不适用二级菜单的 icon,例如:icon:"QuestionCircleOutlined"
。
图标 Icon - Ant Design (antgroup.com)
二级菜单,也就是子路由。需要使用配置项 routes:[{}]
,类似于 vue-router 中的 children。
还有一个配置: hideInMenu:true
可以在菜单中不展示这个路由,包括子路由。例如登录、注册什么的。
menuRender: false
当前路由不展示菜单
代理
也是直接在 umirc.ts 中进行配置。
配置 (umijs.org)
ProComponents
antd pro 内置了 ProComponents。
https://procomponents.ant.design/docs
里面的组件是基于 antd 的二次封装,注意属性及内置方法和 antd 的相同点和差异。比如表格:
https://procomponents.ant.design/components/schema#valuetype-%E5%88%97%E8%A1%A8
表格 Table - Ant Design (antgroup.com)
其中 render 属性类似于插槽,可以在 table 里面插入各种组件。
设置全局 CSS
直接在 src 目录下创建 global.css 的文件,默认被配置为全局样式。目录结构 (umijs.org)
Form 回填数据
antd Form
关于发送请求获取数据
有两种处理方式:
- 通过dispatch一个action到状态仓库,然后状态仓库来发请求,请求回来的数据放入到数据仓库中
- 适用于数据量不多
- 多个组件要共享某一块数据
- 直接在组件里面请求数据
- 数据量很大,在向服务器发送请求的时候,只能分页请求
- 不需要和其他组件共享
说一下 ProTable 里面的 request:
https://procomponents.ant.design/components/table#request
ProTable有一个重要的属性叫做 request ,该属性对应的值一般是一个异步函数,该异步函数自动接受一个params,params中会有默认的当前页码(current)和每一页的条数(pageSiz2e)(当然还会捕获其他参数,可以自己手动log下),这两个值会有默认值,current默认为1,pageSize默认为20,可以通过配置pagination属性来修改current和pageSize的值。
此外,在查询表单查询时和 params
参数发生修改时 request 会重新执行,同时也会触发 onChange 等回调。
<ProTable
// params 是需要自带的参数
// 这个参数优先级更高,会覆盖查询表单的参数
headerTitle="用户列表"
columns={columns} // antd table 需要的表头配置
rowKey={row => row.id} // 配置表格的行 key
pagation={{
showQuickJumper: true,
showSizeChanger: true,
pageSizeOptions: ['10', '20', '30', '40'],
...pagation, // 这个参数是自定义的 state 用于响应式改变 current 和 pageSize
onChange: handlePageChange // 自定义分页数据改变的回调 会自动传入两个参数 current 和 pageSize 需要自己手动更新到 state 中
}}
request={async (
// 第一个参数 params 查询表单和 params 参数的结合 必填
// 第一个参数中一定会有 pageSize 和 current ,这两个参数是 antd 的规范
params,
sort,
filter,
) => {
// 这里需要返回一个 Promise,在返回之前你可以进行数据转化
// 如果需要转化参数可以在这里进行修改
const res = await myQuery({
page: params.current,
pageSize: params.pageSize,
});
return {
// 返回当前页的数据数组
data: res.data.data,
// success 请返回 true,
// 不然 table 会停止解析数据,即使有数据
success: true,
// 不传会使用 data 的长度,如果是分页一定要传
// 数据总数
total: res.data.total,
};
}}
/>
ref.current.reload(); 可以刷新 ProTable。
Warning:Cannot update a component InternalFormItem)while rendering a different component userForm) 该警告出现的原因,是因为在初次渲染组件的时候,我们设置了数据的回填,导致组件初次还没有渲染完毕,又在更新。如何解决,也非常简单,我们等待第一次渲染完毕后再进行数据的回填,所以我们将回填的代码放入useEffect
粒子动画
登录页面的Canvas动画,使用到的是一个第三方库,叫做 react-canvas-nest
react-canvas-nest - npm (npmjs.com)
配置初始化数据
数据流 (umijs.org)
getInitialState 方法可以配置一些初始化数据,编写 src/app.ts
的导出方法 getInitialState()
,其返回值将成为全局初始状态。
可以在其他组件通过const initialState = useModel('@@initialState');
获取。
以登录功能为例(类似于导航守卫):
export async function getInitialState() {
if (location.pathname === '/login') {
// 强行进入登录页
const token = localStorage.getItem('token');
if (token) {
const res = await fetch('/api/user/info');
if (res.data) {
// 说明不仅有 token 而且 token 有效
message.error('请先退出后再登录');
history.go(-1);
}
}
} else {
// 进入其他页面
const res = await fetch('/api/user/info');
if (res.data) {
// 说明不仅有 token 而且 token 有效
const {name, avatar} = res.data;
return {name, avatar};
} else {
// 说明没有 token 或者 token 无效 需要重新登录
localStorage.removeItem('token');
message.error('用户无效,请先登录');
location.href = '/login';
}
}
return {name: '@umijs/max'};
}
请求响应拦截器
请求 (umijs.org)
// 配置请求响应拦截器
export const request = {
timeout: 3000,
requestInterceptors: [function (url, options) {
const token = localStorage.getItem('token');
if (token) {
options.headers['Authorization'] = `Bearer ${token}`;
}
return {url, options};
}],
responseInterceptors: [ // 直接写一个 function,作为拦截器
(response) => {
// 不再需要异步处理读取返回体内容,可直接在data中读出,部分字段可在 config 中找到
const {data = {}, config} = response;
// do something
return response
},
// 一个二元组,第一个元素是 request 拦截器,第二个元素是错误处理
[(response) => {
return response
}, (error) => {
return Promise.reject(error)
}],
// 数组,省略错误处理
[(response) => {
return response
}]]
}
退出登录、logo和标题的默认配置
布局与菜单 (umijs.org)
https://procomponents.ant.design/components/layout
export const layout = () => {
return {
logo: 'https://img.alicdn.com/tfs/TB1YHEpwUT1gK0jSZFhXXaAtVXa-28-27.svg',
menu: {
locale: false,
},
logout: () => {
localStorage.removeItem('token');
location.href = '/login';
message.success('退出成功')
}
};
};
权限管理
权限 (umijs.org)
- 开启配置
umirc.js
中配置access: {},
路由菜单权限
- 在
src/access.ts
提供权限配置
该文件需要默认导出一个方法,导出的方法会在项目初始化时被执行。该方法需要返回一个对象,对象的每一个值就对应定义了一条权限。
export default (initialState) => {
// 在这里按照初始化数据定义项目中的权限,统一管理
// 参考文档 https://umijs.org/docs/max/access
// initialState 是自动传入的 是在 getInitialState 中定义在全局初始化的数据
if (initialState) {
return {
superAdmin: initialState.adminInfo.permission === 1,
normalAdmin: initialState.adminInfo.permission === 1 ||
initialState.adminInfo.permission === 2,
};
} else {
return {
superAdmin: false,
normalAdmin: false,
}
}
};
- 配合路由管理权限
权限 (umijs.org)
这里我自定义了 access 作为区别普通权限和超级权限。
{
name: ' 管理员信息',
path: '/user/manager',
icon: 'TeamOutlined',
component: './Manager',
access: 'superAdmin',
},
{
name: ' 个人信息',
path: '/user/personal',
icon: 'UserOutlined',
component: './Personal',
access: 'normalAdmin',
},
自定义权限
对于页面中某一块区域的权限管理,可以使用 useAccess。
import { useAccess } from 'umi';
const access = useAccess();
useAccess() 方法可以返回一个对象:
{ superAdmin: false, normalAdmin: false, }
,由此获取该页面(路由)的权限。
<Access>
组件拥有 accessible
和 fallback
两个属性,当 accessible
为 true
时会渲染子组件,当 accessible
为 false
会渲染 fallback
属性对应的 ReactNode
。
<Access
accessible={access.superAdmin}
fallback={<div>Can not read foo content.</div>}
>
Foo content.
</Access>
图表
图表 (umijs.org)
所有图表 (ant.design)