使用element 2.14 实现表格虚拟滚动组件

  • 使用element 2.14 实现表格虚拟滚动组件已关闭评论
  • 67 次浏览
  • A+
所属分类:Web前端
摘要

下述代码为组件实现代码复制即可食用,默认只展示 一屏数据加两条全选存在些许问题, 使用row-key时,如果行过多滚动时会不会很流畅

下述代码为组件实现代码复制即可食用,默认只展示 一屏数据加两条

全选存在些许问题, 使用row-key时,如果行过多滚动时会不会很流畅

特别需要注意的是 行高必须要保持一致

<template>   <div class="t-table" :id="TTableId">     <el-table       ref="el-table"       :tooltip-effect="tooltipEffect"       :data="tableData"       border       :height="height"       :row-class-name="rowClassName"       :key="tableKey"       :row-key="getRowKey"       @select-all="         val => {           this.selectAll(val, !isAll);         }       "       @select="select"       @current-change="handleCurrentChange"       :highlight-current-row="highlightCurrentRow"       :header-cell-style="headerCellStyle"       :cell-style="cellStyle"       :empty-text="emptyText"       @row-click="         (row, column, event) => this.rowClick(row, column, event, 'table')       "     >       <el-table-column type="index" v-if="index" align="center" fixed>         <template slot-scope="scope">           {{ start > 0 ? scope.$index + start + 1 : scope.$index + 1 }}         </template>       </el-table-column>       <el-table-column         type="selection"         fixed         :reserve-selection="true"         :width="$TableWidth.px46"         v-if="selection"         :selectable="rowSelectable"       ></el-table-column>       <slot></slot>     </el-table>   </div> </template>  <script> export default {   components: {   },   name: "TTable",   props: {     //表格数据列表     saveDATA: {       typeof: Array,       default: []     },     //表格高度     height: {       typeof: Number,       default: 400     },     //一行高度 为了计算高度,需要行高固定且不能出现换行数据     itemHeight: {       typeof: Number,       default: 49     },      rowClassName: {       typeof: Function,       default: () => {}     },     tooltipEffect: {       typeof: String,       default: ""     },     //行key 必须填写saveDATA里面的变量名     rowKey: {       typeof: String,       default: ""     },     //是否显示复选框列     selection: {       typeof: Boolean,       default: false     },     //是否显示序号列     index: {       typeof: Boolean,       default: false     },     //行禁选方法     rowSelectable: {       typeof: Function,       default: () => {}     },     //是否可以单选     highlightCurrentRow: {       typeof: Boolean,       default: false     },     //表头样式     headerCellStyle: {       typeof: Object     },     //表格样式     cellStyle: {       typeof: Object     },     //无数据文字     emptyText: {       typeof: String,       default: ""     },     colDisable: {       typeof: Array     },     //组件id 如果一个页面需要使用多个ttable组件 需要给此参数赋唯一值     TTableId: {       typeof: String,       default: "t_table"     }   },   data() {     return {       tableData: [], //展示数据列表       tableRef: null, // 设置了滚动的那个盒子       tableWarp: null, // 被设置的transform元素       fixLeft: null, // 固定左侧--设置的transform元素       fixRight: null, // 固定右侧--设置的transform元素       tableFixedLeft: null, // 左侧固定列所在的盒子       tableFixedRight: null, // 右侧固定列所在的盒子       scrollTop: 0, //滚动高度       scrollNum: 0, //当前滚动了几屏数据  scrollTop / (itemHeight * pageList)       scrollRowNum: 0, //记录当前滚动了多少条数据       start: 0, //默认截取数据开始位数       end: this.pageList + 2, //默认截取数据结束位数       selectList: [], //当前选中的列表       isAll: false, //是否全选       tableKey: "", //表格key       currentRow: "", //单选数据     };   },   watch: {     //每滚动一条数据则更改展示数据源并且更改容器偏移量     scrollRowNum(newV) {       this.start = newV - 1;       this.end = this.pageList + newV + 1;       if (this.start <= 0) this.start = 0;       this.tableData = this.saveDATA.slice(this.start, this.end);       if (this.currentRow) this.setCurrentRow(this.currentRow);       requestAnimationFrame(() => {         // 计算偏移量         let translateY = `translateY(${this.start * this.itemHeight}px)`;         this.tableWarp.style.transform = translateY;         if (this.fixLeft) {           this.fixLeft.style.transform = translateY;         }         if (this.fixRight) {           this.fixRight.style.transform = translateY;         }         this.doLayout();       });     },       saveDATA: {       handler(val) {         this.init();         this.initMounted();       },       deep: true // 深度监听     },     pageList() {       this.init();       this.initMounted();     }   },   created() {   },   mounted() {     this.$nextTick(() => {       this.tableSetRef = this.GetTableRef();     });   },   methods: {     clearIndex() {       this.start = 0;       this.end = this.pageList + 2;        this.scrollNum = 0;       if (this.tableRef) this.tableRef.scrollTop = 0;     },     //初始化表格内容盒子     initMounted() {       this.$nextTick(() => {         let box = document.getElementById(this.TTableId);         // 设置了滚动的盒子         this.tableRef = this.$refs["el-table"].bodyWrapper;         // 左侧固定列所在的盒子         this.tableFixedLeft = box.querySelector(           ".el-table .el-table__fixed .el-table__fixed-body-wrapper"         );         // 右侧固定列所在的盒子         this.tableFixedRight = box.querySelector(           ".el-table .el-table__fixed-right .el-table__fixed-body-wrapper"         );         /**          * fixed-left | 主体 | fixed-right          */         // 创建内容盒子divWarpPar并且高度设置为所有数据所需要的总高度         let dwp = box.getElementsByClassName("divWarpPar")[0];         if (dwp) {           dwp.style.height = this.saveDATA.length * this.itemHeight + "px";         } else {           let divWarpPar = document.createElement("div");           divWarpPar.className = "divWarpPar";           // 如果这里还没获取到saveDATA数据就渲染会导致内容盒子高度为0,可以通过监听saveDATA的长度后再设置一次高度           divWarpPar.style.height =             this.saveDATA.length * this.itemHeight + "px";           // 新创建的盒子divWarpChild           let divWarpChild = document.createElement("div");           divWarpChild.className = "fix-warp divWarpChild";           // 把tableRef的第一个子元素移动到新创建的盒子divWarpChild中           divWarpChild.append(this.tableRef.children[0]);           // 把divWarpChild添加到divWarpPar中,最把divWarpPar添加到tableRef中           divWarpPar.append(divWarpChild);           this.tableRef.append(divWarpPar);         }         // left改造         let dlp = box.getElementsByClassName("divLeftPar")[0];         if (dlp) {           dlp.style.height = this.saveDATA.length * this.itemHeight + "px";         } else {           let divLeftPar = document.createElement("div");           divLeftPar.className = "divLeftPar";           divLeftPar.style.height =             this.saveDATA.length * this.itemHeight + "px";           let divLeftChild = document.createElement("div");           divLeftChild.className = "fix-left";           this.tableFixedLeft &&             divLeftChild.append(this.tableFixedLeft.children[0]);           divLeftPar.append(divLeftChild);           this.tableFixedLeft && this.tableFixedLeft.append(divLeftPar);         }         // right改造         let drp = box.getElementsByClassName("divRightPar")[0];         if (drp) {           drp.style.height = this.saveDATA.length * this.itemHeight + "px";         } else {           let divRightPar = document.createElement("div");           divRightPar.className = "divRightPar";           divRightPar.style.height =             this.saveDATA.length * this.itemHeight + "px";           let divRightChild = document.createElement("div");           divRightChild.className = "fix-right";           this.tableFixedRight &&             divRightChild.append(this.tableFixedRight.children[0]);           divRightPar.append(divRightChild);           this.tableFixedRight && this.tableFixedRight.append(divRightPar);         }          // 被设置的transform元素         this.tableWarp = box.querySelector(           ".el-table .el-table__body-wrapper .fix-warp"         );         this.fixLeft = box.querySelector(           ".el-table .el-table__fixed .el-table__fixed-body-wrapper .fix-left"         );         this.fixRight = box.querySelector(           ".el-table .el-table__fixed-right .el-table__fixed-body-wrapper .fix-right"         );         this.tableRef.addEventListener("scroll", this.onScroll);       });     },     // 初始化数据     init() {       this.clearIndex();       this.tableData = this.saveDATA.slice(this.start, this.end);     },     // 滚动事件     onScroll() {       //解决固定行 与 非固定行 滚动时错位问题       let body = document.getElementById(this.TTableId);       let fixedBox = body.getElementsByClassName(         "el-table__fixed-body-wrapper"       );       if (fixedBox) {         for (let i = 0; i < fixedBox.length; i++) {           const element = fixedBox[i];           element.scrollTop = this.tableRef.scrollTop;         }       }       //,       this.scrollTop = this.tableRef.scrollTop; //获取顶部距离       this.scrollNum =         Math.floor(this.scrollTop / (this.itemHeight * this.pageList)) + 1; //计算滚动到几屏数据       this.scrollRowNum = Math.ceil(this.scrollTop / this.itemHeight); //计算已经滚动了多少条数据       //移除掉滚动时遗留的hover-row       if (this.GetTableRef()) {         const elements = body.getElementsByClassName(           "hover-row"         );         for (let i = 0; i < elements.length; i++) {           elements[i].classList.remove("hover-row");         }       }     },     //获取表格ref     GetTableRef() {       return this.$refs["el-table"];     },     //刷新表格key     RefreshTableKey() {       this.tableKey = this.$common.GetNextStr();       this.clearIndex();       this.initMounted();     },     //设置行key     getRowKey(row) {       return this.$common.GetValueByPath(row, this.rowKey);     },     //单行选择触发事件     select(val) {       this.selectList = val;       this.$emit("selection-change", val);     },     //全选触发事件     selectAll(val, isAll) {       this.isAll = isAll;       if (isAll) {         let list = JSON.parse(JSON.stringify(this.saveDATA));         if (this.rowSelectable)           this.selectList = list.filter((m, i) => this.rowSelectable(m, i));         else this.selectList = list;         let dList = this.selectList.filter(           m => val.findIndex(k => JSON.stringify(k) == JSON.stringify(m)) == -1         );         dList.forEach(row => {           this.GetTableRef().toggleRowSelection(row);         });       } else {         this.clearSelection();       }       this.$emit("selection-change", this.selectList);     },     //清空表格     clearSelection() {       this.GetTableRef().clearSelection();       this.selectList = [];       this.$emit("selection-change", this.selectList);     },      //单行选中触发事件     handleCurrentChange(val) {       this.currentRow = val;       this.$emit("current-change", val);     },     //设置指定行为单行选中     setCurrentRow(row) {       this.currentRow = row;       this.GetTableRef().setCurrentRow(row);     },     rowClick(row, column, event) {       this.$emit("row-click", row, column, event);     },     //重新布局表格     doLayout() {       this.GetTableRef().doLayout();     },     },   computed: {     pageList() {       return Math.ceil(this.height / this.itemHeight);     }   } }; </script> <style> .t-table .el-table__body-wrapper {   overflow: auto !important;   background-blend-mode: overlay !important; } </style>

下面为调用示例

<template>   <div class="t-table" id="t_table">     <TTable :saveDATA="saveDATA" :height="200">       <el-table-column fixed label="id" prop="id"></el-table-column>       <el-table-column  fixed label="姓名" prop="name"></el-table-column>       <el-table-column label="年龄" prop="age"></el-table-column>       <el-table-column         label="地址"         prop="address"         min-width="300"       ></el-table-column>       <el-table-column         label="地址"         prop="address"         min-width="300"       ></el-table-column>       <el-table-column         label="地址"         prop="address"         min-width="300"       ></el-table-column>       <el-table-column         label="地址"         prop="address"         min-width="300"       ></el-table-column>       <el-table-column         label="地址"         prop="address"         min-width="300"       ></el-table-column>       <el-table-column         label="地址"         prop="address"       min-width="300"       ></el-table-column>     </TTable>   </div> </template>  <script> import TTable from "../../../src/components/TTable.vue"; export default {   components: {     TTable,   },   props: {},   data() {     return {       saveDATA: [], // 所有数据     };   },   watch: {},   created() {    },   mounted() {     this.init();   },   methods: {     init() {       this.saveDATA = [];        for (let i = 0; i < 2000; i++) {         this.saveDATA.push({           id: i,           name: `张三${i}`,           age: i,           address: "重庆市渝中区两路口阿士大夫撒多富士达浪费的水立方as的裂缝阿萨德立法技术放两三分啥地方了as的裂缝as了巅峰就啥的立法啥的立法阿斯蒂芬了as的裂缝就啥砥砺奋进阿萨德理发店发了"         });       }     }   } }; </script>