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

总结

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

相关推荐
明教卢师傅30 分钟前
JS实现文件点击或者拖拽上传
前端·javascript
布列瑟农的星空32 分钟前
WeakMap+AbortController——优雅地取消请求
前端
二川bro38 分钟前
Vuex Actions 多参数传递的解决方案及介绍
开发语言·前端·javascript
dmy43 分钟前
后端1小时上手vue开发到上线
前端·vue.js·vite
codingandsleeping1 小时前
OSI 七层网络模型
前端·网络协议·http
codingandsleeping1 小时前
TCP 三次握手与四次挥手
前端·网络协议
GUIQU.2 小时前
【JavaScript】ES6+ 新特性
前端·javascript·es6
小白_ysf2 小时前
《前端面试题之 CSS篇(第一集)》
前端·css·面试题
患得患失9492 小时前
【前端】【css】flex布局详解
前端·css
gqkmiss2 小时前
AbortController:让异步操作随时说停就停
前端·async·异步·steam·abortcontroller