diff --git a/packages/@core/ui-kit/form-ui/src/form-api.ts b/packages/@core/ui-kit/form-ui/src/form-api.ts index bdc44de787d..80a9f67f444 100644 --- a/packages/@core/ui-kit/form-ui/src/form-api.ts +++ b/packages/@core/ui-kit/form-ui/src/form-api.ts @@ -16,7 +16,6 @@ import { isRef, toRaw } from 'vue'; import { Store } from '@vben-core/shared/store'; import { bindMethods, - createMerge, formatDate, isDate, isDayjsObject, @@ -330,24 +329,114 @@ export class FormApi { } /** - * 合并算法有待改进,目前的算法不支持object类型的值。 - * antd的日期时间相关组件的值类型为dayjs对象 - * element-plus的日期时间相关组件的值类型可能为Date对象 - * 以上两种类型需要排除深度合并 + * 深度合并两个对象,支持嵌套对象、数组直接覆盖,忽略 Date 和 Dayjs 对象深度合并 + * + * 主要用于合并表单当前值 [target] 与传入的新值 [source] + * 合并策略: + * - 基本类型直接覆盖 + * - 数组直接替换 + * - 非日期类对象进行递归合并 + * + * @param target - 当前对象(通常是 form.values) + * @param source - 新传入的对象(通常是 fields) + * @returns 返回合并后的新对象,不修改原对象 + * + * @example + * fieldMergeFn({ a: { b: 1 } }, { a: { c: 2 }, d: 3 }); + * // 返回: { a: { b: 1, c: 2 }, d: 3 } */ - const fieldMergeFn = createMerge((obj, key, value) => { - if (key in obj) { - obj[key] = - !Array.isArray(obj[key]) && - isObject(obj[key]) && - !isDayjsObject(obj[key]) && - !isDate(obj[key]) - ? fieldMergeFn(obj[key], value) - : value; + const fieldMergeFn = ( + target: Record, + source: Record, + ) => { + const result = { ...target }; + + for (const key in source) { + const targetValue = result[key]; + const sourceValue = source[key]; + + // 如果 sourceValue 是 null 或 undefined,保留旧值 + if (sourceValue === null || sourceValue === undefined) { + continue; + } + + // 如果 sourceValue 是数组,直接覆盖 + if (Array.isArray(sourceValue)) { + result[key] = sourceValue; + } + // 如果 sourceValue 是对象(非 Date、非 Dayjs),进行深度合并 + else if ( + isObject(sourceValue) && + !isDate(sourceValue) && + !isDayjsObject(sourceValue) + ) { + result[key] = + isObject(targetValue) && + !isDate(targetValue) && + !isDayjsObject(targetValue) + ? fieldMergeFn(targetValue, sourceValue) + : sourceValue; + } + // 其他情况(如基本类型)直接赋值 + else { + result[key] = sourceValue; + } } - return true; - }); - const filteredFields = fieldMergeFn(fields, form.values); + + return result; + }; + + /** + * 从对象中提取指定字段路径的子集,支持多级嵌套字段(如 'user.address.city') + * + * @param obj - 要从中提取字段的对象 + * @param filedNames - 字段路径数组,可以是多级字段(例如 ['user.name', 'user.age']) + * @returns 返回一个新对象,仅包含 `filedNames` 所指定的字段及其值 + * + * @example + * const obj = { + * user: { name: 'Alice', age: 25 }, + * email: 'alice@example.com' + * }; + * pickFields(obj, ['user.name', 'email']); + * // 返回: { user: { name: 'Alice' }, email: 'alice@example.com' } + */ + function pickFields(obj: Record, filedNames: string[]) { + const result: Record = {}; + + for (const path of filedNames) { + const keys: string[] = path.split('.'); + let value: any = obj; + let target: any = result; + + for (let i = 0; i < keys.length; i++) { + const key = keys[i] as string; + + if (value && typeof value === 'object' && key in value) { + value = value[key]; + if (i === keys.length - 1) { + // 最后一级字段存在才赋值 + target[key] = value; + } else { + // 初始化中间结构 + target[key] = target[key] || {}; + target = target[key]; + } + } else { + // 路径不存在则跳过 + break; + } + } + } + + return result; + } + + const fieldNames = (this.state?.schema ?? []).map((item) => item.fieldName); + const filteredFields = pickFields( + fieldMergeFn(form.values, fields), + fieldNames, + ); this.handleStringToArrayFields(filteredFields); form.setValues(filteredFields, shouldValidate); }