懒加载-搜索节点-新增节点-删除节点
因为是手打,可能会有一些地方打错,会尽可能检查无误
拖拽相关有备注出来,相关功能基本参考element-ui节点树拖拽
<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="父节点编号" prop="parentId">
<Input v-model="menuData.parentId">
</FormItem>
<FormItem label="菜单名称" prop="menuName">
<Input v-model="menuData.menuName">
</FormItem>
<FormItem label="菜单状态" 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:{ //表单数据
menuCode:'',
parentId:'',
menuName:'',
state:'',
remark:'',
menuAttr:'',
menuUrl:'',
id:'',
menuLevel:'',
},
initData:[], //保存初始化的数据
ruleValidate:{}, //表单验证规则
treeData:[{ //最初节点数据
title:"菜单管理",
loading:false,
hasChild:true,
expand:true,
searched:false,
id:0,
children:[]
}],
dragState:{ //用于拖拽数据存储相关参数
draggingNode:null,
dropNode:null,
allowDrop:true
},
currentData:null, //拖拽的数据
inputVal:null, //新查询条件
oldInputVal:null //旧查询条件
}
},
mounted(){
const {menuData}=this.$refs;
this.$validate.getSelect(this); //获取下拉框的数据
this.setChildren(this.treeData[0],'0'); //初始化打开一级菜单
},
methods:{
loadData(item,callback){ //异步请求数据(懒加载)
let that=this;
that.$http.get('路径',参数).then(res=>{
if(res.success){
item.children=that.handleData(res.data); //将子节点推入
callback(item.children); //iview自身方法
}else{
//提示错误信息;
}
})
},
handleData(data){ //操作数据
let that=this;
data.forEach(function(val){
val.title=val.menuName; //节点名称
if(val.hasChild==1){ //如果有子节点,传入loading和children属性
val.loading=false;
val.children=[];
}
val.hasChild=val.hasChild==1?true:false; //判定是否有子节点
val.selected=false; //用于在选中节点时调用
val.searched=false; //用于在搜索节点时判定是否相关节点
})
return data;
},
//新增节点,这里的逻辑不是很完美,因为是懒加载,我这里是把新增的节点推入原始数据中全部显示
append(elm,node,data){
let that=this;
let children=data.children||[];
that.$http.post('******',参数).then(resNew=>{ //新节点基本信息
if(resNew.success){
that.$http.get('*******',参数).then(resAll=>{ //获取所有子节点
children=that.handleData(resAll.data);
that.$set(data,'children',children); //
let icon=elm.getElementsByClassName('hzbankicon')[0];
if(icon.classList.contains('hzbankicon-content-p')){ //添加子节点后,如果原本无子节点的节点修改图标为文件夹
icon.classList.remove('*******');
icon.classList.add('*******');
}
if(!data.expand){ //设置自动展开父节点
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){//删除节点
let that=this;
const parentKey=root.find(el=>el===node).parent; //获取父节点nodeKey
const parent=root.find(el=>el.nodeKey===parentKey).node;
const index=parent.children.indexOf(data);
that.$MsgModal.confirm({
content:"确定删除当前节点?",
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){ //只有一个子节点时,删除后,修改图标
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]);//操作数据取消空值,自定义的方法
if(!that.$validate.compare(data,that.initData)){ //校验是否修改数据
// 如果未进行修改提示未修改,不能提交·
return;
}
that.$refs[name].validate(valid=>{ //提交表单验证规则是否通过
if(valid){
that.$MsgModal.confirm({
content:"确定保存修改吗?",title:"提示",icon:"warning",
ok:function(){
that.$http.put('********',参数).then(res=>{
document.getElementsByClassName('ivu-tree-title-selected')[0].innerHTML=data.menuName;
let selectedNode=that.$refs.menuTree.getSelectedNodes()[0]; //获取节点数据并进行更新
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}){ //自定义节点内容
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;
//便利节点,找到与鼠标上相匹配的节点获取信息
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;
}
//如果拖拽节点的父节点=指向节点,不能移入
if(dropNode.node.id===draggingNode.node.parentId){
dropInner=false;
}
//如果已经是最后一个元素,则不能在同一父节点下往后移动
if(dropNode.parent===draggingNode.parent
&& root[dropNode.nodeKey+1]
&& root[dropNode.nodeKey+1].node.id===draggingNode.node.id){
dropNext=false;
}
//如果是第一个节点,则不能在同一父节点下往前移动
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:()=>{ //点击选中节点
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, //是否查询节点
['*******']:node.node.selected //是否选中节点
}
],
},data.title)
]),
h('span',{ //操作按钮
class:'btn-box isHide'
},[
h('span',{ //新增节点
class:'***** ***** menu-add',
on:{
click:(e)=>{
let elmLi=e.currenetTarget.parentNode.parentNode.parentNode;
this.append(elmLi,node,data);
}
}
}),
h('span',{ //删除节点
class:'*** *** menu-remove',
on:{
click:(e)=>{
let parentLi=e.currenetTarget.parentNode.parentNode.parentNode.parentNode.parentNode;
this.remove(parentLi,root,node,data)
}
}
})
])
]);
},
dragAboutDel(data,key){ //先删除拖拽节点
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){
//当没有子节点的时候,删除loading,children;优化菜单显示
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){ //根据拖拽种类操作节点数据
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('********',参数).then(res=>{
if(res.success){
current.children=that.handleData(res.data);
}else{
//提示获取数据失败
}
});
},
search(val){ //搜索节点
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{
//提示未找到相关节点
}
}else{
//提示查询失败
}
})
}else{
}
that.oldInputVal=val;
},
removeSearched(treeData){ //给所有节点移除查询样式
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){ //便利节点找到搜索词条相关
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('********',参数).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('********',参数).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>