import { NameListMap } from '../../_util/NameListMap' ; import { FieldNameList , FieldNamePath } from '../../types/form' ; import { TransedDependency , TransedDependencies , Dependencies , DependencyType , Dependency , } from '../../types/dependency' ; import { toArray } from '../../_util/common' ; import assign from 'lodash/assign' ; import isUndefined from 'lodash/isUndefined' ; import isObject from 'lodash/isObject' ; import isEqual from 'lodash/isEqual' ; import { triggerPlugin , triggerActionPlugin } from '../../rope/triggerPlugin' ; import { HOOK_MARK } from '../FieldGroupContext' ; import { getStoreValue } from '../utils/valueUtil' ; export default class DepStore { public getName : ( name : FieldNamePath ) => FieldNameList = null ! ; public depsByFieldNameList : NameListMap < FieldNameList , TransedDependencies > = new NameListMap ( ) ; public parse ( fieldConfig , deps , getName ) { const fieldNameList = getName ( fieldConfig . fieldKey || fieldConfig . name ) ; Object . keys ( deps ) . forEach ( ( type ) => { const dependency = deps [ type as DependencyType ] ; const { cases , ... globalDep } = dependency ; if ( cases ) { cases . forEach ( ( kase , index ) => { const finalDep = assign ( { } , globalDep , kase ) ; this . parseCase ( fieldNameList , type , finalDep , fieldConfig , getName , index ) ; } ) ; } else { this . parseCase ( fieldNameList , type , globalDep , fieldConfig , getName ) ; } } ) ; } private parseCase ( fieldNameList , type , dependency , fieldConfig , getName , caseIndex ? ) { const { relates , inputs , output , defaultOutput , ignores , type : depPlugin , autoResetValue , } = dependency ; const relatedFieldNameLists = relates . map ( ( relate ) => getName ? getName ( relate ) : toArray ( relate ) ) ; relatedFieldNameLists . forEach ( ( relatedFieldNameList ) => { if ( ! this . depsByFieldNameList . get ( relatedFieldNameList ) ) { this . depsByFieldNameList . set ( relatedFieldNameList , Object . create ( null ) ) ; } const transedDependency = { name : fieldNameList , type : depPlugin , relates : relatedFieldNameLists , ignores : normalizeIgnores ( ignores , relatedFieldNameLists . length ) , inputs , output , defaultOutput , autoResetValue : autoResetValue !== false , ... ( type === 'source' ? { remoteSource : fieldConfig . remoteSource } : { } ) , } ; if ( this . depsByFieldNameList . get ( relatedFieldNameList ) [ type ] ) { if ( ! isUndefined ( caseIndex ) ) { if ( caseIndex === 0 ) { this . depsByFieldNameList . get ( relatedFieldNameList ) [ type ] . push ( [ transedDependency ] ) ; } else { const depsOfType = this . depsByFieldNameList . get ( relatedFieldNameList ) [ type ] ; const lastDepOfType = depsOfType [ depsOfType . length - 1 ] ; lastDepOfType . push ( transedDependency ) ; } } else { this . depsByFieldNameList . get ( relatedFieldNameList ) [ type ] . push ( [ transedDependency ] ) ; } } else { this . depsByFieldNameList . get ( relatedFieldNameList ) [ type ] = [ [ transedDependency ] ] ; } } ) ; } private getDepForm = ( ctx ) => { const { getFieldValueByFieldKey , setFieldValueByFieldKey } = ctx . form . getInternalHooks ( HOOK_MARK ) ; return { getFieldValue : getFieldValueByFieldKey , setFieldValue : setFieldValueByFieldKey , } ; } ; private getDepCtx = ( ctx ) => { return assign ( { } , ctx , { form : assign ( { } , ctx . form , this . getDepForm ( ctx ) ) } ) ; } ; public triggerDependency ( originCtx , depsOfType , cascadePayload ) { const ctx = this . getDepCtx ( originCtx ) ; const { form } = ctx ; const { getPrevFieldNameByFieldKey , isNeverUpdate } = form . getInternalHooks ( HOOK_MARK ) ; Object . keys ( depsOfType ) . forEach ( ( type ) => { const allDeps = depsOfType [ type as DependencyType ] ; allDeps . forEach ( ( deps ) => { for ( let i = 0 , len = deps . length ; i < len ; i += 1 ) { const isLastCase = i === deps . length - 1 ; const dep = deps [ i ] ; const { name : affectedFieldNameList , relates , inputs , ignores , output , defaultOutput , type : depPlugin , autoResetValue , } = dep ; if ( type === 'value' ) { const isWilling = isWillingSetValue ( affectedFieldNameList , cascadePayload ) ; if ( isWilling ) { return true ; } } const values = [ ] ; let canDep = false ; for ( let i = 0 , len = relates . length ; i < len ; i += 1 ) { const relatedFieldNameList = relates [ i ] ; const relatedFieldValue = form . getFieldValue ( relatedFieldNameList ) ; const prevRelatedFieldValue = getStoreValue ( cascadePayload . cascadePrevStore , getPrevFieldNameByFieldKey ( relatedFieldNameList ) ) ; if ( ( isNeverUpdate && isNeverUpdate ( relatedFieldNameList ) ) || prevRelatedFieldValue !== relatedFieldValue ) { canDep = true ; } values . push ( relatedFieldValue ) ; } /**
                     * 不能直接通过值没变就直接返回，例如拷贝（或者有初始值）动态添加的场景
                     */ if ( canDep === false ) { return ; } if ( depPlugin ) { if ( type === 'source' && autoResetValue ) { clearValueForSourceDependency ( affectedFieldNameList , ctx , cascadePayload ) ; } const depPluginCtx = assign ( { } , ctx , { name : affectedFieldNameList , values , relates , } ) ; const isMatched = triggerPlugin ( 'dependency' , depPluginCtx , dep ) ; if ( isMatched !== false ) { return ; } } let fn ; if ( type === 'source' ) { fn = autoResetValue ? ( _name , _source ) => { clearValueForSourceDependency ( _name , ctx , cascadePayload ) ; form . setFieldSource ( _name , _source ) ; } : form . setFieldSource ; } else if ( type === 'disabled' ) { fn = ( _name , _disabled ) => { const prevDisabled = form . getFieldDisabled ( _name ) ; if ( prevDisabled !== _disabled ) { form . setFieldDisabled ( _name , _disabled ) ; } } ; } else if ( type === 'visible' ) { fn = ( _name , _visible ) => { const prevVisible = form . getFieldVisible ( _name ) ; if ( prevVisible !== _visible ) { form . setFieldVisible ( _name , _visible ) ; } } ; } else { fn = form . setFieldValue ; } const isIgnored = singleMatch ( ignores , values ) ; if ( isIgnored ) { if ( isLastCase ) { fn ( affectedFieldNameList , defaultOutput ) ; } continue ; } /**
                     * 远程数据源关联只执行一个case
                     */ if ( type === 'source' && ! inputs && dep . remoteSource ) { return this . cascadeSource ( ctx , dep , cascadePayload ) ; } const isAllMatched = inputs && allMatch ( inputs , values ) ; if ( isAllMatched ) { fn ( affectedFieldNameList , output ) ; return ; } if ( isLastCase ) { fn ( affectedFieldNameList , defaultOutput ) ; } } } ) ; } ) ; } private cascadeSource ( ctx , dep , cascadePayload ) { const { form } = ctx ; const { name , relates , remoteSource , autoResetValue } = dep ; if ( dep . inputs || dep . type ) return ; const fetchDepInfo = ( relates as FieldNameList [ ] ) . reduce ( ( memo , relatedFieldNameList ) => { const value = form . getFieldValue ( relatedFieldNameList ) ; memo [ relatedFieldNameList [ relatedFieldNameList . length - 1 ] ] = value ; return memo ; } , { } as Record < string , any > ) ; autoResetValue && clearValueForSourceDependency ( name , ctx , cascadePayload ) ; return triggerActionPlugin ( ctx , assign ( { } , remoteSource , { params : assign ( { } , remoteSource . params , fetchDepInfo ) } ) ) . then ( ( source ) => { form . setFieldSource ( name , source ) ; } ) ; } } function clearValueForSourceDependency ( fieldNameList , ctx , cascadePayload ) { const { form } = ctx ; const { getFieldNameByFieldKey } = form . getInternalHooks ( HOOK_MARK ) ; /** fieldKey 转换成 fieldName，来判断value在不在cascadePayload里 */ const fieldNamePathForValue = getFieldNameByFieldKey ( fieldNameList ) ; const isWilling = isWillingSetValue ( fieldNamePathForValue , cascadePayload ) ; if ( ! isWilling ) { form . setFieldValue ( fieldNameList , undefined ) ; } } function isWillingSetValue ( fieldNameList , cascadePayload ) { const { cascadeTrigger , cascadeStore } = cascadePayload ; const isWilling = ( cascadeTrigger === 'setFieldsValue' || cascadeTrigger === 'setFields' ) && hasOwnPropWithNameList ( fieldNameList , cascadeStore ) ; return isWilling ; } /**
 * 不清空 - (ctx.cascadeTrigger === setFieldsValue || setFields) && values.hasOwnProperty(x);
 * 否则清空
 */ export function hasOwnPropWithNameList ( fieldNameList , payload ) { const len = fieldNameList . length ; let recurValue = payload ; for ( let i = 0 ; i < len ; i += 1 ) { const namePath = fieldNameList [ i ] ; if ( isObject ( recurValue ) && recurValue . hasOwnProperty ( namePath ) ) { recurValue = recurValue [ namePath ] ; } else { return false ; } } return true ; } inputs values result export function singleMatch ( inputs , values ) { return inputs . some ( ( input , index ) => equal ( input , values [ index ] ) ) ; } rules values result export function allMatch ( inputs , values ) { return ! inputs . some ( ( input , index ) => ! equal ( input , values [ index ] ) ) ; } inputs value return export function equal ( inputs , value ) { if ( inputs === '*' ) { return true ; } for ( let i = 0 , len = inputs . length ; i < len ; i += 1 ) { const input = inputs [ i ] ; if ( isEqual ( input , value ) ) { return true ; } } return false ; } function normalizeIgnores ( ignores , num ) { if ( ! ignores ) { return fill ( num , [ undefined ] ) ; } return ignores ; } function fill ( num , value ) { const result = [ ] ; for ( let i = 0 ; i < num ; i += 1 ) { result [ i ] = value ; } return result ; }