el-table组件,针对max-height属性,处理calc属性值下滚动条消失的问题

1,851 阅读2分钟

问题产生原因

  • 借助Element官方文档,了解到 max-height属性 合法值为 数字或单位为 px的高度(详见下图);因而,当赋值为 calc(100vh - 100px) 等时,并不符合官方要求

    image.png

  • 分别使用合法值calc计算值max-height赋值,比较二者于样式上有何差异

    后者较前者,于 el-table__body-wrapperel-table__fixed-body-wrapper 两个DOM元素中,均缺失了max-height属性,进而最终导致滚动条无法显现(下图仅针对el-table__body-wrapper做对比展示)

    image.png

    image.png

  • 具体缺失原因,只能查阅源代码才能明晰

    依照路径node_modules/element-ui/table/src/table.vue,进入对应组件文件

    通过按键组合Ctrl+F,调出编辑器搜索框,分别搜索上文提到的两个DOM元素

    分析得知,二者分别绑定了 bodyHeightfixedBodyHeight 两大对象

    image.png

    image.png
    通过进一步搜索,可知悉二者均为计算属性

      bodyHeight() {
        const { headerHeight = 0, bodyHeight, footerHeight = 0} = this.layout;
        if (this.height) {
          return {
            height: bodyHeight ? bodyHeight + 'px' : ''
          };
        } else if (this.maxHeight) {
          const maxHeight = parseHeight(this.maxHeight);
          if (typeof maxHeight === 'number') {
            return {
              'max-height': (maxHeight - footerHeight - (this.showHeader ? headerHeight : 0)) + 'px'
            };
          }
        }
        return {};
      },
    
      fixedBodyHeight() {
        if (this.height) {
          return {
            height: this.layout.fixedBodyHeight ? this.layout.fixedBodyHeight + 'px' : ''
          };
        } else if (this.maxHeight) {
          let maxHeight = parseHeight(this.maxHeight);
          if (typeof maxHeight === 'number') {
            maxHeight = this.layout.scrollX ? maxHeight - this.layout.gutterWidth : maxHeight;
            if (this.showHeader) {
              maxHeight -= this.layout.headerHeight;
            }
            maxHeight -= this.layout.footerHeight;
            return {
              'max-height': maxHeight + 'px'
            };
          }
        }
        return {};
      },
    

    上方代码中定义的 max-height属性,会经过 parseHeight方法 处理后返回

    export function parseHeight(height) {
      if (typeof height === 'number') {
        return height;
      }
      if (typeof height === 'string') {
        if (/^\d+(?:px)?$/.test(height)) {
          return parseInt(height, 10);
        } else {
          return height;
        }
      }
      return null;
    }
    

    parseHeight方法参数值为 calc(100vh - 100px)等时,会原封不动地返回。该返回值在 bodyHeightfixedBodyHeight 计算属性内部,均无法通过 typeof maxHeight === 'number' 判断;最终导致返回 {}

处理方法

  • 借助组件继承,可以方便以任意组件为基础,做调整进一步开发

  • 依赖此API,可以在 el-table组件 的基础上,对 bodyHeightfixedBodyHeight 内部逻辑进行调整

    /* el-change.js */
    
    // 引入待继承的组件
    import { Table } from 'element-ui'
    // 引入所涉及的外部工具方法
    import { parseHeight } from 'element-ui/packages/table/src/util'
    
    const TablePatch = {
      // 继承组件
      extends: Table,
      computed: {
        bodyHeight() {
          xxxx;
          if (xxx) {
            xxx;
          } else if (xxx) {
            xxx;
            if (xxx) {
              xxx;
            }
    
            // 针对calc定义的高度,增补计算逻辑(增补内容,以上方为参照)
            if (typeof maxHeight === "string") {
              if (maxHeight.startsWith("calc")) {
                return {
                  "max-height": `calc(${maxHeight}
                                      - ${footerHeight}px
                                      - ${this.showHeader
                                          ? headerHeight
                                          : 0}px)`,
                };
              }
            }
          }
          xxx;
        },
        fixedBodyHeight() {
          if (xxx) {
            xxx;
          } else if (xxx) {
            xxx;
            if (xxx) {
              xxx;
            }
    
            // 针对calc定义的高度,增补计算逻辑(增补内容,以上方为参照)
            if (typeof maxHeight === "string") {
              if (maxHeight.startsWith("calc")) {
                const { gutterWidth, headerHeight, footerHeight } = this.layout;
    
                maxHeight = this.layout.scrollX
                  ? `calc(${maxHeight} - ${gutterWidth}px)`
                  : maxHeight;
                this.showHeader &&
                  (maxHeight = `calc(${maxHeight} - ${headerHeight}px)`);
                maxHeight = `calc(${maxHeight} - ${footerHeight}px)`;
    
                return { "max-height": maxHeight };
              }
            }
          }
          xxx;
        },
      },
    };
    
    export default {
      install(Vue) {
        // 全局注册组件(使用源组件名称)
        Vue.component(Table.name, TablePatch);
      },
    };
    
    /* main.js */
    xxx
    // 引入自定义插件
    import elChange from '@/plugins/el-change'
    
    // 使用插件
    Vue.use(elChange)
    xxx
    

实现效果

image.png