import * as React from 'react'; import {BasicConfig, BasicContainer, BasicContainerPropsInterface} from '../Container/types'; import {CallbackController, callbackItem} from './CallbackController'; import {compileExpressionString, isExpression, parseExpressionString} from '../../util/vm'; import {connect} from 'react-redux'; import {RootState} from '../../data/reducers'; import {bindActionCreators, Dispatch} from 'redux'; import {actionCreators, ITriggerAction, TRIGGER_SET_DATA_PAYLOAD} from './actions'; import {DataCustomer} from '../DataCustomer/Controller'; import {parseExpressString} from '../../parser/index'; export class TriggerEventItem { /** * 事件名称 */ event: string; /** * 指定的目标DataCustomer */ targetCustomer: string | string[]; /** * 传递给目标的参数 */ params: (string | number | boolean)[]; /** * 调试模式 */ debug: boolean; /** * 事件触发的条件 */ condition?: string; } export class TriggerPropsInterface extends BasicContainerPropsInterface { info: BasicConfig; /** * 当前Container的数据模型对象 */ $data: Object; /** * 通过表格组件, 渲染之后, 获取到的每一行的数据 */ $item?: Object; /** * 通过表格组件, 渲染之后, 获取到的第几行 */ $index?: number; /** * React组件Key */ key: string | number; /** * 底层组件设置数据模型值使用 */ $setData: (name: string, value: any) => void; /** * 父级的Container组件的model值 */ model: string; /** * 父级Container组件的dataCustomer */ dataCustomer: DataCustomer; } interface TriggerProps extends TriggerPropsInterface { /** * 当前trigger的数据模型 */ $trigger: {}; /** * 设置触发器的store缓存 */ triggerSetData: typeof actionCreators.triggerSetData; } class Trigger extends BasicContainer { private callbackController: CallbackController; private taskQueue: any[]; private isRunningTaskQueue: boolean; constructor(props: TriggerProps) { super(props); this.callbackController = new CallbackController(); this.taskQueue = []; this.eventHandle = this.eventHandle.bind(this); this.isRunningTaskQueue = false; } componentWillMount() { let trigger = this.props.info.trigger; if (trigger instanceof Array) { trigger.forEach(tr => { let event = tr.event; let customerList: string | string[] = tr.targetCustomer; let params = tr.params; let condition = tr.condition; let debug = tr.debug; if (typeof customerList === 'string') { customerList = [customerList]; } if (!customerList) { return; } customerList.forEach(targetCustomer => { this.callbackController.registerCallback(event, targetCustomer, params, debug, condition); }); }); } } async componentWillReceiveProps(nextProps: TriggerProps) { if (this.props.$trigger !== nextProps.$trigger && this.taskQueue.length > 0 && !this.isRunningTaskQueue) { let prev = null; this.isRunningTaskQueue = true; for (let task of this.taskQueue) { let runTime = this.getRuntimeContext(nextProps, this.context); runTime.$prev = prev; try { prev = await this.props.dataCustomer.execCustomer({ customer: task, runTime, model: this.props.model, prev, props: this.props, context: this.context }); if (prev === false) { break; } } catch (e) { break; } } this.isRunningTaskQueue = false; this.taskQueue = []; } } render() { let info = this.props.info; if (!this.isUnderContainerEnv()) { console.error('trigger属性只能在container组件内部使用'); return this.props.children; } let children = React.Children.map(this.props.children, (child: React.ReactElement) => { return React.cloneElement(child, { info: info, $data: this.props.$data, $parent: this.props.$parent, $trigger: this.props.$trigger, $setData: this.props.$setData, dataCustomer: this.props.dataCustomer, eventHandle: this.eventHandle, model: this.props.model }); }); return ( children ); } private async eventHandle(eventName: string, args: Object) { let isExist = this.callbackController.hasCallback(eventName); if (!isExist) { return; } let callbackInfo = this.callbackController.getCallbackInfo(eventName); if (!(callbackInfo instanceof Array)) { callbackInfo = [callbackInfo]; } await this.execCallbackInfo(callbackInfo, args); } private async execCallbackInfo(callbackInfo: callbackItem[], args: Object = {}) { callbackInfo = callbackInfo.filter(info => { if (!info.targetCustomer) { console.error('you should provider an targetCustomer props'); return false; } return true; }); let items: TRIGGER_SET_DATA_PAYLOAD[] = []; for (let info of callbackInfo) { let params = info.params || {}; let debug = info.debug; let runTime = this.getRuntimeContext(this.props, this.context); let context = { ...runTime, $args: args }; let output: any; if (typeof params === 'string' && isExpression(params)) { output = parseExpressionString(params, context); } else { output = compileExpressionString(params, context, [], true); } let condition: boolean = true; if (typeof info.condition === 'string' && isExpression(info.condition)) { condition = !!parseExpressionString(info.condition, context); } if (debug) { console.log(`---------- TRIGGER DEBUG START ---------`); console.log('event: ' + info.event); console.log('targetCustomer: ' + info.targetCustomer); console.log('params: ', info.params); console.log('condition: ', info.condition, 'result: ', condition); console.log('$args: ', args); console.log('---------- TRIGGER DEBUG END -----------'); } if (!condition) { continue; } let customerGroups = this.props.dataCustomer.getGroups(); let customerList: string | string[] = info.targetCustomer; if (typeof customerList === 'string') { customerList = [customerList]; } customerList.forEach(targetCustomer => { // $this 自动指向内置的$SELF_PASS_CUSTOMER if (targetCustomer === '$this') { targetCustomer = '$SELF_PASS_CUSTOMER'; } if (isExpression(targetCustomer)) { targetCustomer = parseExpressString(targetCustomer, runTime); } if (customerGroups.has(targetCustomer)) { let customers = customerGroups.get(targetCustomer); customers.forEach(customer => { this.taskQueue.push(customer); items.push({ model: this.props.model, customer: customer, data: output }); }); } else { this.taskQueue.push(targetCustomer); items.push({ model: this.props.model, customer: targetCustomer, data: output }); } }); } this.props.triggerSetData(items); } } const mapStateToProps = (state: RootState, ownProps: TriggerPropsInterface) => { return { $trigger: state.trigger[ownProps.model] || {} }; }; const mapDispatchToProps = (dispatch: Dispatch) => bindActionCreators({ triggerSetData: actionCreators.triggerSetData, }, dispatch); export default connect(mapStateToProps, mapDispatchToProps)(Trigger);