【后台管理系统】—— Ant Design Pro入门学习&项目实践笔记(二)

前言:上一篇梳理了上手Ant Design Pro须要了解的一些基础知识,这一篇记录一些在开发【后台管理系统】登陆注册、数据获取、列表展现等功能时须要注意的地方。javascript


1、与服务器交互的通常步骤html

  • 代理到后端,配置跨域
  1. 修改 config/config.js
  2. 配置 proxy 属性。只要 proxy 和 mock url 不一样,是能够共存的。
    proxy: {
        '/login': {
            target: 'http://192.168.1.106:9099',
            changeOrigin: true,
            pathRewrite: { '^/login': '' },
        },
    }
    
  • 添加要请求的接口
  1. 修改 services/api.js (能够是service目录下其余自定义文件)
    import request from '@/utils/request';  //ant design pro封装的reques请求文件
    
    export async function fakeAccountLogin(params) {
      return request('/login', {
        method: 'POST',
        body: params
      });
    }  
  • 在modal里面写effect调取接口方法java

  1. 修改 model/login.js (能够是model目录下其它自定义文件)
    import { fakeAccountLogin, getFakeCaptcha } from '@/services/api'; //请求的接口方法
    
    namespace: 'login', //要惟一
    
    state: {
        list: []  //后台返回的数据存储在该list中,名字想怎么起怎么起
    },
    
    effects: {
        *login({ payload }, { call, put }) {  //login是界面要调取的接口名称
             const response = yield call(fakeAccountLogin, payload);  //yield call()真正调用接口,传递数据,返回响应的方法
             yield put({                   //一个yield put只能触发一个action
                   type: 'queryList',   //经过调用这个reducer把返回数据传给list
                   payload: response, 
             });
    
             reducers: {
                   queryList(state, action) {
                      return {
                          ...state,
                          list: action.payload,  //这就拿到数据啦
                      };
                   },
             }
        }
    }
    
  • 组件中建立链接react

  1. 在 pages/User/Login.js 组件中经过 dva提供的connect高阶组件链接组件和dva,传入namespace(惟一)得到其中的state和effects(dispatch方法)json

    import { connect } from 'dva'
    
    @connect(({ login, loading }) => ({   //login是namespace  loading是对应使用的方法
         login,       //login是namespace
         submitting: loading.effects['login/login'],  //login 命名空间的login请求接口(入口)
    })) 
  2. 通常在componentDidMount生命钩子中发送请求获取数据redux

    componentDidMount() {
     //注意务必先使用dva中的connect创建链接,不然是没法调用props中的dispatch法的
            this.props.dispatch({
                //调用model中的方法发起请求,(model的命名空间+方法名)
                type: 'mbit/firstRequest',
                //设置参数
                payload:{
                      args1:"参数1",
                      args2:"参数2",
                },
            });
    }
  3. 登陆提交等操做方法中发送请求获取数据后端

    handleSubmit = (err, values) => {
            const { type } = this.state;
            if (!err) {
                const { dispatch } = this.props;
                dispatch({
                     type: 'login/login',
                     payload: {
                           login_type: "usernameAndPassword",
                           credentials: {
                                username: values.userName,
                                password: values.password
                           },
                          ...values
                     },
                });
            }
    };
    
  • 获取数据后的其它操做api

  1. 显示后台返回的数据跨域

    const {Login: { list },loading} = this.props;  //这个就在对应namespace下面list数组,以前存放后台返回数据的list数组
    
    <Table columns={columns} dataSource={list?list.content:[]} rowKey="id"/>  //dataSource里面是经过list获取到的数据
  2. 跳转路由页面数组

    import { routerRedux } from 'dva/router';
    
    yield put(routerRedux.replace('/'));
  3. 将获取到的数据存入localStorage

    localStorage.setItem('login_token', response.data.token);
  4. 每次请求中带着token  获取localStorage中的token封装进请求头中(修改 request.js 请求文件)

    let login_token;
    
    newOptions.headers = {
         Accept: 'application/json',
         'Content-Type': 'application/json; charset=utf-8',
         ...newOptions.headers,
    };
          
    if (localStorage.getItem('login_token') != null){
         login_token = localStorage.getItem('login_token');
         newOptions.headers['AuthorizationToken'] = localStorage.getItem('login_token');
    }
    

 

