mirrorx

1、什么是MirrorX?

MirrorX是基于Redux封装的一种状态机。比如实际使用的时候假设我们要从后台读取值班信息、把接口返回的数据做一些处理(按对象的类型进行分类渲染),然后将处理好的数据显示在界面上。想想就得用很多代码,而且都要放在组件的里
要是有一个框架,组件里只需要调一行代码就能解决,是不是很不错?MirrorX就是用来做这件事的。

2、怎么能做到1行解决?

学过面向对象编程的,应该都知道封装性可以使控制层代码更加简练。但是React里如果要处理刚才说的那件事,既需要state和props控制权,也要知道来来往往的上下文(比如当前用户是谁、VIP等级是多少),最令React程序员难受的是,当前用户信息在UserView组件里,当前的GoodsView组件没有对UserView组件的访问权。因此,需要有一个统一的地方来跨越组件的障碍,存储这些信息,把这个机制封装好了,就可以实现一行解决。

3、具体是怎样的机制?

网上有很多文章讲Redux,看完Redux,再搜索MirroX就可以知道具体的机制。简单的来说,就是系统有若干个状态仓库,我们可以把上下文变量都分门别类放在不同仓库里,比如用户信息、商品信息;放入的时候都是调用动作来实现,比如读取商品信息、更改商品数量。而动作可以选择是否调用服务,如果前台更改商品数量,直接改状态仓库里的数值即可,无需调用服务;如果调用了服务,则一般来说是功能是需要服务器交互的。

 配置默认的路由方式
mirror.defaults({ historyMode: 'hash' });

一、简单的Model层,包含动作和模型

model.js

import { actions } from "mirrorx";
import * as api from "./HttpUtils"; // 把自定义的请求文件HttpUtils.js引入进来,名称定为api

export default {
    name: "GoodsManager", // 这里写的名字将会成为状态仓库的名称
    initialState: { // 这里可以写初始化时状态机里的初始状态
        userId: "0001"
    },
    // reducer:状态机处理函数
    reducers: {
        // 这个updateState是默认的,它用来主动更新状态机里的各种状态
        // state和data都是Object对象
        // state是框架传入的,开发者调用的时候,data才是对应的第一个参数
        // ...是ES6的对象扩展运算符,后面...data会自动覆盖...state的同名属性
        updateState: (state, data) => ({ ...state, ...data })
        // 后面还可以写其他的reducer,切记第一个参数一定是state
    },
    effects: {
        // 动作处理函数:获取商品
        // param是对象,getState是框架传入的函数对象,用来方便获取当前状态机的状态
        // 开发者调用的时候,不用给getState形参赋值
        async GetGoods(param, getState) {
            // Promise的同步操作运算,获取Axios返回的data
            let { data } = await api.GetGoodsApi(param);
            // 调用当前状态机的updateState方法(也就是上面写的那个函数)
            // 由调用可见,只放了一个Object类型的参数
            actions.GoodsManager.updateState({ goods: data.data });
        }
    }
};

实例  

import { actions } from "mirrorx";
import * as authService from "../services/auth";
import Toolkit from "../utils/Toolkit";
import set from "lodash/set";
import "antd-mobile/lib/toast/style/css";
import Toast from "antd-mobile/lib/toast";
import 'antd-mobile/lib/modal/style/css';
import Modal from 'antd-mobile/lib/modal';


const alert = Modal.alert;
const defaultState = {
  hddngrTypeList: [],
  msgTypeList: [],
  hiddenTrouble: null,
  isShowNextStep: false,
  isShowSelectPerson: false,
  isShowMeetHouse: false,
  getFlowCnfg: null,
  NextLink: null,
  naturalDisastersList: [],
};

