antd 表格动态行合并

合并效果

在这里插入图片描述

步骤方法

1.在computed节点下动态计算每次要合并的行数

  computed: {
    columns() {
      return [
        {
          title: "区域",
          dataIndex: "area",
          customRender: (text, row, index) => {
            const obj = {
              children: text !== null ? text : "",
              attrs: {
                rowSpan: 1,
              },
            };
            obj.attrs.rowSpan = this.renderCells(text, this.currentTable, "area",index);
            return obj;
          },
        },
        {
          title: "名称",
          dataIndex: "name",
        },
        {
          title: "数量",
          dataIndex: "count",
        },
        {
          title: "进度",
          dataIndex: "progress",
        },
      ];
    },
  }

2.在methods节点下定义合并单元格的方法

renderCells(text, data, key, index) {
      if (data.length < 1) {
        return 1;
      }
      if (text === "" || text === null) {
        data[index].rowNum = 1;
        return 1;
      }
      // 上一行该列数据是否一样
      if (index !== 0 && text === data[index - 1][key] && index % this.pagination.pageSize != 0) {
        data[index].rowNum = 0;
        return 0;
      }
      let rowSpan = 1;
      // 判断下一行是否相等
      for (let i = index + 1; i < data.length; i++) {
        if (text !== data[i][key]) {
          break;
        }
        rowSpan++;
      }
      data[index].rowNum = rowSpan;
      return rowSpan;
    }

3.如果是一次性获取所有数据进行分页的话,计算columns的时候需要进行修改一下

    columns() {
      return [
        {
          title: "区域",
          dataIndex: "area",
          customRender: (text, row, index) => {
            const obj = {
              children: text !== null ? text : "",
              attrs: {
                rowSpan: 1,
              },
            };
            obj.attrs.rowSpan = this.renderCells(text, this.currentTable, "area", Number(index)+Number(this.pagination.pageSize * (this.pagination.current - 1)));
            return obj;
          },
        },
        {
          title: "名称",
          dataIndex: "name",
        },
        {
          title: "数量",
          dataIndex: "count",
        },
        {
          title: "进度",
          dataIndex: "progress",
        },
      ];
    },

参考文章:ant design vue 动态表格合并

合并效果

如果我们想要实现名称列相同的进行合并,需要在原来的代码上进行修改。合并的前提是区域相同,并且名称相同的情况下我们才进行合并,只需要在计算合并行数的时候需要加一层判断即可。
在这里插入图片描述

    renderCells(text, data, key, index) {
      if (data.length < 1) {
        return 1;
      }
      if (text === "" || text === null) {
        data[index].rowNum = 1;
        return 1;
      }
      // 上一行该列数据是否一样
      if (index !== 0 && text === data[index - 1][key] && index % this.pagination.pageSize != 0) {
        if(key === 'name'){
          if(data[index-1]['area'] === data[index]['area']){
            data[index].rowNum = 0
            return 0;
          }
        }else{
          data[index].rowNum = 0;
          return 0;
        }

      }
      let rowSpan = 1;
      // 判断下一行是否相等
      for (let i = index + 1; i < data.length; i++) {
        if(key === 'name' && data[i]['area'] !== data[index]['area']){
          break;
        }
        if (text !== data[i][key] ) {
          break;
        }
        rowSpan++;
      }
      data[index].rowNum = rowSpan;
      return rowSpan;
    },

单元格可编辑

非合并的行

可以参考官方的ant-design-vue可编辑单元格,官方示例如下:

<template>
  <div>
    <a-button class="editable-add-btn" @click="handleAdd">
      Add
    </a-button>
    <a-table bordered :data-source="dataSource" :columns="columns">
      <template slot="name" slot-scope="text, record">
        <editable-cell :text="text" @change="onCellChange(record.key, 'name', $event)" />
      </template>
      <template slot="operation" slot-scope="text, record">
        <a-popconfirm
          v-if="dataSource.length"
          title="Sure to delete?"
          @confirm="() => onDelete(record.key)"
        >
          <a href="javascript:;">Delete</a>
        </a-popconfirm>
      </template>
    </a-table>
  </div>
