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

news2024/11/23 19:30:18

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

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

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

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

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

接下来,我把我对这种复杂场景的处理方案分享出来,大家可以参考,共同探讨,看看还有没有更好的解决方案(前端使用的是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 original[key] === 'object' && Array.isArray(original[key]) && typeof modified[key] === 'object' && Array.isArray(modified[key])) {
          const subDiffObject = subObtainingObjectsDiffrences(original[key], modified[key], primaryKeys, excludeKeys)
          if (Object.keys(subDiffObject).length !== 0) {
            diffObject[key] = subDiffObject.modifiedArray
            diffObject[key + 'New'] = subDiffObject.newArray
            diffObject[key + 'Removed'] = subDiffObject.removedArray
          }
        } else if (typeof original[key] === 'object' && typeof modified[key] === 'object') {
          const subDiffObject = obtainingObjectsDiffrences(original[key], modified[key], primaryKeys, excludeKeys)
          if (Object.keys(subDiffObject).length !== 0) {
            diffObject[key] = subDiffObject
          }
        } else if (original[key] !== modified[key]) {
          // 更新modifiedData对象中的属性值
          diffObject[key] = modified[key]
          levelDiffFlag = true
        }
      }
      if (primaryKeys.includes(key)) {
        primaryKey = key
      }
    }
    // 设置主键字段值
    if (levelDiffFlag && primaryKey !== undefined) {
      diffObject[primaryKey] = original[primaryKey]
    }
  }
  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 original[0]) {
    if (primaryKeys.includes(key)) {
      subPrimaryKey = key
    }
  }
  // 具有相同主键的初始列表
  const commonOrignal = original.filter(itemA => modified.some(itemB => itemA[subPrimaryKey] === itemB[subPrimaryKey]))
  // 具有相同主键的修改列表
  const commonModified = modified.filter(itemA => original.some(itemB => itemA[subPrimaryKey] === itemB[subPrimaryKey]))
  // 删除列表
  const removedArray = original.filter(itemA => !modified.some(itemB => itemA[subPrimaryKey] === itemB[subPrimaryKey]))
  if (removedArray.length !== 0) {
    diffObject.removedArray = removedArray
  }
  // 新增列表
  const newArray = modified.filter(itemA => !original.some(itemB => itemA[subPrimaryKey] === itemB[subPrimaryKey]))
  if (newArray.length !== 0) {
    diffObject.newArray = newArray
  }
  // 修改条目:如果属性值是对象,则递归调用obtainingObjectsDiffrences进行比较
  const modifiedArray = []
  commonOrignal.forEach(subOrignal => {
    const subModified = commonModified.filter(item => item[subPrimaryKey] === subOrignal[subPrimaryKey])[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是要后端统一进行事务处理的,不要前端单独调用后端新增的、修改的、删除的方法

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

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/893780.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

LED电子显示屏在安防监控中心的作用

在安防监控中心,调度中心被视为核心要素,而LED电子显示屏则成为完整调度系统中人机互动的中心环节,涵盖人员调度、计划制定等关键决策,其地位举足轻重,主导全局。LED电子显示屏展示系统主要用途包括信息交流、人机互动…

使用GPT 自动化您的代码库

推荐:使用 NSDT场景编辑器 助你快速搭建可二次编辑的3D应用场景 介绍 随着人工智能领域的发展和演变,我们已经看到了GPT,ChatGPT,Bard等强大工具的兴起。程序员正在使用这些工具来简化他们的工作流程并优化他们的代码库。它使他们…

RWKV系列2-RWKV-LM

训练数据集 https://data.deepai.org/enwik8.zip 使用分类参考 https://zhuanlan.zhihu.com/p/639629050 模型分类和使用任务 解码参数,推荐值: 小说和对话:temp 1.2 topp 0.5 或 temp 1.4 topp 0.4 或 temp 1.7 topp 0.3 或 temp 2 top…

Openlayers实战:移动鼠标至重叠几何图形上,获取多层所有features信息

在Openlayers的实际项目中,经常会出现在某个区域内有多个矢量层叠加的情况,这个时候点击内部一点,我们要获取到所有矢量层的信息。如果做到这一点呢,这个示例就演示了两个图层叠加,获取到全部信息的情形。 效果图 源代码 /* * @Author: 大剑师兰特(xiaozhuanlan),还是…

Leetcode61 旋转链表

给你一个链表的头节点 head ,旋转链表,将链表每个节点向右移动 k 个位置。 示例1: 输入:head [1,2,3,4,5], k 2 输出:[4,5,1,2,3] 示例2: 输入:head [0,1,2], k 4 输出:[2,0,1] …

材料行业可以转IC设计后端吗?

近来有许多材料行业的小伙伴通过后台来问我对于职业规划的看法,甚至有些小伙伴直接点明了某个行业适不适合自己,那么我这边仅以近年来比较热门的数字芯片设计来展开讲讲,材料适不适合转行做IC呢。 对于理工科的同学而言,选择哪个…

网络安全设备篇——加密机

加密机是一种专门用于数据加密和解密的网络安全设备。它通过使用密码学算法对数据进行加密,从而保护数据的机密性和完整性。加密机通常被用于保护敏感数据,如金融信息、个人身份信息等。 加密机的主要功能包括: 数据加密:加密机使…

药品最新研究信息查询系统

查找最新药物研究进展信息在患者治疗选择、医疗实践、科学研究、药物监管和政策制定、教育和学术研究等方面都具有重要的应用价值。它可以为各个领域的人员提供最新的科学依据和决策支持,促进医学领域的发展和提高医疗质量。 但在查找药物最新研究进展信息时通常需要…

【数据库服务网格】浅谈Database Mesh及未来

文章目录 前言1. 服务网格:Service Mesh服务网格优势 2. 数据库服务网格:Database Mesh3. 数据服务网格:Data Mesh 前言 Database Mesh,这一概念是由开源软件shardingsphere的作者张亮,最早于2018年提出的。其含义是D…

又双叒叕!五大数据库全方位注释,抗性宏基因组分析项目再次升级!

基于宏基因组测序的抗性基因分析是目前ARGs分析的重要手段,五大数据库全面注释分析,一网打尽ARGs、MRGs、BRGs、MGEs、致病菌注释。 项目报告不仅包含抗性基因的多样性、丰度和分布模式,还能获得包括抗性组变化驱动因素、指示基因识别、抗性组…

Java智慧工地系统源码(微服务+Java+Springcloud+Vue+MySQL)

智慧工地系统是依托物联网、互联网、AI、可视化建立的大数据管理平台,是一种全新的管理模式,能够实现劳务管理、安全施工、绿色施工的智能化和互联网化。围绕施工现场管理的人、机、料、法、环五大维度,以及施工过程管理的进度、质量、安全三…

SpringCloud最新最全面试题

目录 一、简单说一说什么是微服务? 二、微服务有哪些优缺点? 三、微服务、分布式、集群的区别? 四、什么是Eureka? 五、Eureka有那两大组件? 六、actuator是什么? 七、Discovery是什么? …

4.1 C++ Boost 字符串处理库

Boost 库是一个由C/C语言的开发者创建并更新维护的开源类库,其提供了许多功能强大的程序库和工具,用于开发高质量、可移植、高效的C应用程序。Boost库可以作为标准C库的后备,通常被称为准标准库,是C标准化进程的重要开发引擎之一。…

docker 搭建 ElasticSearch

1、拉取镜像 docker pull elasticsearch:8.8.12、在机器本地新建文件夹并赋予权限 mkdir -p /home/elasticsearch/configmkdir -p /home/elasticsearch/datamkdir -p /home/elasticsearch/pluginschmod 777 /home/elasticsearch/configchmod 777 /home/elasticsearch/datachm…

全开放式耳机什么品牌好?全开放式耳机推荐

​在音乐的世界中,开放式耳机提供了更真实、更通透的音质体验,开放式耳机采用不入耳设计,佩戴更为稳固舒适,还允许外界的声音自由地流入,使你在享受音乐的同时,也能保持对周围环境的感知,户外运…

WebDriver API及对象识别技术

html页面的iframe的切换 定位到客户管理 新增客户 会无法定位到新增客户,因为在另外一个iframe框架之中。 iframe是html中的框架标签,表示文档中可以嵌入文档,或者说是浮动的框架。在selenium中iframe同样如此,如果驱动器对象处于当前iframe框架中,此时驱动器对象是…

商业计划书的写作

商业计划书是一份描述企业发展的文件,是企业经营者素质的体现,是企业拥有良好融资能力、实现跨越式发展的重要条件之一。一份好的商业计划书,是建立投资者、合作者、消费者信心的重要依据,获得、投资、合作的关键。 企业商业计划…

❤ Vue2完整项目进一步配置配置(二)

❤ Vue2完整项目进一步配置配置(二) (1)配置自定义端口号:9528 port: 9528, // 端口号autoOpenBrowser: true, //是否自动打开浏览器

《知识图谱互联互通白皮书》正式发布,合合信息携手电子标准院共同推动技术规范化发展

知识图谱是将各种数据和信息进行结构化处理后形成的一种“语义知识库”,也是人工智能的重要组成部分。 为助力知识图谱系统间的互联互通,推进知识要素的规范、有序和可靠流动,近期,中国电子技术标准化研究院依托知识图谱产业推进…

适用于Android™的Windows子系统Windows Subsystem fo r Android™Win11安装指南

文章目录 一、需求二、Windows Subsystem for Android™Win11简介三、安装教程1.查看BIOS是否开启虚拟化2.安装Hyper-V、虚拟机平台3.启动虚拟机管理程序(可选)4.安装适用于Android™的Windows子系统5.相关设置 一、需求 需要在电脑上进行网课APP(无客户端只有App&…