export default {
  name: "report",
  initialState: defaultState,
  reducers: {
    change: (state, data) => {
      return { ...state, ...data };
    },
    reset: (state, data) => {
      return defaultState;
    },
  },
  effects: {
    // 获取编号
    async getHddngrcodes(params, getState) {
      let request = {
        project: "ccep",
        model: "model:com.chinacreator.flwmgr.base.noRule.noRule",
        action: "getBusicode",
        rtype: "e ",
        data: params,
      };
      return authService
        .postPeople(request)
        .then((data) => {
          if (data.status === "1") {
            if (data.response.flag) {
              return data.response.value;
            }
          }
        })
        .catch((err) => {
          return { err };
        });
    },
    // 获取隐患列表
    async getNaturalDisastersList2(params, getState) {
      const loading = Toast.loading("加载中...", 30);

      let request = {
        project: "ccep",
        model:
          "model:com.chinacreator.xtyjoa.emergency.hddngrInfo.model.hddngrInfo",
        action: "hddngrInfoQuery",
        rtype: "e ",
        data: params,
      };
      let { naturalDisastersList } = getState().report;

      authService
        .postPeople(request)
        .then((data) => {
          Toast.hide(loading);
          if (data.status === "1") {
            let list = [];
            if (data.response.datas) {
              list = naturalDisastersList;
              if (params.pageIndex === 1) {
                list = data.response.datas;
              }
              if (data.response.totalSize <= list.length) {
                if (params.pageIndex !== 1) {
                  Toolkit.handleError({ message: "滑动到底" });
                }
              } else {
                if (params.pageIndex !== 1) {
                  data.response.datas.map((item, key) => {
                    list.push(item);
                  });
                }
              }
              actions.report.change({
                naturalDisastersList: list,
              });
            }
          }
        })
        .catch((err) => {
          Toolkit.handleError(err);
        });
    },
    // 获取自然灾害列表
    async getNaturalDisastersList(params, getState) {
        const loading = Toast.loading("加载中...", 30);
  
        let request = {
          project: "ccep",
          model:
            "model:com.chinacreator.xtyjoa.emergency.hddngrInfo.model.hddngrInfo",
          action: "hddngrInfoQuery",
          rtype: "e ",
          data: params,
        };
        let { naturalDisastersList } = getState().report;
  
        authService
          .postPeople(request)
          .then((data) => {
            Toast.hide(loading);
            let totalSize = data.response.totalSize
            if (data.status === "1") {
              let list = [];
              if (data.response.datas) {
                list = naturalDisastersList;
                if (params.pageIndex === 1) {
                  list = data.response.datas;
                }
                if (data.response.totalSize <= list.length) {
                  if (params.pageIndex !== 1) {
                    Toolkit.handleError({ message: "滑动到底" });
                  }
                } else {
                  if (params.pageIndex !== 1) {
                    data.response.datas.map((item, key) => {
                      list.push(item);
                    });
                  }
                }
                actions.report.change({
                  naturalDisastersList: list,
                });
              }else {
                if(totalSize === 0 ){
                  actions.report.change({
                    naturalDisastersList: [],
                  });
                }
              }
            }
          })
          .catch((err) => {
            Toolkit.handleError(err);
          });
      },
    /**
     * 隐患保存
     * @returns {Promise.<void>}
     */
    async saveHddngrData2(params, getState) {
      let savelist = {
        project: "ccep",
        model:
          "model:com.chinacreator.xtyjoa.emergency.hddngrInfo.model.hddngrInfo",
        action: params.apply_id ? "modify" : "add",
        rtype: "o",
        data: params,
      };
      authService
        .postPeople(savelist)
        .then((data) => {
          const { model } = getState().report;
          if (data.status === "1") {
            Toolkit.message("保存成功");
            set(model, "apply_id", data.response.apply_id);
            actions.report.change({
              hiddenTrouble: data.response,
              model,
            });
          }
        })
        .catch((err) => {
          Toolkit.handleError(err);
        });
    },
       /**
     * 自然灾害保存
     * @returns {Promise.<void>}
     */
    async saveHddngrData(params, getState) {
        let savelist = {
          project: "ccep",
          model:
            "model:com.chinacreator.xtyjoa.emergency.hddngrInfo.model.hddngrInfo",
          action: params.modify ? "modify" : "add",
          rtype: "e",
          data: params,
        };
        authService
          .postPeople(savelist)
          .then((data) => {
            const { model } = getState().report;
            if (data.status === "1") {
              // Toolkit.message("保存成功");
              set(model, "apply_id", data.response.apply_id);
              actions.report.change({
                hiddenTrouble: data.response,
                model,
              });
              alert('提示', `保存成功!`, [
                {
                    text: '确定', onPress: () => {
                     Toolkit.routeBack()
                    }
                },
            ]);
            }
          })
          .catch((err) => {
            Toolkit.handleError(err);
          });
      },
       /**
     * 隐患删除
     * @returns {Promise.<void>}
     */
    async deleteHddngrData(params, getState) {
        let savelist = {
          project: "ccep",
          model:"model:com.chinacreator.xtyjoa.emergency.hddngrInfo.model.hddngrInfo",
          action: "remove",
          rtype: "e",
          data: params,
        };
        authService
          .postPeople(savelist)
          .then((data) => {
            const { model } = getState().report;
            if (data.status === "1") {
              Toolkit.message("删除成功");
            }
          })
          .catch((err) => {
            Toolkit.handleError(err);
          });
      },
    /**
     * 隐患 送下一环节配置数据
     * @returns {Promise.<void>}
     */
    async getFlowStartConfig(params, getState) {
      authService
        .getFlowStartConfig(params)
        .then((data) => {
          if (data.success) {
            if (data.response != {}) {
              actions.report.getFlowNextLink({ flowId: data.response.flow_id });
              // actions.report.dict()
              actions.report.getFlowCnfgs(data.response.flow_id);
            }
          }
        })
        .catch((err) => {
          Toolkit.handleError(err);
        });
    },
    /**
     * 查询流程下一环节(下一环节及处理人)
     * @returns {Promise.<void>}
     */
    async getFlowNextLink(params, getState) {
      authService
        .getFlowNextLink(params)
        .then((data) => {
          actions.report.change({
            NextLink: data.output,
          });
        })
        .catch((err) => {
          Toolkit.handleError(err);
        });
    },
    /**
     * getFlowCnfg
     * @returns {Promise.<void>}
     */
    async getFlowCnfgs(params, getState) {
      authService
        .getFlowCnfg(params)
        .then((data) => {
          if (data.status === 1) {
            actions.report.change({
              isShowNextStep: true,
              getFlowCnfg: data.output,
            });
          }
        })
        .catch((err) => {
          Toolkit.handleError(err);
        });
    },
    /**
     * 送下一环节提交
     * @returns {Promise.<void>}
     */
    async startFlowInst(params, getState) {
      authService
        .startFlowInst(params)
        .then((data) => {
          if (data.status === 1) {
            Toolkit.message("送下一环节成功");
            actions.report.change({
              isShowNextStep: false,
              hiddenTrouble: null,
              NextLink: null,
            });
            Toolkit.routeBack();
          }
        })
        .catch((err) => {
          Toolkit.handleError(err);
        });
    },
  },
};
model实例

 二、改造组件,变成由MirrorX托管组件

