vue多层级复杂数据结构的新旧数据对比方法

最近做的项目里有个需求:审批页需要把修改前和修改后的数据进行对比,有变化的数据需要标红显示,鼠标移入的时候显示变更前的数据和变更后的数据,新增的整条数据也要标红,效果如下:

后台返回的报文结构包含:字符串、数组、对象、数组嵌套对象、对象嵌套对象的形式,示例如下:

{
    newData: {
        skuName: 'test',
        number: '20230720',
        skuBasicInfo: {
            id: 1,
            name: 'test',
            label: '已上架',
            info: { fileName: '申请表.doc', code: '123' }
        },
        skuSettleList: [
            { settleNum: '001', desc: '通讯费',  type: '3' },
            { settleNum: '002', desc: '交通费',  type: '1' },
            { settleNum: '005', desc: '住宿费',  type: '6' }
        ],
        skuMsg: { ownPhone: '13323452345', ownEmail: '123@456.com' },
        province: ['100', '110', '230'],
        createBy: 'admin',
        createTime: '2023-07-20 14:29:18',
    },
    oldData: {
        skuName: 'test',
        number: '20230720',
        skuBasicInfo: {
            id: 1,
            name: 'test',
            label: '失效',
            info: { fileName: '产品信息表.doc', code: '123' }
        },
        skuSettleList: [
            { settleNum: '001', desc: '通讯费',  type: '3' },
            { settleNum: '002', desc: '餐补费',  type: '5' },
        ],
        skuMsg: { ownPhone: '15534568765', ownEmail: '123@456.com' },
        province: ['100'],
        createBy: 'admin',
        createTime: '2023-07-20 14:29:18',
    }
}

 考虑到存在删除整条数据后又新增整条数据的情况,像skuSettleList字段里的数据,就需要取一个唯一标识,根据唯一标识分别取newData和oldData里的数据进行比对,将比对结果存入一个新的字段isChange内,作为判断新增还是老数据变更的标识。

整体思路如下:

以newData的key为准,循环+递归逐层比对,如果有新增数据,则在自增的数据上加isChange字段,赋值为1表示新增;如果是旧数据变更,则在newData的同层级添加'_old'+key,赋值为oldData[key]的值。

1、循环遍历newData的第一层key,先判断newData[key]是否为null,如果是则不走判断逻辑,如果不是则进入下一步判断;

2、判断newData[key]的数据类型,此时会出现三种情况:基本数据类型、对象、数组。

  • 如果是基本数据类型,则直接比对newData[key]和oldData[key];
  • 如果是对象,则调用方法本身,以newData[key]和oldData[key]作为方法的入参;
  • 如果是数组,则需要考虑两种情况:有新增数据、只有旧数据变更。

 页面结构大致如下:

<el-table :data="tableData" :row-class-name="tableRowClassName">
  <el-table-column label="商品名称">
    <template slot-scope="scope">
      <el-popover
              placement="top-start"
              width='330'
              offset='115'
              visible-arrow
              v-if="scope.row._oldskuName"
              trigger="hover">
        <PopoverBox :new-data="scope.row.skuName" :old-data="scope.row._oldskuName"/>
        <span slot="reference">{{ scope.row.skuName }}</span>
      </el-popover>
      <span v-else>{{ scope.row.skuName }}</span>
    </template>
  </el-table-column>
</el-table>

方法完整代码如下:

function compareDataNew(newData, oldData) {
    // 创建一个数组,用于保存需要特殊判断的字段名
    const specialKeys = ['skuSettleList', 'skuTemplateList']
    // 创建一个数组,用于保存specialKeys里对应索引字段数据的唯一标识,要和specialKeys的元素一一对应
    const judgeIds = ['settleNum', 'templateNum']
    // 遍历newData的第一层key
    for (const key in newData) {
        // 判断当前key是否为null,为null不走判断逻辑,否则进入类型判断
        if (newData[key]) {
            // 第一层数据为数组的情况
            if (newData[key].constructor === Array) {
                // 判断是否为空数组,为空则不走判断逻辑
                if (newData[key].length) {
                    // 判断当前数组是基本数据类型还是引用对象类型
                    // 此处没考虑数组内元素类型复杂的情况,例如基本数据类型和引用数据类型都存在,且第一个元素为基本数据类型
                    const item = newData[key][0]
                    // 此处的判断没有考虑二维数组的情况
                    if (item.constructor === Object) {
                        // 如果是对象类型,则遍历每一个对象
                        for (let a = 0; a < newData[key].length; a++) {
                            // 先判断当前key是否属于specialKeys,如果是再进入后续逻辑
                            const judgeIndex = specialKeys.indexOf(key)
                            if (judgeIndex !== -1) {
                                // 获取数据的唯一标识字段
                                const judgeId = judgeIds[judgeIndex]
                                // 给newData[key][a]这条数据添加标识字段isChange,默认值1
                                // 1--新增  0--旧数据变更
                                newData[key][a]['isChange'] = 1
                                // 遍历oldData[key],如果judgeId在oldData中不存在,则说明就是新增数据,如果存在则是旧数据变更
                                for (let b = 0; b < oldData[key].length; b++) {
                                    if (newData[key][a][judgeId] === oldData[key][b][judgeId]) {
                                        newData[key][a]['isChange'] = 0
                                        // 旧数据变更的话,需要逐个比对每一条数据的每个值是否相等,所以需要再次调用自身
                                        compareDataNew(newData[key][a], oldData[key][b])
                                    }
                                }
                            } else {
                                // 当前key不属于specialKeys,不需要特殊处理,所以直接递归调用自身即可
                                compareDataNew(newData[key][a], oldData[key][a])
                            }
                        }
                        continue
                    }
                    // 走到此处说明当前数组是个基本类型的数组,直接对比数组长度
                    // 此处没考虑长度相等但元素不同的情况!!!
                    if (newData[key].length !== oldData[key].length) {
                        newData['_old' + key] = oldData[key]
                    }
                } else {
                    // 进入这一层说明新数组数据都已经删除了,删除的数据页面不会展示,此处只是保留一下旧数据
                    if (oldData[key].length) {
                        newData['_old' + key] = oldData[key]
                    }
                }
                continue
            }
            // 第一层数据为对象的情况下,再次调用自身进行第二层数据的比对
            if (newData[key].constructor === Object) {
                compareDataNew(newData[key], oldData[key])
                continue
            }
            // 第一层数据为基本数据类型的情况下,直接对比值即可
            if (newData[key] !== oldData[key]) {
                newData['_old' + key] = oldData[key]
            }
        }
    }
}

📢📢📢 注意!!!

由于这个方法是基于公司业务情景写的,所以有些情况并未考虑到,例如复杂的数组嵌套、数组内存在多种数据类型的元素、二维数组等,判断并不全面,大家可以根据自己的实际需求完善判断逻辑,本文章仅作记录和思路分享~

评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值