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

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

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

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

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

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

接下来,我把我对这种复杂场景的处理方案分享出来,大家可以参考,共同探讨,看看还有没有更好的解决方案(前端使用的是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.rhkb.cn/news/101137.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

【网络层协议】ARP攻击与欺骗常见的手段以及工作原理

个人主页:insist--个人主页​​​​​​ 本文专栏:网络基础——带你走进网络世界 本专栏会持续更新网络基础知识,希望大家多多支持,让我们一起探索这个神奇而广阔的网络世界。 目录 一、ARP攻击的常见手段 第一种:IP…

罗勇军 →《算法竞赛·快冲300题》每日一题:“超级骑士” ← DFS

【题目来源】http://oj.ecustacm.cn/problem.php?id1810http://oj.ecustacm.cn/viewnews.php?id1023https://www.acwing.com/problem/content/3887/【题目描述】 现在在一个无限大的平面上,给你一个超级骑士。 超级骑士有N种走法,请问这个超级骑士能否…

python刷小红书流量(小眼睛笔记访问量),metrics_report接口,原理及代码,以及x-s签名验证2023-08-21

一、什么是小眼睛笔记访问量 如下图所示,为笔记访问量。 二、小眼睛笔记访问量接口 1、url https://edith.xiaohongshu.com/api/sns/web/v1/note/metrics_report 2、payload data{"note_id": note_id,"note_type": note_type,"report_t…

SOFARPC(笔记)

文章目录 一、快速开始1.1 SOFARPC1.2 基于SOFABoot 二、注册中心三、通讯协议2.1 Bolt基本发布调用方式超时控制协议泛化调用序列化协议自定义线程池 2.2 RESTful基本使用 2.3 其他协议四、架构 附录 官方样例下载地址-sofa-boot-guides 可查看 SOFARPC 方式快速入门 一、快…

HTML5+CSS3+JS小实例:环形文字动画特效

实例:环形文字动画特效 技术栈:HTML+CSS+JS 效果: 源码: 【html】 <!DOCTYPE html> <html><head><meta http-equiv="content-type" content="text/html; charset=utf-8"><meta name="viewport" content=&quo…

深度解读波卡 2.0:多核、更有韧性、以应用为中心

本文基于 Polkadot 生态研究院整理&#xff0c;有所删节 随着波卡 1.0 的正式实现&#xff0c;波卡于 6 月 28 日至 29 日在哥本哈根举办了年度最重要的会议 Polkadot Decoded 2023&#xff0c;吸引了来自全球的行业专家、开发者和爱好者&#xff0c;共同探讨和分享波卡生态的…

C语言好题解析(四)

目录 选择题一选择题二选择题三选择题四选择题五编程题一 选择题一 已知函数的原型是&#xff1a; int fun(char b[10], int *a); 设定义&#xff1a; char c[10];int d; &#xff0c;正确的调用语句是&#xff08; &#xff09; A: fun(c,&d); B: fun(c,d); C: fun(&…

Hbuilder打包后推流拉流都没有画面

背景&#xff1a;我在使用数据线连接手机测试的时候&#xff0c;推流拉流都是正常的额&#xff0c;云打包后&#xff0c;跳转到视频接听页面&#xff0c;就是空白的。 解决方法&#xff1a; manifest.json->APP模块配置->直播推流权限勾上&#xff08;推流&#xff09; 还…

leetcode 516. 最长回文子序列(JAVA)题解

题目链接https://leetcode.cn/problems/longest-palindromic-subsequence/description/?utm_sourceLCUS&utm_mediumip_redirect&utm_campaigntransfer2china 目录 题目描述&#xff1a; 暴力递归&#xff1a; 动态规划&#xff1a; 题目描述&#xff1a; 给你一个…

docker之Consul环境的部署

目录 一、Docker consul的介绍 1.1 template模板(更新) 1.2 registrator&#xff08;自动发现&#xff09; 1.3 agent(代理) 二.consul的工作原理 三.Consul的特性 四.Consul的使用场景 五.搭建Consul的集群 5.1 需求 5.2 部署consul 5.3 主服务器部署[192.168.19.10…

RunnerGo性能测试时如何从数据库获取数据

我们在做性能测试或者场景测试时往往需要从数据库中获取一些真实的系统数据让我们配置的场景更加贴合实际。而RunnerGo也是在最近的大版本更新中推出连接数据库功能&#xff0c;本篇文章也给大家讲解一下具体的操作方法和实际应用场景。 配置数据库 首先进入RunnerGo页面&…

家庭装修设计施工团队进度小程序开发演示

传统装修企业获客难、获客成本高、竞争激烈&#xff0c;我们也是基于整个装修市场整体的需求&#xff0c;从用户角度出发帮助装修设计企业设计制作这款小程序。可以让传统装修企业搭上互联网的快车&#xff0c;形成线上获客裂变&#xff0c;降低获客成本提高客户信任度和签单率…

在mac下,使用Docker安装达梦数据库

前言&#xff1a;因为业务需要安装达梦数据库 获取官网下载tar包&#xff08;达梦官网的下载页面https://www.dameng.com/list_103.html&#xff09;&#xff0c;或者通过命令 一、下载tar包 命令下载&#xff1a;wget -O dm8_docker.tar -c https://download.dameng.com/eco/…

Php“牵手”淘宝商品SKU信息数据采集方法,淘宝API接口申请指南

淘宝天猫商品属性sku信息接口 API 是开放平台提供的一种 API 接口&#xff0c;它可以帮助开发者获取商品的详细信息&#xff0c;包括商品的标题、描述、图片&#xff0c;销量&#xff0c;sku信息等信息。在电商平台的开发中&#xff0c;商品属性接口API是非常常用的 API&#x…

皮爷咖啡基于亚马逊云科技的数据架构,加速数据治理进程

皮爷咖啡&#xff08;Peet’s Coffee&#xff09;是美国精品咖啡品牌&#xff0c;于2017年进入中国&#xff0c;为中国消费者带来传统经典咖啡饮品&#xff0c;并特别呈现更加丰富的品质咖啡饮品体验。通过深入应用亚马逊云科技云原生数据库产品Amazon Redshift以及Amazon DMS等…

Docker 三要素

文章目录 Docker 简介Docker客户端Docker服务器Docker 镜像Docker 容器 Docker 简介 学习完容器的相关概念&#xff0c;开始学习docker的核心组件分别是Docker客户端、Docker服务器、Docker镜像、Docker容器、仓库。 学习之前&#xff0c;我们先思考一个问题&#xff0c;目前…

关于ros工作空间devel下setup.bash的理解

在创建了ros的工作空间之后 在工作空间的devel文件夹中存在几个setup.*sh形式的环境变量设置脚本 使用source命令运行这些脚本文件&#xff0c;则工作空间的环境变量设置可以生效&#xff08;如可以找到该工作空间内的项目&#xff09;。 source devel/setup.bash 设置环境变量…

机器学习之数据清洗

一、介绍 数据清洗是机器学习中的一个重要步骤&#xff0c;它涉及对原始数据进行预处理和修复&#xff0c;以使数据适用于机器学习算法的训练和分析。数据清洗的目标是处理数据中的噪声、缺失值、异常值和不一致性等问题&#xff0c;以提高数据的质量和准确性。 二、方法 处理…

MyBatis进阶:掌握MyBatis动态SQL与模糊查询、结果映射,让你在面试中脱颖而出!!

目录 一、引言 二、MyBatis动态SQL 2.1.if元素使用 2.2.foreach元素使用 三、MyBatis模糊查询 ①使用#{字段名} ②使用${字段名} ③使用concat{%,#{字段名},%} 总结 四、MyBatis结果映射 4.1.案例演示 4.1.1.resultType进行结果映射 4.1.2.resultMap进行结果映射 …

客户服务体系最重要一点——如何进行同理心构建

您可以将所有的时间和精力投入到竞争激烈的商业、网络和发展世界中&#xff0c;但您只能通过一件关键的事情获得成功&#xff1a;卓越的客户服务。 出色的客户服务的关键要素在于一件事&#xff1a;具有同理心的能力。在客户服务领域&#xff0c;同理心是一种神奇的成分&#…