第一步、在项目入口的地方添加(比如在app.js上添加在内存中创建状态机的代码):

// 引入MirrorX的组件
import mirror from 'mirrorx';
// 引入刚刚写的model,注意路径
import model from './model';
// 调用MirrorX,根据模型创建状态机
mirror.model(model);

实例

import 'antd-mobile/lib/modal/style/css';
import './index.css'
import './assets/iconfont/iconfont.css';
import './react-block-ui.css'
import React from 'react'
import mirror, { render, Router, actions } from 'mirrorx'
import EventEmitter from 'eventemitter3'
import VConsole from 'vconsole'

import App from './App'
import registerServiceWorker from './registerServiceWorker'

import application from './models/application'
import auth from './models/auth'
import applyMeeting from './models/applyMeeting'
import applyCar from './models/applyCar'
import applyBussiTrip from './models/applyBussiTrip'
import applyAnnualLeave from './models/applyAnnualLeave'
import applyLeave from './models/applyLeave'
import capitalSpending from './models/capitalSpending'
import report from './models/report'
import nextLink from './models/nextlink'
// 配置默认的路由方式
mirror.defaults({ historyMode: 'hash' });
// 加载模型
mirror.model(application);
mirror.model(auth);
mirror.model(applyMeeting);
mirror.model(capitalSpending);
mirror.model(applyCar);
mirror.model(applyBussiTrip);
mirror.model(applyAnnualLeave);
mirror.model(applyLeave);
mirror.model(report);
mirror.model(nextLink);
// 监听action 
mirror.hook((action) => {
    const platformId = (window.cordova && window.cordova.platformId) ? window.cordova.platformId : 'windows';
    if (platformId === 'android') {
        console.log('调用方法:' + action.type);
    } else {
        console.log('%c调用方法:%s', 'color: #FF9800;font-family:source code pro;', action.type);
    }
    if (action.data) {
        if (platformId === 'android') {
            console.log('更新数据:');
        } else {
            console.log('%c更新数据:', 'color: #9C27B0;font-family:source code pro;');
        }
        console.log(action.data);
    }
    if (action.type === '@@router/LOCATION_CHANGE' && action.payload.pathname !== '/login') {
        actions.auth.change({ from: action.payload.pathname });
    }
});
// 初始化事件总线
const emitter = window.emitter = new EventEmitter();
emitter.on('RENDER', function (params) {
    if (params && params.vconsole) new VConsole();
    // 渲染到DOM节点
    render(<Router><App /></Router>, document.getElementById('root'));
});
registerServiceWorker();
index.js

