在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>

里面的左键函数,是这样的

 1   methods: {
2 leftClick(data, node, dom) {
3 let event = window.event || arguments.callee.caller.arguments[0];
4 var ctrlKeyDowned = event.ctrlKey;
5 var shiftKeyDowned = event.shiftKey;
6 // 走单击事件
7
8 var allTreeNode = this.$refs.treeRef.getNode(1);
9 this.clickTime = "";
10 if (ctrlKeyDowned == false && shiftKeyDowned == false) { // 都没有点击
11 this.cancelSelectTree(); // 取消原来的选中
12 this.leftTreeSelectedArr.splice(0);
13 this.leftTreeSelectedArr.push(data);
14 } else if (ctrlKeyDowned == true && shiftKeyDowned == false) { // 只点击ctrl
15 this.$set(data, "Selected", true);
16 var isIN = this.leftTreeSelectedArr.every(item => {
17 return item.id != data.id;
18 });
19 isIN && this.leftTreeSelectedArr.push(data);
20 if (!isIN) {
21 // 如果已经是选中的了,那么应该取消选择
22 data.Selected = false;
23 this.leftTreeSelectedArr.map((item, i) => {
24 if (item.id == data.id) {
25 this.leftTreeSelectedArr.splice(i, 1);
26 this.$refs.treeRef.setCurrentKey(); // 取消高亮,要不然区分不出来,是不是没有选中
27 }
28 });
29 }
30 } else if (ctrlKeyDowned == false && shiftKeyDowned == true) { // 只点击shift
31 this.delayeringArr.splice(0);
32 this.delayering([allTreeNode]); // 把现在展开的数据都扁平化
33 this.$set(data, "Selected", true);
34 this.leftTreeSelectedArr.push(data);
35 this.shiftTree(); // shifit多选
36 }
37 }
38 }

通过,第三行中的内容,获取到鼠标的点击事件属性,然后从中获取到是都点击了键盘的Ctrl和Shift;

  Ctrl多选就不用过多的介绍了,把点击树结构的内容, 通过去重判断,直接放在leftTreeSelectedArr中就可以了。这里就不做过多的介绍了。具体请看,14至30行代码。下面主要是讲解一下,shift多选。

  Shfit多选,在平常的列表中是很好实现的。我们可以把所有的数据,放在一个一维的数组中,那么任意选择其中的两项的话,就能把数组分割成为三部分。其中的中间部分,也就是第二部分就是Shift多选的结果。请看下面的草图

但是对于树结构的话,就稍微的麻烦一点了,树结构的数据是这样的。

那么他的真实的数据格式应该是这样的。

 1 treeData: [
2 {
3 id: 1,
4 name: "1节点",
5 childrenId: [
6 {
7 id: 2,
8 name: "2节点",
9 childrenId: [
10 {
11 id: 5,
12 name: "5节点",
13 childrenId: []
14 },
15 {
16 id: 6,
17 name: "6节点",
18 childrenId: []
19 }
20 ]
21 },
22 {
23 id: 3,
24 name: "3节点",
25 childrenId: [
26 {
27 id: 7,
28 name: "7节点",
29 childrenId: []
30 }
31 ]
32 },
33 {
34 id: 4,
35 name: "4节点",
36 childrenId: [
37 {
38 id: 8,
39 name: "8节点",
40 childrenId: []
41 },
42 {
43 id: 9,
44 name: "9节点",
45 childrenId: []
46 },
47 {
48 id: 10,
49 name: "10节点",
50 childrenId: [
51 {
52 id: 11,
53 name: "11节点",
54 childrenId: []
55 },
56 {
57 id: 12,
58 name: "12节点",
59 childrenId: []
60 }
61 ]
62 }
63 ]
64 }
65 ]
66 }
67 ]

那么树结构在页面上渲染完成之后就是这样的:

那shift多选是怎么判断的呢,怎么知道这个层级是属于哪个呢,怎么知道这个层级下面的内容需不需选中呢,如果展开了,就是应该选中的,如果没有展开是不是就不需要选中呢。所以的这些问题,如果思考下来的话, 确实比较复杂,如果遍历的话,也是很难的。任意选中两个之后,都不知道应该是向上查找遍历,还是向下查找遍历。所以遍历的话,是不可用的,或者说是不太容易实现的。

  回到问题的本质,在一维的数组,shif多选是很简单的。那么这个树形结构是不是也可以转换成一维的呢。按照这个思路,我们通过递归循环遍历,把这个数组转换成为一维的数组。请看下面的代码

 1     delayering(allTreeNode, pid) {
2 allTreeNode.map(item => {
3 this.delayeringArr.push({
4 id: item.data.id,
5 pid: pid ? pid : "null",
6 name: item.data.name
7 });
8 if (
9 item.hasOwnProperty("childNodes") &&
10 item.childNodes.length &&
11 item.expanded
12 ) { // 通过检查有没有子节点,并且查看是否展开,从而确定是否递归
13 this.delayering(item.childNodes, item.data.id);
14 }
15 });
16 },

