20260711173006

This commit is contained in:
oneao committed 2026-07-11 17:30:09 +08:00
1 parent d080a2a48d
commit 446758b2fe
384 files changed
+153926 -5937

No files matched your search

@@ -0,0 +1,471 @@
/* 比对并分类数据变更
* @param {Array} currentData 变更后的数据数组
* @param {Array} originalData 原始数据数组
* @param {String} primaryKey 主键字段名
* @param {Array} ignoreFields 过滤字段(可传可不传)
* @returns {Object} 包含分类结果的对象 { insertdata: [], updatedata: [], deletedata: [], hasChanges: Boolean }
*/
export function compareAndClassifyData(currentData, originalData, primaryKey) {
const versionFields = ["mx_version", "b_version"];
const getVersionFields = (row) => {
const fields = {};
versionFields.forEach((key) => {
if (Object.prototype.hasOwnProperty.call(row, key)) {
fields[key] = row[key] ?? "";
}
});
return fields;
};
const result = {
inserts: [],
updates: [],
deletes: [],
hasChanges: false, // 默认无变动
};
// 1. 找出新增的数据
result.inserts = currentData.filter(
(row) =>
!row[primaryKey] ||
!originalData.some((originalRow) => originalRow[primaryKey] === row[primaryKey]),
);
// 2. 找出修改的数据
result.updates = currentData.reduce((acc, row) => {
const rowId = row[primaryKey];
if (!rowId) return acc;
const originalRow = originalData.find((r) => r[primaryKey] === rowId);
if (!originalRow) return acc;
const changedFields = {};
Object.keys(row).forEach((key) => {
if (key !== primaryKey && !key.startsWith("v_") && !versionFields.includes(key)) {
const oldVal = originalRow[key];
const newVal = row[key];
const normalize = (v) => (v === null || v === undefined ? "" : v);
if (normalize(newVal) !== normalize(oldVal)) {
// ✅ 避免 undefined 被丢弃
changedFields[key] = newVal ?? "";
}
}
});
// Object.keys(row).forEach((key) => {
// if (key !== primaryKey && !isEqual(row[key], originalRow[key])) {
// if (!key.startsWith('v_')) {
// changedFields[key] = row[key]
// }
// }
// })
if (Object.keys(changedFields).length > 0) {
acc.push({
[primaryKey]: rowId,
...getVersionFields(row),
...changedFields,
});
}
return acc;
}, []);
// 3. 找出删除的数据
result.deletes = originalData.filter(
(originalRow) => !currentData.some((row) => row[primaryKey] === originalRow[primaryKey]),
);
// 4. 是否有任何变动
result.hasChanges =
result.inserts.length > 0 || result.updates.length > 0 || result.deletes.length > 0;
return result;
}
/**
* 在数组对象的每个元素中覆盖式填入 JSON 对象的值
* @param {Array} arr - 目标数组(将被修改)
* @param {object} json - 包含要添加/覆盖的键值对的 JSON 对象
* @returns {Array} 修改后的数组
* @throws {Error} 当参数类型不正确时抛出错误
*/
// export function fillDefaultArray(arr, json) {
// // 基本数据验证
// if (!Array.isArray(arr))
// throw new Error('第一个参数必须是数组')
// if (typeof json !== 'object' || json === null || Array.isArray(json)) {
// throw new Error('第二个参数必须是普通对象(JSON对象)')
// }
// // 覆盖式填充每个元素
// return arr.map((item) => {
// // 只处理对象元素
// if (typeof item === 'object' && item !== null) {
// return { ...item, ...json } // 覆盖已有属性
// }
// return item // 非对象元素保持不变
// })
// }
export function fillDefaultArray(arr, json) {
// 基本数据验证
if (!Array.isArray(arr)) throw new Error("第一个参数必须是数组");
if (typeof json !== "object" || json === null || Array.isArray(json)) {
throw new Error("第二个参数必须是普通对象(JSON对象)");
}
// 原地修改数组中的每个对象
for (const item of arr) {
if (typeof item === "object" && item !== null) {
Object.assign(item, json);
}
}
// 可选:方便链式调用
return arr;
}
// export function fillSerialNoAndDefaults(arr, defaults = {}) {
// if (!Array.isArray(arr)) return;
// arr.forEach((item, index) => {
// if (typeof item === "object" && item !== null) {
// item.mx_xh = index + 1;
// Object.assign(item, defaults);
// }
// });
// }
export function fillSerialNoAndDefaults(arr, defaults = {}) {
if (!Array.isArray(arr)) return;
arr.forEach((item, index) => {
if (typeof item === "object" && item !== null) {
item.mx_xh = index + 1;
Object.entries(defaults).forEach(([key, value]) => {
item[key] = typeof value === "function" ? value(item, index) : value;
});
}
});
}
export function compareAndClassifyData1(currentData, originalData, primaryKey, ignoreFields = []) {
const ignoreSet = new Set(ignoreFields);
const result = {
insertdata: [],
updatedata: [],
deletedata: [],
hasChanges: false,
};
// 工具函数:剔除不需要的字段
const omitFields = (row) => {
const res = {};
Object.keys(row).forEach((key) => {
if (key !== primaryKey && ignoreSet.has(key)) return;
res[key] = row[key];
});
return res;
};
// 1️⃣ 新增
result.insertdata = currentData
.filter(
(row) => !row[primaryKey] || !originalData.some((o) => o[primaryKey] === row[primaryKey]),
)
.map((row) => omitFields(row)); // 在插入时过滤字段
// 2️⃣ 修改
result.updatedata = currentData.reduce((acc, row) => {
const rowId = row[primaryKey];
if (!rowId) return acc;
const originalRow = originalData.find((r) => r[primaryKey] === rowId);
if (!originalRow) return acc;
const changedFields = {};
Object.keys(row).forEach((key) => {
if (key === primaryKey || ignoreSet.has(key)) return; // 忽略指定字段
if (row[key] !== originalRow[key]) changedFields[key] = row[key];
});
if (Object.keys(changedFields).length > 0) {
acc.push({
[primaryKey]: rowId,
...omitFields(changedFields), // 在更新时也过滤字段
});
}
return acc;
}, []);
// 3️⃣ 删除,只保留 primaryKey
result.deletedata = originalData
.filter((o) => !currentData.some((c) => c[primaryKey] === o[primaryKey]))
.map((row) => ({ [primaryKey]: row[primaryKey] }));
// 4️⃣ 是否有变动
result.hasChanges =
result.insertdata.length > 0 || result.updatedata.length > 0 || result.deletedata.length > 0;
return result;
}
/**
* 深度比较两个值是否相等
* @param {*} a 第一个值
* @param {*} b 第二个值
* @returns {boolean} 是否相等
*/
export function isEqual(a, b) {
// 处理基本类型和null/undefined
if (a === b) return true;
if (typeof a !== typeof b) return false;
if (typeof a !== "object" || a === null || b === null) {
return a === b;
}
// 处理数组
if (Array.isArray(a) && Array.isArray(b)) {
if (a.length !== b.length) return false;
return a.every((item, i) => isEqual(item, b[i]));
}
// 处理对象
const keysA = Object.keys(a);
const keysB = Object.keys(b);
if (keysA.length !== keysB.length) return false;
return keysA.every((key) => isEqual(a[key], b[key]));
}
/**
* 检测并提取变更的字段(跳过v_开头的字段)
* @param {object} currentData 当前数据
* @param {object} originalData 原始数据
* @param {string} primaryKey 主键字段名
* @param {string} primaryKeyValue 主键值
* @returns {object} 包含变更数据的对象,结构为 { hasChanges: boolean, updateData: Object }
*/
export function detectChanges(currentData, originalData, primaryKey, primaryKeyValue) {
const updateValue = {};
// 遍历所有字段检测变更
Object.keys(currentData).forEach((key) => {
// 跳过以v_开头的字段
if (key.startsWith("v_")) return;
if (key === "b_type") {
const currentStr = normalizeOwnerCustomer(currentData[key]);
const originalStr = normalizeOwnerCustomer(originalData[key]);
if (currentStr !== originalStr) {
updateValue[key] = currentStr; // 保存时格式化为逗号分隔字符串
}
} else {
// 比较前,先将 undefined 转换为空字符串
const currentValue = currentData[key] === undefined ? "" : currentData[key];
const originalValue = originalData[key] === undefined ? "" : originalData[key];
// 使用JSON.stringify进行深度比较
if (JSON.stringify(currentValue) !== JSON.stringify(originalValue)) {
updateValue[key] = currentValue;
}
}
});
// 如果没有有效变更
if (Object.keys(updateValue).length === 0) {
return { hasChanges: true, updateData: [] };
}
// 返回变更数据和主键
return {
hasChanges: true,
updateData: [
{
[primaryKey]: primaryKeyValue,
...updateValue,
},
],
};
}
/**
* 检测并提取变更的字段(跳过v_开头的字段和指定排除字段)
* @param {object} currentData 当前数据
* @param {object} originalData 原始数据
* @param {Array|string} primaryKeys 主键字段名或数组
* @param {Array} excludeKeys 可选,排除的字段数组
* @returns {object} 包含变更数据的对象,结构为 { hasChanges: boolean, updateData: Array }
*/
export function detectChangesFields(currentData, originalData, primaryKeys, excludeKeys = []) {
const updateValue = {};
Object.keys(currentData).forEach((key) => {
// 跳过以v_开头的字段
if (key.startsWith("v_")) return;
// 跳过排除字段
if (excludeKeys.includes(key)) return;
const currentValue = currentData[key] === undefined ? "" : currentData[key];
const originalValue = originalData[key] === undefined ? "" : originalData[key];
if (JSON.stringify(currentValue) !== JSON.stringify(originalValue)) {
updateValue[key] = currentValue;
}
});
if (Object.keys(updateValue).length === 0) {
return { hasChanges: false, updateData: [] };
}
// 构建主键字段对象
const primaryKeyObj = {};
if (Array.isArray(primaryKeys)) {
primaryKeys.forEach((key) => {
primaryKeyObj[key] = originalData[key];
});
} else {
primaryKeyObj[primaryKeys] = originalData[primaryKeys];
}
return {
hasChanges: true,
updateData: [
{
...primaryKeyObj,
...updateValue,
},
],
};
}
function normalizeOwnerCustomer(val) {
if (typeof val === "string") {
return val.split(",").filter(Boolean).join(","); // '001,002,003'
}
if (Array.isArray(val)) {
return val.map((item) => (Array.isArray(item) ? item[0] : item)).join(",");
}
return "";
}
/**
* 统一数据差异比对(主表 / 子表通用)
*
* @param {Array | object} current 当前数据
* @param {Array | object} original 原始数据
* @param {string | Array} primaryKey 主键
* @param {object} options
* @param {Array} options.ignoreFields 忽略字段(如 v_ 字段)
* @param {boolean} options.isSingle 是否单条主表
*/
export function diffData(
current,
original,
primaryKey,
{ ignoreFields = [], isSingle = false } = {},
) {
const ignoreSet = new Set(ignoreFields);
const result = {
hasChanges: false,
insertdata: [],
updatedata: [],
deletedata: [],
};
// ===========================
// 单表处理
// ===========================
if (isSingle) {
const isNewRecord = !original || Object.keys(original).length === 0 || !original[primaryKey];
// 新增
if (isNewRecord) {
result.hasChanges = true;
result.insertdata.push(JSON.parse(JSON.stringify(current)));
return result;
}
// 修改
const changes = {};
Object.keys(current).forEach((key) => {
if (key.startsWith("v_") || ignoreSet.has(key)) return;
const cur = current[key] ?? "";
const org = original[key] ?? "";
if (JSON.stringify(cur) !== JSON.stringify(org)) {
changes[key] = cur;
}
});
if (Object.keys(changes).length > 0) {
result.hasChanges = true;
result.updatedata.push({ [primaryKey]: current[primaryKey], ...changes });
}
return result;
}
// ===========================
// 子表处理(优化 Map 提升性能)
// ===========================
const pk = Array.isArray(primaryKey) ? primaryKey[0] : primaryKey;
const originalMap = new Map(original.map((item) => [item[pk], item]));
// 新增
result.insertdata = current.filter((row) => !row[pk] || !originalMap.has(row[pk]));
// 修改
result.updatedata = [];
current.forEach((row) => {
const id = row[pk];
if (!id) return;
const oldRow = originalMap.get(id);
if (!oldRow) return;
const changes = {};
Object.keys(row).forEach((key) => {
if (key === pk || key.startsWith("v_") || ignoreSet.has(key)) return;
const cur = row[key] ?? "";
const org = oldRow[key] ?? "";
if (JSON.stringify(cur) !== JSON.stringify(org)) {
changes[key] = cur;
}
});
if (Object.keys(changes).length > 0) {
result.updatedata.push({ [pk]: id, ...changes });
}
});
// 删除
result.deletedata = original
.filter((o) => !current.some((c) => c[pk] === o[pk]))
.map((o) => ({ [pk]: o[pk] }));
result.hasChanges =
result.insertdata.length > 0 || result.updatedata.length > 0 || result.deletedata.length > 0;
return result;
}
// 默认导出所有方法
export default {
isEqual,
compareAndClassifyData,
detectChanges,
fillDefaultArray,
diffData,
fillSerialNoAndDefaults,
};