第二步、在受状态机托管的组件上改一下代码

// 增加对MirrorX的引用
import {connect} from 'mirrorx';
// 这里面GoodsView就是当前受状态机托管组件的class名称,GoodsManager就是第二步里name写的名字
export default connect(state => state.GoodsManager)(GoodsView);

实例

import '../../../common/FormStyle.scss';
import '../ApplyMeal/ApplyMeal.scss';
import React, { Component } from 'react';
import { connect, actions, model } from 'mirrorx'
import loadable from 'common/Loadables';
import { Flex, Box } from 'reflexbox';
import set from 'lodash/set';
import get from 'lodash/get';
import map from 'lodash/map';

import 'antd-mobile/lib/button/style/css';

import Toolkit from "../../../utils/Toolkit";
import moment from 'moment';

import 'antd-mobile/lib/modal/style/css';
import Modal from 'antd-mobile/lib/modal';
// title组件
import TitleComp from '../ApplyCommon/TitleComp';
// 输入框
import TextComp from '../ApplyCommon/TextComp';
// 底部按钮
import BottomButton from '../ApplyCommon/BottomButton';
// 弹出窗-下一环节
import NextStep from '../ApplyCommon/NextStep';


const alert = Modal.alert;

const NavBars = loadable(() => import('components/NavBar/index'));


class ApplyAnnualLeave extends Component {
    constructor() {
        super();
        const userInfo = Toolkit.userInfo();
        this.state = {
            model: {
                apply_no: '',
                creator_account: userInfo.user_account,
                creator_name: userInfo.user_name,
                org_code: userInfo.org_code,
                create_departname: userInfo.oorg_name,
                apply_date: moment().format('YYYY-MM-DD'), //申请日期
                join_date: '',
                work_years: '',
                cleave_days: '',
                aleave_days: 0,
                nleave_days: '',
                leave_days: '',
                start_date: '',
                end_date: '',
                is_lateleave: ['0'],
                apply_reason: '',
                apply_title:`${userInfo.user_name}同志${moment(new Date()).format('YYYY年MM月份')}年假审批表`
            },
            web: true,
            node: false,
            nextStepUser: [] //下一环节人
        }
    }

    clearData = () => {
        actions.applyAnnualLeave.change({
            isShowNextStep: false
        })

    }

    getText = (str) => {
        switch(str){
            case '0': return '草稿';
            case '1': return '进行';
        }
    }

    async queryAleaveDays(params) {
        const {model} = this.state;
        Toolkit.callAction({
            model: 'model:com.chinacreator.xtyjoa.vacation.model.vacationApplication',
            action: 'queryAleaveDays',
            data: params,  
        }).then((res) => {
            if(res.status === '1'){
                let forbidArr = res.response.filter(item=>{
                    return (item.operaState === '0' && item.days > 0) || item.operaState === '1' && item.days > 0
                })
                if(forbidArr.length > 0){
                    alert('提示', `您当前存在${this.getText(forbidArr[0].operaState)}中的休假申请,请处理完再重新申请!`, [
                        {
                            text: '确定', onPress: () => {
                             Toolkit.routeBack()
                            }
                        },
                    ]);
                }else {
                    let arr = res.response.filter(item=>{
                        return item.operaState === '2'
                    })
                    this.setState({
                        model:{
                            ...model,
                            aleave_days:arr[0].days
                        }
                    })
                }

            }
        }).catch((err) => {
            return err
        });
    }