调用的时候,则需要把所有的节点的数据都传过去。

1 this.delayeringArr.splice(0);
2 this.delayering([allTreeNode]); // 把现在展开的数据都扁平化

  调用delayering之后,就能把现在树结构中,已经展开的树结构,格式化成为一个一维的数组。请看下面的截图

当我们把树结构中的数据格式化成为一个一维的数组之后,我们就能判断了。那些是需要选中的。

 1     shiftTree() {
2 console.log("this.leftTreeSelectedArr", this.leftTreeSelectedArr);
3 console.log("this.delayeringArr", this.delayeringArr);
4 // 把第一个和最后一个当成是shift选择的
5 var nodeLength = this.leftTreeSelectedArr.length;
6 var startNode = this.leftTreeSelectedArr[0];
7 var startNodeId = startNode.id;
8 var endNode = this.leftTreeSelectedArr[nodeLength - 1];
9 var endNodeId = endNode.id;
10
11 // var startIndex = this.delayeringArr.filter((item,i)=>{
12 // return itemid == startNodeId;
13 // })
14 // var endIndex = this.delayeringArr.filter((item,i)=>{
15 // return itemid == endNodeId;
16 // })
17 var startIndex, endIndex;
18 this.delayeringArr.map((item, i) => {
19 if (item.id == startNodeId) {
20 startIndex = i;
21 }
22 if (item.id == endNodeId) {
23 endIndex = i;
24 }
25 });
26 if (startIndex > endIndex) {
27 var rongIdex = endIndex;
28 endIndex = startIndex;
29 startIndex = rongIdex;
30 }
31 console.log(startIndex, endIndex);
32 this.leftTreeSelectedArr.splice(0);
33 this.delayeringArr.map((item, i) => {
34 if (i >= startIndex && i <= endIndex) {
35 console.log("需要选中的name", item.name);
36 var node = this.$refs.treeRef.getNode(item.id);
37 this.$set(node.data, "Selected", true);
38 this.leftTreeSelectedArr.push(node.data);
39 }
40 });
41 console.log("this.leftTreeSelectedArr: ", this.leftTreeSelectedArr);
42 }

这个函数的主要目的就是,通过循环,找到对应的数据在扁平化处理之后数组数据中的位置。然后同理,就能找到需要选中的数据,通过设置Selected为true,则可以知道需要选中的节点。

  最后附上完成的代码, 包括其中的打印信息。(注意其中依赖Element的tree组件)

  1 <template>
