Vue.js 元素UI el-table: 如何合并单元格并小计

Vue.js Element UI el-table: How to merge cells and subtotal them

我得到了一个像这样的原件table

| category | name  | price  | count | subtotal |   
|----------|-------|--------|-------|----------| 
| fruit    | apple |   2    |   10  |    20    | 
|----------|-------|--------|-------|----------| 
| fruit    | apple |   3    |   20  |    60    | 
|----------|-------|--------|-------|----------| 
|  meat    | pork  |   4    |   10  |    40    | 
|----------|-------|--------|-------|----------| 
|  meat    | beef  |   5    |   20  |    50    | 

我尝试了一种方法来合并具有相同类别、名称的单元格,现在它变成了:

| category | name  | price  | count | subtotal |   
|----------|-------|--------|-------|----------| 
|          |       |   2    |   10  |    20    | 
|  fruit   | apple |--------|-------|----------| 
|          |       |   3    |   20  |    60    | 
|----------|-------|--------|-------|----------| 
|          |       |   4    |   10  |    40    | 
|   meat   | pork  |--------|-------|----------| 
|          |       |   5    |   20  |    50    | 

但是在上面table牛肉细胞消失了,我期望的是:

| category | name  | price  | count | subtotal |   
|----------|-------|--------|-------|----------| 
|          |       |   2    |   10  |    20    | 
|  fruit   | apple |--------|-------|----------| 
|          |       |   3    |   20  |    60    | 
|----------|-------|--------|-------|----------| 
|          | pork  |   4    |   10  |    40    | 
|   meat   |-------|--------|-------|----------| 
|          | beef  |   5    |   20  |    50    | 

我还想计算相同项目的小计,如下所示:

| category | name  | price  | count | subtotal | subtotal2 |    
|----------|-------|--------|-------|----------|-----------| 
|          |       |   2    |   10  |    20    |           | 
|  fruit   | apple |--------|-------|----------|     80    |
|          |       |   3    |   20  |    60    |           |  
|----------|-------|--------|-------|----------|-----------|  
|          | pork  |   4    |   10  |    40    |           |  
|   meat   |-------|--------|-------|----------|     90    |
|          | beef  |   5    |   20  |    50    |           |  

注:

我可以通过将 columnIndex === 5 添加到 objectSpanMethod 来合并 subtotal2 单元格,但数字不是我想要的: (现在小计2只显示每一项第一行的小计,但我想要小计的总和)

| category | name  | price  | count | subtotal | subtotal2 |    
|----------|-------|--------|-------|----------|-----------| 
|          |       |   2    |   10  |    20    |           | 
|  fruit   | apple |--------|-------|----------|     20    |
|          |       |   3    |   20  |    60    |           |  
|----------|-------|--------|-------|----------|-----------|  
|          | pork  |   4    |   10  |    40    |           |  
|   meat   |-------|--------|-------|----------|     40    |
|          | beef  |   5    |   20  |    50    |           |  

这是我的代码:

HTML

<div id="app">
<template>
    </el-table>
      <h1>DataTable<h1>
    <el-table
      :data="dataTable"
      :span-method="objectSpanMethod"
      style="width: 100%">
      <el-table-column
        prop="category"
        label="category"
        width="180">
      </el-table-column>
      <el-table-column
        prop="name"
        label="name"
        width="180">
      </el-table-column>
       <el-table-column
        prop="price"
        label="price"
        width="180">
      </el-table-column>
      <el-table-column
        prop="count"
        label="count"
        width="180">
      </el-table-column>
       <el-table-column
        label="subtotal"
        align="center"
        prop="subtotal"
        width="180">
       <template slot-scope="scope"> {{scope.row.subtotal=scope.row.price*scope.row.count}}
      </template>
      </el-table-column>
    </el-table>
  </template>
</div>

JS

      data() {
        return {
          dataTable: [{
            category: 'fruit',
            name: 'apple',
            price: 2,
            count: 10,
            subtotal:1
          },{
            category: 'fruit',
            name: 'apple',
            price: 3,
            count: 20,
            subtotal:1
          },{
            category: 'meat',
            name: 'pork',
            price: 4,
            count: 10,
            subtotal:1
          },{
            category: 'meat',
            name: 'beef',
            price: 5,
            count: 10,
            subtotal:1
          }],
           spanArr: [],
        }
      },
 
  methods:{
      onMergeLines() {
      this.dataTable.forEach((item, index) => {
        if (index === 0) {
          this.spanArr.push(1);
          this.position = 0;
        } else {
          if (
            this.dataTable[index].category ===
            this.dataTable[index - 1].category
          ) {
            this.spanArr[this.position] += 1;
            this.spanArr.push(0);
          } else {
            this.spanArr.push(1);
            this.position = index;
          }
        }
      });
    },
    
    objectSpanMethod({ row, column, rowIndex, columnIndex }) {
      console.log(row, column);
      if (columnIndex === 0 || columnIndex === 1 ) {
        const _row = this.spanArr[rowIndex];
        const _col = _row > 0 ? 1 : 0;
        return {
          rowspan: _row,
          colspan: _col,
        };
      }
    }
    },
  mounted() {
    this.onMergeLines();
  },
  }

