iview表格行列合并

发布时间 2023-12-07 11:52:28作者: 柯基与佩奇

官网 demo 并没有对 handleSpan 方法进行详细说明
demo 运行效果如下图

可以看到,第一行的第一列和第二列合并,第三行第一列和第四行第一列合并

这时,我们再看代码

先看    处的代码,rowIndex === 0 指的是第一行, columnIndex === 0 指的是第一列,rowIndexcolumnIndex 是从 0 开始而不是从 1 开始。

if (rowIndex === 0 && columnIndex === 0) 表示的是第一行第一列

return [1, 2] 表示合并 1 行 2 列

else if (rowIndex === 0 && columnIndex === 1)  是指第一行,第二列

return  [0, 0] 表示合并 0 行 0 列,即这是被合并的单元格

这里被合并的单元格一定要返回  return  [0, 0],否则会出现数据下串

上方 处代码表示以第 1 行第 1 列的单元格数据为主,向右合并 1 行 2 列,而第 1 行第 2 列单元格作为被合并的单元格,要返回 return  [0, 0]

处的代码道理相同,以第 3 行第 1 列单元格数据为主向下合并 1 行 2 列,而被合并的第 4 行第 1 列返回  return  [0, 0](这里既可以返回数组,也可以返回对象)

  和    处的代码可以推出,返回 return  [1, 1],表示合并 1 行 1 列,即保持单元格原样,不进行合并

明白这个道理后,我们试着改动一下,看看能不能实现想要的效果

这里我想合并第一行的第二列和第三列,合并第二行第一列第三行第一列和第四行第一列,代码如下

<template>
  <table
    :columns="columns14"
    :data="data5"
    border
    :span-method="handleSpan"
  ></table
></template>
<script>
  export default {
    data() {
      return {
        columns14: [
          { title: "Date", key: "date" },
          { title: "Name", key: "name" },
          { title: "Age", key: "age" },
          { title: "Address", key: "address" },
        ],
        data5: [
          {
            name: "John Brown",
            age: 18,
            address: "New York No. 1 Lake Park",
            date: "2016-10-03",
          },
          {
            name: "Jim Green",
            age: 24,
            address: "London No. 1 Lake Park",
            date: "2016-10-01",
          },
          {
            name: "Joe Black",
            age: 30,
            address: "Sydney No. 1 Lake Park",
            date: "2016-10-02",
          },
          {
            name: "Jon Snow",
            age: 26,
            address: "Ottawa No. 2 Lake Park",
            date: "2016-10-04",
          },
        ],
      };
    },
    methods: {
      handleSpan({ row, column, rowIndex, columnIndex }) {
        if (rowIndex === 0 && columnIndex === 1) {
          return [1, 2];
        } else if (rowIndex === 0 && columnIndex === 2) {
          return [0, 0];
        }
        if (rowIndex === 1 && columnIndex === 0) {
          return { rowspan: 3, colspan: 1 };
        } else if (rowIndex === 2 && columnIndex === 0) {
          return { rowspan: 0, colspan: 0 };
        } else if (rowIndex === 3 && columnIndex === 0) {
          return { rowspan: 0, colspan: 0 };
        }
      },
    },
  };
</script>

上边代码就不做过多解释了,需要注意的是,第二行第一列第三行第一列和第四行第一列合并时,因为有两个被合并的单元格,因此需要两个都返回  return  [0, 0],否则会出现数据和样式问题

效果如下图

相信通过上边两个示例 demo,你已经学会了合并单元格的基本使用,下面看看在项目中如何使用吧

2、项目中的使用

观察上面代码,发现我们没有使用 row 对象和  column 对象,而在实际项目中,也不可能像上边代码那样通过 rowIndex 和  columnIndex  在前端将合并的单元格写死,而是需要根据后台返回给前端的数据进行逻辑判断,决定合并哪些单元格

这里我们讲解一个示例,还用上边的表结构

假设我们想合并 name 值相同的单元格

先修改一下数据,使 name 值相同,再清空  handleSpan 方法,方便查看,代码如下

<template>
  <table
    :columns="columns14"
    :data="data5"
    border
    :span-method="handleSpan"
  ></table
></template>
<script>
  export default {
    data() {
      return {
        columns14: [
          { title: "Date", key: "date" },
          { title: "Name", key: "name" },
          { title: "Age", key: "age" },
          { title: "Address", key: "address" },
        ],
        data5: [
          {
            name: "John Brown",
            age: 18,
            address: "New York No. 1 Lake Park",
            date: "2016-10-03",
          },
          {
            name: "John Brown",
            age: 24,
            address: "London No. 1 Lake Park",
            date: "2016-10-01",
          },
          {
            name: "Joe Black",
            age: 30,
            address: "Sydney No. 1 Lake Park",
            date: "2016-10-02",
          },
          {
            name: "Jon Snow",
            age: 26,
            address: "Ottawa No. 2 Lake Park",
            date: "2016-10-04",
          },
        ],
      };
    },
    methods: { handleSpan({ row, column, rowIndex, columnIndex }) {} },
  };
