Files
admin-ui/src/utils/diffUtils.ts

153 lines
3.7 KiB
TypeScript

/**
* 差异比较工具函数
* 用于编辑时最小化传参,只传递修改过的字段
*/
/**
* 深度比较两个值是否相等
* @param val1 值1
* @param val2 值2
* @returns 是否相等
*/
export function isEqual(val1: any, val2: any): boolean {
// 处理 null 和 undefined
if (val1 === val2) return true;
if (val1 == null || val2 == null) return val1 == val2;
// 处理基本类型
if (typeof val1 !== 'object' || typeof val2 !== 'object') {
return val1 === val2;
}
// 处理数组
if (Array.isArray(val1) && Array.isArray(val2)) {
if (val1.length !== val2.length) return false;
return val1.every((item, index) => isEqual(item, val2[index]));
}
// 处理对象
if (Array.isArray(val1) !== Array.isArray(val2)) return false;
const keys1 = Object.keys(val1);
const keys2 = Object.keys(val2);
if (keys1.length !== keys2.length) return false;
return keys1.every((key) => isEqual(val1[key], val2[key]));
}
/**
* 比较两个对象,返回差异部分
* @param original 原始数据
* @param current 当前数据
* @param options 配置选项
* @returns 差异数据对象
*/
export function getChangedFields<T extends Record<string, any>>(
original: T,
current: T,
options?: {
/** 需要包含的字段(即使没有变化也会包含) */
alwaysInclude?: string[];
/** 需要排除的字段(即使有变化也不会包含) */
exclude?: string[];
/** 字段值转换器,用于提交前转换值 */
transformers?: Record<string, (value: any) => any>;
}
): Partial<T> {
const { alwaysInclude = [], exclude = [], transformers = {} } = options || {};
const changed: Partial<T> = {};
// 遍历当前数据的所有字段
const allKeys = new Set([...Object.keys(original), ...Object.keys(current)]);
allKeys.forEach((key) => {
// 排除指定字段
if (exclude.includes(key)) return;
const originalValue = original[key];
const currentValue = current[key];
// 检查是否需要始终包含
if (alwaysInclude.includes(key)) {
const value = transformers[key] ? transformers[key](currentValue) : currentValue;
(changed as any)[key] = value;
return;
}
// 比较值是否变化
if (!isEqual(originalValue, currentValue)) {
const value = transformers[key] ? transformers[key](currentValue) : currentValue;
(changed as any)[key] = value;
}
});
return changed;
}
/**
* 创建一个用于保存和比较表单数据的工具
* @returns 工具对象
*/
export function createFormDiff<T extends Record<string, any>>() {
let originalData: T | null = null;
return {
/**
* 保存原始数据
* @param data 原始数据
*/
saveOriginal(data: T) {
// 深拷贝保存原始数据
originalData = JSON.parse(JSON.stringify(data));
},
/**
* 获取原始数据
* @returns 原始数据
*/
getOriginal(): T | null {
return originalData;
},
/**
* 获取变化的字段
* @param current 当前数据
* @param options 配置选项
* @returns 差异数据对象
*/
getChanges(
current: T,
options?: {
alwaysInclude?: string[];
exclude?: string[];
transformers?: Record<string, (value: any) => any>;
}
): Partial<T> {
if (!originalData) {
// 如果没有原始数据,返回所有当前数据
return { ...current };
}
return getChangedFields(originalData, current, options);
},
/**
* 检查是否有变化
* @param current 当前数据
* @param exclude 排除的字段
* @returns 是否有变化
*/
hasChanges(current: T, exclude?: string[]): boolean {
if (!originalData) return true;
const changes = getChangedFields(originalData, current, { exclude });
return Object.keys(changes).length > 0;
},
/**
* 重置原始数据
*/
reset() {
originalData = null;
},
};
}