iview節(jié)點樹添加拖拽功能

懶加載-搜索節(jié)點-新增節(jié)點-刪除節(jié)點
因為是手打,可能會有一些地方打錯,會盡可能檢查無誤
拖拽相關有備注出來,相關功能基本參考element-ui節(jié)點樹拖拽
<template>
  <div class="menuBox">
    <div id="treeBox">
      <p class="tilte">菜單列表</p>
      <div class="button-box-list search">
        <Input v-model="inputVal">
        <div>
          <Button  type="primary" @click="search(inputVal)">
             <span class="hzbankicon hzbankicon-btn-searc"></span>
             <span>搜索</span>
          </Button>
        </div>
      </div>
      <Tree :data="treeData" ref="menuTree" :load-data="loadData" :render="renderContent"></Tree>
    </div>
    <div id="formBox">
      <p>菜單詳情</p>
      <Form ref="menuData" :model="menuData" label-position="right" inline :label-width="126" :rules="ruleValidate">
        <FormItem label="菜單編號" prop="menuCode">
          <Input v-model="menuData.menuCode">
        </FormItem>
        <FormItem label="父節(jié)點編號" prop="parentId">
          <Input v-model="menuData.parentId">
        </FormItem>
        <FormItem label="菜單名稱" prop="menuName">
          <Input v-model="menuData.menuName">
        </FormItem>
        <FormItem label="菜單狀態(tài)" prop="state">
          <HZSelect class="selectClass" data-key="menuState" :dataArr="menuState" 
            data-url="..." :selectValue.sync="menuData.state.code"></HZSelect>
        </FormItem>
        <FormItem label="菜單描述" prop="remark">
          <Input v-model="menuData.remark">
        </FormItem>
        <FormItem label="菜單屬性" prop="menuAttr">
          <HZSelect class="selectClass" data-key="menuAttr" :dataArr="menuAttr" 
            data-url="..." :selectValue.sync="menuData.menuAttr.code"></HZSelect>
        </FormItem>
        <FormItem label="菜單URL" prop="menuUrl">
          <Input v-model="menuData.menuUrl">
        </FormItem>
      </Form>
      <p class="submitBox">
        <Button type="primary" class="mr-5" @click="submit(menuData)" id="submit"></Button>
      </p>
    </div>
  </div>
