【亲测有效】element-ui table :span-method(行数和列数合并)

1.官方示例解读

element-ui官网中关于行合并的例子是根据行索引进行合并的,这显然不符合我们日常开发需求,因为通常我们table中的数据都是动态生成的,所以需要做一些改造以达到我们要实现的需求。
首先,我们来解读一下官网实例中的各个参数的意义:

objectSpanMethod({ row, column, rowIndex, columnIndex }) {
  if (columnIndex === 0) {		//用于设置要合并的列
     if (rowIndex % 2 === 0) {	//用于设置合并开始的行,求余数=0,则执行方法体中的代码
       return {
         rowspan: 2,	//合并的行数
         colspan: 1		//合并的列数,设为0则直接不显示
       };
     } else {
       return {
         rowspan: 0,
         colspan: 0
       };
     }
   }
}

研究这个实例后不难发现,实现合并行的方法其实在每一行数据渲染的时候都会执行,只不过在渲染过程中我们设置了它合并的行数和列数,以得到不同的效果。

对于合并行数和列数,我认为有两种场景。

2.开发实例

我是以原生的HTML来作为示例,和Webpack用法一样的。如果是使用原生的HTML,则需要提前下载vuejs、element-ui。(如果是使用Webpack,请忽略)

<!--引入 element-ui 的样式,-->
<link rel="stylesheet" href="https://unpkg.com/element-ui/lib/theme-chalk/index.css">
<!-- 必须先引入vue,  后使用element-ui -->
<script src="https://cdn.jsdelivr.net/npm/vue@2.5.17/dist/vue.js"></script>
<!-- 引入element 的组件库-->
<script src="https://unpkg.com/element-ui/lib/index.js"></script>

2.1实例一(后端返回数据设置合并的行数和列数)

第一种场景后端返回数据的同时也返回合并的行数和列数,直接根据后端返回的结果进行设置合并的行数和列数。(这种的业务场景会复杂一些,不过因需求而异)

HTML:

<head runat="server">
	<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
	<meta name="format-detection" content="telephone=no" />
	<title>后端返回合并的行数和列数结果</title>
	<script src="../Common/Vue+ElementUi/vue.js"></script>
	<script src="../Common/Vue+ElementUi/vue2.6.11.js"></script>
	<link rel="stylesheet" href="../Common/Vue+ElementUi/element-ui.css">
	<script src="../Common/Vue+ElementUi/element-ui.js"></script>
	<script src="../JS/common.js"></script>
</head>
<body leftMargin="0" topMargin="0">
<div id="app">
	<el-row style="margin:5px 5px 5px 20px;">
   		<el-col :span="8">
   		</el-col>
   		<el-col :span="8" style="text-align: right;">
   		</el-col>
	</el-row>
	<el-table :data="tableData" style="width: 100%" border height="100%" :cell-style="cellStyle" :span-method="objectSpanMethod">
		<el-table-column label="用户地址信息" align="center">
			<el-table-column prop="Province.Value" label="省份" width="120"></el-table-column>
				<el-table-column label="地址" width="120" align="center">
					<el-table-column prop="City.Value" label="" width="120"></el-table-column>
					<el-table-column prop="Area.Value" label="" width="120"></el-table-column>
					<el-table-column prop="Name.Value" label="姓名" width="120"></el-table-column>
					<el-table-column prop="Address.Value" label="详细地址" width="120"></el-table-column>
				</el-table-column>
		  </el-table-column>
		</el-table-column>
	</el-table>
</div>
</body>

Script代码:

