import { defineComponent , PropType , reactive , toRaw , watchEffect , watch } from 'vue' ; import ScriptHelper , { resolve } from '../../utils/script-helper' ; import { FieldDefine } from './index' ; /**
 * 动态绑定组件
 * 传入源对象及数据绑定的定义，根据组件名动态渲染出组件进行数据绑定
 */ export default defineComponent ( { name : 'DynamicBinder' , props : { modelValue : { type : Object as PropType < unknown > , default : ( ) => Object . assign ( { } ) , required : true , } , /**
         * 组件的需要绑定数据的定义
         */ fieldDefine : { type : Object as PropType < FieldDefine > , default : ( ) => ( { } ) , required : true , } , bindTransformer : { type : Function , default : undefined , } , } , emits : [ 'update:modelValue' , 'fieldChange' ] , setup ( props , context ) { const state = reactive ( { flatFieldDefine : flatObject ( props . fieldDefine || { } , { } ) , handingModel : Object . assign ( { } ) , } ) ; watchEffect ( ( ) => { state . handingModel = props . modelValue ; state . flatFieldDefine = flatObject ( props . fieldDefine , { } ) ; } ) ; const bindTransformer = props . bindTransformer || defaultTransformer ; const dataBindTransformer = function ( key , value ) { return bindTransformer ( state . handingModel , key , value ) ; } ; return ( ) => ( < div class = "dynamic-binder" > { Object . keys ( state . flatFieldDefine ) . map ( ( key ) => { const define = state . flatFieldDefine [ key ] ; if ( define && predicate ( define , toRaw ( props . modelValue ) ) ) { const bindData = dataBindTransformer ( key , define ) ; const Component = toRaw ( define . component ) ; watch ( ( ) => bindData . value , ( ) => { context . emit ( 'update:modelValue' , state . handingModel ) ; if ( bindData . setValue ) { const setValueCallBack = bindData . setValue ( toRaw ( props . modelValue ) , bindData . bindKey , bindData . value ) ; if ( setValueCallBack ) { setValueCallBack ( ) ; } } else { context . emit ( 'fieldChange' , bindData . bindKey , bindData . value ) ; } } ) ; return ( < Component { ... bindData } v - model = { bindData . value } v - slots = { bindData . vSlots } class = { `${ Component . name }-${ key } dynamic-binder-item` } />); } return null ; } ) } < /div>); } , } ) ; param source param target param prefix function flatObject ( source , target , prefix = '' ) { Object . keys ( source ) . forEach ( ( key ) => { const currentKeyObj = source [ key ] ; if ( ! currentKeyObj || ! ( typeof currentKeyObj === 'object' ) ) { return ; } const component = currentKeyObj . component ; if ( component ) { target [ prefix + key ] = currentKeyObj ; } else { flatObject ( currentKeyObj , target , `${ key }.` ) ; } } ) ; return target ; } param fieldDefine param modelValue function predicate ( fieldDefine , modelValue ) { const bindDefinePredicate = fieldDefine . predicate ; if ( bindDefinePredicate ) { if ( typeof bindDefinePredicate === 'string' ) { return ScriptHelper . executeEl ( modelValue , bindDefinePredicate ) as boolean ; } if ( typeof bindDefinePredicate === 'function' ) { return bindDefinePredicate ( modelValue ) ; } } return true ; } param sourceModel param bindKey param bindDefine function defaultTransformer ( sourceModel , bindKey , bindDefine ) { return reactive ( { bindKey , ... bindDefine , sourceModel , value : bindDefine . getValue ? bindDefine . getValue ( toRaw ( sourceModel ) ) : resolve ( bindKey , sourceModel ) || '' , } ) ; }