vue的这两个组件库的表格的行和列的合并写法是一样的,都是通过span-method方法可以实现的;下面我们就以view ui的表格组件为例;
该方法参数为 4 个对象:
- row: 当前行
- column: 当前列
- rowIndex: 当前行索引
- columnIndex: 当前列索引
该函数可以返回一个包含两个元素的数组,第一个元素代表 rowspan,第二个元素代表 colspan。 也可以返回一个键名为 rowspan 和 colspan 的对象。
看上面对该方法的说明太过于官方,我们直接去组件库把实现的效果图拿过来仔细分析一下;
view ui表格的固定合并行和列
效果图:
实现代码:
<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>
所谓的表格合并,其实就是把指定的某个单元格不显示,让显示的单元格吞并不显示的单元格的位置而已;上图表格中就是第一行的第二列隐藏不显示,第一行第一列占据了被隐藏的单元格;第四行的第一列不显示,第三行第一列占据了被隐藏的单元格;
接下来我们来看看代码是实现的;
//Table标签中定义的一个方法属性,用来控制具体某些单元格的显示或者隐藏的
:span-method="handleSpan"
handleSpan({ row, column, rowIndex, columnIndex }){
/*
row: 行的全部数据
column:列的结构及其数据
rowIndex:行号,当前是第几行
columnIndex:列号,当前是第几列
*/
}
handleSpan方法的执行规则:
当表格需要进行合并时、整个渲染方式就发生了变化、之前可以看作以行为单位、一行一行渲染、而当你使用了 :span-method=“handleSpan” 变量后、渲染方式则改为一个单元格一个单元格渲染,即这个方法 handleSpan需要执行的次数是:列的个数 * 行数。
也就是说、它会按照这样的格式去调用方法:(行值,列结构值,行号,列号),具体例子为:
(row, column, 0, 0)、(row, column, 0, 1)、(row, column, 0, 2)、(row, column, 0, 3)、(row, column, 0, 4);
(row, column, 1, 0)、(row, column, 1, 1)、(row, column, 1, 2)、(row, column, 1, 3)、(row, column, 1, 4);
…
正是因为每个单元格的渲染都会执行一次,我们才可以在此函数中去写一些逻辑来控制表格的显示和隐藏状态;
下面我们看看handleSpan方法里的具体逻辑,了解一下是怎么控制的:
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
};
}
}
列合并
第一个if:
条件为(rowIndex === 0 && columnIndex === 0),意思就是行索引的第0个(也就是第一行)和列索引的第0个(也就是第一列),同时满足这两个条件的就是第一行和第一列的交叉单元格,返回返回一个键名为 rowspan 和 colspan 的对象 [1, 2],对应的就是rowspan :1,就是此单元格在行上就占位自己的一格;colspan :2,就是此单元格在列上要占据两个列单元格,除了自身的的一个单元格还要占据同一列后面的一个单元格;那我们在列上多占一个单元格,那行索引的第0个(也就是第一行)和列索引的第1个(也就是第二列)的单元格要怎么办呢?
第一个else if:
条件为(rowIndex === 0 && columnIndex === 1),意思就是行索引的第0个(也就是第一行)和列索引的第1个(也就是第二列),同时满足这两个条件的就是第一行和第二列的交叉单元格(也就是被占用的单元格),返回返回一个键名为 rowspan 和 colspan 的对象 [0, 0],意思就是在行上和列上都是0,就是不显示了,被隐藏掉了;
行合并
第二个if:
条件为(rowIndex === 2 && columnIndex === 0),意思就是行索引的第2个(也就是第三行)和列索引的第0个(也就是第一列),同时满足这两个条件的就是第三行和第一列的交叉单元格,返回返回一个键名为 rowspan 和 colspan 的对象 [2, 1],对应的就是rowspan :2,就是此单元格在行上要占据两个行单元格,除了自身的的一个单元格还要占据同列另一行的一个单元格;colspan :1,就是此单元格在列上就占位自己的一格;
第二个else if:
条件为(rowIndex === 3 && columnIndex === 0),意思就是行索引的第3个(也就是第四行)和列索引的第0个(也就是第一列),同时满足这两个条件的就是第四行和第一列的交叉单元格(也就是被占用的单元格),返回返回一个键名为 rowspan 和 colspan 的对象 [0, 0],意思就是在行上和列上都是0,就是不显示了,被隐藏掉了;
以上的列和行的合并都是固定的,在已知要显示和隐藏哪一个单元格的情况下是可行的,但是在我们项目中的表格数据都是动态渲染的,就拿行合并来说,需求是把同一列上,行上显示一样的内容合并起来,那我们就不能和上面写的一样的啦,因为我们也不清除要操作哪一行那一列,接下来就要详细说一下动态数据下大额行列合并;
view ui表格的动态数据下的合并行和列
假如数据是后端接口拿来的,我们拿行合并来说,我们也不清楚要合并多少行,从上面给出的例子,我们不难推出;
例如,在第一列的情况下:
- 合并第一行和第二行,那rowspan分别是2、0、1、1
- 合并第一行,第二行和第三行,那rowspan分别是3、0、0、1
- 合并第一行,第二行,第三行和第四行,那rowspan分别是4、0、0、0
- 合并第二行和第三行,那rowspan分别是1、2、0、1
- 合并第三行和第四行,那rowspan分别是1、1、2、0
所以说我们在未知数据的情况下,进行行合并,其实我们唯一不清楚的就是某一列下行单元格返回的rowspan的个数,我们只需要分别拿到rowspan的个数即可;
实现效果图:
第一种写法:
data(){
return {
spanArr:[] // 某一列下需要合并的行数
pos:0,// 索引
}
}
getList(){
const dataList = [
{
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-02'
}
]
//dataList 就是接口返回的未知数据
this.data5 = dataList
this.getSpanArr(res.data)
},
getSpanArr(data) {
// 遍历数据
data.forEach((item,index) => {
// 如果是第一个数据,就将列表spanArr添加一个1,表示暂时只有一个名字相同的、且将索引pos赋值为0
if(index === 0){
this.spanArr.push(1);
this.pos = 0
}else{
// 判断当前元素与上一个元素是否相同(这里我们拿要合并的日期date为例)
if(item.date=== data[index - 1].date){
// 如果相同就将索引为 pos 的值加一
this.spanArr[this.pos] += 1;
// 且将数组添加 0
this.spanArr.push(0);
}else{
// 如果元素不同了,就可以通过索引为 pos 的值知道应该需要合并的行数
// 同时,我们再次添加一个值1,表示重新开始判断某个字段的重复次数
this.spanArr.push(1);
// 同时 索引加一
this.pos = index;
}
}
})
console.log("索引数组:",this.spanArr) // 索引数组:[1,1,2,0]
},
handleSpan ({ row, column, rowIndex, columnIndex }){
//只合并列字段为date的行
if (columnIndex === 0 ) {
return {
//将需要合并的行数赋值给 _row,注意这里由上一个方法的输出[1,1,2,0]可以知道,
rowspan: this.spanArr[rowIndex],
//colspan有两种情况要不是0不显示,要不是1显示,根据rowspan( _row)来确定;
colspan: _row > 0 ? 1 : 0
}
}
},
第二种写法:
getList(){
const dataList = [
{
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-02'
}
]
//dataList 就是接口返回的未知数据
this.getNewListM(dataList )
},
getNewListM(list) {
let a = 1;
let b = 0;
for (let i = 1; i < list.length; i++) {
if (list[i].date== list[i - 1].date) {
a++;
list[b]['1'] = {row: a, col: 1};
list[i]['1'] = {row: 0, col: 1};
} else {
list[i]['1'] = {row: 1, col: 1};
a = 1;
b = i;
}
}
this.data5 = list
},
getNewList2(list) { //复杂写法,自己留着参考用,可以不看这个方法
if (list==null || list.size==0){
this.listData = list;
}
// 排序
list.sort(function (a, b) {
var val1 = a.projectCode+"|"+a.projectId;
var val2 = b.projectCode+"|"+b.projectId;
if (val1 < val2) {
return -1;
} else if (val1 > val2) {
return 1;
} else {
return 0;
}
});
// 设置合并单元格
let tdArray= ['1', '2', '3', '4', '5', '6', '7', '8','9','10']
let a = 1;
let b = 0;
for (let i = 0; i < list.length; i++) {
for (let j = 0; j < tdArray.length; j++){
list[i][tdArray[j]] = {row: 1, col: 1};
}
}
for (let i = 1; i < list.length; i++) {
if ((list[i].projectCode+"|"+list[i].projectId) === (list[i - 1].projectCode+"|"+list[i - 1].projectId)) {
a++;
for (let j = 0; j < tdArray.length; j++){
list[i][tdArray[j]] = {row: 0, col: 1};
list[b][tdArray[j]] = {row: a, col: 1};
}
} else {
for (let j = 0; j < tdArray.length; j++){
list[i][tdArray[j]] = {row: 1, col: 1};
}
a = 1;
b = i;
}
}
this.listData = list;
},
handleSpan({row, column, rowIndex, columnIndex}) {
let colinfo = row[columnIndex + ''];
if (colinfo) {
return [colinfo.row, colinfo.col];
}
},
以上两种写法的实现原理都是一样的,只是处理的顺序不同,都能实现动态数据下表格的合并;