</template>
<script>
  export default{
    name:"***",
    data(){
      return{
        menuState:[],
        menuAttr:[],
        menuData:{          //表單數(shù)據(jù)
          menuCode:'',
          parentId:'',
          menuName:'',
          state:'',
          remark:'',
          menuAttr:'',
          menuUrl:'',
          id:'',
          menuLevel:'',
        },
        initData:[],            //保存初始化的數(shù)據(jù)
        ruleValidate:{},     //表單驗證規(guī)則
        treeData:[{           //最初節(jié)點數(shù)據(jù)
          title:"菜單管理",
          loading:false,
          hasChild:true,
          expand:true,
          searched:false,
          id:0,
          children:[]
        }],
        dragState:{            //用于拖拽數(shù)據(jù)存儲相關參數(shù)
          draggingNode:null,
          dropNode:null,
          allowDrop:true
        },
        currentData:null,      //拖拽的數(shù)據(jù)
        inputVal:null,      //新查詢條件
        oldInputVal:null    //舊查詢條件
      }
    },
    mounted(){
      const {menuData}=this.$refs;
      this.$validate.getSelect(this);        //獲取下拉框的數(shù)據(jù)
      this.setChildren(this.treeData[0],'0');        //初始化打開一級菜單
    },
    methods:{
      loadData(item,callback){          //異步請求數(shù)據(jù)(懶加載)
        let that=this;
        that.$http.get('路徑',參數(shù)).then(res=>{
          if(res.success){
            item.children=that.handleData(res.data);      //將子節(jié)點推入
            callback(item.children);              //iview自身方法
          }else{
            //提示錯誤信息;
          }
        })
      },
      handleData(data){        //操作數(shù)據(jù)
        let that=this;
        data.forEach(function(val){
          val.title=val.menuName;    //節(jié)點名稱
          if(val.hasChild==1){  //如果有子節(jié)點,傳入loading和children屬性
            val.loading=false;
            val.children=[];
          }
          val.hasChild=val.hasChild==1?true:false;      //判定是否有子節(jié)點
          val.selected=false;            //用于在選中節(jié)點時調(diào)用
          val.searched=false;          //用于在搜索節(jié)點時判定是否相關節(jié)點
        })
        return data;
      },
      //新增節(jié)點,這里的邏輯不是很完美,因為是懶加載,我這里是把新增的節(jié)點推入原始數(shù)據(jù)中全部顯示
      append(elm,node,data){          
        let that=this;
        let children=data.children||[];
        that.$http.post('******',參數(shù)).then(resNew=>{      //新節(jié)點基本信息
          if(resNew.success){
            that.$http.get('*******',參數(shù)).then(resAll=>{      //獲取所有子節(jié)點
              children=that.handleData(resAll.data);
              that.$set(data,'children',children);          //
              let icon=elm.getElementsByClassName('hzbankicon')[0];
              if(icon.classList.contains('hzbankicon-content-p')){    //添加子節(jié)點后,如果原本無子節(jié)點的節(jié)點修改圖標為文件夾
                icon.classList.remove('*******');
                icon.classList.add('*******');
              }
              if(!data.expand){     //設置自動展開父節(jié)點
                that.$set(data,'expand',true);
              }
              for(let key in that.menuData){    //展示新菜單信息
                that.menuData[key]=resNew.data[key];  
              }
              that.initData=JSON.parse(JSON.stringify(that.menuData));
              //提示新增成功
            })
          }
        })
      },
      remove(parentLi,root,node,data){//刪除節(jié)點
        let that=this;
        const parentKey=root.find(el=>el===node).parent;    //獲取父節(jié)點nodeKey
        const parent=root.find(el=>el.nodeKey===parentKey).node;
        const index=parent.children.indexOf(data);
        that.$MsgModal.confirm({
          content:"確定刪除當前節(jié)點?",
          title:"提示",
          icon:"warning",
          ok:function(){
            that.$http.delete('********').then(res=>{
              if(res.success){
                parent.children.splice(index,1);
                let parentId=data.parentId;
                root.forEach(function(val,index){    
                  if(parentId=val.node.id){    //只有一個子節(jié)點時,刪除后,修改圖標
                    let childrenNum=val.node.children.length;
                    if(childrenNum==0){
                      let icon=parentLi.getElementsByClassName('hzankicon')[0];
                      icon.classList.remove('******');
                      icon.classList.add('******');
                      delete parent.loading;
                      dalete parent.children;
                    }
                  }
                });
                //提示刪除成功
              }
            })
          }
        })
      },
      submit(name){
        let that=this;
        let data=that.$validate.toJsonData(that[name]);//操作數(shù)據(jù)取消空值,自定義的方法
        if(!that.$validate.compare(data,that.initData)){    //校驗是否修改數(shù)據(jù)
         // 如果未進行修改提示未修改,不能提交·
          return;
        }
        that.$refs[name].validate(valid=>{      //提交表單驗證規(guī)則是否通過
          if(valid){
            that.$MsgModal.confirm({
              content:"確定保存修改嗎?",title:"提示",icon:"warning",
              ok:function(){
                that.$http.put('********',參數(shù)).then(res=>{
                  document.getElementsByClassName('ivu-tree-title-selected')[0].innerHTML=data.menuName;
                  let selectedNode=that.$refs.menuTree.getSelectedNodes()[0];        //獲取節(jié)點數(shù)據(jù)并進行更新
                  for(let key in selectedNode){
                    selectedNode[key]=data[key];
                  }
                  if(res.success){
                    //提示修改成功
                  }else{
                    //提示失敗并展示原因
                  }
                })
              }
            })  
          }
        })
      },
      iconType(hasChild){      //圖標對應class
        let iconType='hzbankicon';
        if(hasChild){
          iconType+=" hzankicon-content-f"
        }else{
          iconType+=" hzankicon-content-f";
        }
        return iconType;
      },
      renderContent(h,{root,node,data}){      //自定義節(jié)點內(nèi)容
        let that=this;
        return h('span',{
          class:'node-box none',
          attrs:{draggable:"true",       //可拖拽},
          style:{userSelect:'none'      //禁止選中},
          on:{
            mouseover:(e)=>{//鼠標移入的效果},
            mouseleave:(e)=>{//鼠標移出的效果},
 //拖拽相關——————————————————————————————————————————————————start
            dragstart:(e)=>{
              this.dragState.draggingNode=node;
            },
            dragover:(e)=>{
              let dropNode;
              //便利節(jié)點,找到與鼠標上相匹配的節(jié)點獲取信息
              root.find(el=>{
                if(el.node.title===e.target.outerText){
                  dropNode=el;
                }
              });
              const draggingNode=that.dragState.draggingNode;
              if(!draggingNode || !dropNode)return;      
              let dropPrev=true;
              let dropInner=true;
              let dropNext=true;
              if(typeof(that.allowDrop)==='function'){
                dropPrev=that.allowDrop(draggingNode.node,dropNode,'prev');
                dropInner=that.allowDrop(draggingNode.node,dropNode,'inner');
                dropNext=that.allowDrop(draggingNode.node,dropNode,'next');
              }
              e.dataTransfer.dropEffect=dropInner?'move':'none';
              if(dropPrev||dropInner||dropNext){
                that.dragState.dropNode=dropNode;
              }
              //如果拖拽節(jié)點的父節(jié)點=指向節(jié)點,不能移入
              if(dropNode.node.id===draggingNode.node.parentId){
                dropInner=false;
              }  
              //如果已經(jīng)是最后一個元素,則不能在同一父節(jié)點下往后移動
              if(dropNode.parent===draggingNode.parent 
                  && root[dropNode.nodeKey+1] 
                  && root[dropNode.nodeKey+1].node.id===draggingNode.node.id){
                dropNext=false;
              }
              //如果是第一個節(jié)點,則不能在同一父節(jié)點下往前移動
              if(dropNode.parent===draggingNode.parent 
                  && draggingNode.nodeKey-1>=0
                  && root[dropNode.nodeKey-1].node.id===draggingNode.node.id){
                dropPrev=false;
              }
              if(draggingNode.node.id===dropNode.node.id || draggingNode.node.id===dropNode.node.parentId){
                dropPrev=false;
                dropInner=false;
                dropNext=false;
              }
              const targetPosition=e.currentTarget.getBoundingClientRect();
              const treePosition=document.getElementsByClassName('ivu-tree')[0].getBoundingClientRect();
              let dropType;
              const prevPercent=dropPrev?(dropInner?0.25:(dropNext?0.45:1)):-1;
              const nextPercent=dropNext?(dropInner?0.75:(dropPrev?0.55:0)):1;
              const distance=e.clientY-targetPosition.top;
              if(distance<targetPosition.height*prevPercent){
                dropType='before';
              }else if(distance>targetPosition.height*nextPercent){
                dropType='after';
              }else if(dropInner){
                dropType='inner';
                e.currentTarget.classList.add('inner');
              }else{
                dropType='none';
              }
              that.dragState.dropType=dropType;
            },
            dragleave:(e)=>{
              e.currentTarget.classList.remove('inner');
            },
            dragend:(e)=>{
              const {draggingNode,dropType,dropNode}=that.dragState;
              e.preventDefault();
              e.dataTransfer.dropEffect='move';
              if(draggingNode && dropNode){
                let draggingNodeCopy={};
                if(dropType !=='none'{
                  that.treeData=that.dragAboutDel(that.treeData,draggingNode.nodeKey);
                }
                draggingNodeCopy=that.currentData;
                if(dropType==='before'){
                  that.treeData=that.dragAboutAdd(that.treeData,dropNode.nodeKey,draggingNodeCopy,'before');
                }else  if(dropType==='after'){
                  that.treeData=that.dragAboutAdd(that.treeData,dropNode.nodeKey,draggingNodeCopy,'after');
                }else  if(dropType==='inner'){
                  that.treeData=that.dragAboutAdd(that.treeData,dropNode.nodeKey,draggingNodeCopy,'inner');
                }
                that.dragState.draggingNode=null;
                that.dragState.dropNode=null;
                that.dragState.allowDrop=true;
              }
            }
          }
        },[
          h('span',{
            class:'title-box',
            on:{
              click:()=>{            //點擊選中節(jié)點
                if(node.nodeKey>0){
                  this.$refs.menuTree.handleSelect(node.node.nodeKey);
                  for(let key in this.menuData){
                    this.menuData[key]=node.node[key];
                  }
                  this.initData=JSON.parse(JSON.stringify(this.menuData));
                }
              }
            }
          },[
            h('span',{
              class:this.iconType(node.node.hasChild),
              style:{marginRight:'3px'},
            }),
            h('span',{
              attrs:{'data-id':data.id},
              class:[
                'ivu-tree-title',{
                  ['*******']:node.node.searched,        //是否查詢節(jié)點
                  ['*******']:node.node.selected          //是否選中節(jié)點
                }
              ],
            },data.title)
          ]),
          h('span',{          //操作按鈕
            class:'btn-box isHide'
          },[
            h('span',{        //新增節(jié)點
              class:'***** ***** menu-add',
              on:{
                click:(e)=>{
                  let elmLi=e.currenetTarget.parentNode.parentNode.parentNode;
                  this.append(elmLi,node,data);
                }
              }
            }),
            h('span',{        //刪除節(jié)點
              class:'*** *** menu-remove',
              on:{
                click:(e)=>{
                  let parentLi=e.currenetTarget.parentNode.parentNode.parentNode.parentNode.parentNode;
                  this.remove(parentLi,root,node,data)
                }
              }
            })
          ])
        ]);
      },
      dragAboutDel(data,key){        //先刪除拖拽節(jié)點
        for(let i=0;i<data.length;i++){
           if(data[i].nodeKey===key){
            this.currentData=data[i];
            data.splice(i,1);
            break;
          }else if(data[i].hasChild==true && data[i].children && data[i].children.length>0){
            //當沒有子節(jié)點的時候,刪除loading,children;優(yōu)化菜單顯示
            data[i].children=this.dragAboutDel(data[i].children,key);
            if(data[i].children.length==0){
              data[i].hasChild=false;
              delete data[i].children;
              delete data[i].loading;
            }
          }
        }
        return data;
      },
      dragAboutAdd(data,key,targetData,type){    //根據(jù)拖拽種類操作節(jié)點數(shù)據(jù)
        for(let i=0;i<data.length;i++){
           if(data[i].nodeKey===key){
              if(type=='before'){
                data.splice(i,0,targetData);
                break;
              }else if(type=='after'){
                data.splice(i+1,0,targetData);
                break;   
              }else if(type=='inner'){
                if(!data[i].hasChild){
                  data[i].hasChild=true;
                  this.$set(data[i],'children',[targetData]);
                  this.$set(data[i],'expand',true);
                  data[i].loading=false;
                }else{
                  data[i].children.push(targetData);
                }
                break;
              }
           }else if(data[i].hasChild==true && data[i].children){
             data[i].children=this.dragAboutAdd(data[i].children,key,targetData,type)
           }
        }
        return data;
      },
      allowDrop(draggingNode,dropNode,type){
        return true;
      },
 //拖拽相關——————————————————————————————————————————————————end
      setChildren(current,id){
        let that=this;
        that.$http.get('********',參數(shù)).then(res=>{
          if(res.success){
            current.children=that.handleData(res.data);
          }else{
            //提示獲取數(shù)據(jù)失敗
          }
        });
      },
      search(val){      //搜索節(jié)點
        let that=this;
        if(!val){
          //提示輸入搜索關鍵詞
        }else if(that.oldInputVal==null&&val || that.oldInputVal!=val){
          that.$http.get('********').then(res=>{
            if(res.success){
              if(res.data.length>0){
                let data=res.data,treeData=that.treeData[0].children;
                that.removeSearched(treeData);
                that.mapSearchData(data,treeData,val);
              }else{
                //提示未找到相關節(jié)點
              }
            }else{
              //提示查詢失敗
            }
          })
        }else{
        }
        that.oldInputVal=val;
      },
      removeSearched(treeData){      //給所有節(jié)點移除查詢樣式
        let that=this;
        for(let i=0,len=treeData.length;i<len;i++){
          treeData[i].searched=false;
          if(treeData[i].children){
            that.removeSearched(treeData[i].children);
          }
        }
      },
      mapSearchData(data,treeData,val){      //便利節(jié)點找到搜索詞條相關
        let that=this;
        for(let i=0,len1=data.length;i<len1;i++){
          for(let j=0,len2=treeData.length;j<len2;j++){
            if(data[i].id==treeData[j].id){
              if(data[i].menuName.indexOf(val)>-1){
                treeData[j].searched=true;
                if(data[i].children && data[i].children.length>0){
                  if(treeData[j].children.length<1){
                    that.$http.get('********',參數(shù)).then(res=>{
                      if(res.success){
                        treeData[j].children=that.handleData(res.data);
                        that.$set(treeData[j],'expand',true);
                        that.mapSearchData(data[i].children,treeData[j].children,val);
                      }else{
                        //提示失敗
                      }
                    })
                  }else{
                    that.mapSearchData(data[i].children,treeData[j].children,val);
                  }
                }
              }else{
                if(data[i].children && treeData[j].children.length<1){
                  that.$http.get('********',參數(shù)).then(res=>{
                    if(res.success){
                      treeData[j].children=that.handleData(res.data);
                      that.$set(treeData[j],'expand',true);
                      that.mapSearchData(data[i].children,treeData[j].children,val);
                    }else{
                      //提示失敗
                    }
                  })
                }else{
                  that.mapSearchData(data[i].children,treeData[j].children,val);
                }
              }
            }
          }
        }    
      }
    }
  }
</script>
最后編輯于
?著作權歸作者所有,轉載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。

友情鏈接更多精彩內(nèi)容