【後臺管理系統】—— 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 裏面

 

參考資料


注:轉載請註明出處

相關文章
相關標籤/搜索