vue table列表组件_element table固定列

vue table列表组件_element table固定列最近接到一个新需求,一个表单组件, 要求横向和纵向两种模式,要求固定表头和首列。这里做个记录。 这样就比较清晰了。剩下的就是核心代码编写了。 接下来遇到的一个问题就是多级表头了。 仔细分析这个dom结构。其实他是分成三行来渲染的, 然后通过rowspan和colspan来实现的…

最近接到一个新需求,一个表单组件, 要求横向和纵向两种模式,要求固定表头和首列。这里做个记录。 首先呢, 当然是踩在巨人的肩膀上, 看看其他的开源库怎么实现的固定表头和首列,

一、结构分析

效果

vue table列表组件_element table固定列

拆分

vue table列表组件_element table固定列

代码

vue table列表组件_element table固定列

分析

其实, 整个表单被分成了三个部分, 每一种颜色是一个单独的表格块,拼接起来的, 每一个部分都是用一个容器包裹着一个table表单, 用于限制宽高和处理滚动

<div class="wrapper"> 
    <table></table>
</div>
  • 黄色部分, 只渲染了表头部分,外部容器overflo:hidden
  • 绿色部分就是核心, 渲染了tbody部分, 外部容器overflow: atuo, 监听他的滚动条事件, 然后同步到固定表头和固定列的滚动条
  • 蓝色部分其实包含了了一个thead和tbody, 然后设置宽度等于首列的宽, overflow: hidden 然后绝对定位覆盖在右边的表单上面
  • 在body部分横向滚动的时候, thead同步滚动, 在body垂直滚动的时候, 左边的tbody也同步滚动

这里我用了两个子组件tableHead和tableBody来分别渲染thead和tbody部分,

vue table列表组件_element table固定列

这样就比较清晰了。剩下的就是核心代码编写了。

二、实现

接下来遇到的一个问题就是多级表头了。

vue table列表组件_element table固定列

仔细分析这个dom结构。其实他是分成三行来渲染的, 然后通过rowspan和colspan来实现的合并单元格 所以我们需要通过算法, 将树形的表头数据摊开成一个二维数组, 然后每个数据节点都需要算出rowspan和colspan。

这里总结一下规律

  • 每一个单元格的colsan应该是他全部子节点的总数,如果他不含子节点,则为1
  • 每一个单元格的rowspan分两种情况, 如果他不含子节点, 就是最大层数-当前曾是 + 1, 如果包含子节点,则为1

丢代码

// 提取全部的列
const getAllColumns = (columns) => {
  const result = [];
  columns.forEach((column) => {
    if (column.childs && column.childs.length) {
      result.push(column);
      result.push.apply(result, getAllColumns(column.childs));
      // result.push(...getAllColumns(column.childs))
    } else {
      result.push(column);
    }
  });
  return result;
}

这个函数的作用是将多个树形结构组成的数组摊平 A、B分别是这个表头的顶级单元格

vue table列表组件_element table固定列
vue table列表组件_element table固定列

// 从树形整理成二维数组
const convertToRows = (originColumns) => {
  let maxLevel = 1;
  // 递归的算出每一个子节点的层数, 和colspan
  const traverse = (column, parent) => {
    if (parent) {
      column.level = parent.level + 1;
      if (maxLevel < column.level) {
        maxLevel = column.level;
      }
    }
    if (column.childs && column.childs.length) {
      let colSpan = 0;
      column.childs.forEach((subColumn) => {
        traverse(subColumn, column);
        colSpan += subColumn.colSpan;
      });
      // 节点的colspan是子节点的和
      column.colSpan = colSpan;
    } else {
    // 不存在则为一
      column.colSpan = 1;
    }
  };

  originColumns.forEach((column) => {
    column.level = 1;
    traverse(column);
  });
  const rows = [];
  for (let i = 0; i < maxLevel; i++) {
    rows.push([]);
  }
  // 转化成一维数组的数据,
  // 注意, 在调用getAllColumns之前, 已经将每一个节点设置好了level
  const allColumns = getAllColumns(originColumns);
  // 转化成二维数组, 计算rowspan
  allColumns.forEach((column) => {
    if (!column.childs.length) {
      column.rowSpan = maxLevel - column.level + 1;
    } else {
      column.rowSpan = 1;
    }
    rows[column.level - 1].push(column);
  });

  return rows;
}

这一步将根据level将这个数组变成一个二维数组、每一层的节点在一个数组内

vue table列表组件_element table固定列

通过这么整理过的数据就可以直接通过循环渲染出来了。

其他细节, 样式的调整

  • colgroup和col可以快速的格式化单元格,而col的个数就是这个table列的个数, 应该是每一个树的最终节点的和
    vue table列表组件_element table固定列
  • 出现纵向滚动条之后会影响到整体宽度, 导致横向混动thead和tbody不同步。产生错位, 如下图
    vue table列表组件_element table固定列

    解决办法是判断当纵向滚动条存在的时候, 在thead添加一个单元格, 宽度为滚动条的宽度

vue table列表组件_element table固定列

 // 获取滚动条宽度
export function getScrollbarWidth() {
  if (Vue.prototype.$isServer) return 0;
  if (scrollBarWidth !== undefined) return scrollBarWidth;
  const outer = document.createElement('div');
  outer.className = 'el-scrollbar__wrap';
  outer.style.visibility = 'hidden';
  outer.style.width = '100px';
  outer.style.position = 'absolute';
  outer.style.top = '-9999px';
  document.body.appendChild(outer);

  const widthNoScroll = outer.offsetWidth;
  outer.style.overflow = 'scroll';

  const inner = document.createElement('div');
  inner.style.width = '100%';
  outer.appendChild(inner);

  const widthWithScroll = inner.offsetWidth;
  outer.parentNode.removeChild(outer);
  scrollBarWidth = widthNoScroll - widthWithScroll;

  return scrollBarWidth;
};
  • 在出现横向滚动条的时候, 左边绝对定位会覆盖一部分横向滚动条, 如下

    vue table列表组件_element table固定列

    所以也要特殊处理, 限制左边的tbody容器的高度减去一个滚动条的高度

  • 如果窗口宽度大于表格的理论宽度, 表格就会自动适应, 底部的tbody边框了。而左边绝对定位的宽度没变, 会导致错位, 所以要给table设置一个宽度, 这个宽度是可以通过列数*每列的宽计算出来的

  • 如果需要操作栏的话, 可以通过vue的作用域插槽来实现, 将当列的数据抛出去就好了

这样, 一个横向的固定表头首列就完成了, 实际上就是element table组建的劣化版。有时间再写一下纵向表单的处理, 相对来说要复杂一下。

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。
转载请注明出处: https://daima100.com/12875.html

(0)

相关推荐

发表回复

您的电子邮箱地址不会被公开。 必填项已用*标注