在之前的文章中介绍了php导出 excel
时进行单元格合并,
在这篇文章中,介绍一下 vue
数据循环时如何进行单元格合并,
这里主要介绍 二维
和 三维
数组循环时数组的合并。
看一下合并后的效果:
不说太多,直接上例子:
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Vue-Table-Demo</title>
<style type="text/css">
* {
margin: 0;
padding: 0;
}
body {
padding: 20px;
}
table {
width: 100%;
border-collapse: collapse;
text-align: center;
margin-bottom: 20px;
}
table th,
td {
border: 1px solid #999;
}
thead th {
height: 40px;
background-color: #f4f4f4;
}
</style>
<script src="https://cdn.bootcdn.net/ajax/libs/vue/2.6.12/vue.min.js"></script>
</head>
<body>
<div id="app" class="div-table" style="width: 1200px">
<data-table1 :data="table1"></data-table1>
<data-table2 :data="table2"></data-table2>
<data-table3 :data="table3"></data-table3>
</div>
<script type="text/x-template" id="table1-template">
<table>
<thead>
<tr>
<th>派工单号</th>
<th>派工日期</th>
<th>本单费用小计</th>
<th>路径</th>
<th>公里数</th>
<th>费用</th>
</tr>
</thead>
<tbody>
<template v-for="(item, index) in data" :key="index">
<template v-for="(subItem, subIndex) in item.table2" :key="subIndex">
<tr>
<!-- 合并单元格:只在第一行显示 -->
<template v-if="subIndex === 0">
<td :rowspan="item.table2.length">{{ item.order }}</td>
<td :rowspan="item.table2.length">{{ item.date }}</td>
<td :rowspan="item.table2.length">{{ item.total }}</td>
</template>
<!-- 其余的单元格正常显示 -->
<td>{{ subItem.path }}</td>
<td>{{ subItem.mileage }}</td>
<td>{{ subItem.fee }}</td>
</tr>
</template>
</template>
</tbody>
<thead>
<tr>
<th>路径</th>
<th>订单号</th>
<th>日期</th>
<th>总价</th>
<th>里程</th>
<th>费用</th>
</tr>
</thead>
<tbody>
<template v-for="(item, index) in data" :key="index">
<template v-for="(subItem, subIndex) in item.table2" :key="subIndex">
<tr>
<!-- 路径始终显示在第一列 -->
<td>{{ subItem.path }}</td>
<!-- 其余列只在第一行合并显示 -->
<template v-if="subIndex === 0">
<td :rowspan="item.table2.length">{{ item.order }}</td>
<td :rowspan="item.table2.length">{{ item.date }}</td>
<td :rowspan="item.table2.length">{{ item.total }}</td>
</template>
<!-- 空列用于占位,防止重复显示 -->
<template v-else>
<td v-if="false"></td>
<td v-if="false"></td>
<td v-if="false"></td>
</template>
<!-- 显示里程和费用 -->
<td>{{ subItem.mileage }}</td>
<td>{{ subItem.fee }}</td>
</tr>
</template>
</template>
</tbody>
</table>
</script>
<script type="text/x-template" id="table2-template">
<table>
<thead>
<tr>
<th>派工单号</th>
<th>派工日期</th>
<th>本单费用小计</th>
<th>路径</th>
<th>公里数</th>
<th>费用</th>
<th>费用名称</th>
<th>金额</th>
<th>备注</th>
</tr>
</thead>
<tbody>
<template v-for="item in data">
<template v-for="(route, routeIdx) in item.table2">
<tr v-for="(fee, feeIdx) in route.table3.length ? route.table3 : [{}]">
<template v-if="feeIdx === 0">
<td v-if="routeIdx === 0" :rowspan="totalFees(item)">{{ item.order }}</td>
<td v-if="routeIdx === 0" :rowspan="totalFees(item)">{{ item.date }}</td>
<td v-if="routeIdx === 0" :rowspan="totalFees(item)">{{ item.total }}</td>
<td :rowspan="route.table3.length || 1">{{ route.path }}</td>
<td :rowspan="route.table3.length || 1">{{ route.mileage }}</td>
<td :rowspan="route.table3.length || 1">{{ route.fee }}</td>
</template>
<td>{{ fee.feeType || '' }}</td>
<td>{{ fee.monye || '' }}</td>
<td>{{ fee.remarks || '' }}</td>
</tr>
</template>
</template>
</tbody>
<thead>
<tr>
<th>路径</th>
<th>订单号</th>
<th>日期</th>
<th>总价</th>
<th>里程</th>
<th>费用</th>
<th>费用类型</th>
<th>金额</th>
<th>备注</th>
</tr>
</thead>
<tbody>
<template v-for="(order, orderIndex) in data" :key="orderIndex">
<template v-for="(route, routeIndex) in order.table2" :key="routeIndex">
<template v-for="(fee, feeIndex) in (route.table3.length ? route.table3 : [null])" :key="feeIndex">
<tr>
<!-- 路径列:每组route下仅显示一次 -->
<template v-if="feeIndex === 0">
<td :rowspan="route.table3.length || 1">{{ route.path }}</td>
</template>
<!-- 订单号、日期、总价:只在第一条route的第一条fee中显示 -->
<template v-if="routeIndex === 0 && feeIndex === 0">
<td :rowspan="order.table2.reduce((sum, r) => sum + Math.max(r.table3.length || 1, 1), 0)">
{{ order.order }}
</td>
<td :rowspan="order.table2.reduce((sum, r) => sum + Math.max(r.table3.length || 1, 1), 0)">
{{ order.date }}
</td>
<td :rowspan="order.table2.reduce((sum, r) => sum + Math.max(r.table3.length || 1, 1), 0)">
{{ order.total }}
</td>
</template>
<!-- 路径下的里程和费用:每组route仅第一fee行展示 -->
<template v-if="feeIndex === 0">
<td :rowspan="route.table3.length || 1">{{ route.mileage }}</td>
<td :rowspan="route.table3.length || 1">{{ route.fee }}</td>
</template>
<!-- 费用明细 -->
<td>{{ fee?.feeType || '' }}</td>
<td>{{ fee?.monye || '' }}</td>
<td>{{ fee?.remarks || '' }}</td>
</tr>
</template>
</template>
</template>
</tbody>
</table>
</script>
<script type="text/x-template" id="table3-template">
<table id="table3">
<thead>
<tr>
<th>派工单号</th>
<th>派工日期</th>
<th>本单费用小计</th>
<th>路径</th>
<th>公里数</th>
<th>费用</th>
<th>费用名称</th>
<th>金额</th>
<th>备注</th>
</tr>
</thead>
<tbody>
<tr v-for="item in data">
<td>{{ item.order }}</td>
<td>{{ item.date }}</td>
<td>{{ item.total }}</td>
<td>{{ item.path }}</td>
<td>{{ item.mileage }}</td>
<td>{{ item.fee }}</td>
<td>{{ item.feeType }}</td>
<td>{{ item.monye }}</td>
<td>{{ item.remarks }}</td>
</tr>
</tbody>
</table>
</script>
<script>
Vue.component('data-table1', {
props: ['data'],
template: '#table1-template'
});
Vue.component('data-table2', {
props: ['data'],
template: '#table2-template',
methods: {
totalFees(item) {
return item.table2.reduce((sum, route) => sum + (route.table3.length || 1), 0);
}
}
});
Vue.component('data-table3', {
props: ['data'],
template: '#table3-template'
});
new Vue({
el: '#app',
data: {
table1: [
{
order: "ASPG20190427", date: "2019-04-27", total: "1200", table2: [
{ path: '苏州—北京', mileage: '7777km', fee: '1000' },
{ path: '上海—北京', mileage: '1000km', fee: '1000' },
{ path: '杭州—苏州', mileage: '1000km', fee: '1000' },
{ path: '成都—内蒙', mileage: '200km', fee: '200' },
{ path: '武汉—太原', mileage: '200km', fee: '200' }
]
},
{
order: "ASPG20190428", date: "2019-04-28", total: "1200", table2: [
{ path: '上海—北京', mileage: '1000km', fee: '1000' },
{ path: '北京—天津', mileage: '200km', fee: '200' }
]
}
],
table2: [
{
order: "ASPG20190427", date: "2019-04-27", total: "1200", table2: [
{
path: '苏州—北京', mileage: '7777km', fee: '1000', table3: [
// { feeType: '油费', monye: '300', remarks:'备注', },
// { feeType: '路桥费', monye: '500', remarks:'备注', }
]
},
{
path: '上海—北京', mileage: '1000km', fee: '1000', table3: [
{ feeType: '油费', monye: '300', remarks: '备注', },
{ feeType: '住宿费', monye: '200', remarks: '备注', },
{ feeType: '路桥费', monye: '500', remarks: '备注', }
]
},
{
path: '杭州—苏州', mileage: '1000km', fee: '900', table3: [
{ feeType: '油费', monye: '300', remarks: '备注', },
]
},
{
path: '成都—内蒙', mileage: '200km', fee: '200', table3: [
// { feeType: '油费', monye: '100', remarks:'备注', },
// { feeType: '路桥费', monye: '100', remarks:'备注', }
]
},
{
path: '武汉—太原', mileage: '200km', fee: '200', table3: [
{ feeType: '油费', monye: '100', remarks: '备注', },
{ feeType: '路桥费', monye: '100', remarks: '备注', }
]
}
]
},
{
order: "ASPG20190428", date: "2019-04-28", total: "1200", table2: [
{
path: '上海—北京', mileage: '1000km', fee: '1000', table3: [
{ feeType: '油费', monye: '300', remarks: '备注', },
{ feeType: '住宿费', monye: '200', remarks: '备注', },
{ feeType: '路桥费', monye: '500', remarks: '备注', }
]
},
{
path: '北京—天津', mileage: '200km', fee: '200', table3: [
{ feeType: '油费', monye: '100', remarks: '备注', },
{ feeType: '路桥费', monye: '100', remarks: '备注', }
]
}
]
}
],
table3: [
{ order: "ASPG20190427", date: "2019-04-27", total: "1200", path: '苏州—北京', mileage: '7777km', fee: '1000', feeType: '油费', monye: '300', remarks: '备注' },
{ order: "ASPG20190427", date: "2019-04-27", total: "1200", path: '苏州—北京', mileage: '7777km', fee: '1000', feeType: '路桥费', monye: '500', remarks: '备注' },
{ order: "ASPG20190427", date: "2019-04-27", total: "1200", path: '上海—北京', mileage: '1000km', fee: '1000', feeType: '油费', monye: '300', remarks: '备注' },
{ order: "ASPG20190427", date: "2019-04-27", total: "1200", path: '上海—北京', mileage: '1000km', fee: '1000', feeType: '住宿费', monye: '200', remarks: '备注' },
{ order: "ASPG20190427", date: "2019-04-27", total: "1200", path: '上海—北京', mileage: '1000km', fee: '1000', feeType: '路桥费', monye: '500', remarks: '备注' },
{ order: "ASPG20190427", date: "2019-04-27", total: "1200", path: '杭州—苏州', mileage: '1000km', fee: '900', feeType: '油费', monye: '300', remarks: '备注' },
{ order: "ASPG20190427", date: "2019-04-27", total: "1200", path: '成都—内蒙', mileage: '200km', fee: '200', feeType: '油费', monye: '100', remarks: '备注' },
{ order: "ASPG20190427", date: "2019-04-27", total: "1200", path: '成都—内蒙', mileage: '200km', fee: '200', feeType: '路桥费', monye: '100', remarks: '备注' },
{ order: "ASPG20190428", date: "2019-04-28", total: "1200", path: '上海—北京', mileage: '1000km', fee: '1000', feeType: '油费', monye: '300', remarks: '备注' },
{ order: "ASPG20190428", date: "2019-04-28", total: "1200", path: '上海—北京', mileage: '1000km', fee: '1000', feeType: '住宿费', monye: '200', remarks: '备注' },
{ order: "ASPG20190428", date: "2019-04-28", total: "1200", path: '上海—北京', mileage: '1000km', fee: '1000', feeType: '路桥费', monye: '500', remarks: '备注' },
{ order: "ASPG20190428", date: "2019-04-28", total: "1200", path: '北京—天津', mileage: '200km', fee: '200', feeType: '油费', monye: '100', remarks: '备注', },
{ order: "ASPG20190428", date: "2019-04-28", total: "1200", path: '北京—天津', mileage: '200km', fee: '200', feeType: '路桥费', monye: '100', remarks: '备注' },
]
},
mounted: function () {
mergeSameCell(document.getElementById('table3'), 1, 0, [0, 1, 2, 3, 4, 5]);
}
});
/**
* [mergeSameCell 单元格合并]
* @param {[type]} tbl [table对应的dom元素]
* @param {[type]} beginRow [从第几行开始合并(从0开始)]
* @param {[type]} endRow [合并到哪一行,负数表示从底下数几行不合并]
* @param {[type]} colIdxes [合并的列下标的数组,如[0,1]表示合并前两列,[0]表示只合并第一列]
* @return {[type]} [description]
*/
function mergeSameCell(tbl, beginRow, endRow, colIdxes) {
var colIdx = colIdxes[0];
var newColIdxes = colIdxes.concat();
newColIdxes.splice(0, 1)
var delRows = new Array();
var rs = tbl.rows;
//endRow为0的时候合并到最后一行,小于0时表示最后有-endRow行不合并
if (endRow === 0) {
endRow = rs.length - 1;
} else if (endRow < 0) {
endRow = rs.length - 1 + endRow;
}
var rowSpan = 1; // 要设置的rowSpan的值
var rowIdx = beginRow; // 要设置rowSpan的cell行下标
var cellValue; // 存储单元格里面的内容
for (var i = beginRow; i <= endRow + 1; i++) {
if (i === endRow + 1) { //过了最后一行的时候合并前面的单元格
if (newColIdxes.length > 0) {
mergeSameCell(tbl, rowIdx, endRow, newColIdxes);
}
rs[rowIdx].cells[colIdx].rowSpan = rowSpan;
} else {
var cell = rs[i].cells[colIdx];
if (i === beginRow) { // 第一行的时候初始化各个参数
cellValue = cell.innerHTML;
rowSpan = 1;
rowIdx = i;
} else if (cellValue != cell.innerHTML) { // 数据改变合并前面的单元格
cellValue = cell.innerHTML;
if (newColIdxes.length > 0) {
mergeSameCell(tbl, rowIdx, i - 1, newColIdxes);
}
rs[rowIdx].cells[colIdx].rowSpan = rowSpan;
rowSpan = 1;
rowIdx = i;
} else if (cellValue === cell.innerHTML) { // 数据和前面的数据重复的时候删除单元格
rowSpan++;
delRows.push(i);
}
}
}
for (var j = 0; j < delRows.length; j++) {
rs[delRows[j]].deleteCell(colIdx);
}
}
</script>
</body>
</html>
参考文章:
1、vue中table跨行合并(demo1 与 demo2)
2、VUE中嵌套多层table
3、js动态合并单元格
本文为冯奎原创文章,转载无需和我联系,但请注明来自冯奎博客fengkui.net
最新评论