前端编辑页面修改后和原始数据比较差异

在软件研发过程中,会遇到很多编辑页面,有时编辑页面和新增页面长的基本上一样,甚至就是一套页面供新增和编辑共用。编辑页面的场景比较多,例如:

场景一、字段比较多,但实际只修改了几个字段,如果把所有字段都回传给后端,冗余字段(未作变更的字段)修改就会有很多,而这些未作变更的字段中还有一些字段是后台有翻译和转换的(例如金额、类型、状态等),如果把这样的字段回传给后台,就会造成数据的变更(其实不想变更),和预期是严重不符的。

场景二、还有一些比较复杂的编辑页面,它会有大集合嵌套小集合,甚至还有多层嵌套的场景,在修改时,可能对各个层级的集合又有一些新增、修改、删除的操作。

场景三、场景一和场景二的混合场景。

基于这些复杂的场景,如果能够的精确的区分开哪些做了新增、哪些做了变更、哪些删掉了,就会让我们的前后端研发小伙伴更轻松的应对这些复杂场景。

接下来,我把我对这种复杂场景的处理方案分享出来,大家可以参考,共同探讨,看看还有没有更好的解决方案(前端使用的是vue的环境):

处理原理:

1、进入编辑页面时,将查询出来的数据复制成两份,一份作为初始数据,一份作为修改数据

2、比对初始数据和修改数据的差异(新增的、修改的、删除的)

3、将差异数据提交给后台,后台进行分门别类处理

比对算法:

/**

* 比较两个对象的差异

* 应用场景:编辑页面中,表单字段比较多(主页面,明细页面),而修改项很少,只需要将修改的字段和必要字段传递给后台就可以,不用传递表单中的所有字段(默认是传递所有字段)

* @param {Array/Object} original 原始对象

* @param {Array/Object} modified 修改后对象

* @param {Array} primaryKeys 主键(后台是根据主键修改数据)

* @param {Array} excludeKeys 过滤字段(不参与比较的字段)

* @return {Object} diffObject 返回有变更的对象字段

*/

export function obtainingObjectsDiffrences(original, modified, primaryKeys = 'id', excludeKeys = \[\]) {

let diffObject = {}

// 如果当前为数组,先分别区分哪些需要新增、修改、删除

if (typeof original === 'object' && Array.isArray(original) && typeof modified === 'object' && Array.isArray(modified)) {

diffObject = subObtainingObjectsDiffrences(original, modified, primaryKeys, excludeKeys)

} else if (typeof original === 'object' && typeof modified === 'object') {

// 当前对象字段是否有变更

let levelDiffFlag = false

// 主键

let primaryKey

for (const key in original) {

if (!excludeKeys.includes(key)) {

if (typeof originalkey === 'object' && Array.isArray(originalkey) && typeof modifiedkey === 'object' && Array.isArray(modifiedkey)) {

const subDiffObject = subObtainingObjectsDiffrences(originalkey, modifiedkey, primaryKeys, excludeKeys)

if (Object.keys(subDiffObject).length !== 0) {

diffObjectkey = subDiffObject.modifiedArray

diffObjectkey + 'New' = subDiffObject.newArray

diffObjectkey + 'Removed' = subDiffObject.removedArray

}

} else if (typeof originalkey === 'object' && typeof modifiedkey === 'object') {

const subDiffObject = obtainingObjectsDiffrences(originalkey, modifiedkey, primaryKeys, excludeKeys)

if (Object.keys(subDiffObject).length !== 0) {

diffObjectkey = subDiffObject

}

} else if (originalkey !== modifiedkey) {

// 更新modifiedData对象中的属性值

diffObjectkey = modifiedkey

levelDiffFlag = true

}

}

if (primaryKeys.includes(key)) {

primaryKey = key

}

}

// 设置主键字段值

if (levelDiffFlag && primaryKey !== undefined) {

diffObjectprimaryKey = originalprimaryKey

}

}

return diffObject

}

/**

* 比对数组之间的差异(新增、修改、删除)

* @param {Array} original 原始数组

* @param {Array} modified 修改后的数组

* @param {Array} primaryKeys 主键列表

* @param {Array} excludeKeys 不参与比对的字段

* @return {Object} diffObject 返回有差异的数组

*/