、关于@connect装饰器

  • 组件写法中调用了 dva 所封装的 react-redux 的 @connect 装饰器
  1. 用来接收绑定的 list 这个 model 对应的 redux store。
  2. 这里的装饰器实际除了 app.state.list 之外还实际接收 app.state.loading 做为参数,这个 loading 的来源是 src/index.js 中调用的 dva-loading这个插件。
    /*
    * src/index.js
    */
    import createLoading from 'dva-loading';
    app.use(createLoading());
    

    它返回的信息包含了 global、model 和 effect 的异步加载完成状况。数据map一

    {
      "global": true,
      "models": {
        "list": false,
        "user": true,
        "rule": false
      },
      "effects": {
        "list/fetch": false,
        "user/fetchCurrent": true,
        "rule/fetch": false
      }
    }
    

    在这里带上 {count: 5} 这个 payload (参数)向 store 进行了一个类型为 list/fetch 的 dispatch,在 src/models/list.js 中就能够找到具体的对应操做。

    import { queryFakeList } from '../services/api';
     
    export default {
      namespace: 'list',
     
      state: {
        list: [],
      },
     
      effects: {
        *fetch({ payload }, { call, put }) {
          const response = yield call(queryFakeList, payload);
          yield put({
            type: 'queryList',
            payload: Array.isArray(response) ? response : [],
          });
        },
        /* ... */
      },
     
      reducers: {
        queryList(state, action) {
          return {
            ...state,
            list: action.payload,
          };
        },
        /* ... */
      },
    };
    
  • View中使用@connect

    @connect(({ list, loading }) => ({
      list,//①
      loading: loading.models.list,//②
    }))
    
  1. connect 有两个参数,mapStateToProps以及mapDispatchToProps,一个将state状态绑定到组件的props,一个将dispatch方法绑定到组件的props

  2. 代码①:将实体list中的state数据绑定到props,注意绑定的是实体list总体,使用时须要list.[state中的具体变量]

  3. 代码②:经过loading将上文“数据map一”中的models的list的key对应的value读取出来。赋值给loading,以方便使用,如表格是否有加载图标(也能够经过key value编写:loading.effects["list/fetch"],以下↓可取多个)

    //pages/Dashboard/WorkPlace.js
    
    @connect(({ user, project, activities, chart, loading }) => ({
      currentUser: user.currentUser,
      project,
      activities,
      chart,
      currentUserLoading: loading.effects['user/fetchCurrent'],
      projectLoading: loading.effects['project/fetchNotice'],
      activitiesLoading: loading.effects['activities/fetchList'],
    }))
    
  • 变量获取

  1. 因,在src/models/list.js

    export default {
      namespace: 'list',
     
      state: {
        list: [],
      },
  2. 故,在view中

    render() {
        const { list: { list }, loading } = this.props;
    

    定义使用时:list: { list }  ,含义:实体list下的state类型的list变量

 

3、项目实践注意点

  • 登陆注册
  1. 登陆关键点:登陆成功后,请求中始终带着存着登陆信息的token,在其余功能中,如须要获取用户信息,则直接从token中获取
  2. 注册关键点:注册必须输入正确的手机验证码,在校验手机号格式正确后就可经过阿里云发送验证码,可是同一号码屡次发送,可能会被封号
  • 数据获取
  1. 表格Table组件中的单选/多选,获取当前选中项的列表数据:record
    {
          title: '资料审核',
          dataIndex: 'detailInfo',
          render: (text, record) => <a onClick={() => this.previewItem(record.id)}>资料详情>></a>
    },  
  2. 在全部选中项的列表数据中删选/计算符合条件的数据:selectRows
    //components/StandardTable/index.js
    
    handleRowSelectChange = (selectedRowKeys, selectedRows) => {
        let { noPayList, payedList, needTotalList } = this.state;
        noPayList = initNoPayList(selectedRows);
        payedList = initPayedList(selectedRows);
        needTotalList = needTotalList.map(item => ({
          ...item,
          total: selectedRows.reduce((sum, val) => sum + parseFloat(val[item.dataIndex], 10), 0),
        }));
        const { onSelectRow } = this.props;
        if (onSelectRow) {
          onSelectRow(selectedRows);
        }
    
        this.setState({ selectedRowKeys, selectedRows, noPayList, payedList, needTotalList });
    };
    

      

  3. 表单Form组件中获取用户提交的数据:fields / fieldsValue
    const okHandle = (record) => {
        form.validateFields((err, fieldsValue) => {
          if (err) return;
          form.resetFields();
          handleRemark(record, fieldsValue);
        });
      };
    注意:要使用Form,必须用Form.create()包裹组件,而后从this.props中获取到Form;经过 form.getFieldDecorator 提交数据
    const CreateForm = Form.create()(props => {
      const { modalVisible, record, form, handleRemark, handleModalVisible } = props;
      const rowObject = {
        minRows: 2, 
        maxRows: 6   
      }  
      const okHandle = (record) => {
        form.validateFields((err, fieldsValue) => {
          if (err) return;
          form.resetFields();
          handleRemark(record, fieldsValue);
        });
      };
      return (
        <Modal
          destroyOnClose
          title="添加备注"
          visible={modalVisible}
          okText="肯定"
          cancelText="取消"
          onOk={() => okHandle(record)}
          onCancel={() => handleModalVisible()}
        >
          <FormItem labelCol={{ span: 5 }} wrapperCol={{ span: 15 }}>
            {form.getFieldDecorator('remark', {
              rules: [{ required: true, message: '请输入至少五个字符的审批备注!', min: 5 }],
            })(
               <TextArea 
                 autosize={rowObject}
               />
              )}
          </FormItem>
        </Modal>
      );
    });
  • 列表展现
  1. componentDidMount() 组件成功挂载后经过this.props中的dispatch方法传递参数、发送请求、获取数据,完成state数据初始化
  2. 定义 columns 列表项数组,传给Table组件的 column属性,其中 title为列表项标题、dataIndex为与服务端返回数据对应字段、render方法对数据处理后展现
  3. 从this.props中拿到的store中存储的 list (或其它)列表数据,传给Table组件的dataSource,列表才能够将column数组中的字段与数据一一对应
  4. 一般,列表上方有【查询】、【编辑】等操做,在输入查询内容时,要对字符串作 去首尾空格,确保执行查询时为完整无空格字符串
     trimStr = str => {
        return str.replace(/(^\s*)|(\s*$)/g,"");
     }
    
    //查询
    handleSearch = e => {
        e.preventDefault();
    
        const { dispatch, form } = this.props;
    
        form.validateFields((err, fieldsValue) => {
          if (err) return;
    
          let values = {
            ...fieldsValue,
            updatedAt: fieldsValue.updatedAt && fieldsValue.updatedAt.valueOf(),
          };
    
          Object.keys(values).forEach(key => {
            if(values[key]){
              values[key] = this.trimStr(values[key])
            }
          });
    
          this.setState({
            formValues: values,
          });
    
          dispatch({
            type: 'agent/fetch',
            payload: {
              currentPage: 1,
              pd: {},
              showCount: 10
            },
          });
        });
    };  
  • 其它
  1. 向对象中添加新的属性与属性值:Object['属性'] = 值
  2. 遍历对象修改每个对象属性:Object.keys(values).forEach(key => { ……})
  3. forEach直接操做原数组,不会返回新值,map会返回新值:在React中根据数据动态循环添加元素,使用map
    <Row gutter={24}>
             { infoPics.map((infoPic, index) => 
                   (<Col key={index} xl={6} lg={12} md={24} sm={24} xs={24}>
                         <div className={styles.infoTitle}>{`代理资料${index+1}`}</div>
                         <div 
                              className={styles.infoPic}
                              style={{ backgroundImage: `url(${infoPic})` }}
                         >
                             { infoPic == '' ? <div className={styles.empty}><Empty /></div> : '' }
                         </div>
                    </Col>)
                  )
             }   
    </Row>
    

  4. 资源文件须要加统一前缀时,在配置文件中定义方法,应用时直接在数据前调用方法便可

    //util/util.js
    export function setFileHost(){
      return 'http://baidu.com/';    
    }
    
    //RightContent.js
    import { setFileHost } from '@/utils/utils'
    
    <Avatar
            size="small"
            className={styles.avatar}
            src={`${setFileHost()+currentUser.headIcon}`}
            alt="avatar"
     /> 
  5. 后台标题:标题修改是在 src/layouts/BasicLayout.js 中找到 getPageTitle 进行修改
  6. 后台Logo:Logo位于 src/components/SideMenu/SideMenu.js 中,原先的logo是props传过来的,因此我在引用logo文件的时候加了import yhzLogo from '../../assets/logo.png';避免参数名重复,另外logo图片文件最好放在src/assets 里面

 

参考资料


注:转载请注明出处

相关文章
相关标签/搜索