<script>
    // 该vue对象,绑定了页面中id是app的那个div
    new Vue(
        {
            el: '#app', //element
            data: {
            	tableData:[],	//表格数据
				columnOrder:[	//列字段排序,用于合并行数和列数使用
					"Province",
					"City",
					"Area",
					"Name",
					"Address",
				]
            },
            mounted(){
				this.Init();
            },
            methods:{
				//初始化
            	Init(){
					const that = this;
            		that.tableData = [
						{
							"Province":{
								"Value":"广东省",
								"RowSpan":3,
								"ColumnSpan":1,
								"CellStyle":{
									"font-weight":800,
									"background-color":"#fff"
								}
							},
							"City":{
								"Value":"广州市",
								"RowSpan":2,
								"ColumnSpan":1,
								"CellStyle":{
									"font-weight":400,
									"background-color":"#fff"
								}
							},
							"Area":{
								"Value":"天河区",
								"RowSpan":1,
								"ColumnSpan":1,
								"CellStyle":{
									"font-weight":400,
									"background-color":"#fff"
								}
							},
							"Name":{
								"Value":"张三",
								"RowSpan":2,
								"ColumnSpan":1,
								"CellStyle":{
									"font-weight":400,
									"background-color":"#fff"
								}
							},
							"Address":{
								"Value":"测试地址1",
								"RowSpan":1,
								"ColumnSpan":1,
								"CellStyle":{
									"font-weight":400,
									"background-color":"#fff"
								}
							}
						},
						{
							"Province":{
								"Value":"广东省",
								"RowSpan":0,
								"ColumnSpan":0,
								"CellStyle":{
									"font-weight":400,
									"background-color":"#fff"
								}
							},
							"City":{
								"Value":"广州市",
								"RowSpan":0,
								"ColumnSpan":0,
								"CellStyle":{
									"font-weight":400,
									"background-color":"#fff"
								}
							},
							"Area":{
								"Value":"白云区",
								"RowSpan":1,
								"ColumnSpan":1,
								"CellStyle":{
									"font-weight":400,
									"background-color":"#fff"
								}
							},
							"Name":{
								"Value":"张三",
								"RowSpan":0,
								"ColumnSpan":0,
								"CellStyle":{
									"font-weight":400,
									"background-color":"#fff"
								}
							},
							"Address":{
								"Value":"测试地址2",
								"RowSpan":1,
								"ColumnSpan":1,
								"CellStyle":{
									"font-weight":400,
									"background-color":"#fff"
								}
							}
						},
						{
							"Province":{
								"Value":"广东省",
								"RowSpan":0,
								"ColumnSpan":0,
								"CellStyle":{
									"font-weight":400,
									"background-color":"#fff"
								}
							},
							"City":{
								"Value":"佛山市",
								"RowSpan":1,
								"ColumnSpan":1,
								"CellStyle":{
									"font-weight":400,
									"background-color":"#fff"
								}
							},
							"Area":{
								"Value":"禅城区",
								"RowSpan":1,
								"ColumnSpan":1,
								"CellStyle":{
									"font-weight":400,
									"background-color":"#fff"
								}
							},
							"Name":{
								"Value":"李四",
								"RowSpan":1,
								"ColumnSpan":1,
								"CellStyle":{
									"font-weight":400,
									"background-color":"#fff"
								}
							},
							"Address":{
								"Value":"测试地址3",
								"RowSpan":1,
								"ColumnSpan":1,
								"CellStyle":{
									"font-weight":400,
									"background-color":"#fff"
								}
							}
						}
					];
            	},
				//单元格样式
            	cellStyle({ row, column, rowIndex, columnIndex }) {
					return row[this.columnOrder[columnIndex]].CellStyle;
				},
				//合并行数和列数
				objectSpanMethod({ row, column, rowIndex, columnIndex }){					
					let obj = {
						rowspan: row[this.columnOrder[columnIndex]].RowSpan,
						colspan: row[this.columnOrder[columnIndex]].ColumnSpan
					}
					return obj;
				}
            }
        }
    );
</script>

执行后的效果截图:

在这里插入图片描述

功能实现讲解:

columnOrder:列字段排序集合,因为数据排序无法固定,故需借此来固定取字段的RowSpan、ColumnSpan。
tableData中的RowSpan:该行该列需要合并的行数,0则不显示。
tableData中的ColumnSpan:该行该列需要合并的列数,0则不显示。

2.2实例二(前端判断内容设置合并的行数和列数)

第二种场景前端通过判断上下行的单元格、同行的左右单元格的内容,如果内容相等则要合并行数或列数。

HTML:

<head runat="server">
	<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
	<meta name="format-detection" content="telephone=no" />
	<title>报表测试</title>
	<script src="../Common/Vue+ElementUi/vue.js"></script>
	<script src="../Common/Vue+ElementUi/vue2.6.11.js"></script>
	<link rel="stylesheet" href="../Common/Vue+ElementUi/element-ui.css">
	<script src="../Common/Vue+ElementUi/element-ui.js"></script>
	<script src="../JS/common.js"></script>
</head>
<body leftMargin="0" topMargin="0">
<div id="app">
	<el-row style="margin:5px 5px 5px 20px;">
   		<el-col :span="8">
   		</el-col>
   		<el-col :span="8" style="text-align: right;">
   		</el-col>
	</el-row>
	<el-table :data="tableData" style="width: 100%" border height="100%"  :span-method="objectSpanMethod">
		<el-table-column label="用户地址信息" align="center">
			<el-table-column prop="Province" label="省份" width="120"></el-table-column>
				<el-table-column label="地址" width="120" align="center">
					<el-table-column prop="City" label="" width="120"></el-table-column>
					<el-table-column prop="Area" label="" width="120"></el-table-column>
					<el-table-column prop="Name" label="姓名" width="120"></el-table-column>
					<el-table-column prop="Address" label="详细地址" width="120"></el-table-column>
				</el-table-column>
		  </el-table-column>
		</el-table-column>
	</el-table>