</script>

运行效果如图

第一行和第二行 name 值相同,下面研究如何合并第 1 行第 2 列和第 2 行第 2 列

这里我们将 data 中的  data5 改为 resData,而将  data5 改为空数组,用 resData 模拟后台返回前端的数据

需要注意,在合并行时,因为需要合并,所以需要合并的数据必须相临,即合并者与被合并者在数组中的下标要相临,否则无法合并。这里我们直接将  resData 中需要合并的数据设为相临,即第一条和第二条,而在实际项目中这块需要前端或后台开发人员根据业务逻辑自己实现

经过以上修改后,代码如下

<template>
  <table
    :columns="columns14"
    :data="data5"
    border
    :span-method="handleSpan"
  ></table
></template>
<script>
  export default {
    data() {
      return {
        columns14: [
          { title: "Date", key: "date" },
          { title: "Name", key: "name" },
          { title: "Age", key: "age" },
          { title: "Address", key: "address" },
        ],
        resData: [
          {
            id: "1",
            name: "John Brown",
            age: 18,
            address: "New York No. 1 Lake Park",
            date: "2016-10-03",
          },
          {
            id: "3",
            name: "John Brown",
            age: 24,
            address: "London No. 1 Lake Park",
            date: "2016-10-01",
          },
          {
            id: "2",
            name: "Joe Black",
            age: 30,
            address: "Sydney No. 1 Lake Park",
            date: "2016-10-02",
          },
          {
            id: "4",
            name: "Jon Snow",
            age: 26,
            address: "Ottawa No. 2 Lake Park",
            date: "2016-10-04",
          },
        ],
        data5: [],
      };
    },
    methods: { handleSpan({ row, column, rowIndex, columnIndex }) {} },
  };
</script>

上面 resData 中,name 为  John Brown 的数据已经相临,分别是第一条和第二条

接下来我们需要对  resData 中的数据进行处理,从而实现 name 值相同的行进行合并

在 methods 新建一个方法 assembleData(data),用来对数据进行处理。在这个方法中要实现以下逻辑,首先将 name 值相同的数据筛选出来,然后根据 name 值相同的数据个数计算出需要合并的行数,我们的示例中有 2 条 name 值相同的数据,即需要合并 2 行,这两行其中之一返回 return  [2, 1],因为是 2 行 1 列,所以返回 2,1;另一个返回 return  [0, 0],因为是被合并单元格,其他不需要合并的单元格返回 return  [1, 1],最后将筛选后的数据进行标记,给到 Table 表格控件,Table 表格控件调用属性 span-method 的方法,进行单元格行列合并

下面就直接上代码了

<template>
  <Table
    :columns="columns14"
    :data="data5"
    border
    :span-method="handleSpan"
  ></Table>
</template>
<script>
export default {
  data() {
    return {
      columns14: [
        {
          title: "Date",
          key: "date",
        },
        {
          title: "Name",
          key: "name",
        },
        {
          title: "Age",
          key: "age",
        },
        {
          title: "Address",
          key: "address",
        },
      ],
      resData: [
        {
          id: "1",
          name: "John Brown",
          age: 18,
          address: "New York No. 1 Lake Park",
          date: "2016-10-03",
        },
        {
          id: "3",
          name: "John Brown",
          age: 24,
          address: "London No. 1 Lake Park",
          date: "2016-10-01",
        },
        {
          id: "2",
          name: "Joe Black",
          age: 30,
          address: "Sydney No. 1 Lake Park",
          date: "2016-10-02",
        },
        {
          id: "4",
          name: "Jon Snow",
          age: 26,
          address: "Ottawa No. 2 Lake Park",
          date: "2016-10-04",
        },
      ],
      data5: [],
    };
  },
  methods: {
    handleSpan({ row, column, rowIndex, columnIndex }) {
      //合并第二列,这里columnIndex==1 根据具体业务要在前端写死
      if (columnIndex == 1) {
        //计算合并的行数列数
        let x = row.mergeCol == 0 ? 0 : row.mergeCol;
        let y = row.mergeCol == 0 ? 0 : 1;
        //console.log(x , y)
        return [x, y];
      }
    },
    assembleData(data) {
      let names = [];
      //筛选出不重复的 name值,将其放到 names数组中
      data.forEach((e) => {
        if (!names.includes(e.name)) {
          names.push(e.name);
        }
      });

      let nameNums = [];
      //将names数组中的 name值设置默认合并0个单元格,放到 nameNums中
      names.forEach((e) => {
        nameNums.push({ name: e, num: 0 });
      });

      //计算每种 name值所在行需要合并的单元格数
      data.forEach((e) => {
        nameNums.forEach((n) => {
          if (e.name == n.name) {
            n.num++;
          }
        });
      });

      //console.log(nameNums)

      //将计算后的合并单元格数整合到 data中
      data.forEach((e) => {
        nameNums.forEach((n) => {
          if (e.name == n.name) {
            if (names.includes(e.name)) {
              e.mergeCol = n.num;
              //删除已经设置过的值(防止被合并的单元格进到这个 if 语句中)
              names.splice(names.indexOf(n.name), 1);
            } else {
              //被合并的单元格设置为 0
              e.mergeCol = 0;
            }
          }
        });
      });

      //将整理后的数据交给表格渲染
      this.data5 = data;
    },
  },
  mounted() {
    //这里 this.resData 模拟后台返回的数据
    this.assembleData(this.resData);
  },
};
</script>

