基于vue的表格,实现基础的固定的表头,固定左侧列
先有主表格,在主表格基础上通过css样式控制实现效果,目前只能固定最左侧一列,或者从左侧开始多列没有实现右侧固定,懒得做,固定的列填入fiexd,目前需要同时在表头和表体传入,最好是上下相同,以后再优化只在表头传值,共三个表格,以下代码为源码,复用时请注明出处1、html代码<template><div class="table_box" :style="...
·
先有主表格,在主表格基础上通过css样式控制实现效果,目前只能固定最左侧一列,或者从左侧开始多列
没有实现右侧固定,懒得做,固定的列填入fiexd,目前需要同时在表头和表体传入,最好是上下相同,以后再优化只在表头传值,
共三个表格,以下代码为源码,复用时请注明出处
1、html代码
<template>
<div class="table_box" :style="{height:heights,textAlign:textAlign}">
<div class="data-grid clearfix">
<div class="grid-head" >
<table class="table_head" ref="thead">
<slot name="head"/>
</table>
</div>
<!--{height:headChildrenLength > 1 ? 'calc(100% - ' + (headChildrenLength*32 -1) + 'px)' : 'calc(100% - 31px)' }-->
<div class="grid-body" :style="{height:'calc(100% - ' + tableHeadHeight + 'px)'}">
<table class="table_body" ref="tbody">
<slot/>
<tbody ref="bodyTr">
<slot name="body"/>
</tbody>
</table>
<p v-if="data === undefined || data.length === 0" class="not_data">暂无数据</p>
</div>
</div>
<!--表格表头固定列-->
<div class="table_head_left">
<table>
<tbody>
<tr>
<td v-for="item in headFixedList"
v-html="item.innerHTML" style="background: #f2f2f2"
:style="{width:item.offsetWidth+'px',height:item.offsetHeight + 'px',borderRight:item.style.borderRight,
minWidth:item.offsetWidth+'px',textAlign:item.style.textAlign}">{{item.innerHTML}}
</td>
</tr>
</tbody>
</table>
</div>
<!--表格主体固定列-->
<div class="table_body_left" :style="{top:tableHeadHeight +2+'px',width:widthHL,
height:'calc(100% - ' + (tableHeadHeight+3) + 'px)'}">
<table class="table_scroll_box">
<tbody>
<tr v-for="item in bodyFixedList">
<td v-for="item1 in item" v-html="item1.innerHTML"
:class="item1.className"
:style="{
width:item1.offsetWidth+'px',
height:item1.offsetHeight + 'px',
borderRight:item1.style.borderRight,
background:item1.classList && item1.classList.length > 1 ? '' : item1.style.background || '#ffffff',
minWidth:item1.offsetWidth+'px',
textAlign:item1.style.textAlign}">
{{item1.innerHTML}}
</td>
</tr>
</tbody>
</table>
</div>
</div>
</template>
2、js
props:{
data:Array,// 接收列表数组,判断是否展示有无数据
height:{type:String,default:''},// 表格高度
right:{type:Boolean,default:false},// 表格的文字位置对于整个表格来说,区别于table-item的传值,传值时直接写right或者left,不写默认为center
left:{type:Boolean,default:false},
},
data(){
return{
textAlign:'left',// 文字位置
heights:'',// 表格高度
boolean:true,// 判断是否传了表格高度值
headChildrenLength:0,// 表头tr的数量
tableHeadHeight:0,// 表头高度
headFixedList:[],// 表头固定列数组
bodyFixedList:[],// 表头固定列数组
}
},
methods:{
initial(){// 初始给表格高度赋值,如果父类没有定义height值,则使用100% 减去下面的值,否则此方法不执行
let height = 0;
let form = document.getElementsByClassName('hz-form');// 表单
let filtering = document.getElementsByClassName('hz-list-filtering');// 横向列表
let oper = document.getElementsByClassName('hz-oper-default');// 表单按钮
let pager = document.getElementsByClassName('hz-pager')[0];// 分页
let priceSettlement = document.getElementsByClassName('price-settlement')[0];// 收费项目下的总计
let tabs = document.getElementsByClassName('tab_box');// 选项卡
let selectSeparatedLine = document.getElementsByClassName('select_separated_line');
if(!!form && form.length !== 0){
for(let i =0;i<form.length ; i++){
height += form[i].offsetHeight
}
}
if(!!tabs && tabs.length !== 0){
for(let i =0;i<tabs.length ; i++){
height += tabs[i].offsetHeight
}
}
if(!!selectSeparatedLine && selectSeparatedLine.length !== 0){
for(let i =0;i<selectSeparatedLine.length ; i++){
height += selectSeparatedLine[i].offsetHeight
}
}
if(!!oper && oper.length !== 0){
for(let i =0;i<oper.length ; i++){
height += oper[i].offsetHeight
}
}
if(!!filtering && filtering.length !== 0){
for(let i=0 ; i<filtering.length;i++){
height += filtering[i].offsetHeight;
}
}
if(!!pager){// 分页只有一个
height += pager.offsetHeight;
height += 8;
}
if(!!priceSettlement){
height += priceSettlement.offsetHeight;
height += 8;
}
this.heights = 'calc(100% - ' + height + 'px)';
},
init(){// 初始化获取表格固定列数据
let _this = this;
let list = [];
Array.from(_this.$refs.thead.children[0].children).forEach(item=>{
if(item.getAttribute('fixed')!== null && item.getAttribute('fixed') !== undefined)list.push(item);
});
_this.headFixedList = list;
// console.dir(this.headFixedList);
setTimeout(()=>{
let data = [];
Array.from(_this.$refs.bodyTr.children).forEach((item)=>{
let list1=[];
Array.from(item.children).forEach((item1)=>{
if(item1.getAttribute('fixed')!== null && item1.getAttribute('fixed') !== undefined)list1.push(item1);
});
data.push(list1);
});
_this.bodyFixedList = data;
// console.log(this.bodyFixedList);
})
}
},
watch:{
data(data){
this.init();
}
},
computed:{
widthHL(){
let widthHL = 0;
this.headFixedList.forEach(item=>{
if(item.style.borderRightWidth) widthHL += item.offsetWidth + parseInt(item.style.borderRightWidth);
else widthHL += item.offsetWidth;
});
return widthHL + 'px';
},
},
mounted(){
if(this.right)this.textAlign = 'right';
if(this.left)this.textAlign = 'left';
this.$nextTick(()=>{
this.headChildrenLength = this.$refs.thead.childNodes.length;// 表头tr数组长度
this.tableHeadHeight = this.$refs.thead.clientHeight;// 表头高度
this.init();
let gridbody = document.querySelector(".grid-body"),// 滚动对象
tableBL = document.querySelector(".table_body_left");// 表格主体固定列对象;
gridbody.addEventListener('scroll',function (e) {// 监听表格容器的滚动事件
// console.log(e);
let currentScrollTop = e.target.scrollTop;
let tableScrollBL = tableBL.getElementsByClassName('table_scroll_box');// 获取表体左侧固定列对象
tableScrollBL[0].style.top = -currentScrollTop+"px";
},false);
if(this.height.endsWith('%')){// 如果是以100%结尾的
this.heights = this.height;
}
if(this.height !== '') {// 如果是传入实际高度值的
this.heights = this.height + 'px';
}
if(this.heights === ''){
this.initial();
let _this = this;
window.onresize = function() {
_this.initial();
};
}
});
/** 给表格赋予最小宽度,
* 当父级元素宽度小于表格最小宽度时给表格设置x轴横向滚动
* 以避免表格在横向任意缩放时产生阴影效果,
* 并且设置浏览器窗口变化监听事件
**/
let childrenList = this.$refs.thead.children[0];// 以head表头宽度计算,表头会存在多级表头,以第一级为准
// console.log(childrenList)
if(childrenList){
let widths = 0;
for(let i =0;i<childrenList.children.length;i++){
if(childrenList.children[i].style.width) {// 如果td有宽度
widths += parseInt(childrenList.children[i].style.width);
}else {// 否则没有宽度,如果colSpan跨列值大于1,用跨列数*80,
// console.dir(childrenList.children[i])
childrenList.children[i].colSpan > 1
? widths += childrenList.children[i].colSpan * 80
: widths += 80;
}
}
let _this = this;
let parentWidth = _this.$refs.tbody.parentNode.parentNode.offsetWidth;
// console.log(parentWidth,widths)
if(parseInt(parentWidth) < widths){
_this.$refs.thead.parentNode.style.minWidth = widths + 'px';
_this.$refs.tbody.parentNode.style.minWidth = widths + 'px';
}
window.addEventListener('resize',function () {
if(_this.$refs.tbody){
let parentWidth = _this.$refs.tbody.parentNode.parentNode.offsetWidth;
// console.log(parentWidth,widths)
if(parseInt(parentWidth) < widths){
_this.$refs.thead.parentNode.style.minWidth = widths + 'px';
_this.$refs.tbody.parentNode.style.minWidth = widths + 'px';
}
}
})
}
}
3、css
.table_box{
position: relative;
width: 100%;
height: 100%;
overflow: hidden;
}
.data-grid{
width: 100%;
height: 100%;
overflow: hidden;
overflow-x: auto;
font-size: 14px;
color:#444;
box-sizing: border-box;
border: 1px solid #dedede;
}
.data-grid td{
border-right: 0;
min-width: 80px;
}
.grid-head{
background-color:#f2f2f2;
border-bottom: 1px solid #dedede;
}
.table_td{
padding: 6px 4px;
font-size: 14px;
position: relative;
}
.table_td .hz-select{
width: 100%;
}
.table_td input{
width: 100%;
}
.table_td img{
width:70px;
height: 70px;
display: inline-block;
margin: 0 2px;
}
.table_td .date-pickers-box > .hz-text{
/*width: 100%;*/
}
.table_head{
width: calc(100% - 12px);
color: #333;
table-layout: fixed;
}
.table_body{
width: 100%;
color:#666;
table-layout: fixed;
}
td .operating{
cursor: pointer;
}
.table_body tr:hover{
background:#ebf7ff;
}
.table_head td{
/*border-right: 1px solid #dedede;*/
}
.table_head td:last-child {
border-right: 1px solid #f2f2f2;
}
.table_body tr:first-child td{
border-top: 0;
}
.table_body td:first-child {
border-left: 0;
}
.table_body td:last-child {
border-right: 0;
}
.table_head_left{
position: absolute;left: 1px;top: 1px;
}
.table_body_left{
position: absolute;
left: 1px;top: 0;
overflow: hidden;
}
.table_scroll_box{
position: absolute;left: 0;top:0;
}
.table_body_left td,.table_body td {
/*border: 1px solid #dedede;*/
border-bottom: 1px solid #dedede;
}
.table_body .table_tr:last-child td{
/*border-bottom: 0;*/
}
.grid-body{
width: 100%;
/*height: calc(100% - 32px);*/
overflow-y: scroll;
}
.grid-body .not_data{
width: 100%;
font-size: 18px;
color: #ccc;
line-height: 100px;
text-align: center;
margin: 0 auto;
}
.grid-body::-webkit-scrollbar{/*滚动条*/
width: 13px;
height: 90%;
background-color:#f2f2f2;
border-left: 1px solid #dedede;
}
.grid-body::-webkit-scrollbar-thumb {/*滚动条里面小方块*/
border-radius: 1px;
background: #c1c1c1;
}
/*.grid-body::-webkit-scrollbar-track {!*滚动条里面轨道*!*/
/*-webkit-box-shadow: inset rgba(0,0,0,0.2);*/
/*}*/
/*.grid-body::-webkit-scrollbar-button{*/
/*background-color: #f1f1f1;*/
/*}*/
.table_td .hz-button{
margin:1px;
}
.table_head_line{
border-bottom: 1px solid #ccc;
width: 100%;
margin: 1px auto
}
.table_td_colspan{
border-top: 1px solid #dedede;
}
.table_td_colspan_lastChild{
border-right: 1px solid #dedede;
}
4、使用测试例子
<hz-table :data="list">
<hz-table-item slot="head">
<hz-table-column width="40" text-align="center" fixed>#</hz-table-column>
<hz-table-column width="80" fixed border-right="1">标准1</hz-table-column>
<hz-table-column width="100">标准2</hz-table-column>
<hz-table-column width="80">标准3</hz-table-column>
<hz-table-column width="100" border-right="1">标准4</hz-table-column>
<hz-table-column width="165" border-right="1">标准5</hz-table-column>
<hz-table-column border-right="1">标准6</hz-table-column>
<hz-table-column border-right="1" width="196">标准7</hz-table-column>
<hz-table-column width="180">标准8</hz-table-column>
<hz-table-column border-right="1">标准9</hz-table-column>
</hz-table-item>
<hz-table-item slot="body" v-for="(item,index) in list" :key="index">
<hz-table-column class="serialNumber" width="40" text-align="center" fixed>{{index+1}}</hz-table-column>
<hz-table-column width="80" fixed border-right="1">{{item.a}}</hz-table-column>
<hz-table-column width="100">{{item.b}}</hz-table-column>
<hz-table-column width="80" class="overflowHiddenEllipsis">
{{item.s}}
</hz-table-column>
<hz-table-column width="100" border-right="1">{{item.g}}</hz-table-column>
<hz-table-column width="165" border-right="1">
{{item.r}}
</hz-table-column>
<hz-table-column border-right="1">
{{item.f}}
</hz-table-column>
<hz-table-column border-right="1" width="196">
{{item.h}}
</hz-table-column>
<hz-table-column width="180">
<button @click="detailEvent(item)">详情</button>
<button @click="editEvent(item)">修改</button>
<button @click="deleteEvent(item)">删除</button>
</hz-table-column>
<hz-table-column border-right="1">
{{item.f}}
</hz-table-column>
</hz-table-item>
</hz-table>
5、补充 hz-table-item , hz-table-column,其实就是tr ,td标签
<template>
<tr class="table_tr" :style="{textAlign:textAlign}">
<slot/>
</tr>
</template>
<script>
export default {
props:{
right:{type:Boolean,default:false},// 表格的文字位置对于整个表格来说,区别于table-item的传值,传值时直接写right或者left,不写默认为center
left:{type:Boolean,default:false},
},
data(){
return{
textAlign:''
}
},
mounted(){
if(this.right)this.textAlign = 'right';
if(this.left)this.textAlign = 'left';
}
}
</script>
<template>
<td class="table_td" :style="{width:width+'px',textAlign:textAlign,borderRight:borderRight + 'px solid #dedede'}" @click="_click">
<slot/>
</td>
</template>
<script>
export default {
props:{
width:{type:String},// 表格的列宽,在表头处设置宽度即可
fixedLeft:{type:Boolean,default:false},// 固定列表左侧列
textAlign:{type:String,default:""},
borderRight:{type:String,default:''},
},
methods:{
_click(){
this.$emit('click')
}
},
}
</script>
更多推荐
已为社区贡献13条内容
所有评论(0)