</div>
</body>

Script代码:

<script>
    <script>
    // 该vue对象,绑定了页面中id是app的那个div
    new Vue(
        {
            el: '#app', //element
            data: {
            	tableData:[],	//表格数据
				columnOrder:[	//列字段排序,用于合并行数和列数
					"Province",
					"City",
					"Area",
					"Name",
					"Address",
				]
            },
            mounted(){
				this.Init();
            },
            methods:{
				//初始化
            	Init(){
					const that = this;
            		that.tableData = [
						{
							"Province":"广东省",
							"City":"佛山市",
							"Area":"禅城区",
							"Name":"张三",
							"Address":"测试地址1"
						},{
							"Province":"广东省",
							"City":"佛山市",
							"Area":"南海区",
							"Name":"张三",
							"Address":"测试地址2"
						},{
							"Province":"广东省",
							"City":"广州市",
							"Area":"白云区",
							"Name":"李四",
							"Address":"测试地址3"
						},{
							"Province":"北京市",
							"City":"北京市",
							"Area":"顺义区",
							"Name":"王五",
							"Address":"测试地址4"
						},
					];
            	},
				//合并行数和列数
				objectSpanMethod({ row, column, rowIndex, columnIndex }){					
					const that = this;
					let obj = {
						rowspan:1,
						colspan:1
					}
					obj = that.getRowSpanQty(row,rowIndex,columnIndex,obj);
					obj = that.getColumnSpanQty(row,rowIndex,columnIndex,obj);
					return obj;
				},
				//获取合并列数
				getRowSpanQty(row,rowIndex,columnIndex,obj){
					const that = this;
					if(rowIndex == 0){
						//当第一行,就循环判断一下行是否存在相同内容,是则行数+1
						let isNeedNextRow = true;	//是否继续循环下一行同一个单元格
						for(let i =0;i < that.tableData.length;i++){
							if(rowIndex != i && isNeedNextRow != false){
								let data = that.tableData[i];
								if(data[this.columnOrder[columnIndex]] == row[this.columnOrder[columnIndex]]){
									obj.rowspan++;
									isNeedNextRow = true;
								}else{
									isNeedNextRow = false;
								}
							}
						}
					}else{
						let data = that.tableData[rowIndex - 1];
						if(data[this.columnOrder[columnIndex]] == row[this.columnOrder[columnIndex]]){
							//当不是第一行,则判断当前单元格与上一行的当前单元格是否一致,一致则不显示
							obj.rowspan = 0;
							obj.colspan = 0;
						}
					}
					return obj;
				},
				//获取合并列数
				getColumnSpanQty(row,rowIndex,columnIndex,obj){
					const that = this;
					let isNeedNextColumn = true;	//是否继续循环当前行下一个单元格
					for(let i = columnIndex;i < this.columnOrder.length;i++){
						if(isNeedNextColumn != false){
							let columnName = this.columnOrder[i+1];
							if(row[this.columnOrder[columnIndex]] == row[columnName]){
								//当前行的单元格=当前行的下一个单元格,则列数+1
								obj.colspan++;
								isNeedNextColumn = true;
							}else{
								isNeedNextColumn = false;
							}
						}
					}

					if(row[this.columnOrder[columnIndex]] == row[this.columnOrder[columnIndex-1]]){
						//当当前行的当前单元格=当前行的上一个单元格,则0不显示
						obj.rowspan = 0;
						obj.colspan= 0;
					}
					return obj;
				}
            }
        }
    );
</script>

执行后的效果截图:

在这里插入图片描述

功能实现讲解:

columnOrder:列字段排序集合,因为数据排序无法固定,故需借此来固定排序方便取数。
getRowSpanQty:获取行需要的合并数。
getColumnSpanQty:获取列需要的合并数。
重点是要理解getRowSpanQtygetColumnSpanQty方法。

那么这两种场景的实现应该能够满足大家日常开发的需求,希望可以帮助到大家,共勉。

声明

本篇文章的内容完全个人原创,如其他文档有与本章内容雷同,均为抄袭本篇文章。请尊重原创!

Logo

旨在为数千万中国开发者提供一个无缝且高效的云端环境,以支持学习、使用和贡献开源项目。

更多推荐