懶加載-搜索節(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>