欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

vue+elementui实现动态多级表头+合并单元格+自适应列宽

程序员文章站 2022-06-08 15:45:06
...

1.动态多级表头的实现

因为项目需求是要根据不同查询条件,显示不同的表头,所以需要动态渲染表头
在表格渲染的时候,label和prop要对应起来,简单一点的办法就是让后端传过来,这是我给后端提供的格式

title:[
	 {label:'一级标题1',item:[   //label为表头的内容,prop为对应的字段名(注意字段名要和后端传过来的数据列表的字段名一致)
	 	{label:'二级标题1',item:[
	 		{label:'三级标题1',prop:'value1'},
	 		{label:'三级标题2',prop:'value2'},
	 		{label:'三级标题3',prop:'value3'},
	 		{label:'三级标题4',prop:'value4'},
	 		{label:'三级标题5',prop:'value5'},
	 	]},
	 	{label:'二级标题1',prop:'value6'},
	 	{label:'二级标题2',prop:'value7'},
	 	{label:'二级标题3',prop:'value8'},
	 	{label:'二级标题4',prop:'value9'},
	 	{label:'二级标题5',prop:'value10'},
	 ]},
	 {label:'一级标题2',prop:'value11'},
],

接收到后端传过来的title列表后,在template中渲染
ps:我的表头嵌套最多三层,所以直接这样写了,需要无限嵌套表头的童鞋自行百度吧有很多

<el-table
:data="tableData">
	<el-table-column v-for="(v,idx) in tableHeader" :key="idx"     //tableHeader为后端传过来的title列表
	:label="v.label" :prop="v.prop" :width="v.width" align="center">
		<el-table-column 
			v-for= "(j,i) in v.item" 
			:key="i"
			:prop="j.prop"
			:label="j.label"
			:width="j.width" align="center">
			<el-table-column 
			v-for= "(m,n) in j.item" 
			:key="n"
			:prop="m.prop"
			:label="m.label"
			:width="m.width" align="center">
		</el-table-column>
		</el-table-column>
	</el-table-column>
</el-table>

2.合并单元格

因为表格中的数据可能相邻几行中的前几列内容是一样的,所以需要合并行

<el-table
:data="tableData"
:span-method="arraySpanMethod">   //添加此方法
	<el-table-column v-for="(v,idx) in tableHeader" :key="idx"   
	:label="v.label" :prop="v.prop" :width="v.width" align="center">
		<el-table-column 
			v-for= "(j,i) in v.item" 
			:key="i"
			:prop="j.prop"
			:label="j.label"
			:width="j.width" align="center">
			<el-table-column 
			v-for= "(m,n) in j.item" 
			:key="n"
			:prop="m.prop"
			:label="m.label"
			:width="m.width" align="center">
		</el-table-column>
		</el-table-column>
	</el-table-column>
</el-table>
export default {
	data() {
		return {
			tableData :[],
			spanArr:[],  //需要合并行的个数
			position:0
		}
	}
}
//获取列表数据
getList(page) {
	this.$axios.post('------URL-----').then(res => {
		this.tableData = res.data.data.data;   //接收列表数据
		this.rowspan();   //计算需要合并的行的个数
	});
},
//获得数据相同的行数
rowspan() {
	//每次调用清空数据
	this.spanArr=[];
	this.position=0
	this.tableData.forEach((item,index) => {
		if( index === 0){
			this.spanArr.push(1);
			this.position = 0;
		}else{
			if(this.tableData[index].id === this.tableData[index-1].id ){
				this.spanArr[this.position] += 1;
				this.spanArr.push(0);
			}else{
				this.spanArr.push(1);
				this.position = index;
			}
		}
	})
},
//合并单元格
arraySpanMethod({ row, column, rowIndex, columnIndex }) {  //表格合并行
	if (columnIndex <= 9 ) {   //我项目中需要前9项的行合并
		const _row = this.spanArr[rowIndex] ;   //需要合并多少行
		const _col = _row > 0 ? 1 : 0;   
		return {
			rowspan: _row,
			colspan: _col
		}
	}
},

3.自适应列宽

百度所知elementui是暂不支持根据内容自适应列宽的,fit属性并没有用

