As the title, a friend has such a demand, feel very common, issued to give you a reference (hydrology
Requirements:
- with
el-table
Display two sets of data, with the difference cells in red and the new rows in green
Outline:
- You need a data set that contains two sets of data to compare
- A unique key is required to determine whether a row of data exists in other data
- Accept the configuration of a table column, used to render the table, and compare the differences only according to the configured data, other data need not be compared
Props for the component can be set up as follows:
props: {
uniqueKey: {
type: String.default: "id"
},
dataGroup: {
type: Array.validator: val= > val.length === 2
},
columns: {
type: Array.required: true}}Copy the code
The default unique ID is ID. Columns are defined as {label, prop… }
The basic style of the component is also simple:
<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>
Copy the code
As shown above, the two tables are simply arranged horizontally. CompletedData here refers to the data that has been diff processed, in the same format as the dataGroup passed in. MarkRowStyles and markRowStyles are both provided by el-Table and refer to row and column styles, respectively, with values of objects or functions that return an object.
Let’s define twoSymbol
, and then marks the data when diff the data withSymbol
Tagging prevents attribute name conflicts.
data() {
return {
DIFF_CELL_KEY: Symbol("diffCells"), // An array of different cell property names
COMPLETED_KEY: Symbol("completed") // Mark completed processing
};
}
Copy the code
Then the diff styling can be determined directly.
methods: {
// If there is no mark after processing, it means that only one set of data appears, i.e., new data
markRowStyles({ row }) {
return (
!row[this.COMPLETED_KEY] && {
backgroundColor: "#E1F3D8"}); },// Find the row data cached in the map based on the unique key of the current row
DataGroup [0]. Find (item => item[uniqueKey] === row[uniqueKey])
// Then determine whether the DIFF_CELL_KEY array contains the attribute name of the current column
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"}); }}Copy the code
Diff = diff; diff = diff; diff = diff;
computed: {
// Process the finished data
completedData({ dataGroup, uniqueKey, columns, DIFF_CELL_KEY, COMPLETED_KEY }) {
// This step is not necessary, according to the business requirements, if the original data cannot be modified, do a deep copy
const _dataGroup = deepClone(dataGroup);
/ / Map < string | number, object >, ts is not ripe; they should be written, so is the row (unique) : the row
const cacheMap = new Map(a);// Iterate over the first group of data, initialize the DIFF_CELL_KEY array, and store it in the map
for (const _row of _dataGroup[0]) {
_row[DIFF_CELL_KEY] = [];
cacheMap.set(_row[uniqueKey], _row);
}
// Run the second set of columns through another loop, because only the attributes defined by columns are processed and the other attributes are not compared
for (const _row of _dataGroup[1]) {
for (const { prop } of columns) {
// If the key is unique, skip it
if (prop === uniqueKey) continue;
// Find the same data from the cache
const original = cacheMap.get(_row[uniqueKey]);
// If you can't find this item, skip it directly
if(! original)continue;
// If not, put a mark in the two sets of data to indicate that it has been processed, not new
_row[COMPLETED_KEY] = true;
original[COMPLETED_KEY] = true;
// Finally, compare the two values and push them into the DIFF_CELL_KEY array if they are the same
// Note that the DIFF_CELL_KEY array exists only in the first group of data
// Since any differences are shown in all tables, there is no need to store every set of data
_row[prop] !== original[prop] && original[DIFF_CELL_KEY].push(prop);
}
}
// Save a copy of the map in this, as it will be used for styling purposes
this.$_cacheMap = cacheMap;
return_dataGroup; }}Copy the code
That’s it. Here’s the complete code:
<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) {
// See if you want to make a deep copy
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(a);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>
Copy the code
Example:
<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: "Address address 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: "The only id".prop: "id" },
{ label: "Name".prop: "name" },
{ label: "Age".prop: "age" },
{ label: "Address".prop: "address"}}; }};</script>
Copy the code
Effect preview:
Extended features TODO:
- configurable
n
Group data for comparison - DELETE_ROW_KEY should be added after more than two groups of data are deleted
- The logic goes something like this: an addition exists only in one set of data; If there is more than one data group but not all data, the data group that does not contain shall be marked as
Deleted data
- The logic goes something like this: an addition exists only in one set of data; If there is more than one data group but not all data, the data group that does not contain shall be marked as
- You can configure diFF styles and customize DIFF rules