目录
- 需求:
- 大概要点:
- 根据刚才的要点可以建立一下组件的props:
- 组件的基本样式也很简单:
- 完事了,最后贴一下完整代码:
- 使用示例:
- 效果预览:
- 扩展功能TODO:
- 总结
如题,朋友有个这样的需求,感觉挺常见,发出来给大家参考一下
需求:
用el-table展示两组数据,有差异的单元格显示红色,新增的显示整行绿色
大概要点:
- 需要一个数据组,里面包含两组需要对比的数据
- 需要一个唯一的key,用来确定某一行的数据是否在其他数据中存在(是否是新增
- 接受一个表格列的配置,用于渲染表格,同时对比差异只按照配置的数据来,其他的数据无需进行对比
根据刚才的要点可以建立一下组件的props:
props: { uniqueKey: { type: String, default: "id" }, dataGroup: { type: Array, validator: val => val.length === 2 }, columns: { type: Array, required: true } }
唯一id默认为id;columns的格式就按照el-table-column的来,定义为{ label, prop, … }
组件的基本样式也很简单:
<template> <div class="diff-table-container"> <el-table v-for="(data, i) in completedData" :key="i" :data="data" :row-style="markRowStyles" :cell-style="markCellStyles" > <el-table-column v-for="item in columns" :key="`${i}${item.prop}`" align="center" v-bind="item" /> </el-table> </div> </template> <style lang="scss" scoped> .diff-table-container { display: flex; align-items: flex-start; .el-table + .el-table { margin-left: 20px; } } </style>
如上所示,就是把两个表格简单的横向排布。这里的completedData指进行diff处理完成之后的数据,格式和传进来的dataGroup是一样的。markRowStyles和markRowStyles都是el-table提供的,分别指行和列的样式,值为对象或返回一个对象的函数。
接下来定义两个Symbol,之后在diff数据的时候会给数据加上标记,用Symbol做标记可以防止属性名冲突。
data() { return { DIFF_CELL_KEY: Symbol("diffCells"), // 一个数组,存储有差异的cell属性名 COMPLETED_KEY: Symbol("completed") // 标记已完成处理 }; }
然后diff的样式处理也可以直接定下来了。
methods: { // 完成处理之后没有标记的,就表示只在一组数据中出现,也就是新增数据 markRowStyles({ row }) { return ( !row[this.COMPLETED_KEY] && { backgroundColor: "#E1F3D8" } ); }, // 根据当前行的唯一key,找到map中缓存的行数据 // 就是dataGroup[0].find(item => item[uniqueKey] === row[uniqueKey]) // 然后判断DIFF_CELL_KEY数组中是否包含当前列的属性名 markCellStyles({ row, column }) { const { $_cacheMap, uniqueKey, DIFF_CELL_KEY } = this; const _cacheRow = $_cacheMap.get(row[uniqueKey]); return ( _cacheRow && _cacheRow[DIFF_CELL_KEY].includes(column.property) && { backgroundColor: "#FDE2E2" } ); } }
最后就是diff的处理了,直接用计算属性去做,处理完成之后返回新数据:
computed: { // 处理完成的数据 completedData({ dataGroup, uniqueKey, columns, DIFF_CELL_KEY, COMPLETED_KEY }) { // 这一步不是必要的,根据业务需求来,如果规定不能修改原数据的话就做一下深拷贝 const _dataGroup = deepClone(dataGroup); // Map<string|number, object>,ts不太熟,应该是这么写,其实就是row[unique]: row const cacheMap = new Map(); // 先遍历一次第一组数据,初始化DIFF_CELL_KEY数组,然后存进map中 for (const _row of _dataGroup[0]) { _row[DIFF_CELL_KEY] = []; cacheMap.set(_row[uniqueKey], _row); } // 遍历第二组数据,里面还有一次循环,因为只处理columns里面定义的属性,其他属性不做对比 for (const _row of _dataGroup[1]) { for (const { prop } of columns) { // 如果是唯一key就直接跳过 if (prop === uniqueKey) continue; // 从缓存中查找相同的一条数据 const original = cacheMap.get(_row[uniqueKey]); // 如果找不到就说明这条数据是新增的,直接跳过 if (!original) continue; // 否则就在两组数据中打一个标识表示已处理过,不是新增的 _row[COMPLETED_KEY] = true; original[COMPLETED_KEY] = true; // 最后对比两个属性值,如果相同就push进DIFF_CELL_KEY数组中 // 注意这里DIFF_CELL_KEY数组只存在于第一组数据当中 // 因为只要有差异就会在所有表格中显示,所以不用每一组数据都存 _row[prop] !== original[prop] && original[DIFF_CELL_KEY].push(prop); } } // 将map存一份到this中,因为会在处理样式的时候用到 this.$_cacheMap = cacheMap; return _dataGroup; } }
完事了,最后贴一下完整代码:
<template> <div class="diff-table-container"> <el-table v-for="(data, i) in completedData" :key="i" :data="data" :row-style="markRowStyles" :cell-style="markCellStyles" > <el-table-column v-for="item in columns" :key="`${i}${item.prop}`" v-bind="item" align="center" /> </el-table> </div> </template> <script> function deepClone(val) { // 看需求要不要做深拷贝 return val; } export default { name: "DiffTable", props: { uniqueKey: { type: String, default: "id" }, dataGroup: { type: Array, validator: val => val.length === 2 }, columns: { type: Array, required: true } }, data() { return { DIFF_CELL_KEY: Symbol("diffCells"), COMPLETED_KEY: Symbol("completed") }; }, computed: { completedData({ dataGroup, uniqueKey, columns, DIFF_CELL_KEY, COMPLETED_KEY }) { const _dataGroup = deepClone(dataGroup); const cacheMap = new Map(); for (const _row of _dataGroup[0]) { _row[DIFF_CELL_KEY] = []; cacheMap.set(_row[uniqueKey], _row); } for (const _row of _dataGroup[1]) { for (const { prop } of columns) { if (prop === uniqueKey) continue; const original = cacheMap.get(_row[uniqueKey]); if (!original) continue; _row[COMPLETED_KEY] = true; original[COMPLETED_KEY] = true; _row[prop] !== original[prop] && original[DIFF_CELL_KEY].push(prop); } } this.$_cacheMap = cacheMap; return _dataGroup; } }, methods: { markRowStyles({ row }) { return ( !row[this.COMPLETED_KEY] && { backgroundColor: "#E1F3D8" } ); }, markCellStyles({ row, column }) { const { $_cacheMap, uniqueKey, DIFF_CELL_KEY } = this; const _cacheRow = $_cacheMap.get(row[uniqueKey]); return ( _cacheRow && _cacheRow[DIFF_CELL_KEY].includes(column.property) && { backgroundColor: "#FDE2E2" } ); } } }; </script> <style lang="scss" scoped> .diff-table-container { display: flex; align-items: flex-start; .el-table + .el-table { margin-left: 20px; } } </style>
使用示例:
<template> <diff-table :data-group="[oldData, newData]" :columns="tableColumns" /> </template> <script> import DiffTable from "./DiffTable.vue"; export default { name: "Index", components: { DiffTable }, data() { return { oldData: [ { id: 1, name: "zhangsan1", age: 23, address: "zxczxczxc" }, { id: 2, name: "zhangsan2", age: 23.5, address: "zxczxczxc" }, { id: 3, name: "zhangsan34", age: 23, address: "zxczxczxc" }, { id: 4, name: "zhangsan4", age: 23, address: "zxczxczxc" }, { id: 5, name: "zhangsan5", age: 23, address: "zxczxczxc" }, { id: 6, name: "zhangsan5", age: 23, address: "zxczxczxc" } ], newData: [ { id: 1, name: "zhangsan1", age: 23, address: "zxczxczxc" }, { id: 2, name: "zhangsan2", age: 23, address: "zxczxczxc" }, { id: 4, name: "zhangsan4", age: 23, address: "地址地址地址" }, { id: 3, name: "zhangsan3", age: 23, address: "zxczxczxc" }, { id: 5, name: "zhangsan5", age: 23, address: "zxczxczxc" }, { id: 7, name: "zhangsan5", age: 23, address: "zxczxczxc" }, { id: 8, name: "zhangsan5", age: 23, address: "zxczxczxc" } ], tableColumns: [ { label: "唯一id", prop: "id" }, { label: "名称", prop: "name" }, { label: "年龄", prop: "age" }, { label: "地址", prop: "address" } ] }; } }; </script>
效果预览:
扩展功能TODO:
- 可配置n组数据进行对比
- 数据超过两组之后应该新增DELETE_ROW_KEY标记一条删除的数据
- 逻辑大概为:只存在于一组数据中的为新增;存在多组数据中但不是所有数据的,不包含的数据组内就要标记为删除的数据
- 可配置diff样式、自定义diff规则等
总结
到此这篇关于利用vue对比两组数据差异的可视化组件的文章就介绍到这了,更多相关vue对比两组数据差异内容请搜索NICE源码以前的文章或继续浏览下面的相关文章希望大家以后多多支持NICE源码!