实现方式:vue + vantui
携手创作,共同成长!这是我参与「掘金日新计划 · 8 月更文挑战」的第1天,点击查看活动详情
实现效果
实现步骤
1.表头部分
支持二级表头展示
- 表格头部一个循环:循环渲染每一列的列名
<van-row class="fake-table__head">
<van-col
v-for="(item, index) in headData"
:key="index"
class="col"
:span="item.span"
>
<div class="header-col">
<span class="title-parent">{{ item.label }}</span>
<div
v-if="item.children"
class="title-children"
>
<span
v-for="(headerChild,inx) in item.children"
:key="headerChild+inx"
:style="{width: headerChild.width ? headerChild.width : '80px'}"
class="title-child"
>{{ headerChild.label }}</span>
</div>
</div>
</van-col>
</van-row>
2.主体部分
- 表格内容两重循环:外层循环渲染行,内层循环渲染每一行的每一列的内容
- 需求特殊:需要多个表格主体,故外层需要循环
<div
v-for="(unit, index) in bodyData"
:key="index"
class="fake-table-day"
>
<div class="time-tag">
{{ unit.rq }}
</div>
<van-row
v-for="(item, idx) in unit.children"
:key="idx+item"
class="fake-table__body"
>
<template v-for="(colItem, colIndex) in headData">
<template v-if="colItem.children">
<template v-for="(colChild,index) in colItem.children">
<van-col
v-if="colChild.prop"
:key="colChild+index"
class="col"
>
{{ item[colChild.prop] ? item[colChild.prop] : '-' }}
</van-col>
</template>
</template>
<template v-else>
<van-col
v-if="colItem.prop"
:key="colIndex"
class="col"
:span="colItem.span"
>
{{ item[colItem.prop] || item[colItem.prop1] || item[colItem.prop2] }}
</van-col>
</template>
</template>
</van-row>
</div>
3.实现固定第一列
实现方式:借鉴el-table,在原有表格上在复制一份dom元素放在最上面,然后隐藏最上面的第二列到最后一列,故可以实现第一列固定的效果
.vant-table{
-webkit-overflow-scrolling: auto;
width: 100%;
overflow-x: hidden;
position: relative;
height: 100%;
.time-tag{
width: 90px;
color: #333;
margin-top: 12px;
margin-bottom:12px;
padding: 2px;
text-align: center;
font-size: 14px;
background: #EAEAEA;
border-radius: 8px;
margin-left: 12px;
}
.fake-table-head-fixed{
transition: all 0.1;
position: fixed;
display: flex;
top: 136px;
z-index: 999;
text-align: center;
background: #FFFFFF;
width: 100%;
box-shadow: 0px 4px 6px 0px rgba(0,0,0,0.08);
flex-wrap: nowrap;
.col{
&:nth-child(1){
z-index: 999999;
background: #FFFFFF;
box-shadow: 0px 0px 12px 1px rgba(0,0,0,0.05)
}
}
}
.col{
min-width: 80px;
flex:0 0 auto;
white-space: nowrap;
font-size: 16px;
color: #333333;
letter-spacing: 0;
text-align: center;
height: 100%;
}
.header-col{
display: flex;
flex-direction: column;
padding:16px 0;
align-items: center;
justify-content: center;
.title-parent{
font-weight: 400px;
font-size: 16px;
}
.title-children{
display: flex;
justify-content: space-around;
.title-child{
min-width: 80px;
color: #999;
font-size: 14px;
margin-top: 6px;
}
}
}
.fake-table{
position: relative;
.col{
white-space: nowrap;
font-size: 16px;
color: #333333;
letter-spacing: 0;
}
.fake-table__head{
scroll-behavior: smooth;
text-align: center;
background: #FFFFFF;
width: 100%;
flex-wrap: nowrap;
.col{
&:nth-child(1){
z-index: 999;
background: #FFFFFF;
box-shadow: 0px 0px 12px 1px rgba(0,0,0,0.05)
}
}
.col{
font-weight: 500;
}
}
.fake-table-day{
.col{
visibility:hidden;
position: relative;
z-index: 999;
&:nth-child(1){
visibility:visible;
background: #FFFFFF;
box-shadow: 0px 0px 12px 1px rgba(0,0,0,0.05)
}
}
}
.fake-table__body{
height:54px;
line-height: 54px;
text-align: center;
background: #FFFFFF;
flex-wrap: nowrap;
&:nth-child(n):not(:last-child){
border-bottom: 1px solid #DEDEDE;
}
.col{
font-weight: 500;
}
}
}
.table-fixed{
position:absolute;
top: 0;
left: 0;
width: 100%;
overflow: scroll;
.time-tag{
visibility:hidden;
}
.fake-table__head{
scroll-behavior: smooth;
position:fixed;
text-align: center;
flex-wrap: nowrap;
position: sticky;
top: 0;
.col{
visibility:hidden;
}
.col{
font-weight: 500;
}
}
.fake-table-day{
.fake-table__body{
.col{
&:nth-child(1){
visibility:hidden;
}
}
}
}
.fake-table__body{
height:54px;
line-height: 54px;
text-align: center;
flex-wrap: nowrap;
&:nth-child(n):not(:last-child){
border-bottom: 1px solid #DEDEDE;
}
.col{
font-weight: 500;
}
}
}
}
4.实现表头实现吸顶效果
利用js动态计算表头与窗口最上面的高度,动态生成表头,实现吸顶效果
function createdTableHeader () {
const [...el_body] = document.querySelectorAll('.table-fixed .fake-table__body ')
if (el_body[0].getBoundingClientRect().top < 200 && !document.querySelector('.fake-table-head-fixed')) {
const el_wrapper = document.querySelector('.fake-table')
const fake_table__head = document.querySelector('.table-fixed .fake-table__head')
const el_fixed_header_top = fake_table__head.cloneNode(true)
el_fixed_header_top.className = 'fake-table-head-fixed'
document.querySelector('.vant-table').appendChild(el_fixed_header_top)
} else if (el_body[0].getBoundingClientRect().top > 200 && document.querySelector('.fake-table-head-fixed')) {
// 拿到父节点:
const fixed_header = document.querySelector('.fake-table-head-fixed')
const parent = fixed_header.parentElement
// 删除:
const removed = parent.removeChild(fixed_header)
}
}
function headerTouched () {
let el = []
const [...el_body] = document.querySelectorAll('.table-fixed .fake-table__body ')
if (el_body[0].getBoundingClientRect().top > 200) {
[...el] = document.querySelectorAll('.fake-table .fake-table__head > .col:not(:first-child)')
} else {
[...el] = document.querySelectorAll('.vant-table .fake-table-head-fixed > .col:not(:first-child)')
}
const viewLeft = el_body[0].getBoundingClientRect().left
el.forEach(unit => {
// unit.style.position = 'relative'
// unit.style.left = `${viewLeft}px`
unit.style.transition = 'all .1s'
unit.style.transform = `translateX(${viewLeft}px)`
})
}
5.表格主体部分滑动时吸顶的表头也随之滑动
setHeaderStyle () {
const [...el_header_col] = document.querySelectorAll('.fake-table .fake-table__head .col:nth-child(n)')
const [...el_body_col] = document.querySelectorAll('.fake-table .fake-table-day .fake-table__body')
// el_header_col.forEach(item => {
// const [...child] = el_body_col[0].children
// console.log(child)
// child.forEach(item => {
// item.style.width = item.offsetWidth + 'px'
// })
// })
const [...el_headerHd] = el_header_col.map(itemHd => {
console.log(itemHd.children[0].children[1].children)
return [...itemHd.children[0].children[1].children]
})
el_headerHd.flat().forEach((item, inx) => {
el_body_col.forEach(unit => {
const [...children] = unit.children
children.forEach((child, index) => {
if (inx === index) {
child.style.width = item.style.width
}
})
})
})
},
fixedTable () {
const THIS = this
const el = document.querySelector('.fake-table')
console.log(el.offsetHeight)
const el_fixed = el.cloneNode(true)
el_fixed.className = 'table-fixed'
document.querySelector('.vant-table').appendChild(el_fixed)
el.style.height = el_fixed.offsetHeight + 'px'
if (!this.isFixedColumn) {
const [...col_el] = document.querySelectorAll('.vant-table .fake-table .fake-table-day .col')
col_el.forEach(item => {
item.style.visibility = 'visible'
})
}
if (this.isFixedHeader) {
el_fixed.ontouchmove = function (e) {
utils.createdTableHeader()
utils.headerTouched()
}
el_fixed.ontouchend = function (e) {
// 动态创建表头
utils.createdTableHeader()
utils.headerTouched()
}
}
},