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. 需要考虑表格容器宽度限制

总结

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

相关推荐
irving同学4623813 分钟前
TypeORM 列装饰器完整总结
前端·后端·nestjs
彭于晏爱编程16 分钟前
你真的了解 Map、Set 嘛
前端
崔璨19 分钟前
详解Vue3的响应式系统
前端·vue.js
摸鱼的鱼lv20 分钟前
🔥 Vue.js组件通信全攻略:从父子传值到全局状态管理,一篇搞定所有场景!🚀
前端·vue.js
IT_陈寒31 分钟前
Java性能优化:10个让你的Spring Boot应用提速300%的隐藏技巧
前端·人工智能·后端
lichong95131 分钟前
【混合开发】vue+Android、iPhone、鸿蒙、win、macOS、Linux之dist打包发布在Android工程asserts里
android·vue.js·iphone
whysqwhw1 小时前
Hippy 跨平台框架扩展原生自定义组件的完整实现方案对比
前端
dasseinzumtode1 小时前
nestJS 使用ExcelJS 实现数据的excel导出功能
前端·后端·node.js
子兮曰1 小时前
🔥C盘告急!WSL磁盘暴增?三招秒清20GB+空间
前端·windows·docker
Jinuss1 小时前
Vue3源码reactivity响应式篇之EffectScope
前端·vue3