查看: 198|回复: 0

Vue3 el-table与a-table合并行实现方法

[复制链接]
发表于 1 小时前 | 显示全部楼层 |阅读模式
本文围绕 Vue3 表格中相同相邻行的合并实现,分别说明 Element Plus 的 el-table 与 Ant Design Vue 的 a-table 的代码写法。两者的共同前提是:先让用于判断合并的字段有序,使相同值相邻,再遍历数据计算每一行的合并跨度;合并规则不是自动识别全表相同值,而是基于相邻行比较。示例均以 name 字段作为合并依据,实际项目可替换为业务分组字段。

一、el-table 通过 span-method 合并首列
el-table 使用 span-method 属性接收方法,方法参数包含 columnIndex、rowIndex,返回 { rowspan, colspan }。示例维护 spanArr:首行 push(1) 并把 position 设为 0;后续行若 name 与前一行相同,则 spanArr[position] 加 1,并 push(0) 表示该单元格被合并;否则 push(1) 并更新 position 为当前索引。objectSpanMethod 只对 columnIndex === 0 返回计算值,其余列返回 { rowspan: 1, colspan: 1 }。onMounted 中调用 sortData,sortData 先用 name.localeCompare 排序,再调用 calculateSpanArr 重算 spanArr。若接口已排序,可省略排序,但仍需计算 spanArr。
  1. <template>
  2.   <div class='about'>
  3.     <h2>element-plus 合并行示例</h2>
  4.     <el-table :data='tableData' :span-method='objectSpanMethod' border style='width: 100%'>
  5.       <el-table-column prop='name' label='Name' />
  6.       <el-table-column prop='age' label='Age' />
  7.       <el-table-column prop='address' label='Address' />
  8.     </el-table>
  9.   </div>
  10. </template>
  11. <script setup>
  12. import { ref, onMounted } from 'vue'
  13. const spanArr = ref([])
  14. const tableData = ref([
  15.   { key: '1', name: 'Mike', age: 30, address: 'Chicago' },
  16.   { key: '2', name: 'Alice', age: 22, address: 'Phoenix' },
  17.   { key: '3', name: 'John', age: 25, address: 'New York' },
  18.   { key: '4', name: 'Mike', age: 31, address: 'Houston' },
  19.   { key: '5', name: 'John', age: 26, address: 'Los Angeles' },
  20.   { key: '6', name: 'jack', age: 22, address: 'aPhoenix' }
  21. ])
  22. const sortData = () => {
  23.   tableData.value.sort((a, b) => a.name.localeCompare(b.name))
  24.   calculateSpanArr()
  25. }
  26. const calculateSpanArr = () => {
  27.   const tempArr = []
  28.   let position = 0
  29.   tableData.value.forEach((item, index) => {
  30.     if (index === 0) {
  31.       tempArr.push(1)
  32.       position = 0
  33.     } else if (item.name === tableData.value[index - 1].name) {
  34.       tempArr[position] += 1
  35.       tempArr.push(0)
  36.     } else {
  37.       tempArr.push(1)
  38.       position = index
  39.     }
  40.   })
  41.   spanArr.value = tempArr
  42. }
  43. const objectSpanMethod = ({ columnIndex, rowIndex }) => {
  44.   if (columnIndex === 0) {
  45.     return {
  46.       rowspan: spanArr.value[rowIndex],
  47.       colspan: spanArr.value[rowIndex] > 0 ? 1 : 0
  48.     }
  49.   }
  50.   return { rowspan: 1, colspan: 1 }
  51. }
  52. onMounted(() => {
  53.   sortData()
  54. })
  55. </script>
复制代码

注意:spanArr[rowIndex] 为 0 时 colspan 返回 0,用于隐藏被合并单元格;若 columnIndex 判断错误,会把合并应用到错误列。排序后重算很关键,数据变化后不重算会导致 rowspan 与行数据错位。