    async componentDidMount() {
        const userInfo = Toolkit.userInfo()
        this.queryAleaveDays({
            creator_account:userInfo.user_account
        })
        this.clearData()
        actions.auth.change({
            tabTitle: '年假申请'
        })
        // 用车类型
        const carTypeList = await Toolkit.requestDict('dataenty:dataitem_root.driveType');
        map(carTypeList, item => {
            return item.label = item.text
        })
        actions.applyAnnualLeave.change({ carTypeList });
    }

    componentWillUnmount() {
        actions.applyAnnualLeave.reset();
    }

    save = async () => {
        const { model } = this.state;

        // 表单验证
        if(!model.join_date){
            alert('提示', `请选择参加工作时间!`, [{text: '确定', onPress: () => {}}]);
            return
        }
        if(!model.leave_days){
            alert('提示', `请填写拟休天数!`, [{text: '确定', onPress: () => {}}]);
            return
        }
        if(!model.start_date){
            alert('提示', `请选择拟休开始时间!`, [{text: '确定', onPress: () => {}}]);
            return
        }
        if(!model.end_date){
            alert('提示', `请选择拟休结束时间!`, [{text: '确定', onPress: () => {}}]);
            return
        }
        if(!model.apply_reason){
            alert('提示', `请填写申请原因!`, [{text: '确定', onPress: () => {}}]);
            return
        }

        if(moment(model.start_date).diff(moment(model.end_date), 'seconds') > 0){
            alert('提示', `结束时间不能小于开始时间!`, [{text: '确定', onPress: () => {}}]);
            return
        }
        const {annualLeave} = this.props.applyAnnualLeave

        const userInfo = Toolkit.userInfo();
        let params = {
            is_lateleave:model.is_lateleave[0],
            apply_type: '1',
            creator_name:model.creator_name,
            create_departname:model.create_departname,
            work_years:model.work_years,
            cleave_days:model.cleave_days,
            aleave_days:model.aleave_days,
            nleave_days:model.nleave_days,
            leave_days:model.leave_days,
            join_date:moment(model.join_date).format('YYYY-MM-DD'),
            start_date:moment(model.start_date).format('YYYY-MM-DD'),
            end_date:moment(model.end_date).format('YYYY-MM-DD'),
            apply_no: model.apply_no, 
            apply_reason:model.apply_reason,
            apply_date: moment().format('YYYY-MM-DD'), //申请日期
            apply_title:`${userInfo.user_name}同志${moment(new Date()).format('YYYY年MM月份')}年假审批表`,
            apply_id: annualLeave&&annualLeave.apply_id,

        }

        if(!annualLeave){
            //生成编号  
            const busiParams = {
                no_rule_code: 'a_l_apply_no',
                params: {
                    year: moment().format('YYYY'),
                    month: moment().format('MM'),
                    org_code: userInfo.org_code
                }
            }
            const busicodes = await actions.applyAnnualLeave.getAnnualcodes(busiParams);

            if (busicodes.err) return Toolkit.handleError(busicodes.err);
            set(model, 'apply_no', busicodes);
            actions.applyAnnualLeave.change({ model })
            params = {
                ...params,
                apply_no: busicodes
            }
        }

       
        actions.applyAnnualLeave.saveAnnualData(params)
    }

    sendNextStep = () => {
        const userInfo = Toolkit.userInfo();
        actions.applyAnnualLeave.getFlowStartConfig({ 'flow_class_code': '0607', 'org_code': `${userInfo.org_code}` })
    }

    componentWillReceiveProps(newProps, newContext) {
        if (newProps.applyAnnualLeave.NextLink != this.props.applyAnnualLeave.NextLink) {
            let next_link = []
            let next_handler = []
            newProps.applyAnnualLeave.NextLink ? newProps.applyAnnualLeave.NextLink.next_link ? newProps.applyAnnualLeave.NextLink.next_link.map((item, key) => {
                next_link.push({
                    value: item.link_id,
                    label: item.link_name,
                    id: item.link_code,
                    key: key
                })

                newProps.applyAnnualLeave.NextLink.next_handler[item.link_id].map((item, key) => {
                    next_handler.push({
                        value: item.user_account,
                        label: item.user_name,
                        id: item.user_account,
                        key: key
                    })
                })
            }) : '' : ''

            this.setState({
                next_link_value: [next_link[0].value],
                next_link: next_link,
                next_handler: next_handler,
                nextStepUser: [
                    { value: next_handler[0].value, label: next_handler[0].label }
                ]
            })
        }
    }

