1. 效果圖

效果圖
2. 頁(yè)面
使用el-table的span-method屬性控制合并行, 和row-class-name屬性控制行樣式
<el-table
:data="tableData"
:height="500"
:span-method="objectSpanMethod"
:row-class-name="getRowClass"
>
...省略...
</el-table>
3. 方法
handleData: 處理表格數(shù)據(jù),將同一名稱(chēng)的數(shù)據(jù)進(jìn)行組合
getSpanArr:獲取單元格的合并行數(shù)
objectSpanMethod:合并單元格
getRowClass:設(shè)置表格行的樣式類(lèi)
/**
* 處理表格數(shù)據(jù),將同一名稱(chēng)的數(shù)據(jù)進(jìn)行組合
*/
const handleData = () => {
// 排序方法1:將相同名稱(chēng)的數(shù)據(jù)移動(dòng)在一起,但會(huì)導(dǎo)致所有數(shù)據(jù)的順序都被改變
// state.tableData.sort((a, b) => {
// if (a.name != b.name) {
// return a.name.localeCompare(b.name) // stringObject.localeCompare(target): 用本地特定的順序來(lái)比較兩個(gè)字符串
// }
// })
// 排序方法2:只移動(dòng)有相同名稱(chēng)的數(shù)據(jù),保持其它數(shù)據(jù)的相對(duì)順序不改變
let keys = [] // 唯一值的數(shù)組
state.tableData.forEach((item, index) => {
if (!keys.includes(item.moduleName)) {
keys.push(item.moduleName)
}
})
let temp = []
keys.forEach((item) => { // 將同一名稱(chēng)的數(shù)據(jù)放在相鄰位置
state.tableData.forEach((cell) => {
if (item === cell.name) {
temp.push(cell)
}
})
})
state.tableData = temp
getSpanArr(state.tableData)
}
let spanArr = [] // 每一條數(shù)據(jù)合并的行數(shù),避免表格錯(cuò)亂!
/**
* 獲取單元格的合并行數(shù)
* */
const getSpanArr = (data) => {
let position // 當(dāng)前合并的行位置(連續(xù)相同名稱(chēng)的第一條數(shù)據(jù)位置)
spanArr = []
data.forEach((item, index) => {
if (index === 0) { // 第一行, 不進(jìn)行處理
spanArr.push(1)
position = 0
} else {
if (data[index].name === data[index - 1].name) {
// 當(dāng)條數(shù)據(jù)跟上一條數(shù)據(jù)名稱(chēng)相同,要合并
spanArr[position] += 1 // 首條相同名稱(chēng)行合并行數(shù)增加
spanArr.push(0) // 當(dāng)前行消除
} else { // 不需要處理的數(shù)據(jù)
spanArr.push(1)
position = index
}
}
})
}
/**
* 合并單元格,此處只合并前三列的屬性值
*
*/
const objectSpanMethod = ({ row, column, rowIndex, columnIndex }) => {
if (columnIndex < 3) { // 對(duì)前三列進(jìn)行操作
const _row = spanArr[rowIndex] // 合并行數(shù)
const _col = _row > 0 ? 1 : 0 // 合并列數(shù),1:不改變,0:被消除
return {
rowspan: _row,
colspan: _col
}
}
}
/**
* 設(shè)置表格行的樣式類(lèi)(去除合并行內(nèi)多余的線(xiàn)條)
*/
const getRowClass = ({ row, rowIndex }) => {
if (spanArr[rowIndex] > 1) { // 相同名稱(chēng)排列的首行數(shù)據(jù)
return 'show-span-row'
} else if (spanArr[rowIndex] === 0 && spanArr[rowIndex + 1] === 0) { // 相同名稱(chēng)不處于末尾的被合并數(shù)據(jù)
return 'center-span'
}
}
4. 樣式
getRowClass設(shè)置的css樣式
.el-table {
:deep(tr) {
&.show-span-row {
td:nth-of-type(n + 3) {
border-bottom: none;
}
}
&.center-span {
td {
border-bottom: none;
}
}
}
}
5. 整體代碼(Vue3)
<template>
<el-table
:data="tableData"
:height="500"
:span-method="objectSpanMethod"
:row-class-name="getRowClass"
>
...省略...
</el-table>
</template>
<script>
import { reactive, ref, toRefs } from '@vue/reactivity'
import { onMounted, watch } from '@vue/runtime-core'
export default {
setup(props, context) {
const state = reactive({
tableData: []
})
onMounted(() => {
handleData()
})
/**
* 處理表格數(shù)據(jù),將同一名稱(chēng)的數(shù)據(jù)進(jìn)行組合
*/
const handleData = () => {
// 排序方法1:將相同名稱(chēng)的數(shù)據(jù)移動(dòng)在一起,但會(huì)導(dǎo)致所有數(shù)據(jù)的順序都被改變
// state.tableData.sort((a, b) => {
// if (a.name != b.name) {
// return a.name.localeCompare(b.name) // stringObject.localeCompare(target): 用本地特定的順序來(lái)比較兩個(gè)字符串
// }
// })
// 排序方法2:只移動(dòng)有相同名稱(chēng)的數(shù)據(jù),保持其它數(shù)據(jù)的相對(duì)順序不改變
let keys = [] // 唯一值的數(shù)組
state.tableData.forEach((item, index) => {
if (!keys.includes(item.moduleName)) {
keys.push(item.moduleName)
}
})
let temp = []
keys.forEach((item) => { // 將同一名稱(chēng)的數(shù)據(jù)放在相鄰位置
state.tableData.forEach((cell) => {
if (item === cell.name) {
temp.push(cell)
}
})
})
state.tableData = temp
getSpanArr(state.tableData)
}
let spanArr = [] // 每一條數(shù)據(jù)合并的行數(shù),避免表格錯(cuò)亂!
/**
* 獲取單元格的合并行數(shù)
* */
const getSpanArr = (data) => {
let position // 當(dāng)前合并的行位置(連續(xù)相同名稱(chēng)的第一條數(shù)據(jù)位置)
spanArr = []
data.forEach((item, index) => {
if (index === 0) { // 第一行, 不進(jìn)行處理
spanArr.push(1)
position = 0
} else {
if (data[index].name === data[index - 1].name) {
// 當(dāng)條數(shù)據(jù)跟上一條數(shù)據(jù)名稱(chēng)相同,要合并
spanArr[position] += 1 // 首條相同名稱(chēng)行合并行數(shù)增加
spanArr.push(0) // 當(dāng)前行消除
} else { // 不需要處理的數(shù)據(jù)
spanArr.push(1)
position = index
}
}
})
}
/**
* 合并單元格,此處只合并前三列的屬性值
*
*/
const objectSpanMethod = ({ row, column, rowIndex, columnIndex }) => {
if (columnIndex < 3) { // 對(duì)前三列進(jìn)行操作
const _row = spanArr[rowIndex] // 合并行數(shù)
const _col = _row > 0 ? 1 : 0 // 合并列數(shù),1:不改變,0:被消除
return {
rowspan: _row,
colspan: _col
}
}
}
/**
* 設(shè)置表格行的樣式類(lèi)(去除合并行內(nèi)多余的線(xiàn)條)
*/
const getRowClass = ({ row, rowIndex }) => {
if (spanArr[rowIndex] > 1) { // 相同名稱(chēng)排列的首行數(shù)據(jù)
return 'show-span-row'
} else if (spanArr[rowIndex] === 0 && spanArr[rowIndex + 1] === 0) { // 相同名稱(chēng)不處于末尾的被合并數(shù)據(jù)
return 'center-span'
}
}
return {
...toRefs(state),
objectSpanMethod
}
}
}
</script>
<style lang="scss" scoped>
.el-table {
:deep(tr) {
&.show-span-row {
td:nth-of-type(n + 3) {
border-bottom: none;
}
}
&.center-span {
td {
border-bottom: none;
}
}
}
}
</style>
5. 其它
objectSpanMethod原理:對(duì)每一個(gè)單元格返回一個(gè)[rowSpan, colSpan]數(shù)組, rowSpan表示當(dāng)前單元格會(huì)展示的行數(shù),colSpan表示當(dāng)前單元格會(huì)展示的列數(shù),設(shè)置為0時(shí)當(dāng)前單元格被消除。
參考文章:
element-ui table :span-method(行合并)
js將數(shù)組中的相同項(xiàng)放在毗鄰位置