关于antd vue table 表格(行,列)合并的一些方法分享

  • 使用的antd vue 版本(v4.26)
  • 对于开发中的业务报表需求,我们经常会遇到,合并表格单元格的场景,通过这些场景总结出一些可用的方法。
  • 主要涉及表格数据的行,列,行列,合并场景。

分析antd vue table 文档的中的操作方法:

  1. 表头只支持列合并,使用 column 里的 colSpan 进行设置。
  2. 表格支持行/列合并,使用 render 里的单元格属性 colSpan 或者 rowSpan 设置为 0 时,设置的表格不会渲染。
  3. 通过column配置项中的customCell|Function(record, rowIndex, column)进行映射数据进行合并。

示例:

使用行合并方法对指定的列进行合并(columns可能是拍平数组,可能是树数组,最后进行递归法兼容两种情况)

js 复制代码
/**
 * @desc 动态指定那一列 进行合并行
 * @param {Array} dataSource 表格数据
 * @param {Array} columns 表格列配置
 * @param {Array} mergeKeys 需要合并的列字段数组
 */
export const mergeRows = (dataSource, columns, mergeKeys) => {
  for (let i = 0; i < mergeKeys.length; i++) {
    const mergeInfo = {};
    const key = mergeKeys[i];
    // 遍历数据源,计算合并信息
    for (let j = 0; j < dataSource.length; j++) {
      const currentValue = dataSource[j][key];
      if (j === 0) {
        mergeInfo[j] = {
          rowSpan: 1,
          value: currentValue,
        };
      } else {
        const prevValue = dataSource[j - 1][key];
        if (currentValue === prevValue) {
          mergeInfo[j] = {
            rowSpan: 0,
            value: currentValue,
          };
          // 向前查找第一个  并更新连续相同值的最早单元格的rowSpan
          let start = j - 1;
          while (start >= 0 && mergeInfo[start].value === currentValue) {
            start--;
          }
          const firstIndex = start + 1; // 连续值的起始位置
          mergeInfo[firstIndex].rowSpan++;
        } else {
          mergeInfo[j] = {
            rowSpan: 1,
            value: currentValue,
          };
        }
      }
    }
    // 递归寻找 columns中的key === mergeKeys[i] 列,添加合并信息
    const findMergeCols = (cols, targetKey) => {
      for (let k = 0; k < cols.length; k++) {
        if (cols[k].key === targetKey) {
          // 重写 customCell 方法
          cols[k].customCell = (record, index) => {
            const { rowSpan } = mergeInfo[index];
            return {
              rowSpan,
            };
          };
          return;
        }
        if (cols[k]?.children && cols[k]?.children.length) {
          findMergeCols(cols[k].children, targetKey);
        }
      }
    };
    findMergeCols(columns, mergeKeys[i]);
  }
  return columns;
};
js 复制代码
在表格数据请求完进行方法调用对类别('kinds')进行行合并
state.tableData = res?.data 
mergeRows(state.tableData,columns,['kinds'])
  • 行列都合并
  • 首先我们需要对指标名称下构造多个相同列数据
  • 其次我们对后端数据进行约定多列时,进行返回 [{target:['vave','vave-1]},{target:['vave','vave-2]},['vave','vave-2]}]
  • 通过customCell的方法 我们进行构造合并行和列的二维数组,再进行数据映射合并
  • html渲染通过插槽索引 映射数值
js 复制代码
// 已知指标名称是行列合并
 unknownMergeCols(state.tableData, columns2, ['target']);
/**
 * @desc 不常规的处理列 合并
 */
export const unknownMergeCols = (dataSource, columns, targetArr) => {
  for (let i = 0; i < targetArr.length; i++) {
    const targetKey = targetArr[i];
    // 计算最大数组长度
    const max_length = dataSource.reduce((max, item) => {
      const value = item[targetKey];
      return Math.max(max, Array.isArray(value) ? value.length : 0);
    }, 0);

    // 递归查找并插入列
    const findMergeCols = (cols) => {
      for (let k = 0; k < cols.length; k++) {
        const col = cols[k];
        if (col.key === targetKey) {
          const arr = [];
          // selfIndex 增加列索引便于html渲染定位  colSpan合并表头
          for (let m = 0; m < max_length; m++) {
            arr.push({ ...col, selfIndex: m, colSpan: m === 0 ? max_length : 0 });
          }
          // 替换目标列为 所有副本arr 跳过已插入的副本,避免重复处理
          cols.splice(k, 1, ...arr);
          break;
        }
        // 递归处理子列
        if (col.children?.length) {
          findMergeCols(col.children);
        }
      }
    };
    findMergeCols(columns);
  }
  // return columns;
};

构造合并二维数组

js 复制代码
/**
 * @desc 构造合并信息的二维数组
 */
export const generateMergeInfo = (dataSource, columns) => {
  const mergeInfo = [];
  // 初始化 行合并信息
  for (let j = 0; j < columns.length; j++) {
    mergeInfo[j] = [];
    for (let i = 0; i < dataSource.length; i++) {
      if (i === 0) {
        // 第一个位置 处理 初始化为 1
        mergeInfo[j][i] = { rowSpan: 1 };
      } else {
        // 后续其他位置处理 和上一个不同的值 初始化为 1,和上一个相同的值 初始化为 0
        const prevValue = dataSource[i - 1][columns[j].key][j];
        const currentValue = dataSource[i][columns[j].key][j];
        if (currentValue === prevValue) {
          mergeInfo[j][i] = { rowSpan: 0 };
          // 向前查找第一个  并更新连续相同值的最早单元格的rowSpan
          let start = i - 1;
          while (start >= 0 && dataSource[start][columns[j].key][j] === currentValue) {
            start--;
          }
          const firstIndex = start + 1; // 连续值的起始位置
          mergeInfo[j][firstIndex].rowSpan++;
        } else {
          mergeInfo[j][i] = { rowSpan: 1 };
        }
      }
    }
  }
  console.log(mergeInfo, '....mergeInfo');

  // 初始化 列合并信息
  for (let i = 0; i < dataSource.length; i++) {
    for (let j = 0; j < columns.length; j++) {
      if (j === 0) {
        // 初始化第一个位置 初始化为 1
        mergeInfo[j][i].colSpan = 1;
      } else {
        // 后续其他位置处理 和上一个不同的值 初始化为 1,和上一个相同的值 初始化为 0
        const prevValue = dataSource[i][columns[j - 1].key][j - 1];
        const currentValue = dataSource[i][columns[j].key][j];
        if (prevValue === currentValue) {
          // 修正为当前单元格
          mergeInfo[j][i].colSpan = 0;
          // 向前查找第一个  并更新连续相同值的最早单元格的colSpan
          let start = j - 1;
          while (start >= 0 && dataSource[i][columns[start].key][start] === currentValue) {
            start--;
          }
          const firstIndex = start + 1; // 连续值的起始位置
          // 修正为正确的索引
          mergeInfo[firstIndex][i].colSpan++;
        } else {
          mergeInfo[j][i].colSpan = 1;
        }
      }
    }
  }
  console.log(mergeInfo, '....mergeInfo2');
  return mergeInfo;
};

// 打印合并二维数组的内容
[[
{rowSpan: 1, colSpan: 2}, {rowSpan: 3, colSpan: 1},
{rowSpan: 0, colSpan: 1}, {rowSpan: 0, colSpan: 1},
{rowSpan: 1, colSpan: 2}, {rowSpan: 1, colSpan: 2}],
[
{rowSpan: 1, colSpan: 0}, {rowSpan: 1, colSpan: 1},
{rowSpan: 1, colSpan: 1}, {rowSpan: 1, colSpan: 1},
{rowSpan: 1, colSpan: 0}, {rowSpan: 1, colSpan: 0}
]]

重写方法

js 复制代码
// 对构造复制的列columns 进行增加customCell方法
arr.forEach((item, colIndex) => {
      item.customCell = (record, rowIndex) => {
        const { rowSpan, colSpan } = mergeInfo[colIndex][rowIndex];
        return {
          rowSpan,
          colSpan,
        };
      };
    });
// 表格渲染 数据
     <a-table
        :columns="columns"
        :data-source="tableData"
        bordered
        size="small"
        :pagination="false"
        :loading="loading"
        :scroll="{ y: 540 }"
      >
        <template #bodyCell="{ column, record, index }">
            <template v-if="column.key === 'target'">
            {{ record.target[column.selfIndex] }} 
            </template>
          </template>
      </a-table>
        

总结:遇到问题不要慌,请看文档思考。 "遇事不决,可问春风。 --剑来"

相关推荐
TitusTong17 分钟前
使用 <think> 标签解析 DeepSeek 模型的推理过程
前端·ollama·deepseek
Hsuna17 分钟前
一句配置让你的小程序自动适应Pad端
前端·javascript
curdcv_po18 分钟前
Vue3移动电商实战 —— 外卖移动端轮播图实现
前端
渔樵江渚上21 分钟前
玩转图像像素:用 JavaScript 实现酷炫特效和灰度滤镜
前端·javascript·面试
hhope21 分钟前
Web江湖之令牌秘籍:Cookie vs LocalStorage,谁才是安全之王?
前端
ak啊22 分钟前
代码生成的核心环节-Template
前端·webpack·源码阅读
全栈然叔26 分钟前
五分钟部署Manus开源版本地应用
前端·后端
前端_yu小白27 分钟前
uniapp路由跳转导致页面堆积问题
前端·uni-app·页面跳转·返回
cong_37 分钟前
🌟 Cursor 帮我 2.5 天搞了一个摸 🐟 岛
前端·后端·github