最近要实现一个类似树形表格,如下图所示,起初想要通过div来绘制,但画出来后发现不好固定前三列和单元格自适应高度,于是就拿el-table
来修改。
实现思路
- 首先合并单元格,
span-method
方法合并时找到单元格第一项和最后一项并添加标识; cell-class-name
通过标识给单元格添加对应class;- 连接线通过伪类来实现,横线通过
cell
绘制,竖线通过td
绘制。
其中繁琐的地方是需要判断找到子项的第一项和最后一项单元格,如果是第一项,则横线的长度要短一些,如果是最后一项,则不画左竖线。
<el-table
ref="evalTable"
height="100%"
:data="tableData"
size="mini"
:span-method="objectSpanMethod"
:cell-class-name="cellClass"
:cell-style="{border: 'none', color: '#5A6E8D'}"
:header-cell-style="{border: 'none', fontWeight: 400, color: '#000'}"
style="width: 100%">
<el-table-column
prop="firstLevelName"
label="省"
fixed
width="90">
</el-table-column>
<el-table-column
prop="secondLevelName"
label="市"
fixed
width="90">
</el-table-column>
<el-table-column
prop="thirdLevelName"
label="区"
fixed
width="90">
</el-table-column>
<el-table-column
label="AAA">
</el-table-column>
<el-table-column
label="BBB">
</el-table-column>
<el-table-column
label="CCC">
</el-table-column>
<el-table-column
label="XXX">
</el-table-column>
</el-table>
export default {
data () {
return {
tableData: [
{ firstLevelName: '贵州省', secondLevelName: '贵阳市', thirdLevelName: '观山湖区' },
{ firstLevelName: '贵州省', secondLevelName: '贵阳市', thirdLevelName: '云岩区' },
{ firstLevelName: '贵州省', secondLevelName: '贵阳市', thirdLevelName: '南明区' },
{ firstLevelName: '贵州省', secondLevelName: '遵义市', thirdLevelName: '仁怀市' },
{ firstLevelName: '贵州省', secondLevelName: '六盘水市', thirdLevelName: '钟山区' },
{ firstLevelName: '贵州省', secondLevelName: '六盘水市', thirdLevelName: '水城县' },
{ firstLevelName: '山西省', secondLevelName: '太原市', thirdLevelName: '万柏林区' },
{ firstLevelName: '山西省', secondLevelName: '太原市', thirdLevelName: '小店区' },
{ firstLevelName: '山西省', secondLevelName: '太原市', thirdLevelName: '尖草坪区' }
]
}
},
methods: {
// 合并行,将名称相同的合并
objectSpanMethod ({ row, column, rowIndex, columnIndex }) {
const data = this.tableData // table数据
const cellValue = row[column.property] // 当前单元格的值
if (cellValue && (columnIndex === 0 || columnIndex === 1 || columnIndex === 2)) {
const prevRow = data[rowIndex - 1] // 上一条数据
let nextRow = data[rowIndex + 1] // 下一条数据
if (prevRow && prevRow[column.property] === cellValue) { // 当有上一条数据,并且和当前值相等时
return { rowspan: 0, colspan: 0 }
} else {
let countRowspan = 1
while (nextRow && nextRow[column.property] === cellValue) { // 当有下一条数据并且和当前值相等时,获取新的下一条
nextRow = data[++countRowspan + rowIndex]
}
if (columnIndex === 2) {
// 第三列
if (!nextRow || (row.secondLevelName !== nextRow.secondLevelName)) {
this.$set(row, 'thirdEnd', true)
}
if (prevRow && (row.secondLevelName !== prevRow.secondLevelName)) {
this.$set(row, 'thirdStart', true)
}
}
if (countRowspan > 1) {
if (columnIndex === 0 || columnIndex === 1) {
this.$set(row, 'thirdStart', true)
}
if (columnIndex === 1) {
if (rowIndex === 0) {
this.$set(row, 'secondStart', true)
}
if (nextRow && (row.firstLevelName !== nextRow.firstLevelName)) {
this.$set(nextRow, 'secondStart', true)
}
if (!nextRow || (row.firstLevelName !== nextRow.firstLevelName)) {
this.$set(row, 'secondEnd', true)
}
}
return { rowspan: countRowspan, colspan: 1 }
}
}
}
this.$refs.evalTable.doLayout() // 有时el-table会错位,调用此方法
},
cellClass ({ row, column, rowIndex, columnIndex }) {
if (columnIndex === 1) {
// 第二列
let className = 'child-cell'
if (row.secondStart) {
className += ' child-cell__first'
}
if (row.secondEnd) {
className += ' child-cell__last'
}
return className
}
if (columnIndex === 2) {
// 第三列
let className = 'child-cell'
if (row.thirdStart) {
className += ' child-cell__first'
}
if (row.thirdEnd) {
className += ' child-cell__last'
}
return className
}
}
}
}
目前需要根据单元格宽度,调节线的位置,等有时间优化看能否自适应
::v-deep .el-table {
.el-table__cell {
vertical-align: top;
&.child-cell {
position: relative;
&::before {
content: "";
position: absolute;
top: 35px;
left: -60px;
width: 1px;
height: calc(100% - 17px);
background-color: #DFE0E1;
}
&__last {
&::before {
width: 0;
}
}
.cell {
position: relative;
overflow: visible;
&::before {
content: "";
position: absolute;
top: 12px;
left: -60px;
width: 50px;
height: 1px;
background-color: #DFE0E1;
}
}
&__first {
.cell {
&::before {
width: 30px;
left: -32px;
}
}
}
}
}
}