前端练习56 表格行数据多级合并

用Vue实现表格行数据的多级合并

题目

一个简单的小需求,要实现一个表格

如果表格两行的内容相同,则进行合并,实现的效果如下:

分析

表格的行数据合并关键是rowspan属性,想要实现合并,我的思路是,如果两行的数据相同,前一行的rowspan则+1,当前行的数据添加一个hidden属性设置true

这个思路需要两层的循环,外层遍历列,内层遍历行

首先设定一个基准值startRowIndex,取startRowIndex对应的数据与当前遍历行的数据进行比对,如果相同则基准数据的rowspan属性+1,当前行的数据的hidden设置为ture,如果不相同则将基准值startRowIndex设为当前行的序号设置

还有一点是,当外层遍历到第二列及以后时的数据时,是否合并不能光依据上面的逻辑,还要添加一个判断条件,那就是它左侧的数据是否是相同的,比如下面图中的两个宜宾市,分属于四川1四川,那就不能合并(虽然省份的表格不会出现,但是其他类型的表格也许会有这种情况)

所以用一个单独的函数来判断左侧的列的数据是否相同,如果全都相同才能合并,否则就不行

代码

我使用Vue实现的,所以给数据添加了key属性,做了一些额外的处理:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
<template>
<div>
<h1>demo11</h1>
<table class="table">
<thead>
<tr>
<th v-for="head in tableHead" :key="head">{{head}}</th>
</tr>
</thead>
<tbody>
<tr v-for="item in tableData" :key="item.key">
<td v-for="data in item.data"
:key="data.value"
:rowspan="data.rowspan"
v-if="!data.hidden">{{data.value}}
</td>
</tr>
</tbody>
</table>
</div>
</template>

<script>
export default {
name: 'demo36',
props: [],
data() {
return {
tableHead: ['省份', '市', '区'],
tableBody: [
['四川', '成都市', '温江区'],
['四川', '宜宾市', '全部市区'],
['四川', '宜宾市', '全部市区2'],
['四川1', '宜宾市', '全部市区3'],
['四川2', '宜宾市', '全部市区4'],
['四川2', '宜宾市4', '全部市区4'],
],
}
},
methods: {
// 合并表格数据
// 将二维数组转换为二维对象数组,如果表格一列的内容相同,则为相同内容的首行添加 rowspan 属性,相同内容的当前行添加 hidden 属性
// 两层遍历,外层遍历列(省份 → 市 → 区),内层遍历行
handleTableData(tableBody, tableHead) {

// 判断左侧列的数据是否相同,全部相同返回 true,否则返回 false
function prevSame(result, i, j, startRowIndex) {
while (j >= 1) {
if (result[i][j - 1].value === result[startRowIndex][j - 1].value) {
j--;
}
else {
return false
}
}
return true
}

// 将数组转换为 [[{value: '四川'}] 这种形式
let result = tableBody.map(v => [...v.map(val => ({ value: val }))]);
// 相同内容的起始行的序列
let startRowIndex = 0;
// 外层遍历列
for (let j = 0; j < tableHead.length; j++) {
// 内层遍历行
for (let i = 0; i < result.length; i++) {
// 起始行的表格内容
let rowSpanName = result[startRowIndex][j].value;
// 当前行的表格内容
const value = result[i][j].value;
// 如果内容相同并且不是首行
if (rowSpanName === value && i > 0) {
// 如果不是第一列,则只有在前面所有列内容相同的情况下才会合并
if (!result[i][j - 1] || prevSame(result, i, j, startRowIndex)) {
// 取起始行的 rowspan 属性
let { rowspan } = result[startRowIndex][j];
// 取起始行的 rowspan 属性如果有初值则 +1, 如果没有的话初值赋予 2
result[startRowIndex][j].rowspan = rowspan ? rowspan + 1 : 2;
// 当前行表格隐藏
result[i][j].hidden = true;
} else {
// 如果不相同则重置其实行
startRowIndex = i
}
} else {
// 如果不相同则重置其实行
startRowIndex = i
}
}
}

// 返回值放到 data 属性中,并且将后两项的值合并作为列表循环时的 key 值
return result.map(v => ({
data: v,
key: v.map(v => v.value).join('')
}));
}
},
computed: {
tableData() {
return this.handleTableData(this.tableBody, this.tableHead)
}
}
}
</script>

<style scoped>
h2 {
margin: 20px 0
}
table, th, td {
border: 1px solid black;
border-collapse: collapse;
}
th, td {
padding: 10px;
}
</style>