前言
基于表格横向有超多数据的需求,渲染量比较大,使用el-table的固定列功能会出现2~3秒的卡顿,所以自己实现了一个固定头+列的表格效果
脱离element-ui又借鉴element-ui,实现类似el-table的固定列和表头的效果
- 1-1固定,1-2只支持上下滑动,2-1只支持左右滑动,2-2上下、左右均可滑动
- 其中下图中1-2和2-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;
}
}