/* eslint-disable @typescript-eslint/no-this-alias */ import { IEvent, IField, IFieldParams, IFieldProps, IFieldStatus, IFieldUI, IForm, IListener, IListenerSet, IOptionManager, IOptionManagerOptionsArray, IRule, IRuleStatus, IRuleTrigger, IValidateResult, IValidator, IValidatorFunc } from '@wowpic/xform-types'; import EventEmitter from 'events'; import OptionManager from './optionManager'; import { clone, computeRules2UI, computeUI2Rules, filterByKeys, isEqual, isUndefined } from './utils'; import Validator from './validator'; export default class Field implements IField { public key: string; public type: any; public status: IFieldStatus; public ui: IFieldUI; public props: IFieldProps; public rules: IRule[]; public initialType: any; public initialStatus: IFieldStatus; public initialUI: IFieldUI; public initialProps: IFieldProps; public listeners: IListener[]; public dirty: boolean; public pristine: boolean; public value: any; public initialValue: any; public valid: boolean; public message: string; public validStatus: IRuleStatus; public options: IOptionManagerOptionsArray; public context: IForm; public optionManager: IOptionManager; private validator: IValidator; private timerMap: { [event: string]: any }; private emitter: EventEmitter; constructor(context: IForm, params: IFieldParams) { this.context = context; this.initialize(params); } public async initialize(params: IFieldParams) { this.dirty = false; this.pristine = true; this.key = params.key; this.type = params.type || 'Input'; this.initialType = params.type || 'Input'; this.status = params.status || 'edit'; this.ui = computeRules2UI({ rules: params.rules, ui: params.ui }) || {}; this.props = clone(params.props) || {}; this.initialStatus = params.status || 'edit'; this.initialUI = clone(params.ui) || {}; this.initialProps = clone(params.props) || {}; this.rules = computeUI2Rules({ rules: params.rules, ui: params.ui }) || []; this.listeners = clone(params.listeners) || []; this.value = isUndefined(params.value) ? null : clone(params.value); this.initialValue = isUndefined(params.value) ? null : clone(params.value); this.valid = true; this.message = ''; this.validStatus = 'normal'; this.emitter = new EventEmitter(); this.emitter.setMaxListeners(1000); this.timerMap = {}; this.optionManager = new OptionManager(this, { options: clone(params.options) || [] }); this.validator = new Validator(this, { rules: this.rules }); await this.validate('change'); } public async validate(trigger: IRuleTrigger = 'submit') { const { valid, message, status } = await this.validator.validate( trigger ); if ( this.valid !== valid || this.message !== message || this.validStatus !== status ) { this.triggerEvent('VALID_CHANGE'); } this.valid = valid; this.message = message; this.validStatus = status; this.emit('VALIDATE', trigger, this.value); return { valid, message, status }; } public async set(state: IListenerSet) { if (state.status) { this.setStatus(state.status); } if (state.ui) { this.setUI(state.ui); } if (state.props) { this.setProps(state.props); } if (state.options) { this.setOptions(state.options); } if (state.type) { this.setType(state.type); } if (!isUndefined(state.value)) { await this.setValue(state.value); } } public async unset(state: IListenerSet) { if (state.status) { this.setStatus(this.initialStatus); } if (state.ui) { this.setUI(filterByKeys(this.initialUI, Object.keys(state.ui))); } if (state.props) { this.setProps( filterByKeys(this.initialProps, Object.keys(state.props)) ); } if (state.type) { this.setType(this.initialType); } if (state.options) { this.optionManager.reset(); } if (!isUndefined(state.value)) { await this.setValue(this.initialValue); } } public purify() { return { dirty: this.dirty, pristine: this.pristine, key: this.key, type: this.type, initialType: this.initialType, status: this.status, initialStatus: this.initialStatus, ui: clone(this.ui), initialUI: clone(this.initialUI), props: clone(this.props), initialProps: clone(this.initialProps), value: clone(this.value), initialValue: clone(this.initialValue), options: clone(this.options), message: this.message, valid: this.valid, validStatus: this.validStatus }; } public setOptions(options: IOptionManagerOptionsArray) { if (!isEqual(this.options, options)) { this.options = clone(options); this.triggerEvent('OPTIONS_CHANGE'); } } public setValid(result: IValidateResult) { const { valid, message, status } = result; if ( this.valid !== valid || this.message !== message || this.validStatus !== status ) { this.triggerEvent('VALID_CHANGE'); } this.valid = valid; this.message = message; this.validStatus = status; } public setStatus(status: IFieldStatus) { if (!isEqual(this.status, status)) { this.status = status; this.triggerEvent('FIELD_CHANGE'); } } public setUI(ui: IFieldUI) { const filterUI = filterByKeys(this.ui, Object.keys(ui)); if (!isEqual(filterUI, ui)) { Object.assign(this.ui, ui); this.triggerEvent('FIELD_CHANGE'); } } public setProps(props: IFieldProps) { const filterProps = filterByKeys(this.props, Object.keys(props)); if (!isEqual(filterProps, props)) { Object.assign(this.props, props); this.triggerEvent('FIELD_CHANGE'); } } public async setValue(value: any) { this.pristine = false; if (!isEqual(this.value, value)) { this.value = value; this.dirty = !isEqual(this.value, this.initialValue); this.triggerEvent('VALUE_CHANGE'); await this.validate('change'); } } public setType(type: any) { if (this.type !== type) { this.type = type; this.triggerEvent('FIELD_CHANGE'); } } public getValue() { return this.value; } public async reset() { await this.setValue(this.initialValue); this.emit('RESET'); } public async empty() { await this.setValue(null); this.emit('RESET'); } public async blur() { this.emit('BLUR'); await this.validate('blur'); } // 自定义组件自行校验 public onValidate(callback: IValidatorFunc) { this.validator.setValidtorFunc(callback); } public on(event: IEvent, listener: (...args: any[]) => void) { this.emitter.on(event, listener); } public emit(event: IEvent, ...args: any[]) { const timer = this.timerMap[event]; clearTimeout(timer); this.timerMap[event] = setTimeout(() => { this.emitter.emit(event, ...args); }); } public removeListener(event: IEvent, listener: (...args: any[]) => void) { this.emitter.removeListener(event, listener); } public off(event: IEvent, listener: (...args: any[]) => void) { this.removeListener(event, listener); } public triggerEvent(event: IEvent) { const key = this.key; const field = this; // 增加代码可读性 const form = this.context; this.emit(event, key, field, form); form.emit('FORM_CHANGE', event, key, field, form); form.listenerManager.trigger(field); } }