2 <div id="MyVue">
3 <el-tree
4 ref="treeRef"
5 :data="treeData"
6 node-key="id"
7 :props="defaultProps"
8 @node-click="leftClick"
9 >
10 <span class="custom-tree-node" slot-scope="{ node, data }">
11 <span :class="data.Selected?'sel':''">{{ node.label }}</span>
12 </span>
13 </el-tree>
14 <div>扁平化数据:{{delayeringArr}}</div>
15 </div>
16 </template>
17 <script>
18 export default {
19 name: "MyVue",
20 data() {
21 return {
22 defaultProps: {
23 children: "childrenId",
24 label: "name"
25 },
26 treeData: [
27 {
28 id: 1,
29 name: "1节点",
30 childrenId: [
31 {
32 id: 2,
33 name: "2节点",
34 childrenId: [
35 {
36 id: 5,
37 name: "5节点",
38 childrenId: []
39 },
40 {
41 id: 6,
42 name: "6节点",
43 childrenId: []
44 }
45 ]
46 },
47 {
48 id: 3,
49 name: "3节点",
50 childrenId: [
51 {
52 id: 7,
53 name: "7节点",
54 childrenId: []
55 }
56 ]
57 },
58 {
59 id: 4,
60 name: "4节点",
61 childrenId: [
62 {
63 id: 8,
64 name: "8节点",
65 childrenId: []
66 },
67 {
68 id: 9,
69 name: "9节点",
70 childrenId: []
71 },
72 {
73 id: 10,
74 name: "10节点",
75 childrenId: [
76 {
77 id: 11,
78 name: "11节点",
79 childrenId: []
80 },
81 {
82 id: 12,
83 name: "12节点",
84 childrenId: []
85 }
86 ]
87 }
88 ]
89 }
90 ]
91 }
92 ],
93 delayeringArr: [], // 扁平化之后的数据
94 leftTreeSelectedArr: [] // 选中的数据
95 };
96 },
97 props: {},
98 mounted() {},
99 components: {},
100 computed: {},
101 methods: {
102 leftClick(data, node, dom) {
103 let event = window.event || arguments.callee.caller.arguments[0];
104 var ctrlKeyDowned = event.ctrlKey;
105 var shiftKeyDowned = event.shiftKey;
106
107 var allTreeNode = this.$refs.treeRef.getNode(1);
108 console.log("allTreeNode: ", allTreeNode);
109 if (ctrlKeyDowned == false && shiftKeyDowned == false) {
110 this.cancelSelectTree(); // 取消原来的选中
111 this.leftTreeSelectedArr.splice(0);
112 this.leftTreeSelectedArr.push(data);
113 } else if (ctrlKeyDowned == true && shiftKeyDowned == false) {
114 // this.leftTreeSelectedArr.splice(0);
115 // data.Selected = true;
116 this.$set(data, "Selected", true);
117 var isIN = this.leftTreeSelectedArr.every(item => {
118 return item.id != data.id;
119 });
120 isIN && this.leftTreeSelectedArr.push(data);
121 console.log("isIN: ", isIN);
122 if (!isIN) {
123 // 如果已经是选中的了,那么应该取消选择
124 data.Selected = false;
125 this.leftTreeSelectedArr.map((item, i) => {
126 if (item.id == data.id) {
127 this.leftTreeSelectedArr.splice(i, 1);
128 this.$refs.treeRef.setCurrentKey(); // 取消高亮,要不然区分不出来,是不是没有选中
129 }
130 });
131 }
132 console.log("this.leftTreeSelectedArr: ", this.leftTreeSelectedArr);
133 } else if (ctrlKeyDowned == false && shiftKeyDowned == true) {
134 this.delayeringArr.splice(0);
135 this.delayering([allTreeNode]); // 把现在展开的数据都扁平化
136 this.$set(data, "Selected", true);
137 this.leftTreeSelectedArr.push(data);
138 this.shiftTree(); // shifit多选
139 }
140 },
141 // 把所有的数据,进行扁平化处理
142 delayering(allTreeNode, pid) {
143 allTreeNode.map(item => {
144 this.delayeringArr.push({
145 id: item.data.id,
146 pid: pid ? pid : "null",
147 name: item.data.name
148 });
149 if (
150 item.hasOwnProperty("childNodes") &&
151 item.childNodes.length &&
152 item.expanded
153 ) {
154 // 通过检查有没有子节点,并且查看是否展开,从而确定是否递归
155 this.delayering(item.childNodes, item.data.id);
156 }
157 });
158 },
159 shiftTree() {
160 console.log("this.leftTreeSelectedArr", this.leftTreeSelectedArr);
161 console.log("this.delayeringArr", this.delayeringArr);
162 // 把第一个和最后一个当成是shift选择的
163 var nodeLength = this.leftTreeSelectedArr.length;
164 var startNode = this.leftTreeSelectedArr[0];
165 var startNodeId = startNode.id;
166 var endNode = this.leftTreeSelectedArr[nodeLength - 1];
167 var endNodeId = endNode.id;
168
169 // var startIndex = this.delayeringArr.filter((item,i)=>{
170 // return itemid == startNodeId;
171 // })
172 // var endIndex = this.delayeringArr.filter((item,i)=>{
173 // return itemid == endNodeId;
174 // })
175 var startIndex, endIndex;
176 this.delayeringArr.map((item, i) => {
177 if (item.id == startNodeId) {
178 startIndex = i;
179 }
180 if (item.id == endNodeId) {
181 endIndex = i;
182 }
183 });
184 if (startIndex > endIndex) {
185 var rongIdex = endIndex;
186 endIndex = startIndex;
187 startIndex = rongIdex;
188 }
189 console.log(startIndex, endIndex);
190 this.leftTreeSelectedArr.splice(0);
191 this.delayeringArr.map((item, i) => {
192 if (i >= startIndex && i <= endIndex) {
193 console.log("需要选中的name", item.name);
194 var node = this.$refs.treeRef.getNode(item.id);
195 this.$set(node.data, "Selected", true);
196 this.leftTreeSelectedArr.push(node.data);
197 }
198 });
199 console.log("this.leftTreeSelectedArr: ", this.leftTreeSelectedArr);
200 }
201 }
202 };
203 </script>
204 <style lang="scss" scoped>
205 #MyVue {
206 width: 100%;
207 height: 100%;
208 user-select: none;
209 .sel{
210 color: aqua;
211 }
212 }
213 </style>

