Ant Design Pro 使用总结

项目的依赖包版本以及整体项目结构请访问://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' 即可.

/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(权限配置)等等需要注意.

之后将 BasicLayOutuseEffect 中的 dispatch({ type: 'user/fetchCurrent' });方法删除, 防止多余的请求发生.
最后, 注意退出登陆的时候将 session 清空即可.


  • 关于国际化:
    公共部分的国际化在ant-prosrc/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 })
  })
/************ 省略 ************/
}

ProLayoutbreadcrumbNamerouters 替换为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 中请求前后的数据处理:
    实际业务中responsererquest 数据结构都是和后端定义好的, 不会完全按照 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}?`);
      }}
    />
  }
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 211,884评论 6 492
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,347评论 3 385
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 157,435评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,509评论 1 284
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,611评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,837评论 1 290
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,987评论 3 408
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,730评论 0 267
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,194评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,525评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,664评论 1 340
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,334评论 4 330
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,944评论 3 313
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,764评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,997评论 1 266
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,389评论 2 360
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,554评论 2 349

推荐阅读更多精彩内容