View UI,即原先的 iView,从 2019 年 10 月起正式更名为 View UI,并使用全新的 Logo
在项目开发中,有时需要将表格的行或列进行合并,View UI (iView)从4.0.0版本开始,增加了 行/列合并 属性,使我们可以方便地对表格行列进行合并,本文介绍这一属性如何使用
1、基本使用介绍
首先,要确保使用了 View UI (iView)4.0.0 以上版本
打开官方文档,可以看到,行/列合并需要设置属性 span-method
通过 span-method="handleSpan",指定一个方法,在这个方法中实现合并行列的逻辑,该方法中有4个参数,分别是 row: 当前行、column: 当前列、rowIndex: 当前行索引、 columnIndex: 当前列索引。先不解释这4个参数具体什么意思了,先看官网示例demo,将 demo 代码复制
<template>
<Table :columns="columns14" :data="data5" border :span-method="handleSpan"></Table>
</template>
<script>
export default {
data () {
return {
columns14: [
{
title: 'Date',
key: 'date'
},
{
title: 'Name',
key: 'name'
},
{
title: 'Age',
key: 'age'
},
{
title: 'Address',
key: 'address'
}
],
data5: [
{
name: 'John Brown',
age: 18,
address: 'New York No. 1 Lake Park',
date: '2016-10-03'
},
{
name: 'Jim Green',
age: 24,
address: 'London No. 1 Lake Park',
date: '2016-10-01'
},
{
name: 'Joe Black',
age: 30,
address: 'Sydney No. 1 Lake Park',
date: '2016-10-02'
},
{
name: 'Jon Snow',
age: 26,
address: 'Ottawa No. 2 Lake Park',
date: '2016-10-04'
}
]
}
},
methods: {
handleSpan ({ row, column, rowIndex, columnIndex }) {
if (rowIndex === 0 && columnIndex === 0) {
return [1, 2];
} else if (rowIndex === 0 && columnIndex === 1) {
return [0, 0];
}
if (rowIndex === 2 && columnIndex === 0) {
return {
rowspan: 2,
colspan: 1
};
} else if (rowIndex === 3 && columnIndex === 0) {
return {
rowspan: 0,
colspan: 0
};
}
}
}
}
</script>
这里官网 demo 并没有对 handleSpan 方法进行详细说明,给我们学习带来一定难度,这也是笔者写这篇教程的原因
demo 运行效果如下图
可以看到,第一行的第一列和第二列合并,第三行第一列和第四行第一列合并
这时,我们再看代码
先看 ① 处的代码,rowIndex === 0 指的是第一行, columnIndex === 0 指的是第一列,rowIndex 和 columnIndex 是从0开始而不是从1开始。
if (rowIndex === 0 && columnIndex === 0) 表示的是第一行第一列
return [1, 2] 表示合并1行2列
else if (rowIndex === 0 && columnIndex === 1) 是指第一行,第二列
return [0, 0] 表示合并0行0列,即这是被合并的单元格
这里被合并的单元格一定要返回 return [0, 0],否则会出现数据下串
上方 ① 处代码表示以第1行第1列的单元格数据为主,向右合并1行2列,而第1行第2列单元格作为被合并的单元格,要返回return [0, 0]
② 处的代码道理相同,以第3行第1列单元格数据为主向下合并1行2列,而被合并的第4行第1列返回 return [0, 0](这里既可以返回数组,也可以返回对象)
由 ① 和 ② 处的代码可以推出,返回 return [1, 1],表示合并1行1列,即保持单元格原样,不进行合并
明白这个道理后,我们试着改动一下,看看能不能实现想要的效果
这里我想合并第一行的第二列和第三列,合并第二行第一列第三行第一列和第四行第一列,代码如下
<template>
<Table :columns="columns14" :data="data5" border :span-method="handleSpan"></Table>
</template>
<script>
export default {
data () {
return {
columns14: [
{
title: 'Date',
key: 'date'
},
{
title: 'Name',
key: 'name'
},
{
title: 'Age',
key: 'age'
},
{
title: 'Address',
key: 'address'
}
],
data5: [
{
name: 'John Brown',
age: 18,
address: 'New York No. 1 Lake Park',
date: '2016-10-03'
},
{
name: 'Jim Green',
age: 24,
address: 'London No. 1 Lake Park',
date: '2016-10-01'
},
{
name: 'Joe Black',
age: 30,
address: 'Sydney No. 1 Lake Park',
date: '2016-10-02'
},
{
name: 'Jon Snow',
age: 26,
address: 'Ottawa No. 2 Lake Park',
date: '2016-10-04'
}
]
}
},
methods: {
handleSpan ({ row, column, rowIndex, columnIndex }) {
if (rowIndex === 0 && columnIndex === 1) {
return [1, 2];
} else if (rowIndex === 0 && columnIndex === 2) {
return [0, 0];
}
if (rowIndex === 1 && columnIndex === 0) {
return {
rowspan: 3,
colspan: 1
};
} else if (rowIndex === 2 && columnIndex === 0) {
return {
rowspan: 0,
colspan: 0
};
} else if (rowIndex === 3 && columnIndex === 0) {
return {
rowspan: 0,
colspan: 0
};
}
}
}
}
</script>
上边代码就不做过多解释了,需要注意的是,第二行第一列第三行第一列和第四行第一列合并时,因为有两个被合并的单元格,因此需要两个都返回 return [0, 0],否则会出现数据和样式问题
效果如下图
相信通过上边两个示例 demo,你已经学会了合并单元格的基本使用,下面看看在项目中如何使用吧
2、项目中的使用
观察上面代码,发现我们没有使用 row 对象和 column 对象,而在实际项目中,也不可能像上边代码那样通过 rowIndex和 columnIndex 在前端将合并的单元格写死,而是需要根据后台返回给前端的数据进行逻辑判断,决定合并哪些单元格
这里我们讲解一个示例,还用上边的表结构
假设我们想合并 name 值相同的单元格
先修改一下数据,使name值相同,再清空 handleSpan 方法,方便查看,代码如下
<template>
<Table :columns="columns14" :data="data5" border :span-method="handleSpan"></Table>
</template>
<script>
export default {
data () {
return {
columns14: [
{
title: 'Date',
key: 'date'
},
{
title: 'Name',
key: 'name'
},
{
title: 'Age',
key: 'age'
},
{
title: 'Address',
key: 'address'
}
],
data5: [
{
name: 'John Brown',
age: 18,
address: 'New York No. 1 Lake Park',
date: '2016-10-03'
},
{
name: 'John Brown',
age: 24,
address: 'London No. 1 Lake Park',
date: '2016-10-01'
},
{
name: 'Joe Black',
age: 30,
address: 'Sydney No. 1 Lake Park',
date: '2016-10-02'
},
{
name: 'Jon Snow',
age: 26,
address: 'Ottawa No. 2 Lake Park',
date: '2016-10-04'
}
]
}
},
methods: {
handleSpan ({ row, column, rowIndex, columnIndex }) {
}
}
}
</script>
运行效果如图
第一行和第二行name值相同,下面研究如何合并第1行第2列和第2行第2列
这里我们将data中的 data5 改为 resData,而将 data5改为空数组,用 resData 模拟后台返回前端的数据
需要注意,在合并行时,因为需要合并,所以需要合并的数据必须相临,即合并者与被合并者在数组中的下标要相临,否则无法合并。这里我们直接将 resData 中需要合并的数据设为相临,即第一条和第二条,而在实际项目中这块需要前端或后台开发人员根据业务逻辑自己实现
经过以上修改后,代码如下
<template>
<Table :columns="columns14" :data="data5" border :span-method="handleSpan"></Table>
</template>
<script>
export default {
data () {
return {
columns14: [
{
title: 'Date',
key: 'date'
},
{
title: 'Name',
key: 'name'
},
{
title: 'Age',
key: 'age'
},
{
title: 'Address',
key: 'address'
}
],
resData: [
{
id: '1',
name: 'John Brown',
age: 18,
address: 'New York No. 1 Lake Park',
date: '2016-10-03'
},
{
id: '3',
name: 'John Brown',
age: 24,
address: 'London No. 1 Lake Park',
date: '2016-10-01'
},
{
id: '2',
name: 'Joe Black',
age: 30,
address: 'Sydney No. 1 Lake Park',
date: '2016-10-02'
},
{
id: '4',
name: 'Jon Snow',
age: 26,
address: 'Ottawa No. 2 Lake Park',
date: '2016-10-04'
}
],
data5: []
}
},
methods: {
handleSpan ({ row, column, rowIndex, columnIndex }) {
}
}
}
</script>
上面 resData 中,name 为 John Brown 的数据已经相临,分别是第一条和第二条
接下来我们需要对 resData 中的数据进行处理,从而实现 name 值相同的行进行合并
在methods 新建一个方法 assembleData(data),用来对数据进行处理。在这个方法中要实现以下逻辑,首先将name值相同的数据筛选出来,然后根据name值相同的数据个数计算出需要合并的行数,我们的示例中有2条name值相同的数据,即需要合并2行,这两行其中之一返回return [2, 1],因为是2行1列,所以返回2,1;另一个返回return [0, 0],因为是被合并单元格,其他不需要合并的单元格返回 return [1, 1],最后将筛选后的数据进行标记,给到 Table表格控件,Table表格控件调用属性 span-method的方法,进行单元格行列合并
下面就直接上代码了
<template>
<Table :columns="columns14" :data="data5" border :span-method="handleSpan"></Table>
</template>
<script>
export default {
data () {
return {
columns14: [
{
title: 'Date',
key: 'date'
},
{
title: 'Name',
key: 'name'
},
{
title: 'Age',
key: 'age'
},
{
title: 'Address',
key: 'address'
}
],
resData: [
{
id: '1',
name: 'John Brown',
age: 18,
address: 'New York No. 1 Lake Park',
date: '2016-10-03'
},
{
id: '3',
name: 'John Brown',
age: 24,
address: 'London No. 1 Lake Park',
date: '2016-10-01'
},
{
id: '2',
name: 'Joe Black',
age: 30,
address: 'Sydney No. 1 Lake Park',
date: '2016-10-02'
},
{
id: '4',
name: 'Jon Snow',
age: 26,
address: 'Ottawa No. 2 Lake Park',
date: '2016-10-04'
}
],
data5: []
}
},
methods: {
handleSpan ({ row, column, rowIndex, columnIndex }) {
//合并第二列,这里columnIndex==1 根据具体业务要在前端写死
if (columnIndex == 1) {
//计算合并的行数列数
let x = row.mergeCol == 0 ? 0:row.mergeCol
let y = row.mergeCol == 0 ? 0:1
//console.log(x , y)
return [x, y]
}
},
assembleData(data){
let names = []
//筛选出不重复的 name值,将其放到 names数组中
data.forEach(e => {
if(!names.includes(e.name)){
names.push(e.name)
}
})
let nameNums = []
//将names数组中的 name值设置默认合并0个单元格,放到 nameNums中
names.forEach(e => {
nameNums.push({name:e,num:0})
})
//计算每种 name值所在行需要合并的单元格数
data.forEach(e => {
nameNums.forEach(n => {
if(e.name == n.name){
n.num++
}
})
})
//console.log(nameNums)
//将计算后的合并单元格数整合到 data中
data.forEach(e => {
nameNums.forEach(n => {
if(e.name == n.name){
if(names.includes(e.name)){
e.mergeCol = n.num
//删除已经设置过的值(防止被合并的单元格进到这个 if 语句中)
names.splice(names.indexOf(n.name),1)
} else {
//被合并的单元格设置为 0
e.mergeCol = 0
}
}
})
})
//将整理后的数据交给表格渲染
this.data5 = data
}
},
mounted(){
//这里 this.resData 模拟后台返回的数据
this.assembleData(this.resData)
}
}
</script>
相关说明已经写在代码注释中
运行效果如下图
可以看到,name值相同的行已经成功合并单元格
下面我们再看一种情况,就是当我们对数据进行改动后,该如何处理
如删除第一条数据。其实也很简单,在删除后,重新请求后台获取最新的 resData,然后调用 assembleData 方法即可,示例代码如下
<template>
<div>
<Table :columns="columns14" :data="data5" border :span-method="handleSpan"></Table>
<Button @click="deleteData" type="error">删除</Button>
</div>
</template>
<script>
export default {
data () {
return {
columns14: [
{
title: 'Date',
key: 'date'
},
{
title: 'Name',
key: 'name'
},
{
title: 'Age',
key: 'age'
},
{
title: 'Address',
key: 'address'
}
],
resData: [
{
id: '1',
name: 'John Brown',
age: 18,
address: 'New York No. 1 Lake Park',
date: '2016-10-03'
},
{
id: '3',
name: 'John Brown',
age: 24,
address: 'London No. 1 Lake Park',
date: '2016-10-01'
},
{
id: '2',
name: 'Joe Black',
age: 30,
address: 'Sydney No. 1 Lake Park',
date: '2016-10-02'
},
{
id: '4',
name: 'Jon Snow',
age: 26,
address: 'Ottawa No. 2 Lake Park',
date: '2016-10-04'
}
],
data5: [],
newresData: [
{
id: '3',
name: 'John Brown',
age: 24,
address: 'London No. 1 Lake Park',
date: '2016-10-01'
},
{
id: '2',
name: 'Joe Black',
age: 30,
address: 'Sydney No. 1 Lake Park',
date: '2016-10-02'
},
{
id: '4',
name: 'Jon Snow',
age: 26,
address: 'Ottawa No. 2 Lake Park',
date: '2016-10-04'
}
]
}
},
methods: {
handleSpan ({ row, column, rowIndex, columnIndex }) {
//合并第二列,这里columnIndex==1 根据具体业务要在前端写死
if (columnIndex == 1) {
//计算合并的行数列数
let x = row.mergeCol == 0 ? 0:row.mergeCol
let y = row.mergeCol == 0 ? 0:1
//console.log(x , y)
return [x, y]
}
},
assembleData(data){
let names = []
//筛选出不重复的 name值,将其放到 names数组中
data.forEach(e => {
if(!names.includes(e.name)){
names.push(e.name)
}
})
let nameNums = []
//将names数组中的 name值设置默认合并0个单元格,放到 nameNums中
names.forEach(e => {
nameNums.push({name:e,num:0})
})
//计算每种 name值所在行需要合并的单元格数
data.forEach(e => {
nameNums.forEach(n => {
if(e.name == n.name){
n.num++
}
})
})
//console.log(nameNums)
//将计算后的合并单元格数整合到 data中
data.forEach(e => {
nameNums.forEach(n => {
if(e.name == n.name){
if(names.includes(e.name)){
e.mergeCol = n.num
//删除已经设置过的值(防止被合并的单元格进到这个 if 语句中)
names.splice(names.indexOf(n.name),1)
} else {
//被合并的单元格设置为 0
e.mergeCol = 0
}
}
})
})
//将整理后的数据交给表格渲染
this.data5 = data
},
deleteData(){
//这里 this.newresData 模拟删除后,后台返回的数据
this.assembleData(this.newresData)
}
},
mounted(){
//这里 this.resData 模拟后台返回的数据
this.assembleData(this.resData)
}
}
</script>
运行效果
添加、修改数据时用法一样,但需要注意,添加数据时将要合并的数据放在相临的位置
至此完