Element中Tree树结构组件中实现Ctrl和Shift多选

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

 

posted @ 2022-05-19 14:05  程先生哈  阅读(743)  评论(3编辑  收藏  举报