vue 项目中, ant-design-vue的实现a-table 表格实现合并列。

3,123 阅读4分钟

代码分析及 customCell 的使用详解

在这个代码片段中,customCell 是 Ant Design Vue 的 Table 组件中的一个功能,用于自定义单元格的属性(如 rowSpancolSpan),以实现行、列的合并。下面是关于这段代码的详细分解:


customCell 的作用

customCell 函数允许我们为表格的每一列定义自定义的单元格属性。通过返回的对象,可以控制:

  • colSpan:决定该单元格是否横跨多列,0 表示不渲染该单元格。
  • rowSpan:决定该单元格是否跨多行,0 表示该单元格和上一个单元格合并。

customCell 的代码逻辑

customCell: (_, index) => {
  if (index === 2) {
    return { rowSpan: 2 }; // 合并当前单元格和下一行的单元格(跨两行)
  }
  if (index === 3) {
    return { rowSpan: 0 }; // 当前单元格与上一行的单元格合并,不再渲染
  }
  if (index === 4) {
    return { colSpan: 0 }; // 当前单元格被合并到别的列,不再渲染
  }
}

1. index === 2

if (index === 2) {
  return { rowSpan: 2 };
}
  • 解释:在 index === 2 时,当前单元格将设置 rowSpan: 2,表示该单元格会纵向合并当前行和下一行的同一列的单元格。
  • 效果:这意味着 第3行的“Home phone”列会占据第3、4两行。

2. index === 3

if (index === 3) {
  return { rowSpan: 0 };
}
  • 解释:在 index === 3 时,当前单元格将设置 rowSpan: 0,表示该单元格与**前一行(index 2)**的单元格合并,并不会单独渲染。
  • 效果:这使得第4行的“Home phone”列第3行的单元格合并为一个。

3. index === 4

if (index === 4) {
  return { colSpan: 0 };
}
  • 解释:在 index === 4 时,当前单元格的 colSpan 设置为 0,表示该单元格不需要单独渲染,它会被横向合并到其他单元格中。
  • 效果:这会导致第5行的“Home phone”列不再显示,整个单元格被隐藏或合并。

示例效果

  • 第3行"Home phone" 列会跨第3、4行。
  • 第4行"Home phone" 列的单元格与第3行的合并,因此不再显示单独的单元格。
  • 第5行"Home phone" 列的单元格不会渲染,因为其 colSpan0

为什么需要 rowSpancolSpan

  • 使用 rowSpan 可以合并垂直方向的单元格,使其展示成一个整体。
  • 使用 colSpan 可以合并水平方向的单元格,隐藏不必要的重复信息。

通过这种方式,可以让表格看起来更加简洁,避免重复信息。像这种“合并单元格”的设计在需要分组显示数据时非常常见。


总结

customCell 函数通过返回 { rowSpan, colSpan } 控制单元格的渲染与合并。

  • 如果某个单元格已经被其他单元格合并,则通过设置 rowSpan: 0colSpan: 0 隐藏它。
  • rowSpan > 1 时,单元格会跨多行,节省空间。

你可以根据需求动态控制单元格的合并,提升表格的可读性。

如果你希望在 dataIndex = 'storageAssignment' 这一列进行合并,并且在合并后的单元格中间显示一个总和,可以通过设置 rowSpan 来合并行,同时动态计算合并后的总和,并在合并后的单元格内显示。

实现步骤

  1. 根据 storageAssignment 列的值进行分组,合并相同值的行。
  2. 合并后的单元格中显示总和(例如对其他列的数值求和)。
  3. 设置 rowSpanrowSpan: 0 以控制单元格合并。

代码实现

代码结构

<template>
  <a-table :columns="columns" :data-source="data" bordered>
    <template #bodyCell="{ column, text, record, index }">
      <template v-if="column.dataIndex === 'storageAssignment'">
        <span v-if="getRowSpan(index) > 0">
          {{ text }} - 总和: {{ getSum(record.storageAssignment) }}
        </span>
      </template>
    </template>
  </a-table>
</template>

<script lang="ts">
import { defineComponent, ref } from 'vue';
import type { TableColumnType } from 'ant-design-vue';

// 数据示例
const data = [
  { key: '1', storageAssignment: 'A', quantity: 10 },
  { key: '2', storageAssignment: 'A', quantity: 20 },
  { key: '3', storageAssignment: 'B', quantity: 15 },
  { key: '4', storageAssignment: 'B', quantity: 25 },
  { key: '5', storageAssignment: 'C', quantity: 30 },
];

export default defineComponent({
  setup() {
    const dataSource = ref(data);

    // 获取 `storageAssignment` 列的合并行数
    const getRowSpan = (index: number) => {
      const current = dataSource.value[index];
      const previous = dataSource.value[index - 1];

      // 如果是第一行或者值与上一行不同,则显示该组的行数
      if (!previous || current.storageAssignment !== previous.storageAssignment) {
        return dataSource.value.filter(
          (item) => item.storageAssignment === current.storageAssignment
        ).length;
      }

      // 否则设置为 0(隐藏当前单元格)
      return 0;
    };

    // 计算 `storageAssignment` 的总和
    const getSum = (assignment: string) => {
      return dataSource.value
        .filter((item) => item.storageAssignment === assignment)
        .reduce((sum, item) => sum + item.quantity, 0);
    };

    const columns: TableColumnType[] = [
      {
        title: 'Storage Assignment',
        dataIndex: 'storageAssignment',
        customCell: (_, index) => ({ rowSpan: getRowSpan(index) }),
      },
      {
        title: 'Quantity',
        dataIndex: 'quantity',
      },
    ];

    return {
      data: dataSource,
      columns,
      getRowSpan,
      getSum,
    };
  },
});
</script>

详细解释

  1. 合并逻辑:getRowSpan 函数

    • 遍历表格的每一行,如果某行的 storageAssignment 与上一行不同,则计算该组的行数并返回该值作为 rowSpan
    • 如果该单元格应该与上一行合并,则返回 0 隐藏该单元格。
    const getRowSpan = (index: number) => {
      const current = dataSource.value[index];
      const previous = dataSource.value[index - 1];
    
      if (!previous || current.storageAssignment !== previous.storageAssignment) {
        return dataSource.value.filter(
          (item) => item.storageAssignment === current.storageAssignment
        ).length;
      }
    
      return 0;
    };
    
  2. 求总和:getSum 函数

    • 遍历所有相同 storageAssignment 的数据,并累加 quantity 值。
    const getSum = (assignment: string) => {
      return dataSource.value
        .filter((item) => item.storageAssignment === assignment)
        .reduce((sum, item) => sum + item.quantity, 0);
    };
    
  3. 合并单元格并显示总和

    • 在合并后的单元格内,通过 {{ text }} - 总和: {{ getSum(record.storageAssignment) }} 显示当前分组的总和。

效果

  • 相同的 storageAssignment 值的行会合并,并且在合并后的单元格中间显示分组的总和
  • 例如:
    A - 总和: 30
    B - 总和: 40
    C - 总和: 30
    

总结

这段代码实现了:

  • 按照 storageAssignment 列的值进行分组并合并相同单元格。
  • 在合并后的单元格内显示该分组的总和。

这种方法在处理类似库存管理或仓储分配的表格时非常实用。你还可以根据需要对数据源和样式进行进一步的定制。