• 【心有猛虎】react-pxq


    这是一个比较完整的简单的react移动端项目,说起来页面少,其实,构思若是精巧,也并不容易做
    先放源码:https://github.com/bailicangdu/react-pxq



    接下来我们简单的看看代码
    项目有用到react-redux和axios处理数据

    //根index.js引用了一些基本的方法
    //定义了渲染组件  <Component />
    import React from 'react';
    import ReactDOM from 'react-dom';
    import Route from './router/';
    import FastClick from 'fastclick';
    import registerServiceWorker from './registerServiceWorker';
    import { AppContainer } from 'react-hot-loader';
    import {Provider} from 'react-redux';
    import store from '@/store/store';
    import './utils/setRem';
    import './style/base.css';
    //解决tab穿透的问题
    FastClick.attach(document.body);
    
    // 监听state变化
    // store.subscribe(() => {
    //   console.log('store发生了变化');
    // });
    
    const render = Component => {
      ReactDOM.render(
        //绑定redux、热加载
        <Provider store={store}>
          <AppContainer>
            <Component />
          </AppContainer>
        </Provider>,
        document.getElementById('root'),
      )
    }
    
    render(Route);
    
    // Webpack Hot Module Replacement API
    if (module.hot) {
      module.hot.accept('./router/', () => {
        render(Route);
      })
    }
    
    registerServiceWorker();
    
    
    //src/utils/setRem.js
    //定义的是单位的转换
    (function(psdw){
      var dpr=0 , rem=0 , scale=0;
      var htmlDOM=document.documentElement;
      dpr=window.devicePixelRatio;
      var currentWidth=htmlDOM.clientWidth;
      scale=currentWidth/psdw;
      rem=psdw/10;
      rem=rem*scale;
      htmlDOM.style.fontSize=rem+'px';
      htmlDOM.setAttribute('data-dpr',dpr)
    })(750)
    
    //src/store/store.js
    //与store相对的就是reducer和action
    //这里用到的是react-thunk
    import {createStore, combineReducers, applyMiddleware} from 'redux';
    import * as home from './home/reducer';
    import * as production from './production/reducer';
    import thunk from 'redux-thunk';
    
    let store = createStore(
      combineReducers({...home, ...production}),
      applyMiddleware(thunk)
    );
    
    export default store;
    
    //reducer里面主要是纯函数
    //src/store/home/reducer.js
    import * as home from './action-type';
    
    let defaultState = {
      orderSum: '', //金额
      name: '', //姓名
      phoneNo: '', //手机号
      imgpath: '', //图片地址
    }
    // 首页表单数据
    export const formData = (state = defaultState , action = {}) => {
      switch(action.type){
        case home.SAVEFORMDATA:
          return {...state, ...{[action.datatype]: action.value}};
        case home.SAVEIMG:
          return {...state, ...{imgpath: action.path}};
        case home.CLEARDATA:
          return {...state, ...defaultState};
        default:
          return state;
      }
    }
    
    
    //src/store/production/reducer.js
    import * as pro from './action-type';
    import Immutable from 'immutable';
    
    let defaultState = {
      /**
       * 商品数据
       * @type {Array}
       * example: [{
       *    product_id: 1, 商品ID 
       *    product_name: "PaiBot(2G/32G)", 商品名称
       *    product_price: 2999, 商品价格
       *    commission: 200, 佣金
       *    selectStatus: false, 是否选择
       *    selectNum: 0, 选择数量
       * }]
       */
      dataList: [],
    }
    
    export const proData = (state = defaultState, action) => {
      let imuDataList;
      let imuItem;
      switch(action.type){
        case pro.GETPRODUCTION: 
          return {...state, ...action}
        case pro.TOGGLESELECT:
          //避免引用类型数据,使用immutable进行数据转换 
          imuDataList = Immutable.List(state.dataList);
          imuItem = Immutable.Map(state.dataList[action.index]);
          imuItem = imuItem.set('selectStatus', !imuItem.get('selectStatus'));
          imuDataList = imuDataList.set(action.index, imuItem);
          // redux必须返回一个新的state
          return {...state, ...{dataList: imuDataList.toJS()}};
        case pro.EDITPRODUCTION:
          //避免引用类型数据,使用immutable进行数据转换 
          imuDataList = Immutable.List(state.dataList);
          imuItem = Immutable.Map(state.dataList[action.index]);
          imuItem = imuItem.set('selectNum', action.selectNum);
          imuDataList = imuDataList.set(action.index, imuItem);
          // redux必须返回一个新的state
          return {...state, ...{dataList: imuDataList.toJS()}};
        // 清空数据
        case pro.CLEARSELECTED:
          imuDataList = Immutable.fromJS(state.dataList);
          for (let i = 0; i < state.dataList.length; i++) {
            imuDataList = imuDataList.update(i, item => {
              item = item.set('selectStatus', false);
              item = item.set('selectNum', 0);
              return item
            })
          }
          return {...state, ...{dataList: imuDataList.toJS()}};
        default: 
          return state;
      }
    }
    
    //action-type里面是对象
    //src/store/production/action-type.js
    // 保存商品数据
    export const GETPRODUCTION = 'GETPRODUCTION';
    // 选择商品
    export const TOGGLESELECT = 'TOGGLESELECT';
    // 编辑商品
    export const EDITPRODUCTION = 'EDITPRODUCTION';
    // 清空选择
    export const CLEARSELECTED = 'CLEARSELECTED';
    
    
    //action 里面通过dispatch把数据发送给其他数据
    import * as pro from './action-type';
    import API from '@/api/api';
    
    // 初始化获取商品数据,保存至redux
    export const getProData = () => {
      // 返回函数,异步dispatch
      return async dispatch => {
        try{
          let result = await API.getProduction();
          result.map(item => {
            item.selectStatus = true;
            item.selectNum = 0;
            return item;
          })
          dispatch({
            type: pro.GETPRODUCTION,
            dataList: result,
          })
        }catch(err){
          console.error(err);
        }
      }
    }
    
    // 选择商品
    export const togSelectPro = index => {
      return {
        type: pro.TOGGLESELECT,
        index,
      }
    }
    
    // 编辑商品
    export const editPro = (index, selectNum) => {
      return {
        type: pro.EDITPRODUCTION,
        index,
        selectNum,
      }
    }
    
    // 清空选择
    export const clearSelected = () => {
      return {
        type: pro.CLEARSELECTED,
      }
    }
    
    //src/store/home/action-type.js
    // 保存表单数据
    export const SAVEFORMDATA = 'SAVEFORMDATA';
    // 保存图片
    export const SAVEIMG = 'SAVEIMG';
    // 清空数据
    export const CLEARDATA = 'CLEARDATA';
    
    //src/store/home/action.js
    import * as home from './action-type';
    
    // 保存表单数据
    export const saveFormData = (value, datatype) => {
      return {
        type: home.SAVEFORMDATA,
        value,
        datatype,
      }
    }
    
    // 保存图片地址
    export const saveImg = path => {
      return {
        type: home.SAVEIMG,
        path,
      }
    }
    
    // 保存图片地址
    export const clearData = () => {
      return {
        type: home.CLEARDATA,
      }
    }
    
    
    //工具函数中定义了异步组件
    //src/utils/asyncComponent.jsx
    import React, { Component } from "react";
    
    export default function asyncComponent(importComponent) {
      class AsyncComponent extends Component {
        constructor(props) {
          super(props);
    
          this.state = {
            component: null
          };
        }
    
        async componentDidMount() {
          const { default: component } = await importComponent();
    
          this.setState({component});
        }
    
        render() {
          const C = this.state.component;
    
          return C ? <C {...this.props} /> : null;
        }
      }
    
      return AsyncComponent;
    }
    
    //路由部门,定义首页就home页面
    //src/router/index.js
    //路由配置是异步加载的
    import React, { Component } from 'react';
    import { HashRouter, Switch, Route, Redirect } from 'react-router-dom';
    import asyncComponent from '@/utils/asyncComponent';
    
    import home from "@/pages/home/home";
    const record = asyncComponent(() => import("@/pages/record/record"));
    const helpcenter = asyncComponent(() => import("@/pages/helpcenter/helpcenter"));
    const production = asyncComponent(() => import("@/pages/production/production"));
    const balance = asyncComponent(() => import("@/pages/balance/balance"));
    
    // react-router4 不再推荐将所有路由规则放在同一个地方集中式路由,子路由应该由父组件动态配置,组件在哪里匹配就在哪里渲染,更加灵活
    export default class RouteConfig extends Component{
      render(){
        return(
          <HashRouter>
            <Switch>
              <Route path="/" exact component={home} />
              <Route path="/record" component={record} />
              <Route path="/helpcenter" component={helpcenter} />
              <Route path="/production" component={production} />
              <Route path="/balance" component={balance} />
              <Redirect to="/" />
            </Switch>
          </HashRouter>
        )
      }
    }
    
    

    有公共组件header和alert

    //header组件
    //src/components/header/header.jsx
    import React, { Component } from 'react';
    import { is, fromJS } from 'immutable';
    import { NavLink } from 'react-router-dom';
    import PropTypes from 'prop-types';
    import ReactCSSTransitionGroup from 'react-addons-css-transition-group';
    import './header.less';
    
    export default class PublicHeader extends Component{
      static propTypes = {
        record: PropTypes.any,
        title: PropTypes.string.isRequired,
        confirm: PropTypes.any,
      }
    
      state = {
        navState: false, //导航栏是否显示
      };
      
      // 切换左侧导航栏状态
      toggleNav = () => {
        this.setState({navState: !this.state.navState});
      }
      // css动画组件设置为目标组件
      FirstChild = props => {
        const childrenArray = React.Children.toArray(props.children);
        return childrenArray[0] || null;
      }
      shouldComponentUpdate(nextProps, nextState) {
        return !is(fromJS(this.props), fromJS(nextProps))|| !is(fromJS(this.state),fromJS(nextState))
      }
    
      render(){
        return(
          <header className="header-container">
            <span className="header-slide-icon icon-catalog" onClick={this.toggleNav}></span>
            <span className="header-title">{this.props.title}</span>
            {
              this.props.record&&<NavLink to="/record" exact className="header-link icon-jilu"></NavLink>
            }
            {
              this.props.confirm&&<NavLink to="/" exact className="header-link header-link-confim">确定</NavLink>
            }
            <ReactCSSTransitionGroup
              component={this.FirstChild}
              transitionName="nav"
              transitionEnterTimeout={300}
              transitionLeaveTimeout={300}>
                {
                  this.state.navState && <aside key='nav-slide' className="nav-slide-list" onClick={this.toggleNav}>
                    <NavLink to="/" exact className="nav-link icon-jiantou-copy-copy">首页</NavLink>
                    <NavLink to="/balance" exact className="nav-link icon-jiantou-copy-copy">提现</NavLink>
                    <NavLink to="/helpcenter" exact className="nav-link icon-jiantou-copy-copy">帮助中心</NavLink>
                  </aside>
                }
            </ReactCSSTransitionGroup>
            
          </header>
        );
      }
    }
    
    //src/components/alert/alert.jsx
    import React, { Component } from 'react';
    import PropTypes from 'prop-types';
    import { is, fromJS } from 'immutable';
    import TouchableOpacity from '@/components/TouchableOpacity/TouchableOpacity';
    import ReactCSSTransitionGroup from 'react-addons-css-transition-group';
    import './alert.less';
    
    export default class Alert extends Component{
      static propTypes = {
        closeAlert: PropTypes.func.isRequired,
        alertTip: PropTypes.string.isRequired,
        alertStatus: PropTypes.bool.isRequired,
      }
      // css动画组件设置为目标组件
      FirstChild = props => {
        const childrenArray = React.Children.toArray(props.children);
        return childrenArray[0] || null;
      }
      // 关闭弹框
      confirm = () => {
        this.props.closeAlert();
      }
      
      shouldComponentUpdate(nextProps, nextState){
        return !is(fromJS(this.props), fromJS(nextProps)) || !is(fromJS(this.state), fromJS(nextState))
      }
      
      render(){
        return (
          <ReactCSSTransitionGroup
            component={this.FirstChild}
            transitionName="alert"
            transitionEnterTimeout={300}
            transitionLeaveTimeout={300}>
            {
              this.props.alertStatus&&<div className="alert-con">
                <div className="alert-context">
                  <div className="alert-content-detail">{this.props.alertTip}</div>
                  <TouchableOpacity className="confirm-btn" clickCallBack={this.confirm}/>
                </div>
              </div>
            }
          </ReactCSSTransitionGroup>
        );
      }
    }
    
    //src/pages/home/home.jsx
    import React, { Component } from 'react';
    import { Link } from 'react-router-dom';
    import { connect } from 'react-redux';
    import { is, fromJS } from 'immutable';
    import PropTypes from 'prop-types';
    import API from '@/api/api';
    import envconfig from '@/envconfig/envconfig';
    import { saveFormData, saveImg, clearData } from '@/store/home/action';
    import { clearSelected } from '@/store/production/action';
    import PublicHeader from '@/components/header/header';
    import PublicAlert from '@/components/alert/alert';
    import TouchableOpacity from '@/components/TouchableOpacity/TouchableOpacity';
    import mixin, { padStr } from '@/utils/mixin';
    import './home.less';
    
    @mixin({padStr})
    class Home extends Component {
      static propTypes = {
        formData: PropTypes.object.isRequired,
        saveFormData: PropTypes.func.isRequired,
        saveImg: PropTypes.func.isRequired,
        clearData: PropTypes.func.isRequired,
        clearSelected: PropTypes.func.isRequired,
      }
    
      state = {
        alertStatus: false, //弹框状态
        alertTip: '', //弹框提示文字
      }
      /**
       * 已选择的商品数据
       * @type {Array}
       */
      selectedProList = []; 
    
      /**
       * 将表单数据保存至redux,保留状态
       * @param  {string} type  数据类型 orderSum||name||phoneNo
       * @param  {object} event 事件对象
       */
      handleInput = (type, event) => {
        let value = event.target.value;
        switch(type){
          case 'orderSum':
            value = value.replace(/D/g, '');
          break;
          case 'name':
          break;
          case 'phoneNo':
            value = this.padStr(value.replace(/D/g, ''), [3, 7], ' ', event.target);
          break;
          default:;
        }
        this.props.saveFormData(value, type);
      }
      
      /*
      上传图片,并将图片地址存到redux,保留状态
       */
      uploadImg = async event => {
        try{
          let formdata = new FormData();
          formdata.append('file', event.target.files[0]);
          let result = await API.uploadImg({data: formdata});
          this.props.saveImg(envconfig.imgUrl + result.image_path);
          console.log(result);
        }catch(err){
          console.error(err);
        }
      }
    
      // 提交表单
      sumitForm = () => {
        const {orderSum, name, phoneNo} = this.props.formData;
        let alertTip = '';
        if(!orderSum.toString().length){
          alertTip = '请填写金额';
        }else if(!name.toString().length){
          alertTip = '请填写姓名';
        }else if(!phoneNo.toString().length){
          alertTip = '请填写正确的手机号';
        }else{
          alertTip = '添加数据成功';
          this.props.clearSelected();
          this.props.clearData();
        }
        this.setState({
          alertStatus: true,
          alertTip,
        })
      }
      
      // 关闭弹款
      closeAlert = () => {
        this.setState({
          alertStatus: false,
          alertTip: '',
        })
      }
      
      // 初始化数据,获取已选择的商品
      initData = props => {
        this.selectedProList = [];
        props.proData.dataList.forEach(item => {
          if(item.selectStatus && item.selectNum){
            this.selectedProList.push(item);
          }
        })
      }
    
      componentWillReceiveProps(nextProps){
        if(!is(fromJS(this.props.proData), fromJS(nextProps.proData))){
          this.initData(nextProps);
        }
      }
    
      shouldComponentUpdate(nextProps, nextState) {
        return !is(fromJS(this.props), fromJS(nextProps)) || !is(fromJS(this.state),fromJS(nextState))
      }
    
      componentWillMount(){
        this.initData(this.props);
      }
      
    
      render() {
        
        return (
          <main className="home-container">
            <PublicHeader title='首页' record />
            <p className="common-title">请录入您的信息</p>
            <form className="home-form">
              <div className="home-form-tiem">
                <span>销售金额:</span>
                <input type="text" placeholder="请输入订单金额" value={this.props.formData.orderSum} onChange={this.handleInput.bind(this, 'orderSum')}/>
              </div>
              <div className="home-form-tiem">
                <span>客户姓名:</span>
                <input type="text" placeholder="请输入客户姓名" value={this.props.formData.name} onChange={this.handleInput.bind(this, 'name')}/>
              </div>
              <div className="home-form-tiem">
                <span>客户电话:</span>
                <input type="text" maxLength="13" placeholder="请输入客户电话" value={this.props.formData.phoneNo} onChange={this.handleInput.bind(this, 'phoneNo')}/>
              </div>
            </form>
            <div>
              <p className="common-title">请选择销售的产品</p>
              <Link to="/production" className="common-select-btn">
                {
                  this.selectedProList.length ? <ul className="selected-pro-list">
                    {
                      this.selectedProList.map((item, index) => {
                        return <li key={index} className="selected-pro-item ellipsis">{item.product_name}x{item.selectNum}</li>
                      })
                    }
                  </ul>:'选择产品'
                }
              </Link>
            </div>
            <div className="upload-img-con">
              <p className="common-title">请上传发票凭证</p>
              <div className="file-lable">
                <span className="common-select-btn">上传图片</span>
                <input type="file" onChange={this.uploadImg}/>
              </div>
              <img src={this.props.formData.imgpath} className="select-img" alt=""/>
            </div>
            <TouchableOpacity className="submit-btn" clickCallBack={this.sumitForm} text="提交" />
            <PublicAlert closeAlert={this.closeAlert} alertTip={this.state.alertTip} alertStatus={this.state.alertStatus} />
          </main>
        );
      }
    }
    
    export default connect(state => ({
      formData: state.formData,
      proData: state.proData,
    }), {
      saveFormData, 
      saveImg,
      clearData,
      clearSelected,
    })(Home);
    
    //src/utils/mixin.js
    export default methods => {
      return target => {
        Object.assign(target.prototype, methods);
      }
    }
    
    /**
     * 字符串填充函数
     * @param  {string} value      目标字符串
     * @param  {array} position 需要填充的位置
     * @param  {string} padstr   填充字符串
     * @return {string}          返回目标字符串
     */
    export const padStr = (value, position, padstr, inputElement) => {
      position.forEach((item, index) => {
        if (value.length > item + index) {
          value = value.substring(0, item + index) + padstr + value.substring(item + index)
        }
      })
      value = value.trim();
      // 解决安卓部分浏览器插入空格后光标错位问题
      requestAnimationFrame(() => {
        inputElement.setSelectionRange(value.length, value.length); 
      })
      return value;
    }
    
    //帮助中心
    //src/pages/helpcenter/helpcenter.jsx
    import React, { Component } from 'react';
    import PublicHeader from '@/components/header/header'; 
    import { is, fromJS } from 'immutable';
    import './helpcenter.less';
    
    export default class HelpCenter extends Component {
    
      shouldComponentUpdate(nextProps, nextState){
        return !is(fromJS(this.props), fromJS(nextProps)) || !is(fromJS(this.state), fromJS(nextState))
      }
    
      render(){
        return (
          <main>
            <PublicHeader title="帮助中心" record />
            <article className="context-con">
              <h2>介绍</h2>
              <p>本项目主要用于理解 react 和 redux 的编译方式,以及 react + redux 之间的配合方式</p>
              <h2>技术要点</h2>
              <p>react:v16.2</p>
              <p>redux:v3.7</p>
              <p>webpack:v3.8</p>
              <p>react-router:v4.2</p>
              <p>ES 6/7/8</p>
              <p>code split</p>
              <p>hot loader</p>
              <p>axios:v0.17</p>
              <p>less:v2.7</p>
              <p>immutable:v3.8</p>
              <p>项目地址 <a href="https://github.com/bailicangdu/react-pxq">github</a></p>
            </article>
          </main>
        )
      }
    }
    
    //src/pages/production/production.jsx
    import React, { Component } from 'react';
    import { is, fromJS } from 'immutable';
    import { connect } from 'react-redux';
    import { getProData, togSelectPro, editPro } from '@/store/production/action';
    import PropTypes from 'prop-types';
    import PublicHeader from '@/components/header/header';
    import './production.less';
    
    class Production extends Component{
      static propTypes = {
        proData: PropTypes.object.isRequired,
        getProData: PropTypes.func.isRequired,
        togSelectPro: PropTypes.func.isRequired,
        editPro: PropTypes.func.isRequired,
      }
      
      /**
       * 添加或删减商品,交由redux进行数据处理,作为全局变量
       * @param  {int} index 编辑的商品索引
       * @param  {int} num   添加||删减的商品数量
       */
      handleEdit = (index, num) => {
        let currentNum = this.props.proData.dataList[index].selectNum + num;
        if(currentNum < 0){
          return
        }
        this.props.editPro(index, currentNum);
      }
      
      // 选择商品,交由redux进行数据处理,作为全局变量
      togSelect = index => {
        this.props.togSelectPro(index);
      }
    
      shouldComponentUpdate(nextProps, nextState) {
        return !is(fromJS(this.props), fromJS(nextProps)) || !is(fromJS(this.state), fromJS(nextState))
      }
      
      componentDidMount(){
        if(!this.props.proData.dataList.length){
          this.props.getProData();
        }
      }
    
      render(){
        return (
          <main className="common-con-top">
            <PublicHeader title='首页' confirm />
            <section className="pro-list-con">
              <ul className="pro-list-ul">
                {
                  this.props.proData.dataList.map((item, index) => {
                    return <li className="pro-item" key={index}>
                      <div className="pro-item-select" onClick={this.togSelect.bind(this, index)}>
                        <span className={`icon-xuanze1 pro-select-status ${item.selectStatus? 'pro-selected': ''}`}></span>
                        <span className="pro-name">{item.product_name}</span>
                      </div>
                      <div className="pro-item-edit">
                        <span className={`icon-jian ${item.selectNum > 0? 'edit-active':''}`} onClick={this.handleEdit.bind(this, index, -1)}></span>
                        <span className="pro-num">{item.selectNum}</span>
                        <span className={`icon-jia`} onClick={this.handleEdit.bind(this, index, 1)}></span>
                      </div>
                    </li>
                  })
                }
              </ul>
            </section>
          </main>
        )
      }
    }
    
    
    export default connect(state => ({
      proData: state.proData,
    }), {
      getProData, 
      togSelectPro, 
      editPro
    })(Production);
    

    //src/pages/balance/balance.jsx
    import React, { Component } from 'react';
    import { is, fromJS } from 'immutable';
    import PublicHeader from '@/components/header/header';
    import TouchableOpacity from '@/components/TouchableOpacity/TouchableOpacity';
    import PublicAlert from '@/components/alert/alert';
    import API from '@/api/api';
    import './balance.less';
    
    class BrokeRage extends Component{
      state = {
        applyNum: '', //输入值
        alertStatus: false, //弹框状态
        alertTip: '', //弹框提示文字
        balance: {  //可提现金额
          balance: 0, 
        },
      }
      // 初始化数据
      initData = async () => {
        try{
          let result = await API.getBalance();
          console.log(result);
          this.setState({balance: result});
        }catch(err){
          console.error(err);
        }
      }
      
      /**
       * 格式化输入数据
       * 格式为微信红包格式:最大 200.00
       * @param  {object} event 事件对象
       */
      handleInput = event => {
        let value = event.target.value;
        if((/^d*?.?d{0,2}?$/gi).test(value)){
          if((/^0+[1-9]+/).test(value)) {
            value = value.replace(/^0+/,'');
          }
          if((/^0{2}./).test(value)) {
            value = value.replace(/^0+/,'0');
          }
          value = value.replace(/^./gi,'0.');
          if(parseFloat(value) > 200){
            value = '200.00';
          }
          this.setState({applyNum: value});
        }
      }
      
      /**
       * 提交判断条件
       */
      sumitForm = () => {
        let alertTip;
        if(!this.state.applyNum){
          alertTip = '请输入提现金额';
        }else if(parseFloat(this.state.applyNum) > this.state.balance.balance){
          alertTip = '申请提现金额不能大于余额';
        }else{
          alertTip = '申请提现成功';
        }
    
        this.setState({
          alertStatus: true,
          alertTip,
          applyNum: '',
        })
      }
      
      /*
      关闭弹框
       */
      closeAlert = () => {
        this.setState({
          alertStatus: false,
          alertTip: '',
        })
      }
    
      shouldComponentUpdate(nextProps, nextState) {
        return !is(fromJS(this.props), fromJS(nextProps)) || !is(fromJS(this.state),fromJS(nextState))
      }
    
      componentDidMount(){
        this.initData();
      }
    
      render(){
        return (
          <main className="home-container">
            <PublicHeader title='提现' record />
            <section className="broke-main-content">
              <p className="broke-header">您的可提现金额为:¥ {this.state.balance.balance}</p>
              <form className="broke-form">
                <p>请输入提现金额(元)</p>
                <p>¥ <input type="text" value={this.state.applyNum} placeholder="0.00" onInput={this.handleInput} maxLength="5" /></p>
              </form>
              <TouchableOpacity className="submit-btn" clickCallBack={this.sumitForm} text="申请提现" />
            </section>
            <PublicAlert closeAlert={this.closeAlert} alertTip={this.state.alertTip} alertStatus={this.state.alertStatus} />
          </main>
        );
      }
    }
    
    export default BrokeRage;
    

    //src/pages/record/components/recordList.jsx
    import React, { Component } from 'react';
    import { is, fromJS } from 'immutable';
    import API from '@/api/api';
    import './recordList.less';
    
    class RecordList extends Component{
      
      state = {
        recordData: [],
      }
      
      /**
       * 初始化获取数据
       * @param  {string} type 数据类型
       */
      getRecord = async type => {
        try{
          let result = await API.getRecord({type});
          this.setState({recordData: result.data||[]})
        }catch(err){
          console.error(err);
        }
      }
    
      componentWillReceiveProps(nextProps){
        // 判断类型是否重复
        let currenType = this.props.location.pathname.split('/')[2];
        let type = nextProps.location.pathname.split('/')[2];
        if(currenType !== type){
          this.getRecord(type);
        }
      }
    
      shouldComponentUpdate(nextProps, nextState){
        return !is(fromJS(this.props), fromJS(nextProps)) || !is(fromJS(this.state), fromJS(nextState))
      }
    
      componentWillMount(){
        let type = this.props.location.pathname.split('/')[2];
        this.getRecord(type);
      }
    
      render(){
        return (
          <div>
            {/* 这个记录页面与数据的渲染形成整体的组件 */}
            <ul className="record-list-con">
            {
              this.state.recordData.map((item, index) => {
                return <li className="record-item" key={index}>
                  <section className="record-item-header">
                    <span>创建时间:{item.created_at}</span>
                    <span>{item.type_name}</span>
                  </section>
                  <section className="record-item-content">
                    <p><span>用户名:</span>{item.customers_name} &emsp; {item.customers_phone}</p>
                    <p><span>商&emsp;品:</span>{item.product[0].product_name}</p>
                    <p><span>金&emsp;额:</span>{item.sales_money} &emsp; 佣金:{item.commission}</p>
                  </section>
                  <p className="record-item-footer">等待管理员审核,审核通过后,佣金将结算至账户</p>
                </li>
              })
            }
            </ul>
          </div>
        );
      }
    
    }
    
    export default RecordList;
    

    //src/pages/record/record.jsx
    import React, { Component } from 'react';
    import { is, fromJS } from 'immutable';
    import { NavLink, Switch, Route, Redirect } from 'react-router-dom';
    import PublicHeader from '@/components/header/header';
    import RecordList from './components/recordList';
    import './record.less';
    
    class Record extends Component {
      state = {
        flagBarPos: '17%',
      }
      /**
       * 设置头部底部标签位置
       * @param  {string} type 数据类型
       */
      setFlagBarPos = type => {
        let flagBarPos;
        switch(type){
          case 'passed':
            flagBarPos = '17%';
          break;
          case 'audited':
            flagBarPos = '50%';
          break;
          case 'failed':
            flagBarPos = '83%';
          break;
          default: 
            flagBarPos = '17%';
        }
        this.setState({flagBarPos})
      }
    
      componentWillReceiveProps(nextProps){
        // 属性变化时设置头部底部标签位置
        let currenType = this.props.location.pathname.split('/')[2];
        let type = nextProps.location.pathname.split('/')[2];
        if(currenType !== type){
          this.setFlagBarPos(type);
        }
      }
    
      shouldComponentUpdate(nextProps, nextState){
        return !is(fromJS(this.props), fromJS(nextProps)) || !is(fromJS(this.state), fromJS(nextState))
      }
      
      componentWillMount(){
        // 初始化设置头部底部标签位置
        let type = this.props.location.pathname.split('/')[2];
        this.setFlagBarPos(type);
      }
      render() {
        return (
          <main className="common-con-top">
            <PublicHeader title='记录' />
            <section className="record-nav-con">
              <nav className="record-nav">
                <NavLink to={`${this.props.match.path}/passed`} className="nav-link">已通过</NavLink>
                <NavLink to={`${this.props.match.path}/audited`} className="nav-link">待审核</NavLink>
                <NavLink to={`${this.props.match.path}/failed`} className="nav-link">未通过</NavLink>
              </nav>
              <i className="nav-flag-bar" style={{left: this.state.flagBarPos}}></i>
            </section>
            {/* 子路由在父级配置,react-router4新特性,更加灵活 */}
            <Switch>
              <Route path={`${this.props.match.path}/:type`} component={RecordList} />
              <Redirect from={`${this.props.match.path}`} to={`${this.props.match.path}/passed`} exact component={RecordList} />
            </Switch>
          </main>
        );
      }
    }
    
    export default Record;
    
    
  • 相关阅读:
    如何从svn上down项目
    查看当前项目的svn地址
    项目启动失败
    新增sql后面可以跟where条件(多表关联新增数据和复制数据)
    递归思想之---斐波拉契数列
    递归思想之---阶乘算法
    java递归思想之---汉诺塔
    将 Docker 镜像体积减小 转载:https://mp.weixin.qq.com/s/kyK6652kchtudZHhSsYx_Q
    工具 转载 https://mp.weixin.qq.com/s/Y1RHEDu0vuH4qm9QtMISFg
    Kubernetes 学习笔记 权威指南第五&六章
  • 原文地址:https://www.cnblogs.com/smart-girl/p/10905429.html
Copyright © 2020-2023  润新知