1. 程式人生 > 程式設計 >利用vue對比兩組資料差異的視覺化元件詳解

利用vue對比兩組資料差異的視覺化元件詳解

目錄
  • 需求:
  • 大概要點:
  • 根據剛才的要點可以建立一下元件的props:
  • 元件的基本樣式也很簡單:
  • 完事了,最後貼一下完整程式碼:
  • 使用示例:
  • 效果預覽:
  • 擴充套件功能TODO:
  • 總結

如題,朋友有個這樣的需求,感覺挺常見,發出來給大家參考一下

需求:

用el-table展示兩組資料,有差異的單元格顯示紅色,新增的顯示整行綠色

大概要點:

  • 需要一個數據組,裡面包含兩組需要對比的資料
  • 需要一個唯一的key,用來確定某一行的資料是否在其他資料中存在(是否是新增
  • 接受一個表格列的配置,用於渲染表格,同時對比差異只按照配置的資料來,gMPenrl其他的資料無需進行對比

根據剛才的要點可以建立一下元件的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="s" 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[thisgMPenrl.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,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,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,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.";

export default {
  name: "Index",components: {
    DiffTable
  },data() {
    return {
      oldData: [
        { id: 1,name: "zhangsan1",age: 23,address: "zxczxczxc" },{ id: 2,name: "zhangsan2",age: 23.5,{ id: 3,name: "zhangsan34",{ id: 4,name: "zhangsan4",{ id: 5,name: "zhangsan5",age: 2gMPenrl3,{ id: 6,address: "zxczxczxc" }
      ],newData: [
gMPenrl        { id: 1,address: "地址地址地址" },name: "zhangsan3",{ id: 7,{ id: 8,tableColumns: [
        { label: "唯一id",prop: "id" },{ label: "名稱",prop: "name" },{ label: "年齡",prop: "age" },{ label: "地址",prop: "address" }
      ]
    };
  }
};
</script>

效果預覽:

利用vue對比兩組資料差異的視覺化元件詳解

擴充套件功能TODO:

  • 可配置n組資料進行對比
  • 資料超過兩組之後應該新增DELETE_ROW_KEY標記一條刪除的資料
    • 邏輯大概為:只存在於一組資料中的為新增;存在多組資料中但不是所有資料的,不包含的資料組內就要標記為刪除的資料
  • 可配置diff樣式、自定義diff規則等

總結

到此這篇關於利用vue對比兩組資料差異的視覺化元件的文章就介紹到這了,更多相關vue對比兩組資料差異內容請搜尋我們以前的文章或繼續瀏覽下面的相關文章希望大家以後多多支援我們!