还有 CodePen:https://codepen.io/itayueat/pen/qBPrYdV

如果你有什么想法,请告诉我table,谢谢!

我的代码比较臃肿,基本可以实现你想要的功能。如果需要,您可以进一步简化它。

var Main = {
  data() {
    return {
      dataTable: [
        {
          date: "11.26",
          category: "fruit",
          name: "apple",
          price: 2,
          count: 10,
          subtotal: 1
        },
        {
          date: "11.26",
          category: "fruit",
          name: "apple",
          price: 3,
          count: 20,
          subtotal: 1
        },
        {
          date: "11.26",
          category: "meat",
          name: "pork",
          price: 4,
          count: 10,
          subtotal: 1
        },
        {
          date: "11.26",
          category: "meat",
          name: "beef",
          price: 5,
          count: 10,
          subtotal: 1
        },
        {
          date: "11.26",
          category: "car",
          name: "motorcycle",
          price: 3,
          count: 20,
          subtotal: 1
        },
        {
          date: "11.27",
          category: "car",
          name: "motorcycle",
          price: 3,
          count: 20,
          subtotal: 1
        },
        {
          date: "11.27",
          category: "car",
          name: "motorcycle",
          price: 3,
          count: 20,
          subtotal: 1
        }
      ]
    };
  },
  created() {
    this.onMergeLines();
  },
  methods: {
    // Processing data calculation
    onMergeLines() {
      const propertys = ["date", "category", "name"];
      const dataTable = this.dataTable;
      this.dataTable = dataTable.map((item, index) => {
        preRow = this.dataTable[index - 1] || {};
        item.subtotal = item.count * item.price || 0;
        item = diffProperty(item, index, 0);
        return item;
      });

      function diffProperty(current, index, keyIndex) {
        const key = propertys[keyIndex],
          preRow = dataTable[index - 1] || {};
        if (current[key] === preRow[key]) {
          if (keyIndex < propertys.length) {
            return diffProperty(current, index, keyIndex + 1);
          } else {
            current.subtotal2 = preRow.subtotal2;
            return current;
          }
        } else {
          let subtotal2 = current.count * current.price || 0;
          for (let i = index + 1; i < dataTable.length; i++) {
            const nextRow = dataTable[i];
            console.log(
              propertys.filter((prop) => current[prop] === nextRow[prop])
                ?.length || 0,
              "c"
            );
            if (
              (propertys.filter((prop) => current[prop] === nextRow[prop])
                ?.length || 0) === propertys.length
            ) {
              subtotal2 += nextRow["count"] * nextRow["price"] || 0;
            } else {
              break;
            }
          }
          current.subtotal2 = subtotal2;
          return current;
        }
      }
    },

    objectSpanMethod({ row, column, rowIndex, columnIndex }) {
      //The index of the column we want to merge
      if (![0, 1, 2, 6].includes(columnIndex)) {
        return {
          rowspan: 1,
          colspan: 1
        };
      }

      const dataTable = this.dataTable,
        property = column["property"];
      const tableLen = dataTable.length;
      let _row = 1,
        _col = 1;
      const preRow = dataTable[rowIndex - 1] || {};

      if (
        row.date === preRow.date &&
        (property === "date" || preRow[property] === row[property])
      ) {
        _row = 0;
      } else if (rowIndex + 1 < tableLen) {
        for (let i = rowIndex + 1; i < tableLen; i++) {
          const nextRow = dataTable[i];
          if (
            nextRow.date === row.date &&
            (property === "date" ||
              (nextRow.category === row.category &&
                row[property] === nextRow[property]))
          ) {
            _row++;
          } else {
            break;
          }
        }
      }

      return {
        rowspan: _row,
        colspan: _col
      };
    }
  }
};
var Ctor = Vue.extend(Main);
new Ctor().$mount("#app");
@import url("//unpkg.com/element-ui@2.15.7/lib/theme-chalk/index.css");
<script src="//unpkg.com/vue/dist/vue.js"></script>
<script src="//unpkg.com/element-ui@2.15.7/lib/index.js"></script>
<div id="app">
  <template>

    </el-table>
    <h1>DataTable<h1>
        <el-table border :data="dataTable" :span-method="objectSpanMethod" style="width: 100%">
          <el-table-column prop="date" label="date" width="180">
          </el-table-column>
          <el-table-column prop="category" label="category" width="180">
          </el-table-column>
          <el-table-column prop="name" label="name" width="180">
          </el-table-column>
          <el-table-column prop="price" label="price" width="180">
          </el-table-column>
          <el-table-column prop="count" label="count" width="180">
          </el-table-column>
          <el-table-column label="subtotal" align="center" prop="subtotal" width="180">
          </el-table-column>
          <el-table-column label="subtotal2" align="center" prop="subtotal2" width="180">
          </el-table-column>
        </el-table>
  </template>
</div>