1、目标效果
源码放在下面,复制粘贴即可
(1)合计行放在头部,且字体颜色变粗、合计行背景色变粗
(2)合计行年龄算平均值且字体颜色为绿色,财产算总数且字体颜色为红色
2、原理
2.1、el-table中show-summary 合计行默认放在尾部
加上如下css即可:父选择器 /deep/ 子选择器实现样式穿透
/* 合计行位置修改开始 */ .el-table { display: flex; flex-direction: column; } .el-table /deep/ .el-table__body-wrapper { order: 1; } /* 合计行位置修改结束*/
2.2、合计行整体样式修改
/* 合计行整体样式修改开始 */ .el-table /deep/ .el-table__footer-wrapper tbody td { background: black; color: white; font-weight: bolder; } /* 合计行整体样式修改结束*/
2.3、合计方法::summary-method="getSummaries"
// 合计行方法 getSummaries(param) { const { columns, data } = param; const sums = []; columns.forEach((column, index) => { if (column.property == 'date') { sums[index] = '总价'; return; } // values是每一列的数据是一个数组 const values = data.map(item => Number(item[column.property])); // 数字列才进行合计计算 if (!values.every(value => isNaN(value))) { // 对values进行一个累加操作,累加那些非NAN的值 const total = values.reduce((prev, curr) => { const value = Number(curr); if (!isNaN(value)) { return prev + curr; } else { return prev; } }, 0); // 年龄计算平均值 if (column.property == 'age') { sums[index] = total / data.length } // 财产计算总数 if (column.property == 'money') { sums[index] = total} } } }); // 将合计结果返回,是一个数组,每个位置的值与表格的列一一对应 return sums }
columns展示每一列的信息,是一个对象数组
data为每一行的信息,也是一个对象数组,不算上合计行哦!
sums为合计行,在typescript中是一个元祖,即数组里面可以存放不同类型的元素
2.4、合计行单元格样式
2.4.1、css方式
.el-table /deep/ .el-table__footer-wrapper tbody td:nth-child(3) { color: lightgreen; font-weight: bolder; } .el-table /deep/ .el-table__footer-wrapper tbody td:nth-child(4) { color: red; font-weight: bolder; }
缺点:需要手动计算每列的位置,一旦列的位置发生变化,不好维护
2.4.2、jsx方式
合计行中返回jsx,生成一个VNode,动态生成样式
// 年龄计算平均值(绿色) if (column.property == 'age') { sums[index] = <span class={'green'}>{total / data.length}</span> } // 财产计算总数(红色) if (column.property == 'money') { sums[index] = <span class={'red'}>{total}</span> }
.red { color: red; font-weight: bolder; } .green { color: lightgreen; font-weight: bolder; }
sums返回VNode
2.5、合计行单元格合并
2.5.1、别人博客的方式(会报错)
watch: { 表格数据: { immediate: true, handler() { const tds = document.querySelectorAll('#table .el-table__footer-wrapper tr>td'); tds[0].colSpan = 2; tds[0].style.textAlign = 'center' tds[1].style.display = 'none' } } },
2.5.2、elementUI中合并行的方法
:span-method是不包括合计行的
2.5.3、本篇文章的方法 (推荐)
在方法一的基础上改造:
watch: { tableData: { immediate: true, handler() { setTimeout(() => { const tds = document.querySelectorAll('#table .el-table__footer-wrapper tr>td'); tds[0].colSpan = 2; tds[0].style.textAlign = 'center' tds[1].style.display = 'none' }, 0) } } },
为什么这样却可以?
Vue 推荐使用 template 来创建 HTML。但不是真实的DOM节点。vue会先利用对象中的render 函数,生成虚拟DOM节点并挂载,挂载的真实DOM是进行异步渲染的,并且在修改完状态后也是异步的方式进行渲染。代码中将document.querySelectorAll操作定义为同步任务,此时事件队列应该是:
同步队列:获取dom元素
异步队列:从虚拟节点转真实节点并进行渲染
根据先同步后异步的执行流程,是无法合并单元格的使用setTimeout 可以将元素放在异步队列执行,等页面加载完毕之后在获取dom元素就可以成功了!
3、源码
App.vue
<template>
<div id="app">
<SummaryTable></SummaryTable>
</div>
</template>
<script>
import SummaryTable from '@/components/SummaryTable'
export default {
components: {
SummaryTable
},
name: 'App',
data() {
return {
}
},
}
</script>
<style>
#app {
font-family: Avenir, Helvetica, Arial, sans-serif;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
text-align: center;
color: #2c3e50;
margin-top: 60px;
}
</style>
SummaryTable.vue
<template>
<div id="app">
<el-table ref="table" id="table" :data="tableData" border style="width: 100%" show-summary
:summary-method="getSummaries">
<el-table-column prop="date" label="日期" width="180">
</el-table-column>
<el-table-column prop="name" label="姓名" width="180">
</el-table-column>
<el-table-column prop="age" label="年龄"></el-table-column>
<el-table-column prop="money" label="财产"></el-table-column>
</el-table>
</div>
</template>
<script>
export default {
name: 'App',
data() {
return {
tableData: [{
date: '2016-05-01',
name: '王小虎',
age: 13,
money: 100
}, {
date: '2016-05-02',
name: '王小虎',
age: 25,
money: 333
}, {
date: '2016-05-03',
name: '王小虎',
age: 33,
money: 555
}, {
date: '2016-05-04',
name: '王小虎',
age: 23,
money: 200
},
{
date: '2016-05-05',
name: '王小虎',
age: 26,
money: 666
}],
}
},
watch: {
tableData: {
immediate: true,
handler() {
setTimeout(() => {
const tds = document.querySelectorAll('#table .el-table__footer-wrapper tr>td');
tds[0].colSpan = 2;
tds[0].style.textAlign = 'center'
tds[1].style.display = 'none'
}, 0)
}
}
},
methods: {
// 合计行方法
getSummaries(param) {
const { columns, data } = param;
const sums = [];
columns.forEach((column, index) => {
if (column.property == 'date') {
sums[index] = '总价';
return;
}
// values是每一列的数据是一个数组
const values = data.map(item => Number(item[column.property]));
if (!values.every(value => isNaN(value))) {
// 对values进行一个累加操作,累加那些非NAN的值
const total = values.reduce((prev, curr) => {
const value = Number(curr);
if (!isNaN(value)) {
return prev + curr;
} else {
return prev;
}
}, 0);
// 年龄计算平均值(绿色)
if (column.property == 'age') {
sums[index] = <span class={'green'}>{total / data.length}</span>
}
// 财产计算总数(红色)
if (column.property == 'money') {
sums[index] = <span class={'red'}>{total}</span>
}
}
});
// 将合计结果返回,是一个数组,每个位置的值与表格的列一一对应
return sums
}
}
}
</script>
<style scoped>
.red {
color: red;
font-weight: bolder;
}
.green {
color: lightgreen;
font-weight: bolder;
}
/* 合计行整体样式修改开始 */
.el-table /deep/ .el-table__footer-wrapper tbody td {
background: black;
color: white;
font-weight: bolder;
}
/* 合计行整体样式修改结束*/
/* 合计行位置修改开始 */
.el-table {
display: flex;
flex-direction: column;
}
.el-table /deep/ .el-table__body-wrapper {
order: 1;
}
/* 合计行位置修改结束*/
</style>