element 动态双表头固定列+列设置+列宽不折行展示 终极解决方案

691 阅读3分钟

先看效果

列设置

image.png

表格双表头固定列、列展示不折行

image.png

image.png

背景

  • 表格需求如下:
  1. 双表头、字段个数不确定、可由另一个模块动态新增,另外产品希望字段一行展示不要有折行也不要省略展示,由于基础字段就有一百多个、所以需要列设置管理展示的字段、需要固定列。故产生以下问题点

    1.1 不确定字段个数所以只能采取动态双表头形式、字段全部由后端返回 1.2 字段一行展示、一般我们都通过挨个设置width来调整、但是字段这么多、还可以动态新增进来根本无法确定所需的字段的宽度,故该挨个设置的方案不行、只能通过动态计算列的宽度 1.3 用列设置管理列展示后、上面的计算列就会产生内容没有占满整个表格宽度的问题,我们希望总列宽小于表格宽度的时候能自动撑开、超过的时候才去计算列宽。

image.png

不计算列宽的话就会产生折行

image.png 1.4 element-ui 目前只支持单表头固定列、查询了下网上的解决方案、大致如下 1、通过给第一级的表头设置一个固定宽度、然后第二级的宽度之和等于第一级比表头设置的宽度。经研究该方法对于固定列的表格可行、但是我们的需求可以进行列设置、列宽不定、无法直接使用,但是能参考解决思路 2、去改element-ui的源码让其支持双表头固定列、但是由于项目是多人合作、打包上线是走的自动化工具、不方便。

解决方案

1.1的问题,很好解决、双表头遍历就行了

image.png

1.2的问题我们需要动态的去计算每一个列的宽度、而每一列我们需要去比较标题和内容的宽取长的数据作为列宽

  • flexWidth(c.code, tableData, c.label, 50)函数具体实现如下
const flexWidth = (prop, tableData, title, num = 0) => {
    if (tableData?.length === 0) {
        //表格没数据不做处理
        return;
    }
    let flexWidth = 0; //初始化表格列宽
    let columnContent = ''; //占位最宽的内容
    let canvas = document.createElement('canvas');
    let context = canvas.getContext('2d');
    context.font = '12px Microsoft YaHei';
    if (prop === '' && title) {
        //标题长内容少的,取标题的值,
        columnContent = title;
    } else {
        // 获取该列中占位最宽的内容
        let index = 0;
        for (let i = 0; i < tableData.length; i++) {
            const now_temp = tableData[i][prop] + '';
            const max_temp = tableData[index][prop] + '';
            const now_temp_w = context.measureText(now_temp).width;
            const max_temp_w = context.measureText(max_temp).width;
            if (now_temp_w > max_temp_w) {
                index = i;
            }
        }
        columnContent = tableData[index][prop];
        //比较占位最宽的值跟标题、标题为空的留出四个位置
        const column_w = context.measureText(columnContent).width;
        const title_w = context.measureText(title).width;
        if (column_w < title_w) {
            columnContent = title || '留四个字';
        }
    }

    // 计算最宽内容的列宽
    let width = context.measureText(columnContent);
  
    flexWidth = width.width + num;

    return  flexWidth + 'px';

1.3的问题 我们解决完列宽后还需要进一步去判断 当前的列宽总和是否大于表格可视区?

image.png

image.png

实现代码如下:

image.png

到了解决element双表头问题了

  1. 参考方案一的方案需要对第一级进行固定列并确定宽度,但是因为可以设置列的原因我们可以去第一个一级表头设置fixed属性,宽度我们可以通过动态的去计算所需要固定的列的宽度,目前我的需求是固定前四列,切这四列涉及到了两个表头、我的处理方法如下
computed: {
    getTableHeader() {
            return this.tableHeader.map((v, i) => {
                if (i == 0) {
                    this.$set(v, 'fixed', 'left');
                    // 加这个给详情页默认固定前四列
                    this.$set(v, 'width', this.topFourTotalWidth);
                }
                return v;
            });
        }
}
watch: {
        tableHeader: {
            handler(val) {
                this.completeCalc = false;
                const tempTableHeader = JSON.parse(JSON.stringify(val));
                this.tableHeaderList = treeToList(tempTableHeader);
                this.$nextTick(() => {
                    setTimeout(() => {
                        let tableDom = this.$refs.table;
                        if (this.tableData.length) {
                            // 判断是否需要设置列宽
                            this.isSetColumnWidth = this.handleCompareWidth(this.tableHeaderList);
                        }
                        this.completeCalc = true;
                        this.$eventBus.$emit('setCompleteCalc', true);
                        setTimeout(() => {
                            // 固定前四列, 固定类的方法在这里你们看这里就可以了
                            this.setFixedField(tableDom, 4);
                            tableDom.doLayout();
                        }, 100);
                    }, 600);
                });
            },
            deep: true,
            immediate: true
        }
    },
 methods: {
// 动态计算规定列宽度
setFixedField(tableDom, num) {
    let topFourFieldList = tableDom.columns.filter((v, index) => index < num);
    let topFourFieldWidthList = \[];
    topFourFieldList.forEach((item) => {
    topFourFieldWidthList.push(item.width);
    });
    this.topFourTotalWidth = this.flexWidthSum(topFourFieldWidthList);
    const dom = Array.from(document.getElementsByClassName('el-table\_\_fixed'))\[0];
    console.log('dom', dom);
    dom.style.width = this.topFourTotalWidth + 'px';
    }
}



另外一个不可忽略的重点就是把element的样式重置一下 不然文字会给固定的列遮挡住了

.el-table th.is-hidden > *,
    .el-table td.is-hidden > * {
        visibility: visible;
    }

至此就完美解决啦,有疑问可以留言探讨哦!