在ElementUI-Tree,有個屬性可讓選項拖曳,預設是false的,可以讓選項用滑鼠拖曳改變位置

加上去後就可以讓選項自由移動位置,但因為目前最多只有三層選單,而如果在第三層將選項移動進去就會變成第四層,這是不被允許的,幸好ElementUI有提供一個函數allow-drop來阻止這樣的行為

可以看到這個Function(draggingNode, dropNode, type),有三個參數
draggingNode:目前正在拖曳的節點
dropNode:放置的節點
type:要放置節點裡面或前後位置

在<el-tree>標籤中加入屬性:allow-drop="allowDrop",:allow-drop="allowDrop"

methods加入此函數,然後裡面回傳false,拖曳時就是全部禁止,那這部分需要做些邏輯判斷

    allowDrop(draggingNode, dropNode, type) {
      return false;
    },

首先不能將節點變成第四層,被拖曳的節點和目前所在的節點總層數不能大於3,例如手機通訊這個節點有兩層(本身節點加上點開還有一層節點的意思),然後想要將手機拖曳到下一層的節點時是不允許的,因為這樣會變成四層節點

在方法中寫console.log先觀察一下參數的內容,當我要拖曳手機通訊中的對講機看會發生什麼事

    allowDrop(draggingNode, dropNode, type) {
      console.log("allowDrop", draggingNode, dropNode, type);
      return false;
    },

可以看到有prev、inner、next,在移動的過程中有出現的東西,第一個節點顯示的名稱是對講機,這是我拖曳的選項,然後下個節點是手機,這是我想放進去的節點,這時候要算出目前節點的總層數

假設目前拖曳的節點有子節點,則再繼續判斷是否有子節點,一直查到沒有子節點為止,這樣的邏輯可以使用遞迴來計算,假設最深的層級為3,而目前節點層級是2,公式就是(3-2)+1,總共為2,再加上父節點的層級,而父節點的層級可以在draggingNode中找到parnet中的level找到,只要這些合起來不大於3就可以完成拖曳

寫一個計算深度的方法countNodeLevel(node),傳進去的參數是(draggingNode.data)這就是目前拖曳的節點資料,先找出是否有子節點,而傳進來的參數(node)有一個屬性children,只要他長度大於0且不為空,就表示有子節點,有子節點就可以用for迴圈來判斷每一個節點中是否還有子節點

我現在拖曳「手機通訊」這個節點,可以看到Node > data (這個就是目前被拖曳的節點)而這個節點底下有個children也就是子節點,一共有三個(手機、對講機、手機通訊)然後catLevel表示分類的節點中為第三層了,將這個節點給定一個變數maxLevel(這個變數要另外再加在data中,預設需為節點的最深層級3,因為如果一開始就先選擇最裡面的節點,深度會變成負數),然後再查找其他節點時就再呼叫自身方法(countNodeLevel)

    countNodeLevel(node) {
      // 找到所有子節點,求出最大深度
      if (node.children != null && node.children.length > 0) {
        for (let i = 0; i < node.children.length; i++) {
          if (node.children[i].catLevel > this.maxLevel) {
            this.maxLevel = node.children[i].catLevel;
          }
          this.countNodeLevel(node.children[i]); // 記得加上this否則vue會找不到方法
        }
      }
    },
這樣找最深度的層級的方法大致上是這樣,這方法是會改變data中的maxLevel,所以先在data中加上去
maxLevel: 0,

然後一樣console.log顯示來看看是否正確

    allowDrop(draggingNode, dropNode, type) {
      console.log("allowDrop", draggingNode, dropNode, type);
      this.countNodeLevel(draggingNode.data);
      console.log("deep:", this.maxLevel);
      return false;
    },

當我不管拖曳哪個地方,都是計算出深度為3的總層數,這是最大的總層數,套用公式上必需要在判斷,最大總層數 – 目前拖曳節點的層數 + 1,只要不大於3,表示可以拖曳,所以再修改一下方法

    allowDrop(draggingNode, dropNode, type) {
      console.log("allowDrop", draggingNode, dropNode, type);
      this.countNodeLevel(draggingNode.data);
            // 目前正在拖曳的節點加上父節點所在的深度不能大於3
      let deep = this.maxLevel - draggingNode.data.catLevel + 1;
      console.log("深度:", deep);
      return false;
    },

當我拖曳對講機時,已經計算出此節點的深度層級是1,而拖曳手機通訊時,計算出的深度層級是2

接下來

判斷拖曳到目標的節點裡面(inner)時是否可以拖曳,也就是dropNode這個參數的data,裡面有個層級level,當我拖曳數位節點到家用電器節點裡面時,是不允許的,因為深度+層級會大於等於3,所以只能在家用電器的節點上下移動而已

而只要拖曳到目標節點的前或後時,要判斷目標節點的父節點是否層級大於等於3,一樣在dropNode中的parent父節點有個層級level,只要不大於等於3,就是允許拖曳

最後修改的allowDrop方法

    allowDrop(draggingNode, dropNode, type) {
      // 被拖曳的節點和目前所在的父節點總層數不能大於3
      console.log("allowDrop", draggingNode, dropNode, type);
      this.countNodeLevel(draggingNode.data);
      // 目前正在拖曳的節點加上父節點所在的深度不能大於3
      let deep = this.maxLevel - draggingNode.data.catLevel + 1;
      console.log("深度:", deep);
      if (type == "inner") {
        return deep + dropNode.level <= 3;
      } else {
        return deep + dropNode.parent.level <= 3;
      }
    },
讓方法返回true或false,這樣就完成了拖曳的效果