实现表格的固定列和表头的效果

前言

基于表格横向有超多数据的需求,渲染量比较大,使用el-table的固定列功能会出现2~3秒的卡顿,所以自己实现了一个固定头+列的表格效果
脱离element-ui又借鉴element-ui,实现类似el-table的固定列和表头的效果

  • 1-1固定,1-2只支持上下滑动,2-1只支持左右滑动,2-2上下、左右均可滑动
  • 其中下图中1-22-1两part的滑动事件应用了el-table的mousewheel

效果

在这里插入图片描述
滑动效果

实现

布局

<div class="tree_table">
  <div class="scroll_table" ref="tableScroll">
     <div class="header" ref="scrollHead" v-mousewheel="handleHeaderMousewheel">
         <ul class="header_row row" :style="{ 'width': ulBoxWidth }">
           <li v-for="(date, index) in dateList" :key="'head' + index">
               {{ date.dateText }}
           </li>  
         </ul>
     </div>
     <div class="body" ref="tableContent">
         <ul v-for="(item, index1) in rowlist" :key="index1" class="row" :style="{ 'width': ulBoxWidth }">
           <li v-for="(date, index) in dateList" :key="'body' + index" class="border-dash">
              <span>{{ index1 }}</span>
           </li>
         </ul>
     </div>
  </div>
  <div class="fixed_table" :class="{'table-fixed-left-scroll': hasLeft}">
     <div class="fixed_header">
         <ul class="header_row row">
            <li style="width:200px;">项目</li>
         </ul>
     </div>
     <div class="fixed_cont" ref="fixedBody" v-mousewheel="handleFixedMousewheel">
         <ul v-for="(item, index) in rowlist" :key="'fixed_item' + index" class="row">
            <li style="width:200px;" class="border-dash">{{ item }}</li>
         </ul>
     </div>
  </div>
</div>

数据

import Mousewheel from '@/utils/mousewheel'
export default {
  name: 'testDemo',
  data() {
    return {
      dateList: [], // header日期
      rowlist: [], // 行数据
      hasLeft: false,
    }
  },
  directives: {
    Mousewheel
  },
  computed: {
    ulBoxWidth() {
      return this.dateList.length * 80 + 'px'
    },
    bodyWrapper() {
      return this.$refs.tableContent
    }
  },
  mounted() {
    this.getRightHeader()
    this.getRows()
    this.bodyWrapper.addEventListener('scroll', this.onScroll, true)
  },
  methods: {
    onScroll() {
      let e = this.bodyWrapper
      this.scrollValue = this.bodyWrapper.scrollLeft
      this.hasLeft = this.scrollValue > 0
      this.$refs.fixedBody.scrollTop = this.bodyWrapper.scrollTop
      this.$refs.scrollHead.scrollLeft = this.bodyWrapper.scrollLeft
    },
    // 滑动左侧固定列
    handleFixedMousewheel(event, data) {
      const bodyWrapper = this.bodyWrapper
      if (Math.abs(data.spinY) > 0) {
        const currentScrollTop = this.bodyWrapper.scrollTop
        if (data.pixelY < 0 && currentScrollTop !== 0) {
          event.preventDefault()
        }
        if (data.pixelY > 0 && bodyWrapper.scrollHeight - bodyWrapper.clientHeight > currentScrollTop) {
          event.preventDefault()
        }
        bodyWrapper.scrollTop += Math.ceil(data.pixelY / 5)
      } else {
        bodyWrapper.scrollLeft += Math.ceil(data.pixelX / 5)
      }
    },
    // 滑动头部日期固定行
    handleHeaderMousewheel(event, data) {
      const { pixelX, pixelY } = data;
      if (Math.abs(pixelX) >= Math.abs(pixelY)) {
        event.preventDefault();
        this.bodyWrapper.scrollLeft += data.pixelX / 5;
      }
    },
    // 时间戳转时间
    timestampToDate(timestamp) {
      var date = new Date(timestamp * 1000)
      var M = date.getMonth() + 1
      var D = date.getDate()
      return M + '月' + D + '日'
    },
    // 计算头部日历表(默认渲染30条)
    getRightHeader() {
      let startT = new Date().getTime()
      let startDatas = []
      for(let i = 0; i < 30; i++) {
        const obj = {}
        obj.timestamp = startT + 24 * 60 * 60
        obj.week = new Date(parseInt(obj.timestamp) * 1000).getDay()
        obj.dateText = this.timestampToDate(obj.timestamp)
        startDatas.push(obj)
        startT = obj.timestamp
      }
      this.dateList = startDatas
    },
    // 模拟行
    getRows() {
      for(let i = 0; i < 30; i++) {
        this.rowlist.push(i+'体验课设置-预约-展示-播放系统')
      }
    }
  }
}

element-ui的mousewheel

import normalizeWheel from 'normalize-wheel'

const isFirefox = typeof navigator !== 'undefined' && navigator.userAgent.toLowerCase().indexOf('firefox') > -1;

const mousewheel = function(element, callback) {
  if (element && element.addEventListener) {
    element.addEventListener(isFirefox ? 'DOMMouseScroll' : 'mousewheel', function(event) {
      const normalized = normalizeWheel(event);
      callback && callback.apply(this, [event, normalized]);
    });
  }
};

export default {
  bind(el, binding) {
    mousewheel(el, binding.value);
  }
};

css

.tree_table {
  width: 1000px; 
  height: 600px;
  margin: 20px;
  border: 1px solid #ddd;
  overflow: hidden; 
  border-bottom: 1px solid #eee; 
  position: relative;
  .scroll_table {
    height:100%; 
    overflow-x: hidden; 
    position: relative; 
    overflow-y: hidden;
    .header {
      width: calc(100% - 200px);
      margin-left: 200px;
      overflow: hidden;
      color: #666;
      font-size: 15px;
      background-color: rgba(242, 242, 242, 1);
      li {
        text-align: center;
      }
    }
    .body {
      width: calc(100% - 200px);
      height: calc(100% - 66px); 
      margin-left: 200px;
      color: #333;
      font-size: 14px;
      overflow-y: auto;
      overflow-x: auto;
    }
  }
  .fixed_table {
    width: 200px; 
    height: 100%;
    position: absolute; 
    top: 0; 
    left: 0;  
    overflow-x: hidden;
    background-color: #fff;
    .fixed_header {
      overflow: hidden;
      color: #666;
      font-size: 15px;
      background-color: rgba(242, 242, 242, 1);
      li {
        padding: 0 10px;
      }
    }
    .fixed_cont {
      overflow: hidden;
      color: #333;
      font-size: 14px;
      height: calc(100% - 66px); 
      li {
        padding: 0 10px;
      }
    }
  }
  .table-fixed-left-scroll {
    box-shadow: 6px 0 6px -4px rgba(0,0,0,.2);
  }
  .row {
    height: 40px;
    line-height: 40px;
    overflow: hidden;
    padding-left: 0;
    text-align: center;
    li {
      position: relative;
      list-style: none;
      float: left;
      width: 80px;
      height: 100%;
      overflow: hidden;
      white-space: nowrap;
      text-overflow: ellipsis;
    }
  }
  .header_row {
    height: 36px;
    line-height: 36px;
    padding-left: 0;
  }
  .border-dash {
    border-right: 1px solid #eee;
  }
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值