基于vue3+element-plus,可自定义配置图标,可配置展开按钮及图标,实现自定义点击展开事件,另外还配置了可拖拽换位,解决element-plus折叠不能自动关闭字节点的问题。
实现效果如图所示
完整代码如下:
<!-- 树形菜单-自定义图标-自定义折叠功能 -->
<template>
<div class="page">
<el-tree
style="width: 600px"
:data="_data"
:props="defaultProps"
:expand-on-click-node="false"
icon="ArrowDownBold"
draggable
:allow-drop="allowDrop"
>
<template #default="{ node, data }">
<div class="left">
<i class="iconfont" v-html="data.icon"></i><span>{{ data.ttt }}</span>
</div>
<div class="right" @click="handleNodeClick(node)">
{{ node.childNodes.length ? (node.expanded ? '折叠' : '展开') : '' }}
<span v-if="node.childNodes.length">
<el-icon v-if="node.expanded"><CaretTop /></el-icon>
<el-icon v-else><CaretBottom /></el-icon>
</span>
</div>
</template>
</el-tree>
</div>
</template>
<script setup lang="ts">
import { reactive, nextTick } from 'vue';
const allowDrop = (draggingNode: any, dropNode: any, type: any) => {
// 拖拽时判定目标节点能否成为拖动目标位置。
// 如果返回 false ,拖动节点不能被拖放到目标节点。
// type 参数有三种情况:'prev'、'inner' 和 'next',分别表示放置在目标节点前、插入至目标节点和放置在目标节点后
return type !== 'inner'; // 不允许插入节点
};
const handleNodeClick = (node: any) => {
console.log('node', node);
nextTick(() => {
// 下面这行就会实现
node.expanded = !node.expanded; // 手动展开折叠点击的元素
if (node.expanded == false) {
zhankaiall(node.childNodes);
}
});
};
const zhankaiall = (arr: any) => {
if (arr.length) {
arr.map((item: any) => {
item.expanded = false;
if (item.childNodes.length) {
zhankaiall(item.childNodes); // 递归关闭子节点
}
});
}
};
let _data = reactive([
{
ttt: '一级标题111',
expand: false,
icon: '',
children: [
{
ttt: '二级标题',
expand: false,
icon: '',
children: [
{
ttt: '三级标题',
expand: false,
icon: '',
children: [
{
ttt: '四级标题',
expand: false,
icon: '',
children: [
{
ttt: '五级标题',
icon: '',
expand: false,
},
],
},
],
},
],
},
],
},
{
ttt: '一级标题222',
expand: false,
icon: '',
children: [
{
ttt: '二级标题',
expand: false,
icon: '',
},
],
},
{
ttt: '一级标题333',
expand: false,
icon: '',
children: [
{
ttt: '二级标题',
expand: false,
icon: '',
},
],
},
]);
const defaultProps = {
children: 'children',
label: 'ttt',
};
</script>
<style lang="scss">
.el-tree-node__expand-icon {
display: none;
// position: absolute;
// right: 15px;
// color: #fff;
// svg {
// transition: 0.3s;
// }
// &::before {
// position: absolute;
// left: -24px;
// content: '展开';
// font-style: normal;
// }
// &.expanded {
// transform: none;
// &::before {
// content: '折叠';
// }
// svg {
// transform: rotate(180deg);
// }
// }
}
.el-tree-node__content {
background: #fd4a4a;
padding: 8px 15px !important;
box-sizing: content-box;
margin-bottom: 10px;
border-radius: 5px;
display: flex;
justify-content: space-between;
color: #fff;
cursor: default;
.right {
cursor: pointer;
}
&:hover {
background: #ff2525;
}
.iconfont {
margin-right: 5px;
}
}
.el-tree-node:focus > .el-tree-node__content {
background: #ff2525;
}
.el-tree-node__children {
.el-tree-node > .el-tree-node__content {
width: 545px;
margin-left: 25px;
background: rgb(241, 207, 54);
border-radius: 5px;
}
.el-tree-node__children {
.el-tree-node > .el-tree-node__content {
width: 520px;
margin-left: 50px;
background: #409eff;
border-radius: 5px;
}
.el-tree-node__children {
.el-tree-node > .el-tree-node__content {
width: 495px;
margin-left: 75px;
background: #67c23a;
border-radius: 5px;
}
.el-tree-node__children {
.el-tree-node > .el-tree-node__content {
width: 470px;
margin-left: 100px;
background: rgb(37, 201, 119);
border-radius: 5px;
}
}
}
}
}
</style>