相关说明已经写在代码注释中

运行效果如下图

可以看到,name 值相同的行已经成功合并单元格

下面我们再看一种情况,就是当我们对数据进行改动后,该如何处理

如删除第一条数据。其实也很简单,在删除后,重新请求后台获取最新的 resData,然后调用 assembleData 方法即可,示例代码如下

<template>
  <div>
    <Table
      :columns="columns14"
      :data="data5"
      border
      :span-method="handleSpan"
    ></Table>
    <Button @click="deleteData" type="error">删除</Button>
  </div>
</template>
<script>
export default {
  data() {
    return {
      columns14: [
        {
          title: "Date",
          key: "date",
        },
        {
          title: "Name",
          key: "name",
        },
        {
          title: "Age",
          key: "age",
        },
        {
          title: "Address",
          key: "address",
        },
      ],
      resData: [
        {
          id: "1",
          name: "John Brown",
          age: 18,
          address: "New York No. 1 Lake Park",
          date: "2016-10-03",
        },
        {
          id: "3",
          name: "John Brown",
          age: 24,
          address: "London No. 1 Lake Park",
          date: "2016-10-01",
        },
        {
          id: "2",
          name: "Joe Black",
          age: 30,
          address: "Sydney No. 1 Lake Park",
          date: "2016-10-02",
        },
        {
          id: "4",
          name: "Jon Snow",
          age: 26,
          address: "Ottawa No. 2 Lake Park",
          date: "2016-10-04",
        },
      ],
      data5: [],
      newresData: [
        {
          id: "3",
          name: "John Brown",
          age: 24,
          address: "London No. 1 Lake Park",
          date: "2016-10-01",
        },
        {
          id: "2",
          name: "Joe Black",
          age: 30,
          address: "Sydney No. 1 Lake Park",
          date: "2016-10-02",
        },
        {
          id: "4",
          name: "Jon Snow",
          age: 26,
          address: "Ottawa No. 2 Lake Park",
          date: "2016-10-04",
        },
      ],
    };
  },
  methods: {
    handleSpan({ row, column, rowIndex, columnIndex }) {
      //合并第二列,这里columnIndex==1 根据具体业务要在前端写死
      if (columnIndex == 1) {
        //计算合并的行数列数
        let x = row.mergeCol == 0 ? 0 : row.mergeCol;
        let y = row.mergeCol == 0 ? 0 : 1;
        //console.log(x , y)
        return [x, y];
      }
    },
    assembleData(data) {
      let names = [];
      //筛选出不重复的 name值,将其放到 names数组中
      data.forEach((e) => {
        if (!names.includes(e.name)) {
          names.push(e.name);
        }
      });

      let nameNums = [];
      //将names数组中的 name值设置默认合并0个单元格,放到 nameNums中
      names.forEach((e) => {
        nameNums.push({ name: e, num: 0 });
      });

      //计算每种 name值所在行需要合并的单元格数
      data.forEach((e) => {
        nameNums.forEach((n) => {
          if (e.name == n.name) {
            n.num++;
          }
        });
      });

      //console.log(nameNums)

      //将计算后的合并单元格数整合到 data中
      data.forEach((e) => {
        nameNums.forEach((n) => {
          if (e.name == n.name) {
            if (names.includes(e.name)) {
              e.mergeCol = n.num;
              //删除已经设置过的值(防止被合并的单元格进到这个 if 语句中)
              names.splice(names.indexOf(n.name), 1);
            } else {
              //被合并的单元格设置为 0
              e.mergeCol = 0;
            }
          }
        });
      });

      //将整理后的数据交给表格渲染
      this.data5 = data;
    },
    deleteData() {
      //这里 this.newresData 模拟删除后,后台返回的数据
      this.assembleData(this.newresData);
    },
  },
  mounted() {
    //这里 this.resData 模拟后台返回的数据
    this.assembleData(this.resData);
  },
};
</script>

运行效果

添加、修改数据时用法一样,但需要注意,添加数据时将要合并的数据放在相临的位置