二、a-table 通过 columns.customCell 返回 rowSpan
a-table 的合并逻辑落在列定义的 customCell 中,返回 { rowSpan }。示例用 spanMap 保存每个索引的 span:计算方式与 el-table 类似,首行 span 为 1;与前一行 name 相同则累加 position 对应 span,并把当前索引 span 置 0;否则当前索引 span 为 1 并更新 position。customCell 的第二个参数是 index,据此从 spanMap 取 rowSpan。被合并行返回 rowSpan: 0 才会隐藏;dataSource 中需要 key 作为唯一标识。原文说明提到 customCell 和 customRender,具体示例的合并逻辑落在 customCell 的 rowSpan 返回值上。
  1. <template>
  2.   <div class='about'>
  3.     <h2>ant-design-vue 合并行示例</h2>
  4.     <a-table :dataSource='tableData' :columns='columns' bordered />
  5.   </div>
  6. </template>
  7. <script setup>
  8. import { ref, onMounted } from 'vue'
  9. const tableData = ref([
  10.   { key: '1', name: 'Mike', age: 30, address: 'Chicago' },
  11.   { key: '2', name: 'Alice', age: 22, address: 'Phoenix' },
  12.   { key: '3', name: 'John', age: 25, address: 'New York' },
  13.   { key: '4', name: 'Mike', age: 31, address: 'Houston' },
  14.   { key: '5', name: 'John', age: 26, address: 'Los Angeles' },
  15.   { key: '6', name: 'jack', age: 22, address: 'aPhoenix' }
  16. ])
  17. const spanMap = ref({})
  18. const calculateSpans = () => {
  19.   const tempMap = {}
  20.   let position = 0
  21.   tableData.value.forEach((item, index) => {
  22.     if (index === 0) {
  23.       tempMap[index] = { span: 1 }
  24.       position = 0
  25.     } else if (item.name === tableData.value[index - 1].name) {
  26.       tempMap[position].span += 1
  27.       tempMap[index] = { span: 0 }
  28.     } else {
  29.       tempMap[index] = { span: 1 }
  30.       position = index
  31.     }
  32.   })
  33.   spanMap.value = tempMap
  34. }
  35. const sortData = () => {
  36.   tableData.value.sort((a, b) => a.name.localeCompare(b.name))
  37.   calculateSpans()
  38. }
  39. const columns = [
  40.   {
  41.     title: 'Name',
  42.     dataIndex: 'name',
  43.     customCell: (_, index) => {
  44.       return { rowSpan: spanMap.value[index]?.span || 0 }
  45.     }
  46.   },
  47.   { title: 'Age', dataIndex: 'age' },
  48.   { title: 'Address', dataIndex: 'address' }
  49. ]
  50. onMounted(() => {
  51.   sortData()
  52. })
  53. </script>
复制代码

排序方法同样在 onMounted 中执行,先排序再 calculateSpans。若只排序不重算 spanMap,或漏掉 rowSpan: 0,会出现合并错位、重复显示。合并遍历是一次 O(n) 计算,不要在 customCell 内重复全量遍历;在数据源更新后重新计算 spanMap 更稳妥。

三、两种表格合并的差异与排查点
el-table 与 a-table 的差异主要在 API:el-table 返回 rowspan/colspan(小写),通过 span-method 统一处理;a-table 在 columns 的 customCell 返回 rowSpan(大写),按列配置。共同点是都要先排序让相同值相邻,再计算跨度;合并依据字段、排序字段和显示列要保持一致。适用场景是报表、分组列表等首列同名数据展示;如果合并字段不是相邻同名,示例逻辑不会自动合并。排查时优先检查四点:数据是否已按合并字段排序;spanArr 或 spanMap 是否在排序后重新计算;el-table 的 columnIndex 是否指向正确列;a-table 被合并行是否返回 rowSpan: 0。接口已排序时可以省略排序步骤,但合并跨度仍需计算。
回复

使用道具 举报

您需要登录后才可以回帖 登录 | 注册

本版积分规则

指导单位

江苏省公安厅

江苏省通信管理局

浙江省台州刑侦支队

DEFCON GROUP 86025

Hacking Group 021A

旗下站点

态势感知中心

应急响应中心

红盟安全

联系我们

官方QQ群:112851260

官方邮箱:security#ihonker.org(#改成@)

官方核心成员

关注微信公众号

Archiver|手机版|小黑屋| ( 沪ICP备2021026908号 )

GMT+8, 2026-10-25 14:38 , Processed in 0.029477 second(s), 29 queries , Gzip On.

Powered by ihonker.com

Copyright © 2015-现在.

  • 返回顶部