vxeTable官网(Vxe Table v4)
vxe-table 是基于 Vue.js 开发的高性能表格组件,功能丰富,支持大数据量渲染、虚拟滚动、排序、筛选、分页、编辑、导出等多种功能。它不是 Vue 官方自带的组件,而是由社区开发和维护的第三方开源项目。适合需要复杂表格功能的中大型项目使用。
小编在开发过程中遇到的vxeTable 横向合并+纵向合并 展示例图如下:
如果要实现上面例图就需要使用到vxeTable中的span-method属性
||
是“或者”,满足任意一个条件即为真;&&
是“并且”,必须同时满足所有条件才为真;- 在判断一个变量是否等于多个不同值时,通常用
||
,而不是&&
。
<template>
<vxe-table
:data="tableData"
:columns="tableColumns"
:span-method="spanMethod"
size="small"
max-height="400"
>
</vxe-table>
</template>
<script>
import vue from 'vue'
export default {
name: 'CS',
data () {
return {
tableData: []
tableColumns: [
{ field: 'A', title: '申报校区', width: '150', align: 'center' },
{ field: 'B', title: '申报部门', width: '150', align: 'center' },
{ field: 'C', title: '项目名称', width: '150', align: 'center' },
{
field: 'D',
title: '项目总预算',
width: '170',
align: 'right',
formatter: 'money'
},
{
title: '基本信息',
visible: true,
align: 'center',
children: [
{ field: 'E', title: '校区校长', width: '120', align: 'center' },
{ field: 'F', title: '项目负责人', width: '120', align: 'center' },
{ field: 'G', title: '申报人', width: '120', align: 'center' }
]
},
{
title: '预算信息',
visible: true,
align: 'center',
children: [
{ field: 'H', title: '子项目名称', width: '120', align: 'center' },
{
field: 'I',
title: '子项目必要性说明',
width: '160',
align: 'center'
},
{ field: 'J', title: '资金性质', width: '120', align: 'center' },
{ field: 'K', title: '部门经济分类', width: '120', align: 'center' },
{
field: 'L',
title: '申报数',
width: '120',
align: 'right',
formatter: 'money'
}
]
},
{
title: '采购信息明细',
visible: true,
align: 'center',
children: [
{ field: 'M', title: '采购名称', width: '120', align: 'center' },
{ field: 'N', title: '采购品目', width: '120', align: 'center' },
{ field: 'O', title: '规格型号', width: '120', align: 'center' },
{
field: 'P',
title: '单价',
width: '120',
align: 'right',
formatter: 'money'
},
{ field: 'Q', title: '采购数量', width: '120', align: 'center' },
{
field: 'R',
title: '采购金额',
width: '120',
align: 'right',
formatter: 'money'
}
]
},
{ field: 'S', title: '审核状态', width: '120', align: 'center' },
{ field: 'T', title: '流程节点', width: '120', align: 'center' },
{ field: 'U', title: '项目开始时间', width: '120', align: 'center' },
{ field: 'V', title: '项目结束时间', width: '120', align: 'center' },
{ field: 'W', title: '是否政府采购', width: '120', align: 'center' },
{ field: 'X', title: '项目概况', width: '120', align: 'center' },
{ field: 'Y', title: '年度', width: '120', align: 'center' },
{ field: 'Z', title: '政策依据', width: '120', align: 'center' },
{ field: 'AA', title: '测算标准', width: '120', align: 'center' },
{
title: '预算信息',
visible: true,
align: 'center',
children: [
{ field: 'AB', title: '子项年度', width: '120', align: 'center' },
{
field: 'AC',
title: '关联财政二级项目名称',
width: '180',
align: 'center'
},
{
field: 'AD',
title: '关联财政子项目名称',
width: '160',
align: 'center'
}
]
}
],
}
},
created () {
this.getTableData()
},
methods: {
// 自定义合并函数,返回计算后的值 (不能用于虚拟滚动、展开行,不建议用于固定列、树形结构)
spanMethod ({ row, _rowIndex, column, visibleData }) {
// 横向合并字段数组
const horizontalFields = ['A', 'B', 'C']
// 竖向合并字段数组
// 这里定义依据字段映射关系,key 是依据字段,value 是要合并的字段
const verticalFields = {
A: ['A', 'B'],
pId: ['C', 'D', 'E', 'F', 'G', 'S', 'T', 'U', 'V', 'W', 'X'],
bId: ['Y', 'Z', 'AA'],
bgtId: ['H', 'I', 'K', 'L', 'AB', 'AC', 'AD']
}
const cellValue = row[column.property]
if (cellValue === null || cellValue === '') {
return { rowspan: 1, colspan: 1 }
}
// 先计算横向合并的 colspan,默认 1
let colspan = 1
const hIndex = horizontalFields.indexOf(column.property)
if (hIndex !== -1) {
// 只有值为 '合计' 或 '小计' 才合并
if (cellValue === '合计' || cellValue === '小计') {
// 判断左边一列是否和当前列值相同,且不为 null 或 ''
if (hIndex > 0) {
const leftField = horizontalFields[hIndex - 1]
const leftValue = row[leftField]
if (
leftValue !== null &&
leftValue !== '' &&
leftValue === cellValue
) {
// 当前单元格隐藏
colspan = 0
}
}
if (colspan !== 0) {
// 计算向右合并的 colspan
for (let i = hIndex + 1; i < horizontalFields.length; i++) {
const nextValue = row[horizontalFields[i]]
if (
nextValue !== null &&
nextValue !== '' &&
(nextValue === '合计' || nextValue === '小计') &&
nextValue === cellValue
) {
colspan++
} else {
break
}
}
}
}
}
// 计算竖向合并的 rowspan,默认 1
let rowspan = 1
let foundBasisField = null
for (const basisField in verticalFields) {
if (verticalFields[basisField].includes(column.property)) {
foundBasisField = basisField
break
}
}
if (foundBasisField) {
// 如果竖向合并字段数组,依据字段key对应的值为 null 或 undefined,则不支持合并,直接返回
if (
row[foundBasisField] === '' ||
row[foundBasisField] === null ||
row[foundBasisField] === 'null' ||
row[foundBasisField] === undefined ||
row[foundBasisField] === 'undefined'
) {
return { rowspan: 1, colspan }
}
// 如果当前单元格值是 '小计' 或 '合计',不做竖向合并,直接返回
if (cellValue === '小计' || cellValue === '合计') {
return { rowspan: 1, colspan }
}
const prevRow = visibleData[_rowIndex - 1]
const currRow = row
if (
prevRow &&
prevRow[foundBasisField] === currRow[foundBasisField] &&
prevRow[column.property] === cellValue
) {
// 上一行相同,隐藏当前单元格
return { rowspan: 0, colspan: 0 }
} else {
// 计算向下连续相同单元格数
let countRowspan = 1
for (let i = _rowIndex + 1; i < visibleData.length; i++) {
const nextRow = visibleData[i]
if (
nextRow[foundBasisField] === currRow[foundBasisField] &&
nextRow[column.property] === cellValue
) {
countRowspan++
} else {
break
}
}
rowspan = countRowspan
}
}
return { rowspan, colspan }
},
async getTableData () {
this.tableData = []
}
}
}
</script>