As the title, a friend has such a demand, feel very common, issued to give you a reference (hydrology

Requirements:

  • withel-tableDisplay 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 withSymbolTagging 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:

  • configurablenGroup 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 asDeleted data
  • You can configure diFF styles and customize DIFF rules