当前位置: 首页 > news >正文

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

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

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

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

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

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

接下来,我把我对这种复杂场景的处理方案分享出来,大家可以参考,共同探讨,看看还有没有更好的解决方案(前端使用的是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是要后端统一进行事务处理的,不要前端单独调用后端新增的、修改的、删除的方法

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

相关文章:

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

在软件研发过程中,会遇到很多编辑页面,有时编辑页面和新增页面长的基本上一样,甚至就是一套页面供新增和编辑共用。编辑页面的场景比较多,例如: 场景一、字段比较多,但实际只修改了几个字段,如…...

docker第一次作业

docker第一次作业 1.安装docker服务,配置镜像加速器 yum install -y yum-utils device-mapper-persistent-data lvm2 yum-config-manager --add-repo https://mirrors.aliyun.com/docker-ce/linux/centos/docker-ce.repo sed -i sdownload.docker.commirrors.aliy…...

Springboot3.0.0+集成SpringDoc并配置knife4j的UI

环境:JDK17,Springboot3,springdoc2,knife4j 4 Springdoc本身也是集成了Swagger3,而knife4j美化了Swagger3的UI Knife4j官网: 快速开始 | Knife4j Springdoc官网 OpenAPI 3 Library for spring-boot 1.pom配置 由于此knife4j内依赖了S…...

电脑运行缓慢?4个方法,加速电脑运行!

“我电脑才用了没多久哎!怎么突然就变得运行很缓慢了呢?有什么方法可以加速电脑运行速度吗?真的很需要,看看我吧!” 电脑的运行速度快会让用户在使用电脑时感觉愉悦,而电脑运行缓慢可能会影响我们的工作效率…...

3.Docker 搭建 MySQL8.0

1、docker仓库搜索mysql docker search mysql2、docker仓库拉取mysql8.0 docker pull mysql:8.0 备注: docker pull mysql //默认拉取最新版本3、查看本地仓库镜像是否下载成功 docker images mysql:8.04、安装运行mysql8.0容器 docker run -p 3306:3306 --name…...

Mybatis的SqlSource SqlNode BoundSql

学习链接 MyBatis SqlSource解析 【Mybatis】Mybatis源码之SqlSource#getBoundSql获取预编译SQL Mybatis中SqlSource解析流程详解 Mybatis TypeHandler解析 图解 Mybatis的SqlSource&SqlNode - processon DynamicSqlSource public class DynamicSqlSource implement…...

html动态爱心代码【二】(附源码)

目录 前言 效果演示 内容修改 完整代码 总结 前言 七夕马上就要到了,为了帮助大家高效表白,下面再给大家带来了实用的HTML浪漫表白代码(附源码)背景音乐,可用于520,情人节,生日,表白等场景&#xff0c…...

【Rust】Rust学习 第十六章无畏并发

安全且高效的处理并发编程是 Rust 的另一个主要目标。并发编程(Concurrent programming),代表程序的不同部分相互独立的执行,而 并行编程(parallel programming)代表程序不同部分于同时执行,这两…...

系统报错mfc100u.dll丢失的解决方法(完美解决dll问题)

系统文件mfc100u.dll丢失和出错,极有可能是盗号木马、流氓软件等恶意程序所导致,其感染相关文件并加载起来,一旦杀毒软件删除被感染的文件,就会导致相关组件缺失,游戏等常用软件运行不起来,且提示“无法启动…...

docker compose的用法

目录 一、Docker-Compose介绍 1.1 Docker-Compose的概述 1.2 Docker-Compose 用来实现Docker容器快速编排 1.3 Docker-compose模板文件简介 二、YAML简介 2.1 YAML的概述 2.2 YAML的基本语法规则 2.3 YAML支持的数据架构 三、配置内部常用字段 四、Docker-compose 常…...

Linux: 使用 ssh 连接其他服务器

通过ifconfig 查看要连接的服务器地址: ubuntuubuntu1804-0172:/media/sangfor/vdc$ ssh ubuntu192.168.11.49 输入要连接的服务器密码: ubuntua192.168.1149 s password: 连接服务器成功:...

[.NET/WPF] CommunityToolkit.Mvvm 异步指令

我们在开发中, 经常会有这样的需求: 点击按钮后, 进行一些耗时的工作工作进行时, 按钮不可再次被点击工作进行时, 会显示进度条, 或者 “加载中” 的动画 RelayCommand CommunityToolkit.Mvvm 中的 RelayCommand 除了支持最简单的同步方法, 还支持以 Task 作为返回值的异步方…...

热烈祝贺汇隆成功入选航天系统采购供应商库

经过航天系统采购平台的严审,浙江汇隆晶片技术有限公司成功入选中国航天系统采购供应商库。航天系统采购平台是航天系统内企业采购专用平台,服务航天全球范围千亿采购需求,目前,已有华为、三一重工、格力电器、科大讯飞等企业、机…...

2019年3月全国计算机等级考试真题(C语言二级)

2019年3月全国计算机等级考试真题(C语言二级) 第1题 负责数据库中查询操作的数据库语言是 A. 数据定义语言 B. 数据管理语言 C. 数据操纵语言 D. 数据控制语言 正确答案:C 第2题 有关系如下图所示,其违反了哪一类完整性约束 …...

MySQL 游标

文章目录 1.游标是什么2.MySQL 游标3.定义游标4.打开游标5.提取数据6.关闭游标参考文献 1.游标是什么 游标(Cursor)是一种用于处理查询结果集的数据库对象,它允许开发者按照特定的顺序逐行遍历查询结果集中的数据。游标通常用于在数据库中执…...

ElasticSearch 7.4学习记录(DSL语法)

上文和大家一起初次了解了很多ES相关的基础知识,本文的内容将会是实际企业中所需要的吗,也是我们需要熟练应用的内容。 面对ES,我们最多使用的就是查询,当我负责这个业务时,现不需要我去考虑如何创建索引,添…...

全志orangepi-zero2驱动编写2,控制电平高低

使用驱动编写控制高低电平 可看我前俩篇文章&#xff1a; 【1】全志orangepi-zeor2驱动编写 【2】驱动函数框架详解 检索芯片手册关键信息 知道GPIO基地址 知道PC偏移地址 知道想要控制的端口的信息 知道数据位如何操作 代码实操 驱动代码 #include <linux/fs.h&…...

软考高级系统架构设计师系列之:论文典型试题写作要点和写作素材总结系列文章四

软考高级系统架构设计师系列之:论文典型试题写作要点和写作素材总结系列文章四 一、论软件的静态演化和动态演化及其应用1.论文题目2.写作要点和写作素材二、论大规模分布式系统缓存设计策略1.论文题目2.写作要点和写作素材三、论基于REST服务的Web应用系统设计1.论文题目2.写…...

06.利用Redis实现点赞功能

学习目标&#xff1a; 提示&#xff1a;学习如何利用Redisson实现点赞功能 学习产出&#xff1a; 解决方案&#xff1a; 点赞后的用户记录在Redis的set数据类型中 1. 准备pom环境 <dependency><groupId>org.springframework.boot</groupId><artifactI…...

【React】生命周期和钩子函数

概念 组件从被创建到挂载到页面中运行&#xff0c;再到组件不用时卸载的过程。 只有类组件才有生命周期。 分为三个阶段&#xff1a; 挂载阶段更新阶段销毁阶段 三个阶段 挂载阶段 钩子函数 - constructor 创建阶段触发 作用&#xff1a;创建数据 之前定义状态是简写&…...

IDEA运行Tomcat出现乱码问题解决汇总

最近正值期末周&#xff0c;有很多同学在写期末Java web作业时&#xff0c;运行tomcat出现乱码问题&#xff0c;经过多次解决与研究&#xff0c;我做了如下整理&#xff1a; 原因&#xff1a; IDEA本身编码与tomcat的编码与Windows编码不同导致&#xff0c;Windows 系统控制台…...

可靠性+灵活性:电力载波技术在楼宇自控中的核心价值

可靠性灵活性&#xff1a;电力载波技术在楼宇自控中的核心价值 在智能楼宇的自动化控制中&#xff0c;电力载波技术&#xff08;PLC&#xff09;凭借其独特的优势&#xff0c;正成为构建高效、稳定、灵活系统的核心解决方案。它利用现有电力线路传输数据&#xff0c;无需额外布…...

Python如何给视频添加音频和字幕

在Python中&#xff0c;给视频添加音频和字幕可以使用电影文件处理库MoviePy和字幕处理库Subtitles。下面将详细介绍如何使用这些库来实现视频的音频和字幕添加&#xff0c;包括必要的代码示例和详细解释。 环境准备 在开始之前&#xff0c;需要安装以下Python库&#xff1a;…...

20个超级好用的 CSS 动画库

分享 20 个最佳 CSS 动画库。 它们中的大多数将生成纯 CSS 代码&#xff0c;而不需要任何外部库。 1.Animate.css 一个开箱即用型的跨浏览器动画库&#xff0c;可供你在项目中使用。 2.Magic Animations CSS3 一组简单的动画&#xff0c;可以包含在你的网页或应用项目中。 3.An…...

在鸿蒙HarmonyOS 5中使用DevEco Studio实现企业微信功能

1. 开发环境准备 ​​安装DevEco Studio 3.1​​&#xff1a; 从华为开发者官网下载最新版DevEco Studio安装HarmonyOS 5.0 SDK ​​项目配置​​&#xff1a; // module.json5 {"module": {"requestPermissions": [{"name": "ohos.permis…...

关于easyexcel动态下拉选问题处理

前些日子突然碰到一个问题&#xff0c;说是客户的导入文件模版想支持部分导入内容的下拉选&#xff0c;于是我就找了easyexcel官网寻找解决方案&#xff0c;并没有找到合适的方案&#xff0c;没办法只能自己动手并分享出来&#xff0c;针对Java生成Excel下拉菜单时因选项过多导…...

数学建模-滑翔伞伞翼面积的设计,运动状态计算和优化 !

我们考虑滑翔伞的伞翼面积设计问题以及运动状态描述。滑翔伞的性能主要取决于伞翼面积、气动特性以及飞行员的重量。我们的目标是建立数学模型来描述滑翔伞的运动状态,并优化伞翼面积的设计。 一、问题分析 滑翔伞在飞行过程中受到重力、升力和阻力的作用。升力和阻力与伞翼面…...

VisualXML全新升级 | 新增数据库编辑功能

VisualXML是一个功能强大的网络总线设计工具&#xff0c;专注于简化汽车电子系统中复杂的网络数据设计操作。它支持多种主流总线网络格式的数据编辑&#xff08;如DBC、LDF、ARXML、HEX等&#xff09;&#xff0c;并能够基于Excel表格的方式生成和转换多种数据库文件。由此&…...

如何配置一个sql server使得其它用户可以通过excel odbc获取数据

要让其他用户通过 Excel 使用 ODBC 连接到 SQL Server 获取数据&#xff0c;你需要完成以下配置步骤&#xff1a; ✅ 一、在 SQL Server 端配置&#xff08;服务器设置&#xff09; 1. 启用 TCP/IP 协议 打开 “SQL Server 配置管理器”。导航到&#xff1a;SQL Server 网络配…...

在 Visual Studio Code 中使用驭码 CodeRider 提升开发效率:以冒泡排序为例

目录 前言1 插件安装与配置1.1 安装驭码 CodeRider1.2 初始配置建议 2 示例代码&#xff1a;冒泡排序3 驭码 CodeRider 功能详解3.1 功能概览3.2 代码解释功能3.3 自动注释生成3.4 逻辑修改功能3.5 单元测试自动生成3.6 代码优化建议 4 驭码的实际应用建议5 常见问题与解决建议…...