element-plus虚拟表格封装 ,实现 单元格合并+无限滚动加载

2,661 阅读2分钟

表格效果

fa5627eb6c12eee43d676e74a29e73e.png

根据官网的例子不使用jsx,实现结构

<template>
  <div class="table-container">
    <el-auto-resizer>
      <template #default="{ width }">
        <el-table-v2
          class="table"
          :columns="columns"
          :data="data"
          :width="width"
          :height="height"
          scrollbar-always-on
          @scroll="handleScroll"
        >
          <template #row="props">
            <Row v-bind="props" />
          </template>
        </el-table-v2>
      </template>
    </el-auto-resizer>
  </div>
</template>

根据官网的例子,使用了autoSize组件获取宽度,只做宽度自适应

scrollbar-always-on 让滚动条一直显示

监听组件scroll事件,滚动到底部时加载更多数据

设置组件入参和数据状态

const props = defineProps({
  data: {
    type: Array,
    defuault: () => [],
  },
  columns: {
    type: Array,
    defuault: () => [],
  },
});
const emit = defineEmits(["scrollToBottom"]);

分别是列数据和表格数据,列数据和表格数据由父组件传入

并定义滚动到底部的抛出事件

let height = 500;

设置宽度为500

function handleScroll(e) {
  if (height - 50 + e.scrollTop >= 50 * data.length - 1) {
    emit("scrollToBottom");
  }
}

定义函数,滚动到底部时,给父组件发布自定义事件

样式改写

<style scoped lang="scss">
.table-container {
  padding: 20px 35px;

  .table {
    width: 100%;
  }
}
::v-deep(.el-table-v2__row-cell) {
  height: calc(100% - -2px);
}

::v-deep(.el-table-v2__header-cell) {
  height: calc(100% - -1px);
  background: #f6f6f6;
  border-right: 0 !important;
  border-bottom: 0 !important;
}
::v-deep(.el-table-v2__body) {
  border-bottom: 1px solid #eee;
  border-left: 1px solid #eee;
}
::v-deep(.el-vl__vertical) {
  right: 0;
}
</style>

table-v2的样式没有v1版本的丰富,提供的属性也不够详细,只能手动修改,修改了边框和滚动条的位置

单元格合并操作


let { columns, data } = props;
let mergeColumns = [0, 1];
columns.forEach((item, i) => {
  item.style = {
    borderRight: "1px solid #eee",
    borderBottom: "1px solid #eee",
    justifyContent: "center",
  };
  if (mergeColumns.includes(i)) {
    item.rowSpan = function ({ rowData, rowIndex, cells, columns }) {
      let maxNum = columns[i - 1]?.rowSpan?.({ rowData, rowIndex, cells, columns });
      let { key } = item;
      let num = 0;
      for (let i = rowIndex; i < data.length; i++) {
        if (data[rowIndex][key] !== data[i][key]) {
          return maxNum ? Math.min(num, maxNum) : num;
        } else {
          num++;
        }
      }
      return num;
    };
  }
});

const Row = ({ rowData, rowIndex, cells, columns }) => {
  for (let i = 0; i < columns.length; i++) {
    const rowSpan = columns[i].rowSpan?.({ rowData, rowIndex, cells, columns });
    if (rowSpan > 1 && rowIndex <= data.length) {
      Object.assign(cells[i].props.style, {
        backgroundColor: "var(--el-color-white)",
        height: `${rowSpan * 50}px`,
        alignSelf: "flex-start",
        zIndex: rowSpan,
      });
    }
  }
  return cells;
};

合并原理:

将需要合并的第一个单元格,调整其样式和层级,让第一个单元格把其他需要合并的其他单元格覆盖

实现方式:

  1. 先对列数据进行操作,改写样式,将内容居中并在每一格加上border,让样式与table-v1的样式相同

  2. 定义需要进行合并单元格的列,第几列需要做合并,就要将合并方法添加至第几列的方法添加至组件中

  3. 实现合并方法,找到该单元格中,后面与其内容相同的单元格

    单元格高度 = 单元格基础高度 * 与其内容相同单元格数量

    单元格层级 = 与其内容相同单元格数量