项目的依赖包版本以及整体项目结构请访问://www.greatytc.com/p/4e9b6ab14f12
- 权限配置: 在
router.config.js
文件中对 每个 需要设置权限的菜单项都添加Routes: ['src/pages/Authorized']
, 此项配置时为了用ant-pro
的权限组件包裹目标组件, 以及authority: ['admin']
属性, 此项时配置不同角色用, 例如:
{
path: '/form',
icon: 'form',
name: 'form',
Routes: ['src/pages/Authorized'],
authority: ['admin'],
routes: [
{
name: 'basic-form',
icon: 'smile',
authority: ['admin'],
Routes: ['src/pages/Authorized'],
path: '/form/basic-form',
component: './Form/BasicForm',
},
]
},
- 切换用户后权限没变: 这里是因为切换用户后, 权限组件没有重新
render
触发, 解决办法是在login
成功后调用一下reloadAuthorized
方法, 即import { reloadAuthorized } from '../utils/Authorized'
即可.
- 关于组件中的
model
绑定问题:转自 《ant design pro
项目中,关于dvajs
的models
使用方法 》 这篇文章。ant-pro
中src/models
用于存放全局的models
,src/pages/models
用于存放页面的models
,引入时需要使用,使用示例:
/src/pages/element/models/compay.js
//有关页面的数据都放在页面的models下,这里举一个获取公司信息的例子
//与后台数据的交互需经由service来完成请求任务
//这里有两个服务,一个是请求公司信息的服务,一个是保存修改后公司新信息的服务
import { queryCompanyInfo,saveCompanyInfo} from '@/services/api';
export default{
//这里给models的命名需唯一,否则会报错
namespace:'company',
state:{
companyInfo:'',
num:10
},
reducers:{
//要想改变state中的数据,必须通过reducers,
//payload是参数
backCompanyInfo(state,{payload:{back}}){
// console.log(back);
return{...state,companyInfo:back};
},
add(state){
const NUM=state.num+1;
return{...state,num:NUM};
}
},
effects:{
*queryCompanyInfoService({payload:{num0,num1}},{put,call}){
//num0和num1都是从页面上dispatch派发过来的参数,在页面的js中你将看到
console.log(num0);
console.log(num1);
//请求后台服务,介由service中的异步方法
const data= yield call(queryCompanyInfo);
// console.log(data);
//打印一下,看看返回的是什么
//这里后台返回来一个对象Object {msg: "ok", code: 200, data: Object},
//这里data.data下放的是我们的企业信息
//将请求到的数据返回给同步的reducers中的backCompanyInfo,
//后面传参的键一定要和上面的backCompanyInfo接受的名字相同,
//涉及到es6的解析语法,要想解析出相同的值,你的键值必须相对应
yield put({type:"backCompanyInfo",payload:{back:data.data}});
},
*saveCompanyInfoService({payload:{newCompanyInfo}},{put,call}){
//将从页面上获取到的新的公司信息返回给后台做保存
//call的参数1是service的异步函数,参数2是从页面上获取到的新的公司信息
const data= yield call(saveCompanyInfo,newCompanyInfo);
console.log(data);
//将后台返回的修改成功的公司数据重新放到state中去,页面将会刷新,
yield put({type:"backCompanyInfo",payload:{back:data.data}});
}
},
}
————————————————
版权声明:本文为CSDN博主「ou得之」文章
原文链接:https://blog.csdn.net/qq_42190134/article/details/88893589
/src/services/api.js
//引入请求
import request from '@/utils/request';
//请求公司信息,不带models传来的参数
export async function queryCompanyInfo(){
//第一个是url,apihost已在src/utils/request.js中连接,以便后期上线调试
//第二个是option对象,一般包含接口的参数字段,method,headers,body等
return request('/company/detail',{
method: 'POST',
headers:{
'Content-Type':'application/json',
'token':'723a96b7934b13f414ee7c866f600c49'
},
}
)
}
//修改企业信息,带参数
export async function saveCompanyInfo(newCompanyInfo){
return request('/company/update',{
method: 'POST',
headers:{
'Content-Type':'application/json',
'token':'723a96b7934b13f414ee7c866f600c49'
},
body:newCompanyInfo
}
)
————————————————
原文链接:https://blog.csdn.net/qq_42190134/article/details/88893589
src/element/Company.js
页面会涉及到绑定models数据,获取绑定的models数据,以及向models传参
//1、引入connect用于绑定models
import { connect } from 'dva';
//2、绑定models与组件的props属性
@connect(({ company }) => ({
companyInfo:company.companyInfo,//需要models中的company中什么state,就引入什么state
}))
//必须放在export default前面,中间不能间隔任何语句,
export default class Company extends React.Component{
constructor(props){
super(props);
this.state=({
companyInfo:''
})
}
componentDidMount(){
//派发异步请求
//绑定models之后就可解析出dispatch
const { dispatch} = this.props;
//向models传参,形式牢记,请求企业信息服务
dispatch({type: 'company/queryCompanyInfoService',payload:{num0:0,num1:1}});
//这里的dispatch也可以写成Promise形式,后面加.then,可以在dispath结束后再执行相应的操作
//也很实用,可以进一步探索
}
componentWillReceiveProps(nextProps){
//第一次能获取到modals数据的地方,
//这个生命周期的nextProps能获取到你将要绑定进入props的companyInfo
console.log(nextProps.companyInfo);
//同样,你可以把companyInfo,映射到你这个类的state中去,通过使用this.setState方法
this.setState({
companyInfo:nextProps.companyInfo
})
}
//不带参数,传给models
add=()=>{
const { dispatch} = this.props;
dispatch({type: 'company/addNum'});
}
//带参传给models、
queryCompany=()=>{
const newCompanyInfo=this.state.companyInformation;
const { dispatch} = this.props;
dispatch({type: 'company/saveCompanyInfoService',payload:{newCompanyInfo}});
}
render(){
return(
<div>
<button onClick={this.add}>同步num+1</button>
<button onCkick={this.queryCompany}>保存公司信息</button>
</div>
)
}}
————————————————
原文链接:https://blog.csdn.net/qq_42190134/article/details/88893589
- 有关
models
中的loading
: 后台管理开发时会经常遇到请求数据时出发一个loading
状态,如果手动去控制展示和消失会非常麻烦。于是ant-pro
为我们提供了一个解决方案——基于dva-loading
封装的一个全局model
,这个loading
经常能在组件的connect
方法中看到,这里列举常用方法:
// 这里只展示绑定 model 时候用到的方法,其他代码省略。
@connect(({loading, myModel})=>({
// 此处用于当 myModel 这个 models 有数据请求行为的时候,loading 为 true,没有请求的时候为 false;
loadingAll: loading.models.myModel,
// 此处用于 myModel 的 effects 中的 myEffects(即在 model 中发送请求),发生了异步请求行为时为 true,没有请求行为时为 false;
loadingList:loading.effects['myModel/myEffects'],
// 此处单纯获取 model 中定义的 initState 中的值;
list: myModel.list,
}))
在页面中的使用方法如下:
export default class Test extends React.Component{
/******* 省略 *******/
query = () => {
dispatch({ type: 'myModel/myEffects', payload: value });
}
render () {
const { loadingAll, loadingList, list } = this.props;
list=list||[];
return(
<Spin spinning={ loadingAll || false }>
<Table dataSource={list} loading={ loadingList || false } />
<Button onClick={ this.query }>请求</Button>
</Spin>
)
}
-
ant-pro
设置未登录时自动跳转到登录页,登录之后不再返回登录页:
---(主要和 `SecurityLayout.jsx` 里面的东西有关系,先睡了,顶不住了,明天再更!)---
在SecurityLayout.jsx
里有这么一段:
componentDidMount() {
this.setState({
isReady: true,
});
// 这里默认请求了user数据 所以框架默认可以进去内容页
const { dispatch } = this.props;
// 这里是在安全校验的 Layout 里面直接进行 user 请求, 之后通过 isLogin 来判断是否已经登录
if (dispatch) {
dispatch({
type: 'user/fetchCurrent',
});
}
}
根据实际业务, 一般并不需要在这里进行user
的相关请求, 关于 user
的信息一般会随着 login
接口一并返回, 因此代码中需要将这一块 删除, 并在 model/login.js
中发起登录请求成功之后获取到 user
信息, 存储在 sessionStorage
中.
然后在 render
函数里进行判断, 将验证方式改为 session
验证:
render() {
const { isReady } = this.state;
const { children, loading, currentUser } = this.props;
// 这里是 ant 官方提示: 你可以把它替换成你自己的登录认证规则(比如判断 token 是否存在)
// ant 做的登录校验, 比较无脑, 只是作为例子罢了, 需要自己根据需求改动.
// const isLogin = currentUser && currentUser.userid;
// 这里是自定义的判断方法, 这里在调用 登录 接口之后, 将个人信息放在了 sessionStorage 里面, 然后通过判断是否有 session 存在改变登录状态.
const isLogin = Session.get('currentUser') && Session.get('currentUser').userid;
const queryString = stringify({
redirect: window.location.href,
});
/********** 省略 ********/
}
注意: 这里取消了
model/user
中的使用, 会导致页面展示以及权限的bug
, 因此在处理和user
相关的界面时, 需要将connect
中 的user
替换成session
中的user
, 涉及的页面主要有:GlobalHeader
中的AvatarDropdown.jsx
(头部个人信息);pages
中的Authorized.jsx
(权限配置)等等需要注意.
之后将 BasicLayOut
里useEffect
中的 dispatch({ type: 'user/fetchCurrent' });
方法删除, 防止多余的请求发生.
最后, 注意退出登陆的时候将 session 清空即可.
- 关于国际化:
公共部分的国际化在ant-pro
的src/locales
文件夹中进行相应的配置.
各个区块部分的国际化在每个组件文件夹的一级菜单中创建locales
文件夹进行相应配置, 例如:src/pages/Form/BasicForm/locales
, 树状结构如下:
└─src
│
├─pages
│ └─Form
│ └─BasicForm
│ └─locales
│ en-US.js
│ zh-CN.js
│ zh-TW.js
├─locales
│ en-US.js
│ zh-CN.js
│ zh-TW.js
│
...
-
menu
设置了hide
之后, 面包屑和pageTitle
不显示: (这里提供一个比较low
的方法, 更高级的等之后再研究.)
首先添加urlToList
方法
const urlToList = url => {
const urllist = url.split('/').filter(i => i);
return urllist.map((urlItem, index) => `/${urllist.slice(0, index + 1).join('/')}`);
}
在 BaseLayOut.jsx
里面添加以下代码
const BasicLayout = props => {
/************ 省略 ************/
const pathSnippets = urlToList(props.location.pathname);
const newRouters = []
pathSnippets.map((url, index) => {
const currentBreadcrumb = getBreadcrumb(breadcrumbNameMap, url);
const { locale, path, component } = currentBreadcrumb
if (!path) return null
return newRouters.push({ breadcrumbName: formatMessage({ id: locale }), path, component })
})
/************ 省略 ************/
}
将 ProLayout
里 breadcrumbName
的 routers
替换为newRouters
, 把 pageTitleRender
自定义为 newRouters
的最后一个元素的breadcrumbName
.
<ProLayout
breadcrumbRender={() => [
{
path: '/',
breadcrumbName: formatMessage({
id: 'menu.home',
defaultMessage: 'Home',
}),
},
...newRouters,
]}
pageTitleRender={() => newRouters.length > 0 && newRouters[newRouters.length - 1].breadcrumbName}
>
</ProLayout>
-
request.js
中请求前后的数据处理:
实际业务中response
和rerquest
数据结构都是和后端定义好的, 不会完全按照ant-pro
中的来, 这时进行请求前后的数据处理就很有必要了, 处理方法是需要在request
中加入中间件或者拦截器进行处理, 二者根据需求选用一个即可, 使用方法见: 中间件
request.use(async (ctx, next) => {
const { req } = ctx; // 使用中间件对请求前后的数据进行处理
const { url, options } = req;
console.log({ req, url, options })
await next();
const { res } = ctx;
// const { success = false } = res; // 假设返回结果为 : { success: false, errorCode: 'B001' }
// if (!success) {
// 对异常情况做对应处理
// }
})
// 克隆响应对象做解析处理
request.interceptors.response.use(async (response) => {
const originalRes = await response.clone().json();
const { status, msg, data } = originalRes;
if (status !== 'SUCCESS') {
notification.error({
description: msg,
message: status,
});
return originalRes
}
if (data.data) {
return {
list: data.data,
status: 'ok',
pageInfo: {
current: data.pageCount, // 一共多少页码
page: data.pageNum, // 当前页码
size: data.pageSize, // 每页数量
total: data.recordsTotal,
},
}
}
return { ...originalRes, status: 'ok' };
})
- React 项目离开页面的时候做数据未保存的提示
import React, { Component, useEffect } from 'react';
import { Prompt } from 'react-router-dom';
// import { Prompt } from 'umi/prompt';
// 监听刷新或者关闭窗口
const Editor = () => {
// 监听窗口事件
useEffect(() => {
const listener = ev => {
ev.preventDefault();
ev.returnValue = '文章要保存吼,确定离开吗?';
};
window.addEventListener('beforeunload', listener);
return () => {
window.removeEventListener('beforeunload', listener)
}
}, []);
return <></>
}
/* ======== 监听跳出本组件 ======== */
render () {
return <Prompt
when={true}
message={(location) => {
return window.confirm(`confirm to leave to ${location.pathname}?`);
}}
/>
}