Element UI 之 Table 树形数据合并行的实现
element
A Vue.js 2.0 UI Toolkit for Web
项目地址:https://gitcode.com/gh_mirrors/eleme/element
免费下载资源
·
一、需求说明
Table 内容为树形结构,但需要合并收费项目重复列,具有子项的项目可展开和收起。如图:
二、遇到问题
1、表格数据格式
[{
id: 1,
name: '篮网',
item: '投篮(%)',
value: Number.parseInt(Math.random() * 100),
children: [{
id: 311,
name: '詹姆斯·哈登',
item: '投篮(%)',
value: Number.parseInt(Math.random() * 100)
}, {
id: 312,
name: '詹姆斯·哈登',
item: '三分(%)',
value: Number.parseInt(Math.random() * 100)
}, {
id: 313,
name: '詹姆斯·哈登',
item: '罚球(%)',
value: Number.parseInt(Math.random() * 100)
}]
}, {
id: 2,
name: '篮网',
item: '三分(%)',
value: Number.parseInt(Math.random() * 100),
children: [{
id: 321,
name: '詹姆斯·哈登',
item: '投篮(%)',
value: Number.parseInt(Math.random() * 100)
}, {
id: 322,
name: '詹姆斯·哈登',
item: '三分(%)',
value: Number.parseInt(Math.random() * 100)
}, {
id: 323,
name: '詹姆斯·哈登',
item: '罚球(%)',
value: Number.parseInt(Math.random() * 100)
}]
}, {
id: 3,
name: '篮网',
item: '罚球(%)',
value: Number.parseInt(Math.random() * 100),
children: [{
id: 31,
name: '詹姆斯·哈登',
item: '投篮(%)',
value: Number.parseInt(Math.random() * 100)
}, {
id: 32,
name: '詹姆斯·哈登',
item: '三分(%)',
value: Number.parseInt(Math.random() * 100)
}, {
id: 33,
name: '詹姆斯·哈登',
item: '罚球(%)',
value: Number.parseInt(Math.random() * 100)
}]
}]
2、表格未合并列前
3、期待合并效果
4、实际合并效果(注意观察项目列是否正确)
5、造成原因
通过打印第一列渲染内容,发现表格树形结构项无论是否展开、是父节点还是子节点,都是顺序执行渲染。这样,Table 标签的 rowspan 或者 colspan 属性所执行的合并行或合并列,合并机制都是按照渲染内容顺序来执行的。则没法做到我们想要的先合并父节点,再合并子节点。
三、解决思路
1、为达到先合并父节点,再合并子节点效果,可将上文提供的数据转为以下数据格式(相同父节点列表中最后一项保留子节点数据)。
[{
id: 1,
name: '篮网',
item: '投篮(%)',
value: Number.parseInt(Math.random() * 100)
}, {
id: 2,
name: '篮网',
item: '三分(%)',
value: Number.parseInt(Math.random() * 100)
}, {
id: 3,
name: '篮网',
item: '罚球(%)',
value: Number.parseInt(Math.random() * 100),
children: [{
id: 31,
name: '詹姆斯·哈登',
item: '投篮(%)',
value: Number.parseInt(Math.random() * 100)
}, {
id: 32,
name: '詹姆斯·哈登',
item: '三分(%)',
value: Number.parseInt(Math.random() * 100)
}, {
id: 33,
name: '詹姆斯·哈登',
item: '罚球(%)',
value: Number.parseInt(Math.random() * 100)
}]
}]
2、可这样一来,发现表格无法展开。但若添加 default-expand-all
则能看到展开项,但依然无法切换树形表格是否展开与收起,造成原因是因为父节点列表中第一项没有 children 数据。
3、所幸的是,Element UI 给我们提供了 toggleRowExpansion
方法,我们可以通过给数据打标识和调用 toggleRowExpansion
方法来实现树形表格的展开与收起。
四、Demo 演示及代码
在真正的业务中,后端给前端的数据更可能是具有相关标识的扁平化数据。本文代码中,也是从扁平化数组整合为符合操作需求的树形结构数据。
// mock.js
export const originTableData = [
{ id: 1, parentId: 0, name: '篮网', item: '投篮(%)', value: Number.parseInt(Math.random() * 100) },
{ id: 2, parentId: 0, name: '篮网', item: '三分(%)', value: Number.parseInt(Math.random() * 100) },
{ id: 3, parentId: 0, name: '篮网', item: '罚球(%)', value: Number.parseInt(Math.random() * 100) },
{ id: 31, parentId: 3, name: '詹姆斯·哈登', item: '投篮(%)', value: Number.parseInt(Math.random() * 100) },
{ id: 32, parentId: 3, name: '詹姆斯·哈登', item: '三分(%)', value: Number.parseInt(Math.random() * 100) },
{ id: 33, parentId: 3, name: '詹姆斯·哈登', item: '罚球(%)', value: Number.parseInt(Math.random() * 100) },
{ id: 34, parentId: 3, name: '凯文·杜兰特', item: '投篮(%)', value: Number.parseInt(Math.random() * 100) },
{ id: 35, parentId: 3, name: '凯文·杜兰特', item: '三分(%)', value: Number.parseInt(Math.random() * 100) },
{ id: 36, parentId: 3, name: '凯文·杜兰特', item: '罚球(%)', value: Number.parseInt(Math.random() * 100) },
{ id: 37, parentId: 3, name: '凯里·欧文', item: '投篮(%)', value: Number.parseInt(Math.random() * 100) },
{ id: 38, parentId: 3, name: '凯里·欧文', item: '三分(%)', value: Number.parseInt(Math.random() * 100) },
{ id: 39, parentId: 3, name: '凯里·欧文', item: '罚球(%)', value: Number.parseInt(Math.random() * 100) },
{ id: 4, parentId: 0, name: '湖人', item: '投篮(%)', value: Number.parseInt(Math.random() * 100) },
{ id: 5, parentId: 0, name: '湖人', item: '三分(%)', value: Number.parseInt(Math.random() * 100) },
{ id: 6, parentId: 0, name: '湖人', item: '罚球(%)', value: Number.parseInt(Math.random() * 100) },
{ id: 61, parentId: 6, name: '勒布朗·詹姆斯', item: '投篮(%)', value: Number.parseInt(Math.random() * 100) },
{ id: 62, parentId: 6, name: '勒布朗·詹姆斯', item: '三分(%)', value: Number.parseInt(Math.random() * 100) },
{ id: 63, parentId: 6, name: '勒布朗·詹姆斯', item: '罚球(%)', value: Number.parseInt(Math.random() * 100) },
{ id: 64, parentId: 6, name: '安东尼·戴维斯', item: '投篮(%)', value: Number.parseInt(Math.random() * 100) },
{ id: 65, parentId: 6, name: '安东尼·戴维斯', item: '三分(%)', value: Number.parseInt(Math.random() * 100) },
{ id: 66, parentId: 6, name: '安东尼·戴维斯', item: '罚球(%)', value: Number.parseInt(Math.random() * 100) },
{ id: 67, parentId: 6, name: '亚历克斯·卡鲁索', item: '投篮(%)', value: Number.parseInt(Math.random() * 100) },
{ id: 68, parentId: 6, name: '亚历克斯·卡鲁索', item: '三分(%)', value: Number.parseInt(Math.random() * 100) },
{ id: 69, parentId: 6, name: '亚历克斯·卡鲁索', item: '罚球(%)', value: Number.parseInt(Math.random() * 100) },
{ id: 7, parentId: 0, name: '快船', item: '投篮(%)', value: Number.parseInt(Math.random() * 100) },
{ id: 8, parentId: 0, name: '快船', item: '三分(%)', value: Number.parseInt(Math.random() * 100) },
{ id: 9, parentId: 0, name: '快船', item: '罚球(%)', value: Number.parseInt(Math.random() * 100) },
{ id: 91, parentId: 9, name: '保罗·乔治', item: '投篮(%)', value: Number.parseInt(Math.random() * 100) },
{ id: 92, parentId: 9, name: '保罗·乔治', item: '三分(%)', value: Number.parseInt(Math.random() * 100) },
{ id: 93, parentId: 9, name: '保罗·乔治', item: '罚球(%)', value: Number.parseInt(Math.random() * 100) },
{ id: 94, parentId: 9, name: '科怀·伦纳德', item: '投篮(%)', value: Number.parseInt(Math.random() * 100) },
{ id: 95, parentId: 9, name: '科怀·伦纳德', item: '三分(%)', value: Number.parseInt(Math.random() * 100) },
{ id: 96, parentId: 9, name: '科怀·伦纳德', item: '罚球(%)', value: Number.parseInt(Math.random() * 100) }
]
<template>
<el-table
ref="table"
border
row-key="id"
:data="formatTableData"
:indent="30"
:span-method="objectSpanMethod"
style="width: 700px;"
>
<el-table-column
prop="name"
label="球队"
width="200"
>
<template v-slot="{ row }">
<div class="cell-content">
<span>{{ row.name }}</span>
<span class="arrow-icon" @click="toggleRowExpansion(row)">
<i
v-if="checkHasChildren(row)"
:class="row.isExpand ? 'el-icon-caret-top' : 'el-icon-caret-bottom'"
/>
</span>
</div>
</template>
</el-table-column>
<el-table-column
prop="item"
label="项目"
width="180"
align="center"
/>
<el-table-column
prop="value"
label="数据"
align="center"
/>
</el-table>
</template>
<script>
import { originTableData } from '../mock'
export default {
name: 'ExampleTable',
data () {
return {
originTableData
}
},
computed: {
formatTableData () {
const data = []
this.originTableData.forEach(originItem => {
const { id, parentId, name, item, value } = originItem
const children = this.originTableData.filter(item => item.parentId === id)
if (parentId === 0) {
data.push({
id,
name,
item,
value,
children,
isExpand: false
})
}
})
return data
}
},
methods: {
checkHasChildren (row) {
return this.formatTableData.filter(item => item.name === row.name)
.some(item => item.children.length > 0)
},
toggleRowExpansion (row) {
row.isExpand = !row.isExpand // 此行代码有副作用,但目前只想到这样实现
const rowList = this.originTableData.filter(item => item.name === row.name)
const expansionRow = rowList[rowList.length - 1]
this.$refs.table && this.$refs.table.toggleRowExpansion(expansionRow, row.isExpand)
},
objectSpanMethod ({ rowIndex, columnIndex }) {
if (columnIndex === 0) {
if (rowIndex % 3 === 0) {
return {
rowspan: 3,
colspan: 1
}
} else {
return {
rowspan: 0,
colspan: 0
}
}
}
}
}
}
</script>
<style lang="scss" scoped>
.cell-content {
display: flex;
justify-content: space-between;
.arrow-icon {
cursor: pointer;
}
}
/deep/ .el-table__row.el-table__row--level-1 > td:first-child .cell .cell-content {
padding-left: 30px;
}
</style>
GitHub 加速计划 / eleme / element
54.06 K
14.63 K
下载
A Vue.js 2.0 UI Toolkit for Web
最近提交(Master分支:3 个月前 )
c345bb45
7 个月前
a07f3a59
* Update transition.md
* Update table.md
* Update transition.md
* Update table.md
* Update transition.md
* Update table.md
* Update table.md
* Update transition.md
* Update popover.md 7 个月前
更多推荐
已为社区贡献2条内容
所有评论(0)