Element中Tree树结构组件中实现Ctrl和Shift多选的更多相关文章

  1. 在html页,使用ctrl,shift多选表格行

    前段时间,项目中遇到这样一个需求.需要在页面中像windows资源管理器中一样可以使用ctrl和shift键来多选. <html> <head> <style> b ...

  2. element模态框dialog中的select组件中选中无反应无显示

    https://blog.csdn.net/PGguoqi/article/details/90240650 在vue里,当你对一个不存在的属性或对象直接“.”进行赋值,或者对数组不存在的索引项直接用 ...

  3. OAF中 遍历HGrid组件中的所有VO行

    在HGrid组件中有如下所示的HeaderVO和LineVO 需要在头上的LOV中触发事件去更新行VO中的值,LOV事件的处理方法见 getLovParameter ,但是由于HGrid的特殊性,不能 ...

  4. vue父组件中获取子组件中的数据

    <FormItem label="上传头像" prop="image"> <uploadImg :width="150" ...

  5. react:在一个组件中调用别的组件中的方法

    先介绍一下要解决的问题:react中一个组件A和一个组件B,其中B是被connect(connect是redux中的方法)包装过的组件,包装成BContainer,A和BContainer的关系是兄弟 ...

  6. element穿梭框el-transfer增加拖拽排序和shift多选checkbox功能

    <template> <div class="demo"> <el-transfer v-model="value" filter ...

  7. angular2的ElementRef在组件中获取不到

    angular2的ElementRef在组件中获取不到 angular2不推荐操作dom,但是实际应用中不可避免的需要使用到dom操作,怎么操作,官方文档提供了一系列api(ElementRef,Vi ...

  8. vue组件中使用iframe元素

    需要在本页面中展示vue组件中的超链接,地址栏不改变的方法: <template> <div class="accept-container"> <d ...

  9. vue项目中的父子组件之间的传值。

    首先说一下父子组件就是在一个vue文件中引入另一个vue文件,被引入vue文件就是子组件,引入vue文件的vue文件就是父组件.而在父组件中是不能直接调用子组件中的变量值的.下面详细说一下,父子组件之 ...

随机推荐

  1. ACM - 动态规划 - UVA323 Jury Compromise

    UVA323 Jury Compromise 题解 考虑用动态规划.该问题要求解的最终状态为,选出的 \(m\) 个人,使得辩方总分与控方总分差的绝对值最小,总分之和最大.即 \(\left| D(\ ...

  2. Linux系统下ifconfig命令使用及结果分析

    Linux下网卡命名规律:eth0,eth1.第一块以太网卡,第二块.lo为环回接口,它的IP地址固定为127.0.0.1,掩码8位.它代表你的机器本身. 1.ifconfig是查看网卡的信息. if ...

  3. 3_Phase Portrait_相图_相轨迹

  4. element el-tree、el-table组件加载数据前闪现 暂无数据 清除

    相信很多人在使用element  el-tree.el-table组件加载数据前会显示一个" 暂无数据 ",体验很不友好,有没有办法处理不显示呢?答案是:有的.废话不多说直接上代码 ...

  5. [翻译]Service workers:PWA背后的英雄

    原文地址:https://medium.freecodecamp.org/service-workers-the-little-heroes-behind-progressive-web-apps-4 ...

  6. Android 动态控制OptionMenu的显示与隐藏

    在有些场景下,可能需要动态的显示和隐藏optionmenu,可以这样实现:如果在activity中默认实现了方法: onCreateOptionsMenu(Menu menu) 那么该OptionMe ...

  7. Android的Activity屏幕切换动画左右滑动切换

    在Android开发过程中,经常会碰到Activity之间的切换效果的问题,下面介绍一下如何实现左右滑动的切换效果,首先了解一下Activity切换的实现,从Android2.0开始在Activity ...

  8. 利用Docker快速部署Mysql

    写在前面 我又来更新了~~~,今天内容较少,主要是利用Docker快速部署Mysql和初始化数据 利用Docker下载Mysql 简洁明了,在命令提示符中输入 docker pull mysql:8. ...

  9. 使用基于Roslyn的编译时AOP框架来解决.NET项目的代码复用问题

    理想的代码优化方式 团队日常协作中,自然而然的会出现很多重复代码,根据这些代码的种类,之前可能会以以下方式处理 方式 描述 应用时可能产生的问题 硬编码 多数新手,或逐渐腐坏的项目会这么干,会直接复制 ...

  10. 检查是否安装ASM

    ASM和管理 ASM是一个有效的抽象层,使Oracle数据库可以与叫做DiskGroups的抽象空间一起使用,而不是直接使用DataFiles. Oracle ASM脱离操作系统的文件系统约束,使得对 ...