import { watch, ref, toRefs, toRef, computed } from 'vue' import { cloneDeep, getFieldValue, isArray, setFieldValue } from '@cip/utils/util' import { getValuesByKeys, setValuesByKeys, getChangeIndex, judgeUseFn } from './util' import { getRulesByFieldConfig } from '@cip/components/helper/d-render' export const useWatchFieldDepend = (props, context, { updateModelValue, updateOtherValue }) => { const { model, fieldKey } = toRefs(props) // 配置的config 注:大部分情况配置config与runningConfig一致 const tableDependOnValues = toRef(props, 'tableDependOnValues') const dependOnValues = ref({}) const outDependOnValues = ref({}) const runningConfig = ref() // 运行时的config const securityConfig = computed(() => props.config ?? {}) // watch(securityConfig, (val) => { // console.log('securityConfig change', val) // }) const dependOn = computed(() => securityConfig.value.dependOn || []) const outDependOn = computed(() => securityConfig.value.outDependOn || []) // 清理数据 const clearValues = () => { updateModelValue() updateOtherValue() } const dependOnWatchCb = ({ changeKeys, changeOldValues }) => { const values = getValuesByKeys(model.value, dependOn.value) const outValues = getValuesByKeys(tableDependOnValues.value, outDependOn.value) // 赋值 dependOnValues.value = values // 获取局部effect的key const privateEffectKeys = changeKeys .map((key, index) => { if (typeof key === 'object') return { ...key, _index: index } return key }) // 塞入初始的index .filter(key => typeof key === 'object') outDependOnValues.value = outValues // 获取全局effect的key const hasGlobalEffectKey = changeKeys.find(key => typeof key === 'string') // 执行全局effect的回调 if (hasGlobalEffectKey) { cb(values, outValues, changeKeys, changeOldValues) } // 执行局部effect的回调 privateEffectKeys.forEach(object => { const privateEffect = object.effect || {} cb(values, outValues, object.key, changeOldValues[object._index], privateEffect) }) } const cb = (values, outValues, keys, oldValues, effect) => { keys = [].concat(keys) // 转化为数组 oldValues = [].concat(oldValues) // 转化为数组 // 经支持 privateEffect if ( !(keys.length === 1 && keys[0] === fieldKey.value) && // 不能只有自己变了变了 oldValues.some(val => val !== undefined) // 存在变更的依赖原始值不为undefined [1,undefined] true [undefined,undefined] false ) { if (effect?.resetValue !== undefined) { // effect.resetValue的值优先级高于config.value.resetValue if (typeof effect.resetValue === 'function') { // effect.resetValue的类型为Function effect.resetValue(getFieldValue(model.value, props.fieldKey), values, outValues) && clearValues() } else { effect.resetValue && clearValues() } } else if (securityConfig.value.resetValue) { clearValues() } } if (!props.readonly) { // changeValue && changeValueByOld 仅在非readonly下生效 const changeValueCb = judgeUseFn('changeValue', securityConfig.value, effect) if (typeof changeValueCb === 'function') changeValue(changeValueCb, values, outValues) const changeValueByOldCb = judgeUseFn('changeValueByOld', securityConfig.value, effect) if (typeof changeValueByOldCb === 'function') { // 不合并 keys.forEach((key, i) => { const oldValue = oldValues[i] if (typeof key === 'object') { key = key.key } changeValueByOld(changeValueByOldCb, { key, oldValue }, values, outValues) }) } } const changeConfigCb = judgeUseFn('changeConfig', securityConfig.value, effect) if (typeof changeConfigCb === 'function') changeConfig(changeConfigCb, values, outValues) } // 变更字段config的方式 const changeConfig = async (cb, values, outValues) => { runningConfig.value = await cb(cloneDeep(securityConfig.value), values, outValues) } const changeValue = async (cb, values, outValues) => { const data = await cb(values, outValues) if (data) { const { value, otherValue } = data updateModelValue(value) updateOtherValue(otherValue) } } const changeValueByOld = async (cb, { key, oldValue }, values, outValues) => { // eslint-disable-next-line standard/no-callback-literal const data = await cb({ key, oldValue }, values, outValues) if (data !== undefined) { const { value, otherValue } = data updateModelValue(value) updateOtherValue(otherValue) } } const watchValue = (target, dependOn) => dependOn.map(key => { if (typeof key === 'object') key = key.key return () => getFieldValue(target.value, key) }) const generateWatchValue = () => { let result = watchValue(model, dependOn.value) if (props.inTable) { result = result.concat(watchValue(tableDependOnValues, outDependOn.value)) } return result } const getChange = (values, oldValues, depend) => { // 转为纯函数 const changeIndex = getChangeIndex(values, oldValues) const changeValue = changeIndex.map(index => values[index]) const changeOldValues = changeIndex.map(index => oldValues[index]) const changeKeys = changeIndex.map(index => depend[index]) // 此处depend为函数私有 return { changeValue, changeOldValues, changeKeys } } // let unwatch // watch([dependOn, outDependOn], debounce((val) => { // console.log(props.fieldKey, '依赖变化', val) // if (unwatch) unwatch() const depend = props.inTable ? dependOn.value.concat(outDependOn.value) : dependOn.value if (depend.length > 0) { /* unwatch = */watch(generateWatchValue(dependOn, outDependOn), (values, oldValues) => { const change = getChange(values, oldValues, depend) dependOnWatchCb(change) }, { immediate: true, deep: true }) } // }, 200, false), { immediate: true }) return { clearValues, dependOnValues, outDependOnValues, runningConfig } } export const useFieldValue = (key, model, updateModel, changeEffect) => { const isArrayKey = computed(() => { return isArray(key.value) }) const value = computed(() => { if (!key.value || key.value.length === 0) return undefined // 根据key的类型不同调用不同的方法获取otherValue if (isArrayKey.value) { return getValuesByKeys(model.value, key.value) } else { return getFieldValue(model.value, key.value) } }) const updateValue = async (val) => { if (!key.value) return if (changeEffect?.value) { // TODO: 需要区分 try { const result = await changeEffect.value(val, key.value, model.value) if (result === false) throw new Error('changeEffect false interrupted data update') } catch (e) { throw new Error('changeEffect reject interrupted data update') } } const innerModel = model.value || {} if (isArrayKey.value) { setValuesByKeys(innerModel, key.value, val) } else { setFieldValue(innerModel, key.value, val) } updateModel(innerModel) } return [value, updateValue] } export const useRules = (config, isReadonly, status, otherValue, dependOnValues) => { const usingRules = computed(() => { return !(isReadonly.value || config.value.disabled || config.value._isShow === false || status.value !== 'read-write') }) const rules = computed(() => { if (usingRules.value) { return getRulesByFieldConfig(config.value, otherValue.value, dependOnValues.value) } else { return [] } }) return { usingRules, rules } }