    endSave = () => {
        const { getFlowCnfg, annualLeave } = this.props.applyAnnualLeave;
        const { nextStepUser, next_link_value, note, web, model } = this.state;

        let transferChannels = "";
        if (note && web) {
            transferChannels = "note,web"
        } else if (note && !web) {
            transferChannels = "note"
        } else if (!note && web) {
            transferChannels = "web"
        }
        let data = {
            messageData: {
                messageContent: JSON.stringify({
                    content: "接收到一条新的请假审批,请及时处理!",
                    action: "open_menu",
                    params: {
                        text: "请假审批",
                        url: "com.chinacreator.xtyjoa.vacation.forms.vacationAppr.html"
                    }
                }),
                transferChannels: transferChannels
            },
            flowData: {
                flowId: getFlowCnfg.flow_id,
                flowCode: getFlowCnfg.flow_code,
                flowClass: getFlowCnfg.flow_class_code.flow_class_code,
                flowClassSname: getFlowCnfg.flow_class_code.flow_class_shortname,
                flowInstName: `${annualLeave.creator_name}同志年假申请表`,
                businessId: `${annualLeave.apply_id}`,
                businessClass: "ApproveExecuteServiceImpl",
                businessParam: JSON.stringify({
                    election_date_time: annualLeave.election_date_time,
                    creator_name: annualLeave.creator_name,
                    apply_reason: annualLeave.apply_reason,
                    apply_id: annualLeave.apply_id,
                    flow_id: getFlowCnfg.flow_id,
                    flow_code: getFlowCnfg.flow_code,
                    flow_name: getFlowCnfg.flow_name,
                    flow_class_code: getFlowCnfg.flow_class_code.flow_class_code,
                    opera_state: '1'
                })
            },
            approveData: {
                nextStepLink: next_link_value[0],
                nextStepUser: [{
                    userAccount: nextStepUser[0].value,
                    userName: nextStepUser[0].label
                }]
            }
        }
        actions.auth.startFlowInst(data)
    }

    onChangeCheckboxItem = (v) => {
        const { web, note } = this.state;
        switch (v) {
            case 'web':
                this.setState({
                    web: !web
                })
                break;
            case 'note':
                this.setState({
                    note: !note
                })
                break;
            default:
                break

        }
    }
    hideModel = () => {
        actions.applyAnnualLeave.change({
            isShowNextStep: false,
        })
    }

    funCallBack = () => {
        this.clearData()
        Toolkit.routeBack()
    }

    // 计算休假天数
    calculateDays = (year) => {

        if(year>=1 && year<10){
            return 5;
        }else  if(year>=10 && year<20){
            return 10;
        }else  if(year>=20){
            return 15;
        }else {
            return 0
        }
    }