export default {
	data() {
		return {
			tableData :[],
			titleList:[],   //调用接口接收到的title列表
			tableHeader:[],  //渲染的列表,已经加上width属性
		}
	}
},
methods:{ 
	//获取列表数据
	getList(page) {
		this.$axios.post('------URL-----').then(res => {
			this.tableData = res.data.data.data;   //接收列表数据
			this.titleList = res.data.data.title;   //接收列表数据
			this.tableHeader = this.getAllItem(this.titleList,this.tableData)  //参数:title列表 数据列表
		});
	},
	//获取每列数据,通过计算加上width属性
	getAllItem(head,data){
		const _this = this
		head.forEach(item => {
			if(item.item){   //递归,判断是否为最后一级标题
				_this.getAllItem(item.item,data)
			}else {
				const arr = data.map(x => x[item.value])  // 获取每一列的所有数据
				arr.push(item.label)  // 把每列的表头也加进去算
				item.width = _this.getMaxLength(arr) + 50 // 每列内容最大的宽度 + 表格的内间距
				return item
			}
		})
		return head
	},
	//使用span标签包裹内容,然后计算span的宽度 width: px
		getTextWidth(str) {
			let width = 0;
			let html = document.createElement('span');
			html.innerText = str;
			html.className = 'getTextWidth';
			document.querySelector('body').appendChild(html);
			width = document.querySelector('.getTextWidth').offsetWidth;
			document.querySelector('.getTextWidth').remove();
			return width;
		},
}	

4.固定表格底部横向滚动条

因为内容过多,当想看表格最后几项内容时,需要把右侧滚动条滑到底部才能看到横向滚动条

<el-table
	:data="tableData"
	:height="table_height"   //给表格添加该属性
	:span-method="arraySpanMethod">   
</el-table>

mounted() {   //在mounted中获取窗口的高,动态计算表格的高
	const that = this;
	that.table_height=document.body.clientHeight-280  //该处减去了头部底部的高度,可按自己需求修改
    window.onresize = function() {
		that.table_height=document.body.clientHeight-280
    };
 },

总体代码

<template>
	<el-table
	:data="tableData"
	:span-method="arraySpanMethod">
		<el-table-column v-for="(v,idx) in tableHeader" :key="idx"   
		:label="v.label" :prop="v.prop" :width="v.width" align="center">
			<el-table-column 
				v-for= "(j,i) in v.item" 
				:key="i"
				:prop="j.prop"
				:label="j.label"
				:width="j.width" align="center">
				<el-table-column 
				v-for= "(m,n) in j.item" 
				:key="n"
				:prop="m.prop"
				:label="m.label"
				:width="m.width" align="center">
			</el-table-column>
			</el-table-column>
		</el-table-column>
	</el-table>
</template>
export default {
	data() {
		return {
			tableData :[],
			titleList:[],   //调用接口接收到的title列表
			tableHeader:[],  //渲染的列表,已经加上width属性
			spanArr:[],
			position:0,
		}
	}
},
mounted() {   //在mounted中获取窗口的高,动态计算表格的高
	const that = this;
	that.table_height=document.body.clientHeight-280  //该处减去了头部底部的高度,可按自己需求修改
    window.onresize = function() {
		that.table_height=document.body.clientHeight-280
    };
 },
methods:{ 
	//获取列表数据
	getList(page) {
		this.$axios.post('------URL-----').then(res => {
			this.tableData = res.data.data.data;   //接收列表数据
			this.titleList = res.data.data.title;   //接收列表数据
			this.tableHeader = this.getAllItem(this.titleList,this.tableData)  //参数:title列表 数据列表
			this.rowspan()
		});
	},
	//获得数据相同的行数
	rowspan() {
		//每次调用清空数据
		this.spanArr=[];
		this.position=0
		this.tableData.forEach((item,index) => {
			if( index === 0){
				this.spanArr.push(1);
				this.position = 0;
			}else{
				if(this.tableData[index].id === this.tableData[index-1].id ){
					this.spanArr[this.position] += 1;
					this.spanArr.push(0);
				}else{
					this.spanArr.push(1);
					this.position = index;
				}
			}
		})
	},
	//合并单元格
	arraySpanMethod({ row, column, rowIndex, columnIndex }) {  //表格合并行
		if (columnIndex <= 9 ) {   //我项目中需要前9项的行合并
			const _row = this.spanArr[rowIndex] ;   //需要合并多少行
			const _col = _row > 0 ? 1 : 0;   
			return {
				rowspan: _row,
				colspan: _col
			}
		}
	},
	//获取每列数据,通过计算加上width属性
	getAllItem(head,data){
		const _this = this
		head.forEach(item => {
			if(item.item){   //递归,判断是否为最后一级标题
				_this.getAllItem(item.item,data)
			}else {
				const arr = data.map(x => x[item.value])  // 获取每一列的所有数据
				arr.push(item.label)  // 把每列的表头也加进去算
				item.width = _this.getMaxLength(arr) + 50 // 每列内容最大的宽度 + 表格的内间距
				return item
			}
		})
		return head
	},
	//使用span标签包裹内容,然后计算span的宽度 width: px
	getTextWidth(str) {
		let width = 0;
		let html = document.createElement('span');
		html.innerText = str;
		html.className = 'getTextWidth';
		document.querySelector('body').appendChild(html);
		width = document.querySelector('.getTextWidth').offsetWidth;
		document.querySelector('.getTextWidth').remove();
		return width;
	},
}	
相关标签: vue