在Element中的树结构中, 实现多选功能,首先的是判断有没有按下键盘ctrl和shift按键。但是在Element中的tree组件的左键点击事件是没有提供$event鼠标属性判断的。所以就需要在函数中使用自身的$event来判断。请看树结构下面左键和右键点击的函数传参的截图。
所以,左键的点击函数,需要自行判断。如下代码示例
<el-tree
class="filter-tree"
:load="loadNode"
lazy
:props="defaultProps"
:filter-node-method="filterNode"
:render-content="renderContent"
ref="treeRef"
:expand-on-click-node="false"
@node-contextmenu="rightClick"
@node-click="leftClick" // 左键点击事件
:highlight-current="true"
node-key="id"
:check-on-click-node="true"
:show-checkbox="false"
check-strictly
></el-tree>
里面的左键函数,是这样的
methods: {
leftClick(data, node, dom) {
let event = window.event || arguments.callee.caller.arguments[0];
var ctrlKeyDowned = event.ctrlKey;
var shiftKeyDowned = event.shiftKey;
// 走单击事件
var allTreeNode = this.$refs.treeRef.getNode(1);
this.clickTime = "";
if (ctrlKeyDowned == false && shiftKeyDowned == false) { // 都没有点击
this.cancelSelectTree(); // 取消原来的选中
this.leftTreeSelectedArr.splice(0);
this.leftTreeSelectedArr.push(data);
} else if (ctrlKeyDowned == true && shiftKeyDowned == false) { // 只点击ctrl
this.$set(data, "Selected", true);
var isIN = this.leftTreeSelectedArr.every(item => {
return item.id != data.id;
});
isIN && this.leftTreeSelectedArr.push(data);
if (!isIN) {
// 如果已经是选中的了,那么应该取消选择
data.Selected = false;
this.leftTreeSelectedArr.map((item, i) => {
if (item.id == data.id) {
this.leftTreeSelectedArr.splice(i, 1);
this.$refs.treeRef.setCurrentKey(); // 取消高亮,要不然区分不出来,是不是没有选中
}
});
}
} else if (ctrlKeyDowned == false && shiftKeyDowned == true) { // 只点击shift
this.delayeringArr.splice(0);
this.delayering([allTreeNode]); // 把现在展开的数据都扁平化
this.$set(data, "Selected", true);
this.leftTreeSelectedArr.push(data);
this.shiftTree(); // shifit多选
}
}
}
通过,第三行中的内容,获取到鼠标的点击事件属性,然后从中获取到是都点击了键盘的Ctrl和Shift;
Ctrl多选就不用过多的介绍了,把点击树结构的内容, 通过去重判断,直接放在leftTreeSelectedArr中就可以了。这里就不做过多的介绍了。具体请看,14至30行代码。下面主要是讲解一下,shift多选。
Shfit多选,在平常的列表中是很好实现的。我们可以把所有的数据,放在一个一维的数组中,那么任意选择其中的两项的话,就能把数组分割成为三部分。其中的中间部分,也就是第二部分就是Shift多选的结果。请看下面的草图
但是对于树结构的话,就稍微的麻烦一点了,树结构的数据是这样的。
那么他的真实的数据格式应该是这样的。
treeData: [
{
id: 1,
name: "1节点",
childrenId: [
{
id: 2,
name: "2节点",
childrenId: [
{
id: 5,
name: "5节点",
childrenId: []
},
{
id: 6,
name: "6节点",
childrenId: []
}
]
},
{
id: 3,
name: "3节点",
childrenId: [
{
id: 7,
name: "7节点",
childrenId: []
}
]
},
{
id: 4,
name: "4节点",
childrenId: [
{
id: 8,
name: "8节点",
childrenId: []
},
{
id: 9,
name: "9节点",
childrenId: []
},
{
id: 10,
name: "10节点",
childrenId: [
{
id: 11,
name: "11节点",
childrenId: []
},
{
id: 12,
name: "12节点",
childrenId: []
}
]
}
]
}
]
}
]
那么树结构在页面上渲染完成之后就是这样的:
那shift多选是怎么判断的呢,怎么知道这个层级是属于哪个呢,怎么知道这个层级下面的内容需不需选中呢,如果展开了,就是应该选中的,如果没有展开是不是就不需要选中呢。所以的这些问题,如果思考下来的话, 确实比较复杂,如果遍历的话,也是很难的。任意选中两个之后,都不知道应该是向上查找遍历,还是向下查找遍历。所以遍历的话,是不可用的,或者说是不太容易实现的。
回到问题的本质,在一维的数组,shif多选是很简单的。那么这个树形结构是不是也可以转换成一维的呢。按照这个思路,我们通过递归循环遍历,把这个数组转换成为一维的数组。请看下面的代码
delayering(allTreeNode, pid) {
allTreeNode.map(item => {
this.delayeringArr.push({
id: item.data.id,
pid: pid ? pid : "null",
name: item.data.name
});
if (
item.hasOwnProperty("childNodes") &&
item.childNodes.length &&
item.expanded
) { // 通过检查有没有子节点,并且查看是否展开,从而确定是否递归
this.delayering(item.childNodes, item.data.id);
}
});
},
调用的时候,则需要把所有的节点的数据都传过去。
this.delayeringArr.splice(0);
this.delayering([allTreeNode]); // 把现在展开的数据都扁平化
调用delayering之后,就能把现在树结构中,已经展开的树结构,格式化成为一个一维的数组。请看下面的截图
当我们把树结构中的数据格式化成为一个一维的数组之后,我们就能判断了。那些是需要选中的。
shiftTree() {
console.log("this.leftTreeSelectedArr", this.leftTreeSelectedArr);
console.log("this.delayeringArr", this.delayeringArr);
// 把第一个和最后一个当成是shift选择的
var nodeLength = this.leftTreeSelectedArr.length;
var startNode = this.leftTreeSelectedArr[0];
var startNodeId = startNode.id;
var endNode = this.leftTreeSelectedArr[nodeLength - 1];
var endNodeId = endNode.id;
// var startIndex = this.delayeringArr.filter((item,i)=>{
// return itemid == startNodeId;
// })
// var endIndex = this.delayeringArr.filter((item,i)=>{
// return itemid == endNodeId;
// })
var startIndex, endIndex;
this.delayeringArr.map((item, i) => {
if (item.id == startNodeId) {
startIndex = i;
}
if (item.id == endNodeId) {
endIndex = i;
}
});
if (startIndex > endIndex) {
var rongIdex = endIndex;
endIndex = startIndex;
startIndex = rongIdex;
}
console.log(startIndex, endIndex);
this.leftTreeSelectedArr.splice(0);
this.delayeringArr.map((item, i) => {
if (i >= startIndex && i <= endIndex) {
console.log("需要选中的name", item.name);
var node = this.$refs.treeRef.getNode(item.id);
this.$set(node.data, "Selected", true);
this.leftTreeSelectedArr.push(node.data);
}
});
console.log("this.leftTreeSelectedArr: ", this.leftTreeSelectedArr);
}
这个函数的主要目的就是,通过循环,找到对应的数据在扁平化处理之后数组数据中的位置。然后同理,就能找到需要选中的数据,通过设置Selected为true,则可以知道需要选中的节点。
最后附上完成的代码, 包括其中的打印信息。(注意其中依赖Element的tree组件)
<template>
<div id="MyVue">
<el-tree
ref="treeRef"
:data="treeData"
node-key="id"
:props="defaultProps"
@node-click="leftClick"
>
<span class="custom-tree-node" slot-scope="{ node, data }">
<span :class="data.Selected?'sel':''">{{ node.label }}</span>
</span>
</el-tree>
<div>扁平化数据:{{delayeringArr}}</div>
</div>
</template>
<script>
export default {
name: "MyVue",
data() {
return {
defaultProps: {
children: "childrenId",
label: "name"
},
treeData: [
{
id: 1,
name: "1节点",
childrenId: [
{
id: 2,
name: "2节点",
childrenId: [
{
id: 5,
name: "5节点",
childrenId: []
},
{
id: 6,
name: "6节点",
childrenId: []
}
]
},
{
id: 3,
name: "3节点",
childrenId: [
{
id: 7,
name: "7节点",
childrenId: []
}
]
},
{
id: 4,
name: "4节点",
childrenId: [
{
id: 8,
name: "8节点",
childrenId: []
},
{
id: 9,
name: "9节点",
childrenId: []
},
{
id: 10,
name: "10节点",
childrenId: [
{
id: 11,
name: "11节点",
childrenId: []
},
{
id: 12,
name: "12节点",
childrenId: []
}
]
}
]
}
]
}
],
delayeringArr: [], // 扁平化之后的数据
leftTreeSelectedArr: [] // 选中的数据
};
},
props: {},
mounted() {},
components: {},
computed: {},
methods: {
leftClick(data, node, dom) {
let event = window.event || arguments.callee.caller.arguments[0];
var ctrlKeyDowned = event.ctrlKey;
var shiftKeyDowned = event.shiftKey;
var allTreeNode = this.$refs.treeRef.getNode(1);
console.log("allTreeNode: ", allTreeNode);
if (ctrlKeyDowned == false && shiftKeyDowned == false) {
this.cancelSelectTree(); // 取消原来的选中
this.leftTreeSelectedArr.splice(0);
this.leftTreeSelectedArr.push(data);
} else if (ctrlKeyDowned == true && shiftKeyDowned == false) {
// this.leftTreeSelectedArr.splice(0);
// data.Selected = true;
this.$set(data, "Selected", true);
var isIN = this.leftTreeSelectedArr.every(item => {
return item.id != data.id;
});
isIN && this.leftTreeSelectedArr.push(data);
console.log("isIN: ", isIN);
if (!isIN) {
// 如果已经是选中的了,那么应该取消选择
data.Selected = false;
this.leftTreeSelectedArr.map((item, i) => {
if (item.id == data.id) {
this.leftTreeSelectedArr.splice(i, 1);
this.$refs.treeRef.setCurrentKey(); // 取消高亮,要不然区分不出来,是不是没有选中
}
});
}
console.log("this.leftTreeSelectedArr: ", this.leftTreeSelectedArr);
} else if (ctrlKeyDowned == false && shiftKeyDowned == true) {
this.delayeringArr.splice(0);
this.delayering([allTreeNode]); // 把现在展开的数据都扁平化
this.$set(data, "Selected", true);
this.leftTreeSelectedArr.push(data);
this.shiftTree(); // shifit多选
}
},
// 把所有的数据,进行扁平化处理
delayering(allTreeNode, pid) {
allTreeNode.map(item => {
this.delayeringArr.push({
id: item.data.id,
pid: pid ? pid : "null",
name: item.data.name
});
if (
item.hasOwnProperty("childNodes") &&
item.childNodes.length &&
item.expanded
) {
// 通过检查有没有子节点,并且查看是否展开,从而确定是否递归
this.delayering(item.childNodes, item.data.id);
}
});
},
shiftTree() {
console.log("this.leftTreeSelectedArr", this.leftTreeSelectedArr);
console.log("this.delayeringArr", this.delayeringArr);
// 把第一个和最后一个当成是shift选择的
var nodeLength = this.leftTreeSelectedArr.length;
var startNode = this.leftTreeSelectedArr[0];
var startNodeId = startNode.id;
var endNode = this.leftTreeSelectedArr[nodeLength - 1];
var endNodeId = endNode.id;
// var startIndex = this.delayeringArr.filter((item,i)=>{
// return itemid == startNodeId;
// })
// var endIndex = this.delayeringArr.filter((item,i)=>{
// return itemid == endNodeId;
// })
var startIndex, endIndex;
this.delayeringArr.map((item, i) => {
if (item.id == startNodeId) {
startIndex = i;
}
if (item.id == endNodeId) {
endIndex = i;
}
});
if (startIndex > endIndex) {
var rongIdex = endIndex;
endIndex = startIndex;
startIndex = rongIdex;
}
console.log(startIndex, endIndex);
this.leftTreeSelectedArr.splice(0);
this.delayeringArr.map((item, i) => {
if (i >= startIndex && i <= endIndex) {
console.log("需要选中的name", item.name);
var node = this.$refs.treeRef.getNode(item.id);
this.$set(node.data, "Selected", true);
this.leftTreeSelectedArr.push(node.data);
}
});
console.log("this.leftTreeSelectedArr: ", this.leftTreeSelectedArr);
}
}
};
</script>
<style lang="scss" scoped>
#MyVue {
width: 100%;
height: 100%;
user-select: none;
.sel{
color: aqua;
}
}
</style>