    // 改变modeldata
    changeModelData = (v, name) => {
        const { model } = this.state;
        set(model, [name], v)
        this.setState({ model },()=>{
            if(moment(model.start_date).diff(moment(model.end_date), 'seconds') > 0){
                alert('提示', `结束时间不能小于开始时间!`, [{text: '确定', onPress: () => {}}]);
                return
            }
        })

        if (name === "join_date") {
        //计算工作年限相关
        let work_years = moment(new Date()).diff(moment(v), "years");
        this.setState({
            model: {
            ...model,
            work_years, //工作年限
            cleave_days:this.calculateDays(work_years) ,//可休
            nleave_days:work_years > 0 ?  this.calculateDays(work_years) - model.aleave_days : 0,//未休
            },
        });
        }
        
    }
    // 改变state
    changeState = (name, v) => {
        this.setState({
            [name]: v
        })
    }
    render() {

        const {
            model: {
                apply_no, creator_name, create_departname,
                join_date, work_years, cleave_days, aleave_days,
                nleave_days, leave_days, start_date, end_date,
                is_lateleave, apply_reason
            }, next_link, next_link_value, next_handler,
             note, web,nextStepUser
        } = this.state;
        const {
            isShowNextStep, annualLeave,
        } = this.props.applyAnnualLeave;

        const isBuxiuList = [
            { value: '0', label: '' },
            { value: '1', label: '' }
        ]
        const renderData = [
            { title: '编号', type: 'readOnly', value: apply_no, name: 'apply_no' },
            { title: '姓名', type: 'readOnly', value: creator_name, name: 'creator_name' },
            { title: '科室(部门)', type: 'readOnly', value: create_departname, name: 'create_departname' },
            { title: '参加工作时间', type: 'datetime', value: join_date, name: 'join_date', mode: 'date' , required:true},
            { title: '工作年限', type: 'readOnly', value: work_years, name: 'work_years' },
            { title: '可休天数', type: 'readOnly', value: cleave_days, name: 'cleave_days' },
            { title: '已休天数', type: 'readOnly', value: aleave_days, name: 'aleave_days' },
            { title: '未休天数', type: 'readOnly', value: nleave_days, name: 'nleave_days' },
            { title: '拟休天数', type: 'input', value: leave_days, name: 'leave_days', required:true },
            { title: '拟休开始时间', type: 'datetime', value: start_date, name: 'start_date',  mode: 'date' , required:true },
            { title: '拟休结束时间', type: 'datetime', value: end_date, name: 'end_date', mode: 'date', required:true },
            { title: '是否补休', type: 'select', value: is_lateleave, name: 'is_lateleave', configs: { options: isBuxiuList }, required:true },
            { title: '申请原因', type: 'textarea', value: apply_reason, name: 'apply_reason', required:true },
        ];
        return (
            <div className={'pageMain relative FormStyle ApplyMeal'}>
                <NavBars />

                <div className={"ApplyMealBox"}>
                    <Flex>
                        <TitleComp
                            // w={2 / 8}
                            data={renderData}
                        />
                        <TextComp
                            // w={6 / 8}
                            data={renderData}
                            change={(v, name) => this.changeModelData(v, name)}
                        />
                    </Flex>
                </div>

                <NextStep
                    isShow={isShowNextStep}
                    next_link={next_link}
                    next_link_value={next_link_value}
                    next_handler={next_handler}
                    nextStepUser={nextStepUser}
                    note={note}
                    web={web}
                    onChangeCheckboxItem={this.onChangeCheckboxItem}
                    hideModel={this.hideModel}
                    endSave={this.endSave}
                    changeState={this.changeState}
                />

                <BottomButton
                    isShow={!isShowNextStep}
                    isAdd={!get(annualLeave, 'apply_id')}
                    save={this.save}
                    sendNextStep={this.sendNextStep}
                    funCallBack={this.funCallBack}
                />
            </div>
        )
    }
}

function dispatch(state) {
    return {
        applyAnnualLeave: state.applyAnnualLeave
    }
}

export default connect(dispatch)(ApplyAnnualLeave);
ApplyAnnualLeave.js

三、在需要调用的地方写下如下代码:

// 引入MirrorX的组件
import mirror from 'mirrorx';
// 引入刚刚写的model,注意路径
import model from './model';
// 调用MirrorX,根据模型创建状态机
mirror.model(model);

这里因为GoodsView只要一加载就需要显示商品列表,因此,我们可以把代码写在constructor(props)函数里:

四、补充说明

  1. 状态机里所有的状态值都会被自动写在托管组件的props里,当发生变化时,也是可以从props里取出来,因此不要尝试去获取或更新组件的state。
  2. 只要状态机里的值变化,受到托管的组件会重新执行render方法,实现自动刷新。
  3. 实际开发时由于存在组件嵌套、组件元素属性值与状态机里的状态名称冲突,各种疑难杂症随之而来。介于我对于Antd、UCF等成熟框架的分析和实战,得出一个结论,大型系统的model.js、service.js一般不会超过5个,而且大多都有命名规范。
  4. 如果是看别人的代码,倒着按顺序找一遍即可,从此前端大神的代码不再难懂
原文地址:https://www.cnblogs.com/it-Ren/p/13660880.html