ElementuUI el-table 树型表格 checkbox多选框选择逻辑
如图所示:
逻辑:
子节点全部选择 => 父节点选择
子节点部分选择 => 父节点半选择
子节点全未选择 => 父节点不选择
代码部分如下:
<template>
<div class="container">
<el-table
:data="tableData"
border
style="width: 100%"
row-key="id"
default-expand-all
:tree-props="{ children: 'children' ,hasChildren: 'children.length>0'}"
ref="tableRef"
@select="handletableSelect"
@select-all="handletableSelectAll"
@selection-change="handletableSelectionChange">
<!-- 多选框列 -->
<el-table-column
type="selection"/>
<!--
id列 注意:该列很重要,必须是row-key 若不需要展示该列,
则需要通过其他手段在行内标记row-key并自行修改
updateCheckboxIndeterminate方法里的逻辑
下边“半选择”状态需要用到
-->
<el-table-column
prop="id"
label="id"
></el-table-column>
<!-- ...其他字段 -->
</el-table>
</div>
</template>
<script>
export default {
name: "TreeTableCheckbox",
data() {
return {
tableData: [
{
id: 1,
date: '2016-05-02',
name: '王小虎',
address: '上海市普陀区金沙江路 1518 弄'
}, {
id: 2,
date: '2016-05-04',
name: '王小虎',
address: '上海市普陀区金沙江路 1517 弄'
}, {
id: 3,
date: '2016-05-01',
name: '王小虎',
address: '上海市普陀区金沙江路 1519 弄',
children: [
{
id: 31,
date: '2016-05-01',
name: '王小虎',
address: '上海市普陀区金沙江路 1519 弄'
}, {
id: 32,
date: '2016-05-01',
name: '王小虎',
address: '上海市普陀区金沙江路 1519 弄'
}
]
}, {
id: 4,
date: '2016-05-03',
name: '王小虎',
address: '上海市普陀区金沙江路 1516 弄'
}
]
tableSelect: [],
}
},
methods: {
// 当用户手动勾选数据行的 Checkbox 时触发的事件
handletableSelect(selection, row) {
if (row.children) { //只对有子节点的行响应
// if (row.isChecked === undefined) row.isChecked = true
if (!row.isChecked) { //由行数据中的元素isChecked判断当前是否被选中
this.traverseChildNodes(row.children, this.$refs.tableRef, true)
row.isChecked = true; //当前行isChecked标志元素切换为false
} else {
this.traverseChildNodes(row.children, this.$refs.tableRef, false)
row.isChecked = false;
}
}
// 处理父级选择逻辑
// tableSelect 字段是 handletableSelectionChange 回调回来的
// 如果不用 this.$nextTick 无法获取到正确数据
this.$nextTick(() => {
// 递归寻找当前子节点的父级节点
let getParentNode = function (data, id) {
for (let i in data) {
if (data.hasOwnProperty(i)) {
if (data[i].id === id) {
return [data[i]]
}
if (data[i].children) {
let node = getParentNode(data[i].children, id);
if (node !== undefined) {
return node.concat(data[i])
}
}
}
}
}
// 设置checkbox半选择状态
// 此处的传入的id就是匹配表格id字段的
let updateCheckboxIndeterminate = function (id, isIndeterminate) {
setTimeout(() => {
// el-table不支持checkbox半选 目前方案是遍历dom获取行节点设置样式
let elementsRow = document.getElementsByClassName("el-table__row");
for (let i = 0; i < elementsRow.length; i++) {
let element = elementsRow.item(i);
// tips: 通过其他手段设置行内row-id的 以下代码需要修改
let childNode = element.childNodes.item(1); // row-key 字段在dom中的索引 第二列=1
if (childNode.innerText === `${id}`) {
let td = element.childNodes.item(0) // 获取要设置半选中状态的checkbox
let div = td.firstChild; // <div class="cell">
let label = div.firstChild; // <label class="el-checkbox">
let span = label.firstChild; // <span class="el-checkbox__input">
if (isIndeterminate)
span.classList.add("is-indeterminate") // 设置半选中状态
else
span.classList.remove("is-indeterminate") // 取消半选中状态
}
}
}, 1)
}
let parentNode = getParentNode(this.$refs.tableRef.data, row.id)
// > 1 说明节点有子节点
if (parentNode.length > 1) {
// 提取兄弟节点ids
let siblingNodeIds = parentNode[1].children.map(item => item.id)
// 提取全选所选择项的ids
let checkedIds = this.tableSelect.map(item => item.id)
// 获取兄弟行选择数
let siblingNodeCheckedIds = siblingNodeIds.filter(id => checkedIds.indexOf(id) !== -1)
if (siblingNodeCheckedIds.length === siblingNodeIds.length) {
// 兄弟节点选择数===兄弟节点数 全选
updateCheckboxIndeterminate(parentNode[1].id, false)
parentNode[1].isChecked = true
this.$refs.tableRef.toggleRowSelection(parentNode[1], true)
} else if (siblingNodeCheckedIds.length === 0) {
// 兄弟节点选择数===0 全不选
parentNode[1].isChecked = false
this.$refs.tableRef.toggleRowSelection(parentNode[1], false)
updateCheckboxIndeterminate(parentNode[1].id, false)
} else if (siblingNodeCheckedIds.length < siblingNodeIds.length) {
parentNode[1].isChecked = true
this.$refs.tableRef.toggleRowSelection(parentNode[1], true)
for (let i = 0; i < parentNode.length; i++) {
if (i > 0) updateCheckboxIndeterminate(parentNode[i].id, true)
}
}
}
})
},
// 当用户手动勾选全选 Checkbox 时触发的事件
handletableSelectAll(selection) {
this.$refs.tableRef.data.map(items => {
if (items.children && items.children.length > 0) {
if (!items.isChecked) {
this.$refs.tableRef.toggleRowSelection(items, true);
items.isChecked = true
this.traverseChildNodes(items.children, this.$refs.tableRef, true)
} else {
this.$refs.tableRef.toggleRowSelection(items, false);
items.isChecked = false;
this.traverseChildNodes(items.children, this.$refs.tableRef, false)
}
} else {
items.isChecked = !items.isChecked;
}
})
},
// 当选择项发生变化时会触发该事件
handletableSelectionChange(selection) {
this.tableSelect = selection
},
// 递归设置子节点
traverseChildNodes(children, ref, selected) {
children.map(item => {
ref.toggleRowSelection(item, selected);
item.isChecked = selected;
if (item.children) {
this.traverseChildNodes(item.children, ref, selected)
}
})
},
},
}
</script>
<style scoped>
</style>
可能有更好的实现逻辑,欢迎交流。