function subObtainingObjectsDiffrences(original, modified, primaryKeys, excludeKeys) {

const diffObject = {}

// 子列表主键

let subPrimaryKey

for (const key in original0) {

if (primaryKeys.includes(key)) {

subPrimaryKey = key

}

}

// 具有相同主键的初始列表

const commonOrignal = original.filter(itemA => modified.some(itemB => itemAsubPrimaryKey === itemBsubPrimaryKey))

// 具有相同主键的修改列表

const commonModified = modified.filter(itemA => original.some(itemB => itemAsubPrimaryKey === itemBsubPrimaryKey))

// 删除列表

const removedArray = original.filter(itemA => !modified.some(itemB => itemAsubPrimaryKey === itemBsubPrimaryKey))

if (removedArray.length !== 0) {

diffObject.removedArray = removedArray

}

// 新增列表

const newArray = modified.filter(itemA => !original.some(itemB => itemAsubPrimaryKey === itemBsubPrimaryKey))

if (newArray.length !== 0) {

diffObject.newArray = newArray

}

// 修改条目:如果属性值是对象,则递归调用obtainingObjectsDiffrences进行比较

const modifiedArray = \[\]

commonOrignal.forEach(subOrignal => {

const subModified = commonModified.filter(item => itemsubPrimaryKey === subOrignalsubPrimaryKey)0

const subModifiedObject = obtainingObjectsDiffrences(subOrignal, subModified, primaryKeys, excludeKeys)

if (Object.keys(subModifiedObject).length !== 0) {

modifiedArray.push(subModifiedObject)

}

})

if (modifiedArray.length !== 0) {

diffObject.modifiedArray = modifiedArray

}

return diffObject

}

调用示例:

const originalUserList = [{

userId: 'a1',

username: 'tom',

password: '123456',

remarks: 'asdfasdfadsf',

roles: [{

roleId: 'r1',

roleName: '管理员',

remarks: 'asdfasdfadsf',

menus: [{

menuId: 'm1',

menuName: '用户管理',

remarks: 'asdfasdfadsf'

}, {

menuId: 'm2',

menuName: '角色管理',

remarks: 'asdfasdfadsf'

}, {

menuId: 'm3',

menuName: '菜单管理',

remarks: 'asdfasdfadsf'

}]

}]

}, {

userId: 'a2',

username: 'cat',

password: '123456',

remarks: 'asdfasdfadsf',

roles: [{

roleId: 'r1',

roleName: '管理员',

remarks: 'asdfasdfadsf',

menus: [{

menuId: 'm1',

menuName: '用户管理',

remarks: 'asdfasdfadsf'

}, {

menuId: 'm2',

menuName: '角色管理',

remarks: 'asdfasdfadsf'

}, {

menuId: 'm3',

menuName: '菜单管理',

remarks: 'asdfasdfadsf'

}]

}]

}]

const modifiedUserList = [{

userId: 'a1',

username: 'tom',

password: '123456789',

remarks: 'asdf',

roles: [{

roleId: 'r1',

roleName: '管理员',

remarks: 'asdf',

menus: [{

menuId: 'm1',

menuName: '企业管理',

remarks: 'asdf'

}, {

menuId: 'm3',

menuName: '菜单管理',

remarks: 'asdf'

}]

}]

}, {

userId: 'a3',

username: 'tom1',

password: '12345686',

remarks: 'asdfasdfadsf',

roles: [{

roleId: 'r1',

roleName: '管理员',

remarks: 'asdfasdfadsf',

menus: [{

menuId: 'm1',

menuName: '用户管理',

remarks: 'asdfasdfadsf'

}, {

menuId: 'm2',

menuName: '角色管理',

remarks: 'asdfasdfadsf'

}, {

menuId: 'm3',

menuName: '菜单管理',

remarks: 'asdfasdfadsf'

}]

}]

}]

const diff = obtainingObjectsDiffrences(originalUserList, modifiedUserList, 'userId', 'roleId', 'menuId', 'remarks')

console.log('原始列表', originalUserList)

console.log('修改列表', modifiedUserList)

console.log('差异列表', diff)

调用结果:

结语

1、对于比较差异的返回字段的命名,前后端小伙伴可以商量着来

2、如果后端的新增保存方法是saveOrUpdate的通用方法,前端可以把对应的新增列表和修改列表合并了

3、如果后端的删除是removeByIds的方法,前端可以把返回的removeArray再按照主键过滤一下

4、注意2和3是要后端统一进行事务处理的,不要前端单独调用后端新增的、修改的、删除的方法

这是我的处理方案,供参考,如果有更有方案可以一起探讨

相关推荐
deli0071 小时前
32K 上下文塞满后,AI 最先忘掉什么?120 轮对话实测
前端
deli0072 小时前
手机拍灯为什么有一条条黑纹?PWM 240Hz 实测
前端
wangruofeng7 小时前
AI Models 1.1.0 发布,查大模型价格、对比模型和找 API 地址
前端·aigc·ai编程
wangruofeng7 小时前
给前端开发者的 100 个设计参考网站
前端·设计
星栈10 小时前
视频即代码,Remotion 能做什么、不能做什么
前端·音视频开发
广州华水科技10 小时前
单北斗GNSS位移监测系统的应用与技术解析
前端
用户479492835691510 小时前
从一句话或代码仓到可交互 HTML:开源 Agent 画图技能 Archify v3.2 实测与选型边界
前端·后端·面试
风骏时光牛马12 小时前
生成一个AI多模态标题,仅输出一条标题,无额外文字、问句、备选、配图、文案等任何附加内容,不需要其他的任务服务 只要标题。
前端
wangruofeng13 小时前
从 GIF 到 AVIF:网页图片格式演进与 2026 年选型指南
前端·架构
清晨Coding14 小时前
为什么用 React Developer Tools 查看组件树这么乱?
前端·react.js