多级表头
数据结构比较复杂的时候,可使用多级表头来展现数据的层次关系。
只需要将el-table-column 放置于el-table-column 中,你可以实现组头。
一般可以直接用官网提供的写法,但是有可能数据会比较多的时候,就需要我们稍微改造一下,方便以后再出现合并的数据,直接可以公用。
合并行或列
多行或多列共用一个数据时,可以合并行或列。
通过给 table 传入span-method方法可以实现合并行或列, 方法的参数是一个对象,里面包含当前行 row、当前列 column、当前行号 rowIndex、当前列号 columnIndex 四个属性。 该函数可以返回一个包含两个元素的数组,第一个元素代表 rowspan,第二个元素代表 colspan。 也可以返回一个键名为 rowspan 和 colspan 的对象。
效果图如下
代码
表格的数据布局 index.vue
<!-- 架型匹配与拆分记录 -->
<template>
<el-table
:data="tableData"
style="width: 100%"
height="100%"
class="record-supply"
:row-key="getRowKeys"
:header-cell-style="{
background: '#fafafa',
fontSize: '14px',
height: '40px',
fontWeight: 'normal',
boxSizing: 'border-box',
color: '#707070'
}"
ref="myTable"
:span-method="spanMethod"
@cell-mouse-enter="handleCellMouseEnter"
@cell-mouse-leave="handleCellMouseLeave"
>
<el-table-column
v-for="item in columns"
:key="item.id"
:label="item.label"
:align="item.align"
:width="item.width"
:label-class-name="item?.labelClass"
:class-name="item?.className"
>
<!-- 如果有子列,递归渲染子列 -->
<template v-if="item.children && item.children.length > 0">
<template v-for="child in item.children" :key="child.id">
<el-table-column
v-if="child?.formatValue"
:label="child.label"
:prop="child.prop"
:width="child.width"
:align="child.align"
:label-class-name="child?.labelClass"
:class-name="child?.className"
:formatter="child?.formatValue"
/>
<el-table-column
v-else
fixed="right"
:label="child.label"
:prop="child.prop"
:width="child.width"
:align="child.align"
:label-class-name="child?.labelClass"
:class-name="child?.className"
>
<template #default="scope">
<div v-if="child.prop === 'projectName'">
<div class="link">{{scope.row?.projectName}}</div>
<div class="grey">{{scope.row?.projectNo ?? scope.row?.projectCode}}</div>
</div>
<div v-if="child.prop === 'matchTime'">
<div style="word-break: break-word; white-space: normal">
{{ scope.row.matchTime ?? '--' }}
</div>
</div>
<!-- 录入架型数据 支架型号/类型 -->
<div v-if="child.prop === 'supplyModel'">
<div class="link">
{{ scope.row.supplyModel ?? '--' }}
</div>
<div class="grey" v-if="scope.row.supplyFirstType || scope.row.supplySecondType">
{{ GET_SUPPORT_TYPE(scope.row.supplyFirstType) }} > {{ GET_SUPPORT_SECOND_TYPE_NAME(scope.row.supplySecondType) }}
</div>
</div>
<!-- ERP匹配数据 支架型号/类型 -->
<div v-if="child.prop === 'model'">
<div class="link">
{{ scope.row.model }}
</div>
<!-- ERP 匹配数据 -->
<div class="grey">
{{ GET_SUPPORT_SECOND_TYPE_NAME(scope.row.secondDeviceType) }}
</div>
</div>
</template>
</el-table-column>
</template>
</template>
</el-table-column>
</el-table>
</template>
<script setup lang="ts">
// import dayjs from 'dayjs'
import {
GET_SUPPORT_TYPE,
GET_SUPPORT_SECOND_TYPE_NAME
} from '@/views/project/device/components/config'
const props = defineProps({
columns: { type: Array<any>, default: [] },
tableData: { type: Array<any>, default: [] }
})
const { columns, tableData } = toRefs(props)
const getRowKeys = (row: any) => {
return row.id
}
const megre = reactive([
'projectNo', // 项目编码
'expectedDeliveryTime', // 排产月
'contractNo', // 合同号
'matchTime', // 匹配日期
'supplyModel', // 支架型号/类型
'supplySingleWeight', // 单重
'supplyDeviceCount', // 数量
'supplyTotalWeight' // 总吨位
])
const spanArr = computed(() => {
const spanArr: any = {}
megre.forEach((m) => {
spanArr[m] = { spanArr: [], pos: 0 }
})
tableData.value.forEach((row: any, i: any) => {
megre.forEach((m: any) => {
if (i == 0) {
spanArr[m].spanArr.push(1)
spanArr[m].pos = 0
} else {
// 批次号相同且项目编码相同的
let flag = row.batchNo === tableData.value[i - 1].batchNo && row.projectNo == tableData.value[i - 1].projectNo
//根据项目编码 合并单元格 一样则合并 为空||不同的情况不合并
if (flag && tableData.value[i - 1].projectNo) {
if (row[m] === tableData.value[i - 1][m]) {
// 相等的合并+1
spanArr[m].spanArr[spanArr[m].pos] += 1
spanArr[m].spanArr.push(0)
} else {
// 不相等push 1,并且可修改下标指向
spanArr[m].spanArr.push(1)
spanArr[m].pos = i
}
} else {
spanArr[m].spanArr.push(1)
spanArr[m].pos = i
}
}
})
})
return spanArr
})
const spanMethod = ({ row, column, rowIndex, columnIndex }: any) => {
// 合并单元格 indexOf
const spanArr1: any = spanArr.value
switch (columnIndex) {
case 0:
return {
rowspan: spanArr1.projectNo.spanArr[rowIndex],
colspan: spanArr1.projectNo.spanArr[rowIndex] == 0 ? 0 : 1
}
case 1:
return {
rowspan: spanArr1.projectNo.spanArr[rowIndex],
colspan: spanArr1.projectNo.spanArr[rowIndex] == 0 ? 0 : 1
}
case 2:
return {
rowspan: spanArr1.contractNo.spanArr[rowIndex],
colspan: spanArr1.contractNo.spanArr[rowIndex] == 0 ? 0 : 1
}
case 3:
return {
rowspan: spanArr1.matchTime.spanArr[rowIndex],
colspan: spanArr1.matchTime.spanArr[rowIndex] == 0 ? 0 : 1
}
case 4:
return {
rowspan: spanArr1.supplyModel.spanArr[rowIndex],
colspan: spanArr1.supplyModel.spanArr[rowIndex] == 0 ? 0 : 1
}
case 5:
return {
rowspan: spanArr1.supplySingleWeight.spanArr[rowIndex],
colspan: spanArr1.supplySingleWeight.spanArr[rowIndex] == 0 ? 0 : 1
}
case 6:
return {
rowspan: spanArr1.supplyDeviceCount.spanArr[rowIndex],
colspan: spanArr1.supplyDeviceCount.spanArr[rowIndex] == 0 ? 0 : 1
}
case 7:
return {
rowspan: spanArr1.supplyTotalWeight.spanArr[rowIndex],
colspan: spanArr1.supplyTotalWeight.spanArr[rowIndex] == 0 ? 0 : 1
}
default:
return {
rowspan: 1,
colspan: 1
}
}
}
let currentIndex = ref('')
let currentColumnIndex = ref('')
const handleCellMouseEnter = (row: any, column: any) => {
//鼠标移入后赋值
currentIndex.value = row.projectNo //row.productCode是行相同的标志
currentColumnIndex.value = column.property //获取列的标题名
}
const handleCellMouseLeave = () => {
//鼠标移走后置空
currentIndex.value = ''
currentColumnIndex.value = ''
}
// // 行的颜色设置 :row-class-name="tableRowClassName" :cell-style="cellStyle"
// const tableRowClassName = ({ row }: any) => {
// let flag = row.projectNo == currentIndex.value && megre.includes(currentColumnIndex.value)
// return flag ? 'quotatemplate-my-hover-row' : ''
// }
// // 鼠标移入除合并单元格的其它单元格,这时候需要单独一行展示。
// const cellStyle = ({ row, column, rowIndex, columnIndex }: any) => {
// let flag =
// row.projectNo == currentIndex.value &&
// currentColumnIndex.value &&
// megre.includes(currentColumnIndex.value)
// return flag ? { background: '#f4f6fa' } : { '': '' }
// }
</script>
<style scoped lang="scss">
.header-nav {
display: flex;
align-items: center;
height: 40px;
border: 1px solid #ebeef5;
background: #fafafa;
border-bottom: none;
border-right: none;
& > div {
flex: 1;
border-right: 1px solid #ebeef5;
text-align: center;
}
}
:deep(.record-supply) {
.el-table__border-left-patch {
width: 0;
}
.column-border {
border-left: 1px solid #ebeef5;
}
.border-none {
border-right: none;
}
.border-right {
border-right: 1px solid #ebeef5;
}
.column-height {
height: 70px;
}
.el-table__cell {
.cell {
padding-left: 8px;
}
}
}
.edit-style {
color: #3076fe;
& > div {
cursor: pointer;
}
}
:deep(.el-table--border::before),
:deep(.el-table--border::after) {
width: 0px;
}
.link {
// cursor: pointer;
color: #262626;
overflow: hidden;
text-overflow: ellipsis;
}
.grey {
color: #a3a3a3;
}
</style>
<style>
/* 移除划过时表格的背景色 || 自行设置划过的背景色 || 不写下方样式将默认颜色 */
.record-supply.el-table .quotatemplate-my-hover-row {
background: #f4f6fa !important;
}
</style>
数据的映射config.ts 页面
import dayjs from 'dayjs'
// 架型匹配与拆分记录
export const splitRecordColumns = (): any[] => [
{
id: 1,
label: '录入架型数据',
width: '',
labelClass: 'column-border',
className: '',
align: 'center',
children: [
{
id: 11,
label: '项目名称/编码',
prop: 'projectName',
width: '172',
align: ''
},
{
id: 12,
label: '排产月',
prop: 'expectedDeliveryTime',
width: '80',
align: 'center',
formatValue: (row: any) =>
row.expectedDeliveryTime ? dayjs(row.expectedDeliveryTime).format('YYYY-MM') : '--'
},
{
id: 13,
label: '合同号',
prop: 'contractNo',
width: '160',
labelClass: 'column-border',
align: 'center',
formatValue: (row: any) => row.contractNo || '--'
},
{
id: 14,
label: '匹配时间',
prop: 'matchTime',
width: '100',
align: ''
},
{
id: 15,
label: '支架型号/类型',
prop: 'supplyModel',
width: '170',
align: ''
},
{
id: 16,
label: '单重',
prop: 'supplySingleWeight',
width: '70',
align: 'center',
formatValue: (row: any) => (row.supplySingleWeight ? row.supplySingleWeight + '吨' : '--')
},
{
id: 17,
label: '数量',
prop: 'supplyDeviceCount',
width: '70',
align: 'center',
formatValue: (row: any) => (row.supplyDeviceCount ? row.supplyDeviceCount + '架' : '--')
},
{
id: 18,
label: '总吨位',
prop: 'supplyTotalWeight',
width: '90',
align: 'center',
formatValue: (row: any) => (row.supplyTotalWeight ? row.supplyTotalWeight + '吨' : '--')
}
]
},
{
id: 2,
label: 'ERP匹配数据',
width: '',
labelClass: '',
className: '',
align: 'center',
children: [
{
id: 21,
label: '排产月',
prop: 'planProduceMonth',
width: '80',
align: '',
formatValue: (row: any) =>
row.planProduceMonth ? dayjs(row.planProduceMonth).format('YYYY-MM') : '--'
},
{
id: 22,
label: '施工号',
prop: 'constructionNo',
width: '110',
align: '',
formatValue: (row: any) => row.constructionNo || '--'
},
{
id: 23,
label: '支架型号/类型',
prop: 'model',
width: '170',
align: ''
},
{
id: 24,
label: '单重',
prop: 'singleWeight',
width: '70',
align: '',
formatValue: (row: any) => (row.singleWeight ? row.singleWeight + '吨' : '--')
},
{
id: 25,
label: '数量',
prop: 'deviceCount',
width: '70',
align: '',
formatValue: (row: any) => (row.deviceCount ? row.deviceCount + '架' : '--')
},
{
id: 26,
label: '总吨位',
prop: 'totalWeight',
width: '90',
align: '',
formatValue: (row: any) => (row.totalWeight ? row.totalWeight + '吨' : '--')
},
{
id: 27,
label: '交审日期',
prop: 'transferDate',
width: '90',
align: '',
formatValue: (row: any) =>
row.transferDate ? dayjs(row.transferDate).format('MM月DD日') : '--'
},
{
id: 28,
label: '备注',
prop: 'remark',
labelClass: 'border-right',
className: 'border-none column-height',
width: '',
align: '',
formatValue: (row: any) => row.remark || '--'
}
]
}
]
注意的是,可能需求不同,但是方法是一样的。只需要把方法中的值替换即可实现公用。
如下图:
注意的项是 要和并的字段不一样,我们需要单独去设置;spanMethod 方法会根据 设定的 merge 值去合并;
spanArr 方法执行计算属性,添加我们的判断条件。需要满足需求条件才会更新数据。
这样就结束了。
展示最后的效果
满足条件的进行合并,不满足的则是单独一行展示