vue+element穿梭树组件记录
近期公司要求实现穿梭树功能,而element上只有穿梭框和树型控件
看了很多文章,在其中发现了很多大神实现的过程,还有一个很不错的组件
组件实现
el-tree-transfer
文档也很详细
代码实现
将其中大概分离了一下,直接复制就可以使用了
原文地址
<template>
<div>
<el-button type="primary" @click="dialogVisible = true">
穿梭树
</el-button>
<el-dialog
title="编辑"
:visible.sync="dialogVisible"
width="45%"
>
<div class="addInfoStyle">
<div>
<span>权限:</span>
<div style="margin:0px 20px;">
<div style=" float: left; border: 1px solid #ccc;padding: 10px; width: 230px;">
<div>未有权限</div>
<el-tree
ref="unOwnTree"
style="height: 330px;overflow-y: auto;"
:data="data"
default-expand-all
show-checkbox
node-key="id"
highlight-current
:props="defaultProps"
/>
</div>
<div style=" float: left;">
<button @click="removeRight">
<i class="el-icon-d-arrow-right" />
</button>
<br>
<button @click="removeLeft">
<i class="el-icon-d-arrow-left" />
</button>
</div>
<div style=" float: right; border: 1px solid #ccc;padding: 10px; width: 230px;">
<div>已有权限</div>
<el-tree
ref="ownTree"
style="height: 330px;overflow-y: auto;"
:data="ownPermissions"
default-expand-all
show-checkbox
node-key="id"
highlight-current
:props="defaultProps"
/>
</div>
</div>
</div>
</div>
<div style="clear: both" />
<span slot="footer" class="dialog-footer">
<el-button @click="dialogVisible = false">取 消</el-button>
<el-button type="primary" @click="save">确 定</el-button>
</span>
</el-dialog>
</div>
</template>
<script>
let currentTree;
export default {
name: 'Infomationtree',
components: {},
data () {
return {
data: [{
id: 1,
label: '一级 1',
children: [{
id: 4,
label: '二级 1-1',
children: [{
id: 9,
label: '三级 1-1-1'
}, {
id: 10,
label: '三级 1-1-2'
}]
}]
}, {
id: 2,
label: '一级 2',
children: [{
id: 5,
label: '二级 2-1'
}, {
id: 6,
label: '二级 2-2'
}]
}, {
id: 3,
label: '一级 3',
children: [{
id: 7,
label: '二级 3-1'
}, {
id: 8,
label: '二级 3-2'
}]
}],
defaultProps: {
children: 'children',
label: 'label'
},
dialogVisible:false,
ownPermissions:[], // 已拥有的树权限数据
};
},
mounted () {},
methods: {
removeRight(){
let $unOwnTree = this.$refs.unOwnTree;
let checkedNodes = $unOwnTree.getCheckedKeys();
console.log("removeRight:",checkedNodes);
checkedNodes.forEach(item =>{
let node = $unOwnTree.getNode(item);
let nodeData = $unOwnTree.getCheckedNodes(item);
if (node !== null){
currentTree = this.$refs.ownTree;
this.appendNode(node);
$unOwnTree.remove(node,nodeData);
}
});
clearInterval(this.interval); // 当移过来的不为空是,停止定时任务。
},
removeLeft(){
let $ownTree = this.$refs.ownTree;
let checkedNodes = $ownTree.getCheckedKeys();
console.log("removeLeft:",checkedNodes);
checkedNodes.forEach(item =>{
let node = $ownTree.getNode(item);
let nodeData = $ownTree.getCheckedNodes(item);
if (node !== null){
currentTree = this.$refs.unOwnTree;
this.appendNode(node);
// 移除左边的
$ownTree.remove(node,nodeData);
}
});
},
appendNode(node){
// 1、如果移动的是一级目录
// ①先判断 当前目录 在右边一个目录的位置
// 先判断是否为一级目录
if (node.level === 1){ // level = 1 说明是一级目录
// 获取节点的 id
const oneMenuNodeData = node.data;
this.oneMenuHandle(oneMenuNodeData);
/*
* 2、如果移动的是二级目
* ① 当右边没有一级目录时
* 一、先要获取一级目录数据
* 二、查看应该插入右边目录位置
* ② 当右边有一级目录
* 一、先要获取二级目录的位置
*/
}else if(node.level ===2){
const oneMenu = JSON.parse(JSON.stringify(node.parent.data)); // 深度拷贝 才不会影响到 左侧的树结构
oneMenu.children = [node.data];
this.oneMenuHandle(oneMenu);
/*
* 3、如果移动的是权限
* ① 当右边没有一级目录时
* 一、当没有二级目录时
* 1、创建,二级目录,一个目录,直接迁移
* 二、当有二级目录时
* 判断权限插入的位置
* ② 当右边有一级目录
* 一、有二级目录。
* 判断权限插入位置
* 二、没有二级目录
* 把二级目录一起迁移过去
**/
}else { // 移动的是权限
const ownTowMenuId = node.parent.data.id;
let towMenuNode = currentTree.getNode(ownTowMenuId);
if (towMenuNode === null){ //当没有二级菜单
const ownOneMenuId = node.parent.parent.data.id;
let oneMenuNode = currentTree.getNode(ownOneMenuId);
let removeTowMenuData = JSON.parse(JSON.stringify(node.parent.data));
// 将权限放到二级菜单中
removeTowMenuData.children = [node.data];
if (oneMenuNode === null){ // 当没有一级菜单
let removeOneMenuData = JSON.parse(JSON.stringify(node.parent.parent.data));
removeOneMenuData.children = [removeTowMenuData];
this.oneMenuHandle(removeOneMenuData);
} else{ // 当有一级菜单
this.oneMenuHandle(removeTowMenuData);
}
} else { //当有二级菜单
this.towMenuHandle(node.data,towMenuNode);
}
}
},
// 当有二级目的时候
towMenuHandle(permission, towMenuNode){
// 获取二级目录下面所有权限
let ownPermissions = towMenuNode.data.children;
let ownPermissionsArr = [];
ownPermissions.forEach(permission =>{
ownPermissionsArr.push(permission.id);
});
this.toInsert(ownPermissionsArr,permission);
},
oneMenuHandle(oneMenuNodeData){
let ownOneMenu = currentTree.getNode(oneMenuNodeData.id);
if (ownOneMenu === null) { // 说明右边未拥有
// 获取右边所有的一级目。
const ownTreeDataArr = currentTree.data;
let ownOneMenus = [];
ownTreeDataArr.forEach(oneMenu =>{
ownOneMenus.push(oneMenu.id);
});
// 获取当前一级目录附近的一级目录的 id 和 插入当前id 之前还是之后。
if (ownOneMenus.length >0){
this.toInsert(ownOneMenus,oneMenuNodeData);
}else {
currentTree.append(oneMenuNodeData,0);
}
}else{ // 右边拥有一级目录时
// 需要右移的二级目录 遍历进行插入
let rightMoveTowMenu = oneMenuNodeData.children;
rightMoveTowMenu.forEach(towMenu =>{
let ownTowMenu = currentTree.getNode(towMenu.id); // 获取右边拥有的二级目录
if(ownTowMenu === null){ // 如果不存在
let ownTowTreeDataArr = ownOneMenu.data.children;
let ownTowMenus = [];
ownTowTreeDataArr.forEach(oneMenu =>{
ownTowMenus.push(oneMenu.id);
});
this.toInsert(ownTowMenus,towMenu);
}else{ // 如果存在
let ownPermissions = [];
// 获取原来的 二级目录下的所有的权限
ownTowMenu.data.children.forEach(ownPermission =>{
ownPermissions.push(ownPermission.id);
});
// 得到 要移动的二级目录下的所有权限
towMenu.children.forEach(unOwnPermission =>{
this.toInsert(ownPermissions,unOwnPermission);
});
}
});
}
},
// 获取当前一级目录附近的一级目录的 id 和 插入当前id 之前还是之后。
getNearbyOneMenu(ownOneMenus,oneMenuId){
let nearbyId = -1;
let isBefore = false;
ownOneMenus.some(id =>{
if (oneMenuId<id){
nearbyId = id;
isBefore = true;
return true;
}
});
// 如果 nearbyId !== -1 则将 ownOneMenus 数组中最大的赋值给 nearbyId
nearbyId = nearbyId !== -1?nearbyId:ownOneMenus[ownOneMenus.length-1];
return {nearbyId:nearbyId,isBefore:isBefore};
},
toInsert(arr,toMoveNode){
const nearbyOnePermission = this.getNearbyOneMenu(arr.sort(),toMoveNode.id);
if (nearbyOnePermission.isBefore) {
currentTree.insertBefore(toMoveNode,nearbyOnePermission.nearbyId);
}else{
currentTree.insertAfter(toMoveNode,nearbyOnePermission.nearbyId);
}
},
}
};
</script>
<style lang='scss' scoped>
</style>