ant-design-vue自动计算a-table每一列的宽度的实现

基于 Canvas 的ant-design-vue的表格列宽自动计算方案

声明: 本文章借鉴了这位朋友的算法,对进行了优化和改正。Ant Design Table自适应列宽,就是这么简单!前言 目前在弄一个动态表格的项目,但由于没法知道每个列具体的内 - 掘金

前言

在开发后台管理系统时,表格是最常用的组件之一。Ant Design Vue 的 Table 组件虽然功能强大,但在列宽自适应方面存在一些不足。本文将介绍一种基于 Canvas 的表格列宽自动计算方案,帮助开发者摆脱手动写width的烦恼。

问题分析

在使用 Ant Design Vue 的 Table 组件时,我们经常会遇到以下问题:

  1. 列宽设置不合理导致内容显示不完整
  2. 手动设置列宽需要反复调整
  3. 不同数据量下同一列的宽度需求不同
  4. 表头文字和内容文字长度不一致

解决方案

核心思路是:

  1. 使用 Canvas 测量文本实际宽度
  2. 遍历数据计算每列最大宽度
  3. 考虑表头文字宽度
  4. 添加适当的内边距

核心代码实现

typescript 复制代码
import type { ColumnType } from "ant-design-vue/es/table";

// 创建 Canvas 上下文
// 使用立即执行函数,避免每一次都重新创建canvas节点以增加使用成本
let context: CanvasRenderingContext2D | null = null;
(() => {
  const canvas = document.createElement("canvas");
  context = canvas.getContext("2d");
})();

// 获取文本宽度
function getTextWidth(text: string, font = "14px Microsoft YaHei") {
  if (!context) return 0;
  context.font = font;
  const textmetrics = context.measureText(text);
  return textmetrics.width;
}

/**
 * 计算表格列宽
 * @param columns 表格表头
 * @param data 表格数据
 * @param options 配置项
 * @returns 计算后的列宽
 */
const calculateColumnWidth = (
  columns: ColumnType[], 
  data: any[],
  options: {
    padding: number;
    font: string;
  }
) => {
  // 存储每列的宽度值
  let widthMap = new Map();
  
  // 遍历数据计算内容宽度
  data.forEach((target) => {
    for (let key in target) {
      // 参考ant-design-vue的官方文档,要考虑dataIndex的两种情况.
      // 1. dataIndex是string
      // 2. dataIndex是一个数组
      const column = columns.find((item) => {
        if (typeof item.dataIndex === "string") {
          return item.dataIndex === key;
        } else if (isArray(item.dataIndex)) {
           // 这里直接取列表中的第一个值进行匹配就好
          return item.dataIndex[0] === key;
        }
      });
      // 优化:如果column中设置了width,则不计算宽度
      if (column?.width) {
        continue;
      }
      let text = target[key];
      // 处理自定义渲染逻辑
      if (column?.customRender) {
        text = column.customRender({
          text,
          value: target[key],
          record: target,
          column,
        });
        if (typeof text === "string") {
          text = text.trim();
        } else if (typeof text === "object") {
          // 如果拿到的不是一个string,那么是使用了h函数将其渲染成了dom
          // 不需要计算宽度
          continue;
        }
      }
      
      // 计算文本宽度
      let keyWidth = getTextWidth(text);
      widthMap.has(key)
        ? widthMap.set(key, widthMap.get(key).concat([keyWidth]))
        : widthMap.set(key, [keyWidth]);
    }
  });

  // 计算平均值
  for (let [mapKey] of widthMap) {
    let valueArr = widthMap.get(mapKey);
    let len = valueArr.length;
    let value = valueArr.reduce((acc: number, cur: number) => acc + 1 / cur, 0);
    widthMap.set(mapKey, len / value);
  }

  // 返回处理后的列配置
  return columns.map((item) => {
     // 如果已经设置了宽度,那么这里就不再设置
    if (item.width) return { ...item, width: item.width };
    
    let textWidth = getTextWidth(item.title as string);
    let index = isArray(item.dataIndex) ? item.dataIndex[0] : item.dataIndex;
    
    if (widthMap.get(index) < textWidth) {
      widthMap.set(index, textWidth);
    }
    
    return {
      ...item,
      width: Math.ceil(widthMap.get(index)) + options.padding,
    };
  });
};

使用方式

typescript 复制代码
const computedColumns = computed(() => {
    calculateColumnWidth(yourColumns, yourData, yourOptions)
})

技术要点

  1. Canvas 测量文本宽度

    • 使用 measureText 方法获取文本实际宽度
    • 支持自定义字体和大小
  2. 数据处理

    • 处理数组类型的 dataIndex
    • 支持自定义渲染函数
    • 考虑表头文字长度
  3. 性能优化

    • 使用 Map 存储中间计算结果
    • 采用平均值计算方式平衡列宽
  4. 可配置性

    • 支持自定义内边距
    • 支持自定义字体
    • 保留手动设置列宽的能力

注意事项

  1. 对于复杂渲染的内容(如自定义组件),建议手动设置列宽
  2. 大量数据时注意性能影响
  3. 不同浏览器下字体渲染可能有差异
  4. 需要考虑表格容器宽度限制

总结

通过这种方案,我们可以实现表格列宽的智能计算,提升用户体验和开发体验。 希望这篇文章能帮助到有类似需求的开发者。如果你有任何问题或建议,欢迎在评论区留言讨论。

相关推荐
未来之窗软件服务15 小时前
一体化系统(九)智慧社区综合报表——东方仙盟练气期
大数据·前端·仙盟创梦ide·东方仙盟·东方仙盟一体化
陈天伟教授18 小时前
人工智能训练师认证教程(2)Python os入门教程
前端·数据库·python
信看19 小时前
NMEA-GNSS-RTK 定位html小工具
前端·javascript·html
Tony Bai19 小时前
【API 设计之道】04 字段掩码模式:让前端决定后端返回什么
前端
苏打水com19 小时前
第十四篇:Day40-42 前端架构设计入门——从“功能实现”到“架构思维”(对标职场“大型项目架构”需求)
前端·架构
king王一帅20 小时前
流式渲染 Incremark、ant-design-x markdown、streammarkdown-vue 全流程方案对比
前端·javascript·人工智能
苏打水com20 小时前
第十八篇:Day52-54 前端跨端开发进阶——从“多端适配”到“跨端统一”(对标职场“全栈化”需求)
前端
Bigger20 小时前
后端拒写接口?前端硬核自救:纯前端实现静态资源下载全链路解析
前端·浏览器·vite
BD_Marathon20 小时前
【JavaWeb】路径问题_前端绝对路径问题
前端