element系列 - table 改造

380 阅读2分钟

系列

element系列 - input 改造

element系列 - select 改造

element系列 - form 改造

element系列 - tree 改造

element系列 - pagination 改造

element系列 - table 改造

需求

  • 给 Table 头 排序事件 加防抖
  • 自适应宽度,上限200px,超过允许两行带省略号显示,增加防抖

github项目地址

给 Table 头 排序事件 加防抖

import Vue from "vue";
import { debounce } from "lodash";
import { Table } from "element-ui";
const elTableHack = {
  install: (Vue) => {
    //ElementUI排序回调添加防抖
    const handleSortClick =
      Table.components.TableHeader.methods.handleSortClick;
    Object.assign(Table.components.TableHeader.methods, {
      //此方法为排序触发的事件,添加防抖,和search频率同步,
      handleSortClick: debounce(
        function (...args) {
          //调用原方法
          handleSortClick.apply(this, args);
          //模拟一个body上的点击事件,主动触发body上的委托 ,触发el-popover等组件的关闭
          document.querySelector("body").click();
        },
        300,
        {
          leading: true,
          trailing: false,
        }
      ),
    });

    Vue.use(Table);
  },
};
Vue.use(elTableHack);

自适应宽度,上限200px,超过允许两行带省略号显示

错误实例:

image.png

分析:

  • 表格宽度需要根据 table 实际内容填充后再计算宽度
  • 每列中获取最大长度的cell,先用canvas获取宽度,再用双行样式比较宽度,得出最大宽度

方法: table源码关于列宽度计算的代码 node_modules\element-ui\packages\table\src\table-layout.js updateColumnsWidth 函数,拷贝整个 updateColumnsWidth 函数,在红框处扩展

image.png

import Vue from "vue";
import { debounce, throttle } from "lodash";
import { Table, TableColumn } from "element-ui";
const elTableHack = {
  install: (Vue) => {
    // 设置最大宽度参数
    Object.assign(TableColumn, {
        mixins: [
          {
            props: {
              maxWidth: {
                type: Number,
                default: 200,
              },
            },
            mounted() {
              this.columnConfig.maxWidth = this.maxWidth;
            },
          },
        ],
    });
    // 转canvas计算dom宽度
    function getTextWidth(text, font = "12px") {
        var canvas =
          getTextWidth.canvas ||
          (getTextWidth.canvas = document.createElement("canvas"));
        var context = canvas.getContext("2d");
        context.font = font;
        var metrics = context.measureText(text);
        return metrics.width;
    }


  Object.assign(Table.mixins, [
    ...Table.mixins,
    {
      watch: {
        data() {
          this.layout.updateColumnsWidth();
          this.$nextTick(() => {
            this.layout.updateElsHeight();
          });
        },
      },
      mounted() {
        const that = this;
        this.layout.updateColumnsWidth = throttle(
            function () {
                /* 原来updateColumnsWidth部分
                * let flexColumns = flattenColumns.filter(
                *  (column) => typeof column.width !== "number"
                * );
                */
                /*** .... */
                
                /** 增加下面部分*/
                flexColumns.forEach((column) => {
                    let minWidth = 0;
                    let width = 0;
                    let maxWidth = 200;
                    if (column) {
                      minWidth = parseFloat(column.minWidth) || 0;
                      width = parseFloat(column.width) || 0;
                      maxWidth = parseFloat(column.maxWidth) || 200;
                    }
                    if (width) {
                      return;
                    }
                    const cells = [
                      ...that.$el.querySelectorAll(`td.${column.id}`),
                      ...that.$el.querySelectorAll(`th.${column.id}`),
                    ];
                    
                    /** 当前列最大cell */
                    const maxCell = cells.reduce(
                      (pre, cell) => {
                        const text = cell.innerText.replace(/\s/g, "");
                        const textWidth = getTextWidth(text);
                        if (textWidth > pre.textWidth) {
                          pre.text = text;
                          pre.cell = cell;
                          pre.textWidth = textWidth;
                        }
                        return pre;
                      },
                      {
                        cell: null,
                        text: "",
                        textWidth: 0,
                      }
                    );
                    let res = 0;
                    if (maxCell.cell) {
                      maxCell.cell.classList.add("for-count-cell");
                      res = (maxCell.cell.scrollWidth || 0) + 20;
                      maxCell.cell.classList.remove("for-count-cell");
                    }
                    const max = Math.max(res, minWidth);
                    const dist = Math.min(maxWidth, max);
                    column.minWidth = dist;
              });
                
                /* 原来updateColumnsWidth部分
                *flattenColumns.forEach((column) => { 
                *  if (typeof column.width === 'number' && column.realWidth) column.realWidth = null;
                *});
                */
                /*** .... */
            }.bind(this.layout),
              100
            );
          },
        },
      ]);
    Vue.use(Table);
  },
};
Vue.use(elTableHack);
  • 全局css

<style>
/** 用于不换行计算最大宽度 */
.for-count-cell {
  display: inline-block !important;
  white-space: nowrap !important;
  width: auto !important;
  overflow: auto !important;
}
/** 最多两行 */
.el-table__cell .cell {
  -webkit-box-orient: vertical;
  -webkit-line-clamp: 2;
  display: -webkit-box;
  font-weight: 400;
  overflow: hidden;
  text-align: center;
  text-overflow: ellipsis;
}
</style>

效果对比

  • 没有自适应宽度较宽,效果比较差

image.png

  • 添加自适应宽度 最大200 且最多两行,效果较好

image.png