</template>
<script>
const EditableCell = {
  template: `
      <div class="editable-cell">
        <div v-if="editable" class="editable-cell-input-wrapper">
          <a-input :value="value" @change="handleChange" @pressEnter="check" /><a-icon
            type="check"
            class="editable-cell-icon-check"
            @click="check"
          />
        </div>
        <div v-else class="editable-cell-text-wrapper">
          {{ value || ' ' }}
          <a-icon type="edit" class="editable-cell-icon" @click="edit" />
        </div>
      </div>
    `,
  props: {
    text: String,
  },
  data() {
    return {
      value: this.text,
      editable: false,
    };
  },
  methods: {
    handleChange(e) {
      const value = e.target.value;
      this.value = value;
    },
    check() {
      this.editable = false;
      this.$emit('change', this.value);
    },
    edit() {
      this.editable = true;
    },
  },
};
export default {
  components: {
    EditableCell,
  },
  data() {
    return {
      dataSource: [
        {
          key: '0',
          name: 'Edward King 0',
          age: '32',
          address: 'London, Park Lane no. 0',
        },
        {
          key: '1',
          name: 'Edward King 1',
          age: '32',
          address: 'London, Park Lane no. 1',
        },
      ],
      count: 2,
      columns: [
        {
          title: 'name',
          dataIndex: 'name',
          width: '30%',
          scopedSlots: { customRender: 'name' },
        },
        {
          title: 'age',
          dataIndex: 'age',
        },
        {
          title: 'address',
          dataIndex: 'address',
        },
        {
          title: 'operation',
          dataIndex: 'operation',
          scopedSlots: { customRender: 'operation' },
        },
      ],
    };
  },
  methods: {
    onCellChange(key, dataIndex, value) {
      const dataSource = [...this.dataSource];
      const target = dataSource.find(item => item.key === key);
      if (target) {
        target[dataIndex] = value;
        this.dataSource = dataSource;
      }
    },
    onDelete(key) {
      const dataSource = [...this.dataSource];
      this.dataSource = dataSource.filter(item => item.key !== key);
    },
    handleAdd() {
      const { count, dataSource } = this;
      const newData = {
        key: count,
        name: `Edward King ${count}`,
        age: 32,
        address: `London, Park Lane no. ${count}`,
      };
      this.dataSource = [...dataSource, newData];
      this.count = count + 1;
    },
  },
};
</script>

在这里插入图片描述

动态合并可编辑单元格

实际运用过程中,可能会遇到需要动态合并可编辑的单元格,已知scopedSlots可以帮助我们实现编辑单元格,customRender中可以自定义动态合并单元格。但是经过实践发现这两者无法同时使用。参考ant-design-vue表格动态合并可编辑单元格的解决方法发现可以通过JSX语法来解决scopedSlots和customRender冲突的问题

  computed: {
    columns() {
      return [
        {
          title: "区域",
          dataIndex: "area",
          scopedSlots: { customRender: 'area' },
          customRender: (text, row, index) => {
            const obj = {
              children: <editable-cell text={text} onChange={(val) => this.onCellChange(row.id,'area',val)}></editable-cell>,
              attrs: {
                rowSpan: 1,
              },
            };
            obj.attrs.rowSpan = this.mergeCells(text, this.currentTable, "area", Number(index)+Number(this.pagination.pageSize * (this.pagination.current - 1)));
            return obj;
          },
        },
        {
          title: "名称",
          dataIndex: "name",
         
        },
        {
          title: "数量",
          dataIndex: "count",
        },
        {
          title: "进度",
          dataIndex: "progress",
        },
      ];
    },
  },
实现效果

在这里插入图片描述

Logo

前往低代码交流专区

更多推荐