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

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章