效果:
代码:
index.vue
<template>
<div class="my-table">
<el-table
v-loading="table.loading"
:data="table.data"
border
size="mini"
:header-cell-style="headerCellStyle"
:span-method="spanMethod"
>
<!-- 使用递归组件渲染表头 -->
<myColumn
v-for="(item, index) in table.header"
:key="index"
:header="item"
/>
</el-table>
</div>
</template>
<script>
// 引入组件
import myColumn from "./my-column.vue";
export default {
name: "Test1",
components: { myColumn },
data() {
return {
table: {
loading: false,
header: [],
data: [],
},
};
},
computed: {},
created() {},
mounted() {
// 表头
this.table.header = [
{
prop: "date",
label: "月份",
width: "100px",
children: [],
fixed: false,
},
{
prop: "project1",
label: "",
width: "100px",
children: [],
},
{
prop: "project2",
label: "项目",
width: "100px",
position: "111",
children: [],
},
{
prop: "index",
label: "序号",
width: "50px",
children: [],
},
{
prop: "deliveryInfo",
label: "线下合计",
width: "560px",
children: [
{
prop: "name",
label: "2024",
width: "80px",
children: [
{
prop: "name",
label: "金额",
width: "80px",
},
{
prop: "name",
label: "%",
width: "80px",
children: [],
},
],
},
{
prop: "name",
label: "2023",
width: "80px",
children: [
{
prop: "name",
label: "金额",
width: "80px",
},
{
prop: "name",
label: "%",
width: "80px",
children: [],
},
],
},
{
prop: "name",
label: "增长",
width: "80px",
children: [
{
prop: "name1",
label: "金额",
width: "80px",
},
{
prop: "name",
label: "%",
width: "80px",
children: [],
},
],
},
],
},
{
prop: "deliveryInfo",
label: "东北",
width: "560px",
children: [
{
prop: "name",
label: "2024",
width: "80px",
children: [
{
prop: "name",
label: "金额",
width: "80px",
},
{
prop: "name",
label: "%",
width: "80px",
children: [],
},
],
},
{
prop: "name",
label: "2023",
width: "80px",
children: [
{
prop: "name",
label: "金额",
width: "80px",
},
{
prop: "name",
label: "%",
width: "80px",
children: [],
},
],
},
{
prop: "name",
label: "增长",
width: "80px",
children: [
{
prop: "name",
label: "金额",
width: "80px",
},
{
prop: "name",
label: "%",
width: "80px",
children: [],
},
],
},
],
},
];
// 数据
this.table.data = [
{
date: "2016-05-03",
project1: "数量",
project2: "鞋",
province: "上海",
city: "普陀区",
address: "上海市普陀区金沙江路 1518 弄",
index: 0,
name1: 10086,
},
{
date: "2016-05-02",
project1: "数量",
project2: "包服配",
province: "上海",
city: "普陀区",
address: "上海市普陀区金沙江路 1518 弄",
index: 1,
},
{
date: "2016-05-04",
name: "王小虎",
project1: "收入",
project2: "实数收入",
province: "上海",
city: "普陀区",
address: "上海市普陀区金沙江路 1518 弄",
zip: 200333,
},
{
date: "2016-05-01",
name: "王小虎",
project1: "收入",
project2: "无税收入",
province: "上海",
city: "普陀区",
address: "上海市普陀区金沙江路 1518 弄",
zip: 200333,
},
{
date: "2016-05-08",
name: "王小虎",
project1: "收入",
project2: "有税收入",
province: "上海",
city: "普陀区",
address: "上海市普陀区金沙江路 1518 弄",
zip: 200333,
},
{
date: "2016-05-06",
name: "王小虎",
project1: "附加税金",
project2: "附加税金",
province: "上海",
city: "普陀区",
address: "上海市普陀区金沙江路 1518 弄",
zip: 200333,
},
{
date: "2016-05-07",
name: "王小虎",
project1: "附加税金1",
project2: "附加税金2",
province: "上海",
city: "普陀区",
address: "上海市普陀区金沙江路 1518 弄",
zip: 200333,
},
];
},
methods: {
spanMethod({ row, column, rowIndex, columnIndex }) {
// const rowData = Object.values(row);
// if ((rowData[1] === rowData[2])) { // 根据实际需求变更
if ((row['project1'] === row['project2'])) { // 根据实际需求变更
// 跨列数据相同合并
if (columnIndex === 1) {
return [1, 2];
} else if (columnIndex === 2) {
return [0, 0]
}
} else {
if (columnIndex === 1) {
// 列数据相同合并
const currentValue = row[column.property];
const preRow = this.table.data[rowIndex - 1];
const preValue = preRow ? preRow[column.property] : null;
if (currentValue === preValue) {
return [0, 0]
} else {
let rowspan = 1;
for (let i = rowIndex + 1; i < this.table.data.length; i++) {
const nextRow = this.table.data[i]
const nextValue = nextRow[column.property]
if (nextValue === currentValue) {
rowspan++;
} else {
break;
}
}
return [rowspan, 1];
}
}
}
},
headerCellStyle({ row, column, rowIndex, columnIndex }) {
let base = { "background": "#4389f94d",color: "#333" };
if (column.label === "") {
// 隐藏
return { display: "none" };
}
if (column.property === "project2") { // 根据实际需求变更
this.$nextTick(() => {
if (document.getElementsByClassName(column.id).length !== 0) {
document
.getElementsByClassName(column.id)[0]
.setAttribute("colSpan", 2); // 根据实际需求变更
}
});
}
return base;
},
},
};
</script>
<style lang="scss" scoped></style>
my-column.vue
<template>
<el-table-column
:prop="header.prop"
:label="header.label"
:fixed="header.fixed"
:min-width="header.width"
align="center"
show-overflow-tooltip
>
<template v-if="header.children && header.children.length">
<myColumn v-for="(child, index) in header.children" :key="index" :header="child" />
</template>
</el-table-column>
</template>
<script>
export default {
name: "myColumn",
components: {},
props: {
header: {
type: Object,
required: true,
},
},
data() {
return {};
},
mounted() {},
methods: {},
};
</script>
<style lang="scss" scoped></style>
附加:index.html模版
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<title>demo</title>
<!-- 引入样式 -->
<link
rel="stylesheet"
href="https://unpkg.com/element-ui/lib/theme-chalk/index.css"
/>
<!-- 引入组件库 -->
<script src="https://unpkg.com/vue@2/dist/vue.js"></script>
<script src="https://unpkg.com/element-ui/lib/index.js"></script>
</head>
<body>
<div id="app">
<div class="my-table">
<el-table
v-loading="table.loading"
:data="table.data"
border
size="mini"
:header-cell-style="headerCellStyle"
:span-method="spanMethod"
>
<!-- 使用递归组件渲染表头 -->
<mycolumn
v-for="(item, index) in table.header"
:key="index"
:header="item"
/>
</el-table>
</div>
</div>
<script type="text/javascript">
//定义组件
Vue.component("mycolumn", {
props: {
header: {
type: Object,
required: true,
},
},
template: `<el-table-column :prop="header.prop" :label="header.label" :fixed="header.fixed" :min-width="header.width" align="center" show-overflow-tooltip>
<template v-if="header.children && header.children.length">
<mycolumn v-for="(child, index) in header.children" :key="index" :header="child" />
</template>
</el-table-column>`,
});
var vm = new Vue({
el: "#app",
data() {
return {
table: {
loading: false,
header: [],
data: [],
},
};
},
mounted() {
// 表头
this.table.header = [
{
prop: "date",
label: "月份",
width: "100px",
children: [],
fixed: false,
},
{
prop: "project1",
label: "",
width: "100px",
children: [],
},
{
prop: "project2",
label: "项目",
width: "100px",
position: "111",
children: [],
},
{
prop: "index",
label: "序号",
width: "50px",
children: [],
},
{
prop: "deliveryInfo",
label: "线下合计",
width: "560px",
children: [
{
prop: "name",
label: "2024",
width: "80px",
children: [
{
prop: "name",
label: "金额",
width: "80px",
},
{
prop: "name",
label: "%",
width: "80px",
children: [],
},
],
},
{
prop: "name",
label: "2023",
width: "80px",
children: [
{
prop: "name",
label: "金额",
width: "80px",
},
{
prop: "name",
label: "%",
width: "80px",
children: [],
},
],
},
{
prop: "name",
label: "增长",
width: "80px",
children: [
{
prop: "name1",
label: "金额",
width: "80px",
},
{
prop: "name",
label: "%",
width: "80px",
children: [],
},
],
},
],
},
{
prop: "deliveryInfo",
label: "东北",
width: "560px",
children: [
{
prop: "name",
label: "2024",
width: "80px",
children: [
{
prop: "name",
label: "金额",
width: "80px",
},
{
prop: "name",
label: "%",
width: "80px",
children: [],
},
],
},
{
prop: "name",
label: "2023",
width: "80px",
children: [
{
prop: "name",
label: "金额",
width: "80px",
},
{
prop: "name",
label: "%",
width: "80px",
children: [],
},
],
},
{
prop: "name",
label: "增长",
width: "80px",
children: [
{
prop: "name",
label: "金额",
width: "80px",
},
{
prop: "name",
label: "%",
width: "80px",
children: [],
},
],
},
],
},
];
// 数据
this.table.data = [
{
date: "2016-05-03",
project1: "数量",
project2: "鞋",
province: "上海",
city: "普陀区",
address: "上海市普陀区金沙江路 1518 弄",
index: 0,
name1: 10086,
},
{
date: "2016-05-02",
project1: "数量",
project2: "包服配",
province: "上海",
city: "普陀区",
address: "上海市普陀区金沙江路 1518 弄",
index: 1,
},
{
date: "2016-05-04",
name: "王小虎",
project1: "数量",
project2: "实数收入",
province: "上海",
city: "普陀区",
address: "上海市普陀区金沙江路 1518 弄",
zip: 200333,
},
{
date: "2016-05-01",
name: "王小虎",
project1: "收入",
project2: "无税收入",
province: "上海",
city: "普陀区",
address: "上海市普陀区金沙江路 1518 弄",
zip: 200333,
},
{
date: "2016-05-08",
name: "王小虎",
province: "上海",
city: "普陀区",
address: "上海市普陀区金沙江路 1518 弄",
zip: 200333,
},
{
date: "2016-05-06",
name: "王小虎",
province: "上海",
city: "普陀区",
address: "上海市普陀区金沙江路 1518 弄",
zip: 200333,
},
{
date: "2016-05-07",
name: "王小虎",
province: "上海",
city: "普陀区",
address: "上海市普陀区金沙江路 1518 弄",
zip: 200333,
},
];
},
methods: {
spanMethod({ row, column, rowIndex, columnIndex }) {
// const rowData = Object.values(row);
// if ((rowData[1] === rowData[2])) { // 根据实际需求变更
if (row["project1"] === row["project2"]) {
// 根据实际需求变更
// 跨列数据相同合并
if (columnIndex === 1) {
return [1, 2];
} else if (columnIndex === 2) {
return [0, 0];
}
} else {
if (columnIndex === 1) {
// 列数据相同合并
const currentValue = row[column.property];
const preRow = this.table.data[rowIndex - 1];
const preValue = preRow ? preRow[column.property] : null;
if (currentValue === preValue) {
return [0, 0];
} else {
let rowspan = 1;
for (let i = rowIndex + 1; i < this.table.data.length; i++) {
const nextRow = this.table.data[i];
const nextValue = nextRow[column.property];
if (nextValue === currentValue) {
rowspan++;
} else {
break;
}
}
return [rowspan, 1];
}
}
}
},
headerCellStyle({ row, column, rowIndex, columnIndex }) {
let base = { background: "#4389f94d", color: "#333" };
if (column.label === "") {
// 隐藏
return { display: "none" };
}
if (column.property === "project2") {
// 根据实际需求变更
this.$nextTick(() => {
if (document.getElementsByClassName(column.id).length !== 0) {
document
.getElementsByClassName(column.id)[0]
.setAttribute("colSpan", 2); // 根据实际需求变更
}
});
}
return base;
},
},
});
</script>
</body>
</html>