一、案例:表头(二级表头)合并、首列数据相同合并
效果图:
1、表头第1、2列合并
第一种写法(普通表头):
<el-table
ref="main"
:data="tableData"
border
:header-cell-style="headerStyle"
style="width: 100%;">
<el-table-column align="center" width="40" label="年级" fixed prop="nj" />
<el-table-column align="center" width="80" label="班级" fixed prop="bj" />
</el-table>
methods: {
headerStyle({ row, column, rowIndex, columnIndex }) {
const comStyle = {
backgroundColor: "#4095e5",
color: "#fff",
fontSize: "500",
};
if (rowIndex === 0) {
row[0].colSpan = 0; // 将表头第一列和第二列合并,内容展示为第二列的内容
row[1].colSpan = 2;
if (columnIndex === 0) { // 将表头第一列隐藏
return {
display: "none",
...comStyle,
};
}
}
return comStyle;
},
}
第2种写法(二级表头):
<el-table
ref="main"
:data="tableData"
border
:header-cell-style="headerStyle"
style="width: 100%;">
<el-table-column align="center" label="班级" width="120">
<el-table-column align="center" width="40" prop="nj" />
<el-table-column align="center" width="80" prop="bj" />
</el-table-column>
</el-table>
headerStyle({ row, column, rowIndex, columnIndex }) {
const comStyle = {
backgroundColor: "#4095e5",
color: "#fff",
fontSize: "500",
};
// 1.1 让第0行的第0列跨2行
if (rowIndex === 0 && columnIndex === 0) {
this.$nextTick(() => {
document
.getElementsByClassName(column.id)[0]
.setAttribute("rowSpan", 2);
return comStyle;
});
}
// 1.2 被覆盖的进行隐藏
if (rowIndex === 1 && (columnIndex == 0 || columnIndex == 1)) {
return {
display: "none",
...comStyle,
};
}
return comStyle;
},
2、行根据某字段内容一致的进行合并
给表格增加属性:span-method="spanMethod"
spanMethod({ row, column, rowIndex, columnIndex }) {
// console.log('row', row, 'column', column, 'rowIndex', rowIndex, 'columnIndex', columnIndex);
//定义需要合并的列字段,有哪些列需要合并,就自定义添加字段即可
const fields = ['nj']
// 当前行的数据
const cellValue = row[column.property]
if (rowIndex == 0) { // 实现表头下面第一行数据,第一列合计展示时2列合并
if (columnIndex === 0) {
return {rowspan: 1, colspan: 0} // 隐藏表头下面第一行的第一列
}
if (columnIndex === 1) {
return {rowspan: 1, colspan: 2} // 将表头下面第一行的第一列和第二列合并
}
}
// 判断只合并定义字段的列数据
if (cellValue && fields.includes(column.property)) {
const prevRow = this.tableData[rowIndex - 1] //上一行数据
let nextRow = this.tableData[rowIndex + 1] //下一行数据
// 当上一行的数据等于当前行数据时,当前行单元格隐藏
if (prevRow && prevRow[column.property] === cellValue) {
return { rowspan: 0, colspan: 0 }
} else {
// 反之,则循环判断若下一行数据等于当前行数据,则当前行开始进行合并单元格
let countRowspan = 1 //用于合并计数多少单元格
while (nextRow && nextRow[column.property] === cellValue) {
nextRow = this.tableData[++countRowspan + rowIndex]
}
if (countRowspan > 1) {
return { rowspan: countRowspan, colspan: 1 }
}
}
}
},
3、完整示例代码
<template>
<div class="page-wrapper">
<div class="search-wrapper">
<el-row type="flex" justify="space-between">
<div class="search-item width-31">
<el-radio-group v-model="type">
<el-radio :label="1">按班级汇总</el-radio>
<el-radio :label="2">按年级汇总</el-radio>
</el-radio-group>
</div>
<div class="search-item margin-right-0">
<el-button size="mini" type="primary">查询</el-button>
<el-button size="mini" type="primary" plain>重置</el-button>
<el-button size="mini" type="primary" icon="el-icon-download" plain>导出</el-button>
</div>
</el-row>
</div>
<el-table
ref="main"
:data="tableData"
border
:header-cell-style="headerStyle"
:span-method="spanMethod"
style="width: 100%;">
<template v-if="type === 1">
<el-table-column align="center" width="40" label="年级" fixed prop="nj" />
<el-table-column align="center" width="80" label="班级" fixed prop="bj" />
</template>
<template v-if="type === 2">
<el-table-column align="center" width="100" label="年级" fixed prop="type" />
</template>
<el-table-column align="center" label="人数">
<el-table-column align="center" label="男" prop="num1" />
<el-table-column align="center" label="女" prop="money1" />
</el-table-column>
<el-table-column align="center" width="160" prop="num2">
<template slot="header">
<div class="expand-header-wrapper">
<span class="title">姓名</span>
<span class="btn" @click="showColumn('open1')">{{!open1 ? '+展开' : '-收起'}}</span>
</div>
</template>
</el-table-column>
<template v-if="open1">
<el-table-column
v-for="item in colunms1"
:key="'姓名_' + item.name"
:label="item.name"
:label-class-name="'more-header'"
align="center">
<el-table-column
v-for="citem in item.children"
:key="'姓名_' + item.name + citem.name"
:label="citem.name"
:prop="citem.prop"
:label-class-name="'more-header'"
align="center"/>
</el-table-column>
</template>
<el-table-column align="center" min-width="200" prop="num2">
<template slot="header">
<div class="expand-header-wrapper">
<span class="title">地址</span>
<span class="btn" @click="showColumn('open2')">{{!open2 ? '+展开' : '-收起'}}</span>
</div>
</template>
</el-table-column>
<template v-if="open2">
<el-table-column
v-for="item in colunms2"
:key="'地址_' + item.name"
:label="item.name"
:label-class-name="'more-header'"
align="center">
<el-table-column
v-for="citem in item.children"
:key="'地址_' + item.name + citem.name"
:label="citem.name"
:prop="citem.prop"
:label-class-name="'more-header'"
align="center"/>
</el-table-column>
</template>
<el-table-column align="center" width="100" prop="num2">
<template slot="header">
<div class="expand-header-wrapper">
<span class="title">排名</span>
<span class="btn" @click="showColumn('open3')">{{!open3 ? '+展开' : '-收起'}}</span>
</div>
</template>
<template>
<el-table-column align="center" label="年排" prop="num6" />
<el-table-column align="center" label="班排" prop="money6" />
</template>
</el-table-column>
<template v-if="open3">
<el-table-column
v-for="item in colunms3"
:key="'排名_' + item.name"
:label="item.name"
:label-class-name="'more-header'"
align="center">
<el-table-column
v-for="citem in item.children"
:key="'排名_' + item.name + citem.name"
:label="citem.name"
:prop="citem.prop"
:label-class-name="'more-header'"
align="center"/>
</el-table-column>
</template>
</el-table>
</div>
</template>
<script>
export default {
name: 'demo',
data() {
return {
type: 1,
tableData: [
{ nj: '合计', type: '合计', bj: '合计', num1: "1111", money1: "1111", num2: "1111", money2: "1111", sale: '111', num3: -2129, money3: "111", num4: "111", money4: "111", num5: "111", money5: "1111",},
{ nj: '初一', type: '初一', bj: "1班", num1: "1", money1: "1", num2: "1", money2: "1", sale: '1', num3: "1", money3: "1", num4: "1", money4: "1", num5: "1", money5: "1",},
{ nj: '初一', type: '初一', bj: "2班", num1: "1", money1: "1", num2: "1", money2: "1", sale: '1', num3: "1", money3: "1", num4: "1", money4: "1", num5: "1", money5: "1",},
{ nj: '初一', type: '初一', bj: "3班", num1: "1", money1: "1", num2: "1", money2: "1", sale: '1', num3: "1", money3: "1", num4: "1", money4: "1", num5: "1", money5: "1",},
{ nj: '初二', type: '初二', bj: "1班", num1: "1", money1: "1", num2: "1", money2: "1", sale: '1', num3: "1", money3: "1", num4: "1", money4: "1", num5: "1", money5: "1",},
{ nj: '初二', type: '初二', bj: "2班", num1: "1", money1: "1", num2: "1", money2: "1", sale: '1', num3: "1", money3: "1", num4: "1", money4: "1", num5: "1", money5: "1",},
{ nj: '初二', type: '初二', bj: "3班", num1: "1", money1: "1", num2: "1", money2: "1", sale: '1', num3: "1", money3: "1", num4: "1", money4: "1", num5: "1", money5: "1",},
{ nj: '初三', type: '初三', bj: "1班", num1: "1", money1: "1", num2: "1", money2: "1", sale: '1', num3: "1", money3: "1", num4: "1", money4: "1", num5: "1", money5: "1",},
{ nj: '初三', type: '初三', bj: "2班", num1: "1", money1: "1", num2: "1", money2: "1", sale: '1', num3: "1", money3: "1", num4: "1", money4: "1", num5: "1", money5: "1",},
{ nj: '初三', type: '初三', bj: "3班", num1: "1", money1: "1", num2: "1", money2: "1", sale: '1', num3: "1", money3: "1", num4: "1", money4: "1", num5: "1", money5: "1",},
{ nj: '高一', type: '高一', bj: "1班", num1: "1", money1: "1", num2: "1", money2: "1", sale: '1', num3: "1", money3: "1", num4: "1", money4: "1", num5: "1", money5: "1",},
{ nj: '高一', type: '高一', bj: "2班", num1: "1", money1: "1", num2: "1", money2: "1", sale: '1', num3: "1", money3: "1", num4: "1", money4: "1", num5: "1", money5: "1",},
{ nj: '高一', type: '高一', bj: "3班", num1: "1", money1: "1", num2: "1", money2: "1", sale: '1', num3: "1", money3: "1", num4: "1", money4: "1", num5: "1", money5: "1",},
{ nj: '高二', type: '高二', bj: "1班", num1: "1", money1: "1", num2: "1", money2: "1", sale: '1', num3: "1", money3: "1", num4: "1", money4: "1", num5: "1", money5: "1",},
{ nj: '高二', type: '高二', bj: "2班", num1: "1", money1: "1", num2: "1", money2: "1", sale: '1', num3: "1", money3: "1", num4: "1", money4: "1", num5: "1", money5: "1",},
{ nj: '高三', type: '高三', bj: "1班", num1: "1", money1: "1", num2: "1", money2: "1", sale: '1', num3: "1", money3: "1", num4: "1", money4: "1", num5: "1", money5: "1",},
],
open1: false,
open2: false,
open3: true,
colunms1: [
{
name: "外形",
children: [
{ name: "身高", prop: "num3" },
{ name: "体重", prop: "money3" },
],
},
{
name: "发型",
children: [
{ name: "颜色", prop: "num4" },
{ name: "长短", prop: "money4" },
],
},
],
colunms2: [
{
name: "省市",
children: [
{ name: "省份", prop: "num3" },
{ name: "城市", prop: "money3" },
],
},
{
name: "区域",
children: [
{ name: "区域", prop: "num4" },
{ name: "街道", prop: "money4" },
],
},
{
name: "村委",
children: [
{ name: "村落", prop: "money5" },
{ name: "门牌", prop: "money5" },
],
},
{
name: "人员情况",
children: [
{ name: "兄弟", prop: "money5" },
{ name: "姐妹", prop: "money5" },
],
},
],
colunms3: [
{
name: "主科",
children: [
{ name: "语文", prop: "num3" },
{ name: "数学", prop: "money3" },
{ name: "英语", prop: "money3" },
],
},
{
name: "其他文科学科",
children: [
{ name: "历史", prop: "num4" },
{ name: "政治", prop: "money4" },
{ name: "地理", prop: "money4" },
],
},
{
name: "其他理科学科",
children: [
{ name: "物理", prop: "num4" },
{ name: "生物", prop: "money4" },
{ name: "化学", prop: "money4" },
],
},
],
}
},
created() {},
methods: {
/** 表头合并控制 */
headerStyle({ row, column, rowIndex, columnIndex }) {
const comStyle = {
backgroundColor: "#4095e5",
color: "#fff",
fontSize: "500",
};
/*
// 1.1 让第0行的第0列跨2行
if (rowIndex === 0 && columnIndex === 0) {
this.$nextTick(() => {
document
.getElementsByClassName(column.id)[0]
.setAttribute("rowSpan", 2);
return comStyle;
});
}
// 1.2 被覆盖的进行隐藏
if (rowIndex === 1 && (columnIndex == 0 || columnIndex == 1)) {
return {
display: "none",
...comStyle,
};
} */
if (this.type === 1 && rowIndex === 0) {
row[0].colSpan = 0; // 将表头第一列和第二列合并,内容展示为第二列的内容
row[1].colSpan = 2;
if (columnIndex === 0) { // 将表头第一列隐藏
return {
display: "none",
...comStyle,
};
}
}
return comStyle;
},
/** 合并行、列 */
spanMethod({ row, column, rowIndex, columnIndex }) {
// console.log('row', row, 'column', column, 'rowIndex', rowIndex, 'columnIndex', columnIndex);
//定义需要合并的列字段,有哪些列需要合并,就自定义添加字段即可
let fields = []
if (this.type === 1) {
fields.push('nj')
} else {
fields.push('type')
}
// 当前行的数据
const cellValue = row[column.property]
if (this.type === 1 && rowIndex == 0) {
if (columnIndex === 0) {
return {rowspan: 1, colspan: 0} // 隐藏表头下面第一行的第一列
}
if (columnIndex === 1) {
return {rowspan: 1, colspan: 2} // 将表头下面第一行的第一列和第二列合并
}
}
// 判断只合并定义字段的列数据
if (cellValue && fields.includes(column.property)) {
const prevRow = this.tableData[rowIndex - 1] //上一行数据
let nextRow = this.tableData[rowIndex + 1] //下一行数据
// 当上一行的数据等于当前行数据时,当前行单元格隐藏
if (prevRow && prevRow[column.property] === cellValue) {
return { rowspan: 0, colspan: 0 }
} else {
// 反之,则循环判断若下一行数据等于当前行数据,则当前行开始进行合并单元格
let countRowspan = 1 //用于合并计数多少单元格
while (nextRow && nextRow[column.property] === cellValue) {
nextRow = this.tableData[++countRowspan + rowIndex]
}
if (countRowspan > 1) {
return { rowspan: countRowspan, colspan: 1 }
}
}
}
},
/** 展开列 */
showColumn(key) {
this[key] = !this[key]
this.$nextTick(() => {
// 由于elementui框架问题,展开列后表格高度变小,需要重新布局、渲染表格
this.$refs.main.doLayout();
})
},
}
}
</script>
<style lang="scss" scoped>
@import '../static/styles/page.scss';
::v-deep .el-radio-group label {
width: 100px !important;
}
.expand-header-wrapper {
position: relative;
.btn {
position: absolute;
right: 10px;
color: #fefa83;
text-decoration: underline;
font-style: italic;
cursor: pointer;
}
}
::v-deep .el-table__header {
.more-header {
background: #cba43f !important;
}
}
::v-deep .el-table__row:first-child .cell{
color: #333;
font-weight: 700;
&:first-child {
font-style: italic;
}
}
</style>
二、案例:表头合并(二级表头)、首列数据相同合并、最后一列根据首列字段相同合并行
效果图:
2.1 实现代码
<el-table
ref="overTargetDetailsTable2222"
:data="tableData2"
border
:header-cell-style="spanHeader2"
:span-method="spanMethod2">
<el-table-column align="center" prop="city" label="区域" fixed width="100" />
<el-table-column align="center" prop="region" label="城市" fixed width="120" />
<el-table-column align="center" label="基础信息">
<el-table-column align="center" label="姓名" prop="name"></el-table-column>
<el-table-column align="center" label="性别" prop="sex"></el-table-column>
<el-table-column align="center" label="年龄" prop="age"></el-table-column>
<el-table-column align="center" label="身高" prop="height"></el-table-column>
<el-table-column align="center" label="体重" prop="weight"></el-table-column>
<el-table-column align="center" label="爱好" min-width="300" prop="like"></el-table-column>
</el-table-column>
</el-table>
data() {
return {
tableData2: [
{
city: '上海',
region: '普陀',
name: '张三',
sex: '女',
age: '22',
height: '162',
weight: '100',
like: '打游戏',
},
{
city: '上海',
region: '黄埔',
name: '张三2',
sex: '男',
age: '23',
height: '172',
weight: '140',
like: '',
},
{
city: '上海',
region: '徐汇',
name: '张三3',
sex: '女',
age: '25',
height: '162',
weight: '100',
like: '',
},
{
city: '陕西',
region: '西安',
name: 'A',
sex: '女',
age: '25',
height: '162',
weight: '100',
like: '跳舞',
},
{
city: '江苏',
region: '南京',
name: '李四',
sex: '男',
age: '22',
height: '162',
weight: '130',
like: '打篮球',
},
{
city: '江苏',
region: '苏州',
name: '李四2',
sex: '女',
age: '20',
height: '162',
weight: '90',
like: '',
},
{
city: '江苏',
region: '无锡',
name: '李四3',
sex: '男',
age: '28',
height: '182',
weight: '150',
like: '',
},
{
city: '浙江',
region: '杭州',
name: '王五',
sex: '女',
age: '19',
height: '162',
weight: '130',
like: '唱歌',
},
{
city: '浙江',
region: '宁波',
name: '王五2',
sex: '男',
age: '29',
height: '170',
weight: '170',
like: '',
},
{
city: '浙江',
region: '嘉兴',
name: '王五3',
sex: '女',
age: '19',
height: '162',
weight: '130',
like: '',
},
],
}
}
/** 表头合并控制 */
spanHeader2({ row, column, rowIndex, columnIndex }) {
let comStyle = {};
// 1.1 让第0行的第0列跨2行
if (rowIndex === 0) {
row[0].colSpan = 0; // 将表头第一列和第二列合并,内容展示为第二列的内容
row[1].colSpan = 2;
if (columnIndex === 0) { // 将表头第一列隐藏
return {
display: "none",
};
}
}
return comStyle;
},
/** 合并行、列 */
spanMethod2({ row, column, rowIndex, columnIndex }) {
// console.log('row', row, 'column', column, 'rowIndex', rowIndex, 'columnIndex', columnIndex);
if (columnIndex === 0) {
//定义需要合并的列字段,有哪些列需要合并,就自定义添加字段即可
const fields = ['city']
// 当前行的数据
const cellValue = row[column.property]
let countRowspan = 1 //用于合并计数多少单元格
// 判断只合并定义字段的列数据
if (cellValue && fields.includes(column.property)) {
const prevRow = this.tableData2[rowIndex - 1] //上一行数据
let nextRow = this.tableData2[rowIndex + 1] //下一行数据
// 当上一行的数据等于当前行数据时,当前行单元格隐藏
if (prevRow && prevRow[column.property] === cellValue) {
return { rowspan: 0, colspan: 0 }
} else {
// 反之,则循环判断若下一行数据等于当前行数据,则当前行开始进行合并单元格
let countRowspan = 1 //用于合并计数多少单元格
while (nextRow && nextRow[column.property] === cellValue) {
nextRow = this.tableData2[++countRowspan + rowIndex]
}
if (countRowspan > 1) {
return { rowspan: countRowspan, colspan: 1 }
}
}
}
}
if (columnIndex === 7) {
const prevRow2 = this.tableData2[rowIndex - 1] //上一行数据
let nextRow2 = this.tableData2[rowIndex + 1] //下一行数据
// 当上一行的数据等于当前行数据时,当前行单元格隐藏
if (prevRow2 && prevRow2.city === row.city) {
return { rowspan: 0, colspan: 0 }
} else {
// 反之,则循环判断若下一行数据等于当前行数据,则当前行开始进行合并单元格
let countRowspan2 = 1 //用于合并计数多少单元格
while (nextRow2 && nextRow2.city === row.city) {
nextRow2 = this.tableData2[++countRowspan2 + rowIndex]
}
if (countRowspan2 > 1) {
return { rowspan: countRowspan2, colspan: 1 }
}
}
}
},