虚拟列表动态

55 阅读4分钟
 <template>
  <div class="home" style="overflow:hidden" id="main-inner-content">
    <!-- <LF55AddOptions/> -->
    <!-- <LF55BaseContainer />
    <LF55BaseContainer /> -->
    <!-- <LF55DynamicTable 
    :needPagination="true"/>  -->
    <div ></div>
    <BaseTable
        :columns="tableColumns"
        :tableData="tableData"
        :isScroll="true"
        :tableHeight="550"
        :border="true"
        :rowKey="record=>{record.id}"
        @headerDragend="handleTableScroll"
         ref="table">
          <!-- <template v-slot:fileName="record">
             <span>{{record}}</span>
          </template> -->
          <!-- <template slot="opration">
             <el-button size="small">删除</el-button>
          </template> -->
          <template slot="opration">
            <el-button size="small">编辑</el-button>
             <el-button size="small">删除</el-button>
          </template>
    </BaseTable>
    <!-- <div>555</div> -->
  </div>
</template>

<script>
import {getTableData} from '@/api/dataExport.js';
// import tableHeightDivVue from './tableHeightDiv.vue';
export default {
  name: 'Home',
  components: {

  },
  data() {
    return {
        tableColumns:[
            {
            title: "id",
            // scopedSlots: { customRender: "fileName" },
            dataIndex: "key",
            width:100,
            ellipsis: false
            },
            {
            title: "文件名",
            // scopedSlots: { customRender: "fileName" },
            dataIndex: "oppFlgValue",
            width:100,
            ellipsis: false
            },
            {
            title: "模型名称",
            dataIndex: "oppNam",
            width: 100,
            ellipsis: false
            },
            {
            title: "模型状态",
            dataIndex:'oppRgn',
            //   scopedSlots: { customRender: "" },
            //   width: 100,
            ellipsis: false
            },
            {
            title: "模型标签",
            dataIndex: "oppNbr",
            ellipsis: false,
            width: 150
            },
            {
            title: "模型标签",
            dataIndex: "oppRgnValue",
            ellipsis: false,
            width: 150
            },
             {
            title: "模型标签",
            dataIndex: "ipAdrValue",
            ellipsis: false,
            // width: 150
            },
             {
            title: "模型标签",
            dataIndex: "oppBrnValue",
            ellipsis: false,
            // width: 150
            },
            {
            title: "模型标签",
            dataIndex: "ipAdr",
            ellipsis: false,
            // width: 150
            },
            {
            title: "操作",
            scopedSlots: { customRender: "opration" },
            //   width: 100,
            //   align: "center",
            //   fixed: "right",
            // ellipsis: true
            }
        ],
        tableData:[
            {fileName:'666',modelName:'55555555555555555555555555555555555555555555555555555',modelTopicValue:'333',modelStateValue:'666'}
        ],
        datas:[],//总元素
        lineHeight:40,//预估高度
        cacheArr:[],//缓存数组
        listAllHeight:0,//滚动区域总和
        isAllHeight:false,//是否所有节点的真实高度以获取完成
        allHeightIndex:0,//所有节点的真实高度以获取完成对应的元素
        isScroll:true,//是否滚动到最底部
        stopScrollIndex:0,//滚动到最底部对应的元素
        tableHeightArr:[]

    }
  },
  computed:{
  },
  created() {
    // this.initPosition(this.lineHeight)
  },
  mounted() {
    this.initData(this.lineHeight)
    // this.handleTableScroll()
  },
  methods:{
    headerDragend(newWidth, oldWidth, column, event) {
        console.log(newWidth, oldWidth, column, event)
        this.handleTableScroll()
       
    },
     getListAllHeight() {
         return (this.cacheArr && this.cacheArr.length > 0
        ? this.cacheArr[this.cacheArr.length - 1].end
        : 0) + 'px'
        // return this.cacheArr[this.cacheArr.length-1].end
    },
    initData(lineHeight) {
        // for(let i=0;i<10000;i++){
        //     this.datas.push({fileName:''+i,id:i})
        //     i%2===0 && (this.datas[i].fileName = 'llllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllll')
        //     i%3===0 && (this.datas[i].fileName = 'lllllllllllllllllllllllllllllllllllllkkkkkkkkklmmcsllllllllllllllllllllllllllllllllhddddddddddddddddddddddddddddddddddddddlllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllll')
        //      i===9998 && (this.datas[i].fileName = 'lllllllllllllllllllllllllllllllllllllkkkkkkkkklmmcsllllllllllllllllllllllllllllllllhddddddddddddddddddddddddddddddddddddddlllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllll看到基督教基督教的亟待解决的就打架大家基督教基督教的就打架大家巨大的积极的角度亟待解决的大家假的假的假的假的坚决打击就打架大家觉得就打架大家假的假的假的假的的男男女女你就大胆的当年年底iiiiiiiiiiiiiiiiiiiii哦顶顶顶顶顶顶是看可是可是可是姐姐大家都送哦时间参加就参加')
        //     i===9999 && (this.datas[i].fileName = 'lllllllllllllllllllllllllllllllllllllkkkkkkkkklmmcsllllllllllllllllllllllllllllllllhddddddddddddddddddddddddddddddddddddddlllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllll看到基督教基督教的亟待解决的就打架大家基督教基督教的就打架大家巨大的积极的角度亟待解决的大家假的假的假的假的坚决打击就打架大家觉得就打架大家假的假的假的假的的男男女女你就大胆的当年年底iiiiiiiiiiiiiiiiiiiii哦顶顶顶顶顶顶是看可是可是可是姐姐大家都送哦时间参加就参加')
        // }
    //    this.allHeightIndex = this.datas.length-1
    //    this.stopScrollIndex = this.datas.length-1
    //    this.cacheArr = this.datas.map((item,index) => {
    //       return {
    //         index,
    //         start:index*lineHeight,
    //         end:(index+1)*lineHeight,
    //         height:lineHeight
    //       }
    //    })
    //    let allHeight = 500
    //    let pageCount = Math.round(allHeight/this.lineHeight)
    //    this.tableData = this.datas.slice(0, pageCount);
    let params ={
        startDate: '2023-03-05',
        endDate: '2023-08-31',
        custSource: 'VBS',
        custUid: 'PCIF1259018467',
        pageNum: 1,
        pageSize: 10000,

    }
    getTableData(params).then(res => {
        this.datas = res.data.page.list
        for(let i=0;i<10;i++){
            this.datas = this.datas.concat(JSON.parse(JSON.stringify(res.data.page.list)))
        }
        console.log(this.datas)
        for(let j=0;j<this.datas.length;j++){
            this.datas.push({
                id:j
            })
            this.datas[j].key = j
            j%2===0 && (this.datas[j].oppFlgValue = this.datas[j].oppFlgValue+'llllllllllllllllllllllllll')
            j%3===0 && (this.datas[j].oppFlgValue = this.datas[j].oppFlgValue+'lllllllllllllllllllllllllllllllllllllkvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvcccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccckkkkkkkklmmcsl')
        }
       this.allHeightIndex = this.datas.length-1
       this.stopScrollIndex = this.datas.length-1
       this.cacheArr = this.datas.map((item,index) => {
          return {
            index,
            start:index*lineHeight,
            end:(index+1)*lineHeight,
            height:lineHeight
          }
       })
       let allHeight = 500
       let pageCount = Math.round(allHeight/this.lineHeight)
       this.tableData = this.datas.slice(0, pageCount);
       this.initPosition()
    }).finally(()=> {
         for(let j=0;j<10000;j++){
            this.datas.push({
                id:j
            })
            this.datas[j].key = j
            j%2===0 && (this.datas[j].oppFlgValue = this.datas[j].oppFlgValue+'llllllllllllllllllllllllll')
            j%3===0 && (this.datas[j].oppFlgValue = this.datas[j].oppFlgValue+'lllllllllllllllllllllllllllllllllllllkvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvcccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccckkkkkkkklmmcsl')
        }
        this.allHeightIndex = this.datas.length-1
       this.stopScrollIndex = this.datas.length-1
       this.cacheArr = this.datas.map((item,index) => {
          return {
            index,
            start:index*lineHeight,
            end:(index+1)*lineHeight,
            height:lineHeight
          }
       })
       let allHeight = 500
       let pageCount = Math.round(allHeight/this.lineHeight)
       this.tableData = this.datas.slice(0, pageCount);
       this.initPosition()
        console.log(this.datas)
    })
    },
    initPosition() {
        this.updatePosition(0,0)
        // let allHeight = 500
        // let lineHeight = 40
        this.dom = this.$refs.table.$refs.BaseTable.bodyWrapper
        var element = this.$refs.table.$refs.BaseTable.bodyWrapper
        var div = document.createElement("div");  //创建段落元素
        div.style.height= this.listAllHeight
        div.style.position = 'absolute'
        div.style.width = '100px'
        div.className = 'myDiv'
        element.insertBefore(div,element.children[0]);
        this.loading = false
        this.dom.addEventListener('scroll', () => {
          this.handleTableScroll()
        })
    },
    updatePosition(startIndex,scrollTop) {
        this.$nextTick(() => {
          let trNodes = [...document.querySelectorAll('.el-table__row')]
          let itemIndex = startIndex
          let endIndex = itemIndex+2
          //计算滚动时,前一个元素留下来的部分(没完全滚动上去)
          let startRest = this.cacheArr[startIndex].height - scrollTop%this.cacheArr[startIndex].height
          this.tableHeightArr = []
          for(let i=0;i<trNodes.length;i++){
                let node = trNodes[i]
                let newHeight = node.clientHeight;
                this.tableHeightArr = trNodes
                let oldHeight = this.cacheArr[itemIndex].height
                 console.log('newHeight',node.getBoundingClientRect().height , window.getComputedStyle(node).height,node,newHeight,oldHeight)
                const dValue = newHeight - oldHeight
                if (dValue) {
                    this.cacheArr[itemIndex].height = node.clientHeight;
                    startRest = this.cacheArr[startIndex].height - scrollTop%this.cacheArr[startIndex].height
                    // 当前节点与底部的距离 = 上一个节点与底部的距离 + 当前节点的高度
                    this.cacheArr[itemIndex].end = itemIndex > 0 ? this.cacheArr[itemIndex - 1].end + newHeight : this.cacheArr[itemIndex].height;
                    // 当前节点与顶部的距离 = 上一个节点与底部的距离
                    this.cacheArr[itemIndex].start = itemIndex > 0 ? this.cacheArr[itemIndex - 1].end : 0;
                    // 更改一个节点就需要更改之后所有的值,不然会造成空白
                    for (let j = itemIndex + 1, len = this.cacheArr.length; j < len; j++) {
                        this.cacheArr[j].start = this.cacheArr[j - 1].end;
                        this.cacheArr[j].end += dValue;
                    }
                }
                //计算可视区内元素
                let curHeight = 0
                curHeight =  itemIndex>startIndex? startRest + this.cacheArr[itemIndex].end-this.cacheArr[startIndex].end : startRest //细看
                // console.log(itemIndex,curHeight,trNodes)
                if(curHeight>500){
                    this.tableData = this.datas.slice(startIndex, endIndex+1);
                    //计算所有元素是否已全部获取完真实高度(首次可视区内最后一个元素为总元素的最后一个)
                    // !this.isAllHeight && itemIndex === this.cacheArr.length-1 && (this.allHeightIndex = startIndex )&& (this.isAllHeight=true)
                    if(itemIndex === this.cacheArr.length-1){
                        this.allHeightIndex = startIndex 
                        // this.isAllHeight=true
                    }
                    break;
                }
                itemIndex++
                // endIndex = itemIndex+2
                endIndex = itemIndex
          }
        //   console.log(this.tableData,this.tableHeightArr)
          itemIndex = itemIndex>this.cacheArr.length-1?this.cacheArr.length-1:itemIndex
          //全部为真实节点,this.listAllHeight就不再更新了
          if(startIndex<=this.allHeightIndex){
                this.listAllHeight = this.getListAllHeight()
                document.querySelector('.myDiv').style.height = this.listAllHeight
          }
          if(scrollTop !== undefined){
            // startIndex = startIndex>this.cacheArr.length-1?this.cacheArr.length-1:startIndex
            // 计算滚动条是否滚到最底部
            let isScrollHeight = startIndex<this.cacheArr.length-1?(startRest + this.cacheArr[itemIndex].end-this.cacheArr[startIndex+1].end):this.cacheArr[startIndex].height//(可视区元素总高度)
            this.isScroll = isScrollHeight>500
            //滚动条滚到最底部时(首次可视区元素总高度小于可视区高度并且可视区内最后一个元素为总元素的最后一个)
            if(!this.isScroll && itemIndex===this.cacheArr.length-1){
                this.stopScrollIndex = startIndex
            //     this.startOffset =  scrollTop-scrollTop%this.cacheArr[startIndex].height
            //     let tableBody = document.querySelector('.el-table__body') 
            //     tableBody.style.transform = this.getTransform()
            //    return
            }else{
                //不同滚动方式,滚动条滚到最底部时对应的元素不同,所以没有到达最底部时将 this.stopScrollIndex恢复到最大值,以防止下次向下滚动计算的临界值有问题
                this.stopScrollIndex = this.cacheArr.length-1
            }
            //可视区第一个元素的向上偏移
            this.startOffset =  scrollTop-scrollTop%this.cacheArr[startIndex].height
            // console.log(startIndex,(scrollTop-this.cacheArr[startIndex].start)%this.cacheArr[startIndex].height,scrollTop%this.cacheArr[startIndex].height)
            // this.startOffset =  scrollTop-((scrollTop-this.cacheArr[startIndex].start)%this.cacheArr[startIndex].height)
            let tableBody = document.querySelector('.el-table__body') 
            tableBody.style.transform = this.getTransform()
          }
        })
     
    },
    getTransform () { 
        return `translate3d(0,${this.startOffset}px,0)`; 
    }, 
    findItemIndex(scrollTop) {
        let low = 0; 
        let high = this.cacheArr.length - 1;
        while(low <= high) {
            const mid = Math.floor((low + high) / 2);
            const { start, end } = this.cacheArr[mid];
            if (scrollTop >= start && scrollTop <= end) {
                high = mid;
                break;
            } else if (scrollTop > end) {
                low = mid + 1;
            } else if (scrollTop < start) {
                high = mid - 1;
            }
        }
        // console.log('high',high,'stopScrollIndex',this.stopScrollIndex)
        //滚动到最底部时,不需要再计算了
        // high = high>=this.stopScrollIndex?this.stopScrollIndex:high
        return high;
    },
    handleTableScroll() {
        // this.updatePosition(0,0)
        // console.log(this.tableData,this.cacheArr)
        let allHeight = 500
        let lineHeight = 40
        // this.dom = this.$refs.table.$refs.BaseTable.bodyWrapper
        // var element = this.$refs.table.$refs.BaseTable.bodyWrapper
        // var div = document.createElement("div");  //创建段落元素
        // div.style.height= this.listAllHeight
        // div.style.position = 'absolute'
        // div.style.width = '100px'
        // div.className = 'myDiv'
        // element.insertBefore(div,element.children[0]);
        // this.loading = false
        // this.dom.addEventListener('scroll', () => {
            // 滚动距离
            let scrollTop = this.dom.scrollTop
            //获取当前滚动距离对应的第一个元素
            let startIndex = this.findItemIndex(scrollTop) 
            console.log('scrollTop',scrollTop,'startIndex',startIndex,'stopScrollIndex',this.stopScrollIndex)

            //当前滚动条已经到最底端了,就不需要再执行更新操作了
            if(startIndex >this.stopScrollIndex){
                return
            }

            // let startIndex = this.tableData[0].id
            // for(let i=this.tableData[0].id;i<this.tableData[this.tableData.length-1].id;i++){
            //     if(this.cacheArr[i].end===scrollTop){
            //       startIndex = i+1
            //       break;
            //     }
            //     if(this.cacheArr[i].end>scrollTop){
            //       startIndex = i
            //       break;
            //     }
            // }

            // 预估可见数量
            const viewCount = Math.ceil(allHeight / lineHeight);
            let endIndex = startIndex + viewCount
            this.tableData = this.datas.slice(startIndex, endIndex)
            //获取真实高度并缓存
            this.updatePosition(startIndex,scrollTop)
        // })
    }
  }
}
</script>
<style scoped lang="less">
/deep/.el-table th.el-table__cell{
    height:50px
}
</style>