<template>
<div>
<div class="UsermanagementRole">
<!-- 可选资源树 -->
<div class="UsermanagementRole__left">
<div class="UsermanagementRole__left__title">
<span class="font">角色名称</span>
<span class="font">角色描述</span>
</div>
<div class="UsermanagementRole__left__content">
<el-scrollbar>
<el-tree
ref="selectTreeRef"
:data="treeDataShow.left"
:props="defaultProps"
node-key="id"
highlight-current
show-checkbox
:expand-on-click-node="false"
:default-expand-all="true"
@check-change="
(data: any) => {
onChangeCurrent(data, 'left')
}
"
>
<template #default="{ data }">
<span class="custom-tree-node">
<span class="key">{{ data.roleName }}</span>
<span
:style="{
marginLeft:
data.pid === '-1' && data.hasChildren
? '110px'
: data.pid !== '-1' && data.hasChildren
? '85px'
: '0',
}"
class="val"
:title="data.roleDesc"
>
{{ data.roleDesc }}
</span>
</span>
</template>
</el-tree>
</el-scrollbar>
</div>
</div>
<!-- 操作按钮 -->
<div class="UsermanagementRole__btn">
<el-button
type="primary"
:disabled="!checkedKeysL.length || !treeDataShow.left.length"
@click="handleRelation"
>
<el-icon><arrow-right-bold /></el-icon>
</el-button>
<el-button
type="primary"
:disabled="!checkedKeysR.length || !treeDataShow.right.length"
style="margin: 0"
@click="handleCancelRelation"
>
<el-icon><arrow-left-bold /></el-icon>
</el-button>
</div>
<!-- 已选资源树 -->
<div class="UsermanagementRole__right">
<div class="UsermanagementRole__right__title">
<span class="font">已选角色名称</span>
</div>
<div class="UsermanagementRole__right__content">
<el-scrollbar>
<el-tree
ref="relationTreeRef"
:default-expand-all="true"
:data="treeDataShow.right"
:props="defaultProps"
node-key="id"
highlight-current
show-checkbox
:expand-on-click-node="false"
@check-change="
(data: any) => {
onChangeCurrent(data, 'right')
}
"
/>
</el-scrollbar>
</div>
</div>
</div>
</div>
</template>
<script setup lang="ts">
import { toRefs, reactive, watch } from 'vue'
interface NodKeys {
child: any[]
fatherL: string[]
fatherR: string[]
}
const props = defineProps({
sourceData: {
type: Array as any,
default: () => [],
},
})
const emits = defineEmits(['getSelectedData'])
const state = reactive<{
selectTreeRef: any
relationTreeRef: any
selectTreeData: any
relationTreeData: any
nodKeys: NodKeys
checkedKeysL: string[]
checkedKeysR: string[]
treeDataShow: any
}>({
selectTreeRef: null,
relationTreeRef: null,
selectTreeData: [],
relationTreeData: [],
nodKeys: {
// 所有子节点,左右的所有父节点
child: [],
fatherL: [],
fatherR: [],
},
checkedKeysL: [], // 控制可选关联勾选keys集合
checkedKeysR: [], // 控制已关联勾选keys集合
treeDataShow: {
left: null,
right: null,
},
})
const defaultProps = {
children: 'children',
label: 'roleName',
disabled: 'disabled',
}
// 处理树数据
const handleTreeData = () => {
const fatherKeysL: string[] = []
const fatherKeysR: string[] = []
const childKeys: any[] = []
function leftFun(data: any) {
return data.map((item: any) => {
const obj: any = {
...item,
disabled: item.disabled,
}
if (item.children?.length) {
const arr = leftFun(item.children).filter((el: any) => el)
if (arr.length) {
fatherKeysL.push(item.id)
obj.children = arr
return obj
}
return null
}
childKeys.push(item)
if (state.relationTreeData.includes(item.id)) return null
return obj
})
}
function rightFun(data: any) {
return data.map((item: any) => {
const obj: any = {
...item,
}
if (item.children?.length) {
const arr = rightFun(item.children).filter((el: any) => el)
if (arr.length) {
fatherKeysR.push(item.id)
obj.children = arr
return obj
}
return null
}
if (state.relationTreeData.includes(item.id)) {
return obj
}
return null
})
}
if (state.selectTreeData.length) {
const left = leftFun(state.selectTreeData).filter((el: any) => el)
const right = state.relationTreeData.length
? rightFun(state.selectTreeData).filter((el: any) => el)
: []
state.nodKeys.fatherL = fatherKeysL
state.nodKeys.fatherR = fatherKeysR
state.nodKeys.child = childKeys
if (state.checkedKeysR.length) {
state.checkedKeysL = []
}
if (state.checkedKeysL.length) {
state.checkedKeysR = []
}
state.treeDataShow = { left, right }
} else {
state.treeDataShow = { left: [], right: [] }
}
emits('getSelectedData', state.treeDataShow.right)
}
// 选择节点回调
const onChangeCurrent = (data: any, type: string) => {
if (type === 'left') {
state.checkedKeysL = state.selectTreeRef.getCheckedKeys()
}
if (type === 'right') {
state.checkedKeysR = state.relationTreeRef.getCheckedKeys()
}
}
// 关联资源
const handleRelation = () => {
const menuObj = state.nodKeys.child.filter((item) =>
state.checkedKeysL.includes(item.id)
)
// 选中并添加的资源数据
const addTreeData = menuObj.map((el) => el.id)
state.relationTreeData = state.relationTreeData.concat(addTreeData)
handleTreeData()
state.checkedKeysL = state.selectTreeRef.getCheckedKeys()
state.checkedKeysR = state.relationTreeRef.getCheckedKeys()
}
// 取消关联资源
const handleCancelRelation = () => {
const menuObj = state.nodKeys.child.filter((item) =>
state.checkedKeysR.includes(item.id)
)
// 选中要移除的资源
const removeTreeData = menuObj.map((el) => el.id)
state.relationTreeData = state.relationTreeData.filter(
(item: any) => !removeTreeData.includes(item)
)
handleTreeData()
state.checkedKeysL = state.selectTreeRef.getCheckedKeys()
state.checkedKeysR = state.relationTreeRef.getCheckedKeys()
}
watch(
() => props.sourceData,
(sourceData) => {
if (sourceData.length) {
state.selectTreeData = sourceData
handleTreeData()
}
},
{
immediate: true,
}
)
const {
selectTreeRef,
relationTreeRef,
treeDataShow,
checkedKeysL,
checkedKeysR,
} = toRefs(state)
</script>
<style scoped lang="scss">
.UsermanagementRole {
display: flex;
height: 60vh;
&__left,
&__right {
display: flex;
flex-direction: column;
border: 1px solid #aaa;
border-radius: 3px;
&__title {
display: flex;
align-items: center;
justify-content: space-between;
height: 30px;
padding: 0 10px;
background-color: #f5f7fa;
.font {
font-size: 16px;
}
.operate {
.el-dropdown-link {
display: flex;
color: #5d9af4;
cursor: pointer;
}
}
}
&__content {
display: flex;
flex: 1;
flex-direction: column;
min-height: 0;
padding: 10px;
.search {
margin-bottom: 10px;
}
}
}
&__left {
width: 60%;
&__title {
padding: 0 24px;
box-sizing: border-box;
.font {
display: inline-block;
width: 50%;
text-align: left;
}
}
}
&__right {
width: 35%;
}
&__btn {
display: flex;
flex-direction: column;
align-items: center;
align-self: center;
justify-content: space-evenly;
width: 100px;
height: 40%;
}
.custom-tree-node {
flex: 1;
display: flex;
align-items: center;
justify-content: space-between;
.key {
width: 120px;
}
.val {
width: calc(100% - 180px);
text-align: left;
overflow: hidden;
}
}
}
</style>
基于Vue3+Element Plus的el-tree的穿梭框封装
最新推荐文章于 2024-06-01 15:46:07 发布