ElementUI、ElementPlus树组件功能很不错,但是官方的树形组件没有显示线条,感觉稍微不够大气。于是网上查了一些资料,找了很多也感觉也不够完美,最后找到一个还不错的实现方案,并且再美化改进一下,分享给大家。
一、基于Vue2+ElementUI的例子
(1)示例代码
<template>
<div class="tree-container">
<div style="padding: 20px;">
<el-checkbox v-model="isSelectAll" @change="handleCheckedAllTreeNodeChange">全选/全不选</el-checkbox>
<el-button size="mini" style="margin-left: 20px;" @click="handleGetCheckedNodesAndKeys(true)">获取已勾选节点</el-button>
</div>
<el-tree
ref="treeRef"
show-checkbox
icon-class="el-icon-arrow-right"
:node-key="'id'"
:data="treeList"
:props="defaultProps"
:default-expand-all="true"
:expand-on-click-node="false"
:default-checked-keys="[5]"
>
<span slot-scope="{ data }">
<template v-if="data.children">
<div v-if="data.children.length > 0">
<!-- <i class="el-icon-folder" :style="'font-size: 14px; padding: 0 5px 0 5px'"/> -->
<i :style="'font-size: 13px; padding: 0 5px 0 5px'"><svg viewBox="64 64 896 896" data-icon="apartment" width="1em" height="1em" fill="currentColor" aria-hidden="true" focusable="false" class=""><path d="M908 640H804V488c0-4.4-3.6-8-8-8H548v-96h108c8.8 0 16-7.2 16-16V80c0-8.8-7.2-16-16-16H368c-8.8 0-16 7.2-16 16v288c0 8.8 7.2 16 16 16h108v96H228c-4.4 0-8 3.6-8 8v152H116c-8.8 0-16 7.2-16 16v288c0 8.8 7.2 16 16 16h288c8.8 0 16-7.2 16-16V656c0-8.8-7.2-16-16-16H292v-88h440v88H620c-8.8 0-16 7.2-16 16v288c0 8.8 7.2 16 16 16h288c8.8 0 16-7.2 16-16V656c0-8.8-7.2-16-16-16zm-564 76v168H176V716h168zm84-408V140h168v168H428zm420 576H680V716h168v168z"></path></svg></i>
<span>{
{ data.id + ' - ' + data.label }}</span>
</div>
<div v-else>
<i class="leaf-node-line"></i>
<!-- <i class="el-icon-folder" :style="'padding: 0 5px 0 5px'"/> -->
<i :style="'font-size: 13px; padding: 0 5px 0 5px'"><svg viewBox="64 64 896 896" data-icon="apartment" width="1em" height="1em" fill="currentColor" aria-hidden="true" focusable="false" class=""><path d="M908 640H804V488c0-4.4-3.6-8-8-8H548v-96h108c8.8 0 16-7.2 16-16V80c0-8.8-7.2-16-16-16H368c-8.8 0-16 7.2-16 16v288c0 8.8 7.2 16 16 16h108v96H228c-4.4 0-8 3.6-8 8v152H116c-8.8 0-16 7.2-16 16v288c0 8.8 7.2 16 16 16h288c8.8 0 16-7.2 16-16V656c0-8.8-7.2-16-16-16H292v-88h440v88H620c-8.8 0-16 7.2-16 16v288c0 8.8 7.2 16 16 16h288c8.8 0 16-7.2 16-16V656c0-8.8-7.2-16-16-16zm-564 76v168H176V716h168zm84-408V140h168v168H428zm420 576H680V716h168v168z"></path></svg></i>
<span>{
{ data.id + ' - ' + data.label }}</span>
</div>
</template>
<template v-else>
<div style="margin-left: 0px;">
<i class="leaf-node-line"></i>
<!-- <i class="el-icon-document" :style="'padding: 0 5px 0 5px'"></i> -->
<i :style="'font-size: 13px; padding: 0 5px 0 5px'"><svg viewBox="64 64 896 896" data-icon="deployment-unit" width="1em" height="1em" fill="currentColor" aria-hidden="true" focusable="false" class=""><path d="M888.3 693.2c-42.5-24.6-94.3-18-129.2 12.8l-53-30.7V523.6c0-15.7-8.4-30.3-22-38.1l-136-78.3v-67.1c44.2-15 76-56.8 76-106.1 0-61.9-50.1-112-112-112s-112 50.1-112 112c0 49.3 31.8 91.1 76 106.1v67.1l-136 78.3c-13.6 7.8-22 22.4-22 38.1v151.6l-53 30.7c-34.9-30.8-86.8-37.4-129.2-12.8-53.5 31-71.7 99.4-41 152.9 30.8 53.5 98.9 71.9 152.2 41 42.5-24.6 62.7-73 53.6-118.8l48.7-28.3 140.6 81c6.8 3.9 14.4 5.9 22 5.9s15.2-2 22-5.9L674.5 740l48.7 28.3c-9.1 45.7 11.2 94.2 53.6 118.8 53.3 30.9 121.5 12.6 152.2-41 30.8-53.6 12.6-122-40.7-152.9zm-673 138.4a47.6 47.6 0 0 1-65.2-17.6c-13.2-22.9-5.4-52.3 17.5-65.5a47.6 47.6 0 0 1 65.2 17.6c13.2 22.9 5.4 52.3-17.5 65.5zM522 463.8zM464 234a48.01 48.01 0 0 1 96 0 48.01 48.01 0 0 1-96 0zm170 446.2l-122 70.3-122-70.3V539.8l122-70.3 122 70.3v140.4zm239.9 133.9c-13.2 22.9-42.4 30.8-65.2 17.6-22.8-13.2-30.7-42.6-17.5-65.5s42.4-30.8 65.2-17.6c22.9 13.2 30.7 42.5 17.5 65.5z"></path></svg></i>
<span>{
{ data.id + ' - ' + data.label }}</span>
</div>
</template>
</span>
</el-tree>
</div>
</template>
<script>
export default {
data() {
return {
// 树列表
treeList: [
{
id: 1,
label: "香烟 WiFi 啤酒",
children: [
{
id: 3,
label: '香烟',
children: [
{
id: 4,
label: '煊赫门',
},
{
id: 5,
label: 'ESSE双爆珠',
disabled: true,
},
],
},
{
id: 2,
label: '后端开发技术',
disabled: true,
children: [
{
id: 6,
label: 'Java编程技术',
children: [],
},
{
id: 7,
label: '数据库',
disabled: true,
children: [
{
id: 8,
label: '关系型数据库',
children: [
{
id: 9,
label: 'MySQL',
},
{
id: 10,
label: 'Oracle',
disabled: true,
},
],
},
{
id: 11,
label: '非关系型数据库',
children: [
{
id: 12,
label: 'Redis',
},
{
id: 13,
label: 'Elasticsearch',
disabled: true,
},
],
}
],
},
],
},
],
},
],
// 对象关系映射
defaultProps: {
label: 'label',
children: 'children',
},
// 是否全选所有节点标志
isSelectAll: false,
};
},
methods: {
/**
* 是否全选所有节点
*/
handleCheckedAllTreeNodeChange() {
if (this.isSelectAll) {
// 深度遍历将子节点全选中
for (let i = 0; i < this.treeList.length; i++) {
this.$refs.treeRef.setChecked(this.treeList[i], true, true);
}
} else {
// 全部不选中
this.$refs.treeRef.setCheckedNodes([]);
}
const leafOnly = true;
this.handleGetCheckedNodesAndKeys(leafOnly);
},
/**
* 获取当前已被选中的节点集合
*/
handleGetCheckedNodesAndKeys(leafOnly) {
console.log('getCheckedNodes =>', this.$refs.treeRef.getCheckedNodes(leafOnly));
console.log('getCheckedKeys =>', this.$refs.treeRef.getCheckedKeys(leafOnly));
}
},
}
</script>
<style lang="less" scoped>
// 设置树形组件节点的定位和左内边距
.tree-container /deep/ .el-tree-node {
position: relative;
padding-left: 13px;
}
// 设置树形组件节点的 before 伪类的样式
.tree-container /deep/ .el-tree-node:before {
width: 1px;
height: 100%;
content: '';
position: absolute;
top: -38px;
bottom: 0;
left: 0;
right: auto;
border-width: 1px;
border-left: 1px solid #b8b9bb;
}
// 设置树形组件节点的 after 伪类的样式
.tree-container /deep/ .el-tree-node:after {
width: 13px;
height: 13px;
content: '';
position: absolute;
left: 0;
right: auto;
top: 12px;
bottom: auto;
border-width: 1px;
border-top: 1px solid #b8b9bb;
}
// 设置树形组件首节点的左边框不显示
.tree-container /deep/ .el-tree > .el-tree-node:before {
border-left: none;
}
// 设置树形组件首节点的顶部边框不显示
.tree-container /deep/ .el-tree > .el-tree-node:after {
border-top: none;
}
// 设置树形组件末节点的 before 伪类的高度
.tree-container /deep/ .el-tree .el-tree-node:last-child:before {
height: 50px;
}
// 设置树形组件节点字体大小、以及取消左内边距
.tree-container /deep/ .el-tree .el-tree-node__content {
color: #000;
font-size: 14px;
padding-left: 0 !important;
}
// 设置树形组件孩子节点左内边距
.tree-container /deep/ .el-tree .el-tree-node__children {
padding-left: 11.5px;
}
// 设置树形组件复选框左右外边距
.tree-container /deep/ .el-tree .el-tree-node__content > label.el-checkbox {
margin: 0 5px 0 5px !important;
}
// 设置树形组件展开图标定位、图层、内边距
.tree-container /deep/ .el-tree .el-tree-node__expand-icon {
position: relative;
z-index: 99;
}
// 设置树形组件叶子节点的默认图标不显示
.tree-container /deep/ .el-tree .el-tree-node__expand-icon.is-leaf {
display: none;
}
// 设置树形组件叶子节点的横线
.tree-container /deep/ .el-tree .leaf-node-line {
width: 23px;
height: 13px;
content: '';
position: absolute;
left: 13px;
right: auto;
top: 12px;
bottom: auto;
border-width: 1px;
border-top: 1px solid #b8b9bb;
}
// 设置树形组件有叶子节点的左外边距
.tree-container /deep/ .el-tree .el-tree-node__content:has(.is-leaf){
// color: aqua;
margin-left: 24px !important;
}
</style>
(2)效果如下~
二、基于Vue3+ElementPlus的例子
1.普通示例
(1)示例代码
<template>
<div class="element-plus-tree">
<el-tree
:data="treeData"
:props="defaultProps"
:show-checkbox="false"
:default-expand-all="true"
:highlight-current="true"
:expand-on-click-node="false"
:indent="22"
>
<template #default="{ node, data }">
<span v-if="!node.isLeaf" style="display: flex; align-items: center;">
<el-icon v-if="node.expanded" style="margin: 0 6px 0 2px;" size="16"><FolderOpened /></el-icon>
<el-icon v-else style="margin: 0 6px 0 2px;" size="16"><Folder /></el-icon>
<small @click="handleNodeClick(node, data)">{
{ node.label }}</small>
</span>
<span v-else style="display: flex; align-items: center;">
<el-icon style="margin: 0 6px 0 2px;" size="16"><Document /></el-icon>
<small @click="handleNodeClick(node, data)">{
{ node.label }}</small>
</span>
</template>
</el-tree>
</div>
</template>
<script setup>
import {
onMounted, ref, getCurrentInstance } from 'vue'
// 代理对象
const {
proxy } = getCurrentInstance()
// 树型数据
const treeData = ref(
[
{
label: '香烟 WiFi 啤酒',
expanded: true,
children: [
{
label: '香烟',
children: [
{
label: '煊赫门'
},
{
label: 'ESSE双爆珠'
}
],
},
{
label: '后端开发技术',
children: [
{
label: 'Java编程技术'
},
{
label: 'Python编程技术'
}
],
},
{
label: '数据库',
children: [
{
label: '关系型数据库',
children: [
{
label: 'MySQL'
},
{
label: 'Oracle'
}
],
},
{
label: '非关系型数据库',
children: [
{
label: 'Redis'
},
{
label: 'Elasticsearch'
}
],
}
],
},
{
label: 'AI人工智能'
},
],
},
{
label: '火腿 iPad 泡面'
},
]
)
// 树节点属性映射关系
const defaultProps = {
children: 'children',
label: 'label',
}
/**
* 树组件点击事件句柄方法
*/
const handleNodeClick = (node, data) => {
console.log(
'%c 树组件点击事件句柄方法 %c handleNodeClick',
'padding: 1px; background-color: #35495e; color: #fff',
'padding: 1px; background-color: #5e7ce0; color: #fff',
)
console.log('%c ∟ %c node %c =>', 'text-indent: 10px', 'padding: 1px; background-color: #41b883; color: #fff', 'color: #000', node)
console.log('%c ∟ %c data %c =>', 'text-indent: 10px', 'padding: 1px; background-color: #41b883; color: #fff', 'color: #000', data)
}
onMounted(() => {
// ...
})
</script>
<style lang="less" scoped>
.element-plus-tree {
padding: 100px;
:deep(.el-tree) {
.el-tree-node {
/* ^ 所有节点 */
i.el-tree-node__expand-icon {
padding: 6px;
&::before {
font-family: element-ui-icons;
font-style: normal;
content: "\e6d9";
color: #000000;
border: 1px solid #606266;
border-radius: 2px;
}
svg {
display: none; // 隐藏所有节点的 svg 图标
}
}
/* / 所有节点 */
/* ^ 已展开的父节点 */
i.el-tree-node__expand-icon.expanded {
transform: rotate(0deg); // 取消旋转
-webkit-transform: rotate(0deg); // 取消旋转
&::before {
font-family: element-ui-icons;
font-style: normal;
content: "\e6d8";
color: #000000;
border: 1px solid #606266;
border-radius: 2px;
}
}
/* / 已展开的父节点 */
/* ^ 叶子节点 */
i.el-tree-node__expand-icon.is-leaf {
&::before {
display: none;
}
}
/* / 叶子节点 */
/* ^ 复选框 */
.el-checkbox {
margin: 0 7px 0 2px;
.el-checkbox__inner {
width: 14px;
height: 14px;
border-radius: 2px;
border: 1px solid #bbb;
}
.el-checkbox__input.is-checked .el-checkbox__inner,
.el-checkbox__input.is-indeterminate .el-checkbox__inner {
border: 1px solid #5e7ce0;
}
}
/* / 复选框 */
.el-tree-node__content {
small {
font-size: 13px;
}
}
}
}
}
</style>
(2)效果如下~
2.高级示例
(1)示例代码
<template>
<div class="element-plus-tree">
<el-tree
:data="treeData"
:props="defaultProps"
:show-checkbox="true"
:default-expand-all="true"
:highlight-current="true"
:expand-on-click-node="false"
:indent="22"
>
<template #default="{ node, data }">
<span v-if="!node.isLeaf" style="display: flex; align-items: center;">
<el-icon v-if="node.expanded" style="margin: 0 6px 0 0px;" size="16"><FolderOpened /></el-icon>
<el-icon v-else style="margin: 0 6px 0 0px;" size="16"><Folder /></el-icon>
<small @click="handleNodeClick(node, data)">{
{ node.label }}</small>
</span>
<span v-else style="display: flex; align-items: center;">
<el-icon style="margin: 0 6px 0 0px;" size="16"><Document /></el-icon>
<small @click="handleNodeClick(node, data)">{
{ node.label }}</small>
</span>
</template>
</el-tree>
</div>
</template>
<script setup>
import {
onMounted, ref, getCurrentInstance } from 'vue'
// 代理对象
const {
proxy } = getCurrentInstance()
// 树型数据
const treeData = ref(
[
{
label: '香烟 WiFi 啤酒',
expanded: true,
children: [
{
label: '香烟',
children: [
{
label: '煊赫门'
},
{
label: 'ESSE双爆珠'
}
],
},
{
label: '后端开发技术',
children: [
{
label: 'Java编程技术'
},
{
label: 'Python编程技术'
}
],
},
{
label: '数据库',
children: [
{
label: '关系型数据库',
children: [
{
label: 'MySQL'
},
{
label: 'Oracle'
}
],
},
{
label: '非关系型数据库',
children: [
{
label: 'Redis'
},
{
label: 'Elasticsearch'
}
],
}
],
},
{
label: 'AI人工智能'
},
],
},
{
label: '火腿 iPad 泡面'
},
]
)
// 树节点属性映射关系
const defaultProps = {
children: 'children',
label: 'label',
}
/**
* 树组件点击事件句柄方法
*/
const handleNodeClick = (node, data) => {
console.log(
'%c 树组件点击事件句柄方法 %c handleNodeClick',
'padding: 1px; background-color: #35495e; color: #fff',
'padding: 1px; background-color: #5e7ce0; color: #fff',
)
console.log('%c ∟ %c node %c =>', 'text-indent: 10px', 'padding: 1px; background-color: #41b883; color: #fff', 'color: #000', node)
console.log('%c ∟ %c data %c =>', 'text-indent: 10px', 'padding: 1px; background-color: #41b883; color: #fff', 'color: #000', data)
}
onMounted(() => {
// ...
})
</script>
<style lang="less" scoped>
.element-plus-tree {
padding: 100px;
:deep(.el-tree) {
/* ---- ---- ---- ---- ^(节点对齐)---- ---- ---- ---- */
.el-tree-node {
/* ^ 所有节点 */
i.el-tree-node__expand-icon {
padding: 6px;
&::before {
font-family: element-ui-icons;
font-style: normal;
content: "\e6d9";
color: #000000;
border: 1px solid #606266;
border-radius: 2px;
}
svg {
display: none; // 隐藏所有节点的 svg 图标
}
}
/* / 所有节点 */
/* ^ 已展开的父节点 */
i.el-tree-node__expand-icon.expanded {
transform: rotate(0deg); // 取消旋转
-webkit-transform: rotate(0deg); // 取消旋转
&::before {
font-family: element-ui-icons;
font-style: normal;
content: "\e6d8";
color: #000000;
border: 1px solid #606266;
border-radius: 2px;
}
}
/* / 已展开的父节点 */
/* ^ 叶子节点 */
i.el-tree-node__expand-icon.is-leaf {
&::before {
display: none;
}
}
/* / 叶子节点 */
/* ^ 复选框 */
.el-checkbox {
margin: 0 7px 0 2px;
.el-checkbox__inner {
width: 14px;
height: 14px;
border-radius: 2px;
border: 1px solid #bbb;
}
.el-checkbox__input.is-checked .el-checkbox__inner,
.el-checkbox__input.is-indeterminate .el-checkbox__inner {
border: 1px solid #5e7ce0;
}
}
/* / 复选框 */
.el-tree-node__content {
small {
font-size: 13px;
}
}
}
/* ---- ---- ---- ---- /(节点对齐)---- ---- ---- ---- */
/* ---- ---- ---- ---- ^(文字高亮)---- ---- ---- ---- */
.el-tree-node.is-current {
.el-tree-node__content {
small {
color: #5e7ce0;
}
}
.el-tree-node__children {
small {
color: unset;
}
}
}
/* ---- ---- ---- ---- /(文字高亮)---- ---- ---- ---- */
/* ---- ---- ---- ---- ^(新增辅助线)---- ---- ---- ---- */
/* ^ 树节点 */
.el-tree-node {
position: relative;
width: auto;
// width: max-content; // 显示文字宽度
padding-left: 13px;
&::before {
width: 1px;
height: 100%;
content: '';
position: absolute;
top: -38px;
bottom: 0;
left: 0;
right: auto;
border-width: 1px;
border-left: 1px solid #b8b9bb;
}
&::after {
width: 13px;
height: 13px;
content: '';
position: absolute;
z-index: 0;
left: 0;
right: auto;
top: 12px;
bottom: auto;
border-width: 1px;
border-top: 1px solid #b8b9bb;
}
.el-tree-node__content {
position: relative;
z-index: 1;
color: #000;
padding-left: 0 !important;
/* ^ 复选框 */
.el-checkbox {
margin: 0 10px 0 5.5px;
}
/* / 复选框 */
}
.el-tree-node__children {
padding-left: 12px;
}
&:last-child::before {
height: 50px;
}
}
/* / 树节点 */
/* ^ 第一层节点 */
> .el-tree-node {
padding-left: 0;
&::before {
border-left: none;
}
&::after {
border-top: none;
}
}
/* / 第一层节点 */
/* ^ 叶子节点 */
i.el-tree-node__expand-icon.is-leaf {
display: none;
}
/* / 叶子节点 */
/* ^ 设置子节点左外边距 */
.el-tree-node__content:has(.is-leaf) {
// color: #00ffff;
margin-left: 25px !important;
}
/* / 设置子节点左外边距 */
/* ---- ---- ---- ---- /(新增辅助线)---- ---- ---- ---- */
}
}
</style>
(2)效果如下~
三、基于Vue3+DevUI的附赠例子
1.导入相关依赖
npm i vue-devui -D
2.官方文档
3.普通示例
(1)示例代码
<template>
<div class="vue-devui-tree">
<div>
<span>是否展开所有节点 : </span>
<el-switch
v-model="isExpandAllNodes"
size="small"
@change="handleIsExpandAllNodesChange"
/>
</div>
<DevTree ref="treeRef" :data="data">
<template #icon="{ nodeData, toggleNode }">
<span
v-if="!nodeData.isLeaf"
class="devui-tree__node-folder"
@click="
(event) => {
event.stopPropagation();
toggleNode(nodeData);
}
"
>
<!-- 未展开图标 -->
<svg v-if="!nodeData.expanded" style="margin-right: 8px" width="16px" height="16px" viewBox="0 0 16 16" version="1.1" xmlns="http://www.w3.org/2000/svg">
<g stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
<rect x="0.5" y="0.5" width="15" height="15" rx="2" stroke="#666666"></rect>
<path fill="#666666" d="M8.75,4 L8.75,7.25 L12,7.25 L12,8.75 L8.749,8.75 L8.75,12 L7.25,12 L7.249,8.75 L4,8.75 L4,7.25 L7.25,7.25 L7.25,4 L8.75,4 Z"></path>
</g>
</svg>
<!-- 已展开图标 -->
<svg v-else style="margin-right: 8px" width="16px" height="16px" viewBox="0 0 16 16" version="1.1" xmlns="http://www.w3.org/2000/svg">
<g stroke-width="1" fill="none" fill-rule="evenodd">
<rect x="0.5" y="0.5" width="15" height="15" rx="2" stroke="#666666"></rect>
<rect x="4" y="7" width="8" height="2" fill="#666666"></rect>
</g>
</svg>
</span>
<span
v-else
class="devui-tree__node-leaf"
>
</span>
</template>
<template #content="{ nodeData }">
<svg style="margin-right: 7px" viewBox="0 0 16 16" width="16" height="16">
<path
:d="`${
nodeData.isLeaf
? 'M13,6 L9,6 L9,5 L9,2 L3,2 L3,14 L13,14 L13,6 Z M12.5857864,5 L10,2.41421356 L10,5 L12.5857864,5 Z M2,1 L10,1 L14,5 L14,15 L2,15 L2,1 Z'
: nodeData.expanded
? 'M16,6 L14,14 L2,14 L0,6 L16,6 Z M14.7192236,7 L1.28077641,7 L2.78077641,13 L13.2192236,13 L14.7192236,7 Z M6,1 L8,3 L15,3 L15,5 L14,5 L14,4 L7.58578644,4 L5.58578644,2 L2,2 L2,5 L1,5 L1,1 L6,1 Z'
: 'M14,6 L14,5 L7.58578644,5 L5.58578644,3 L2,3 L2,6 L14,6 Z M14,7 L2,7 L2,13 L14,13 L14,7 Z M1,2 L6,2 L8,4 L15,4 L15,14 L1,14 L1,2 Z'
}`"
stroke-width="1"
fill="#8a8e99"
></path>
</svg>
<small>{
{ nodeData.label }}</small>
</template>
</DevTree>
</div>
</template>
<script setup>
import {
onMounted, ref, getCurrentInstance, toRaw } from 'vue'
import {
Tree as DevTree } from "vue-devui"
import 'vue-devui/style.css'
// 代理对象
const currentInstance = getCurrentInstance()
// 树型DOM引用
const treeRef = ref()
// 是否展开所有节点
const isExpandAllNodes = ref(false)
// 树型数据
const data = ref(
[
{
label: '香烟 WiFi 啤酒',
expanded: true,
children: [
{
label: '香烟',
children: [
{
label: '煊赫门'
},
{
label: 'ESSE双爆珠'
}
],
},
{
label: '后端开发技术',
children: [
{
label: 'Java编程技术'
},
{
label: 'Python编程技术'
}
],
},
{
label: '数据库',
children: [
{
label: '关系型数据库',
children: [
{
label: 'MySQL'
},
{
label: 'Oracle'
}
],
},
{
label: '非关系型数据库',
children: [
{
label: 'Redis'
},
{
label: 'Elasticsearch'
}
],
}
],
},
{
label: 'AI人工智能'
},
],
},
{
label: '火腿 iPad 泡面'
},
]
)
/**
* 是否展开所有节点句柄方法
*/
const handleIsExpandAllNodesChange = (val) => {
if (val) {
handleExpandAllNodes()
} else {
handleCollapseAllNodes()
}
}
/**
* 将树节点全部展开句柄方法
*/
const handleExpandAllNodes = () => {
treeRef.value.treeFactory.expandAllNodes()
}
/**
* 将树节点全部收起句柄方法
*/
const handleCollapseAllNodes = () => {
const treeFactory = treeRef.value.treeFactory
const treeData = treeFactory.treeData._rawValue
recursionCollapseNode(treeData)
}
/**
* 递归收起节点句柄方法
*/
const recursionCollapseNode = (treeData) => {
for (let node of treeData) {
if (node.expanded) {
treeRef.value.treeFactory.collapseNode(node)
}
if (node.children && node.children.length > 0) {
recursionCollapseNode(node.children)
}
}
}
onMounted(
async () => {
// Todo
}
)
</script>
<style lang="less" scoped>
.vue-devui-tree {
padding: 100px;
:deep(.devui-tree) {
.devui-tree__node-leaf {
// display: none;
position: relative;
width: 16px;
height: 16px;
margin-right: 6px;
// background-color: #ccc;
// border: 1px solid #ccc;
// border-radius: 2px;
}
.devui-tree__node-vline {
margin-left: 4.5px;
background-color: #cccccc !important;
}
.devui-tree__node-hline {
margin-left: 4.5px;
background-color: #cccccc !important;
}
.devui-tree__node {
.devui-tree__node-content {
&.active {
// background-color: unset;
small {
color: #5e7ce0;
}
}
&:hover {
// background-color: unset;
}
small {
font-size: 13px;
transition: all ease 0.3s;
&:hover {
color: #5e7ce0;
}
}
}
}
}
}
</style>
(2)效果如下~
4.高级示例
(1)示例代码
<template>
<div class="vue-devui-tree">
<DevTree ref="treeRef" :data="data" check>
<template #icon="{ nodeData, toggleNode }">
<span
v-if="!nodeData.isLeaf"
class="devui-tree__node-folder"
@click="
(event) => {
event.stopPropagation();
toggleNode(nodeData);
}
"
>
<!-- 未展开图标 -->
<svg v-if="!nodeData.expanded" style="margin-right: 7px" width="16px" height="16px" viewBox="0 0 16 16" version="1.1" xmlns="http://www.w3.org/2000/svg">
<g stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
<rect x="0.5" y="0.5" width="15" height="15" rx="2" stroke="#666666"></rect>
<path fill="#666666" d="M8.75,4 L8.75,7.25 L12,7.25 L12,8.75 L8.749,8.75 L8.75,12 L7.25,12 L7.249,8.75 L4,8.75 L4,7.25 L7.25,7.25 L7.25,4 L8.75,4 Z"></path>
</g>
</svg>
<!-- 已展开图标 -->
<svg v-else style="margin-right: 7px" width="16px" height="16px" viewBox="0 0 16 16" version="1.1" xmlns="http://www.w3.org/2000/svg">
<g stroke-width="1" fill="none" fill-rule="evenodd">
<rect x="0.5" y="0.5" width="15" height="15" rx="2" stroke="#666666"></rect>
<rect x="4" y="7" width="8" height="2" fill="#666666"></rect>
</g>
</svg>
</span>
<span
v-else
class="devui-tree__node-leaf"
>
</span>
</template>
<template #content="{ nodeData }">
<svg style="margin: 0 7px" viewBox="0 0 16 16" width="16" height="16">
<path
:d="`${
nodeData.isLeaf
? 'M13,6 L9,6 L9,5 L9,2 L3,2 L3,14 L13,14 L13,6 Z M12.5857864,5 L10,2.41421356 L10,5 L12.5857864,5 Z M2,1 L10,1 L14,5 L14,15 L2,15 L2,1 Z'
: nodeData.expanded
? 'M16,6 L14,14 L2,14 L0,6 L16,6 Z M14.7192236,7 L1.28077641,7 L2.78077641,13 L13.2192236,13 L14.7192236,7 Z M6,1 L8,3 L15,3 L15,5 L14,5 L14,4 L7.58578644,4 L5.58578644,2 L2,2 L2,5 L1,5 L1,1 L6,1 Z'
: 'M14,6 L14,5 L7.58578644,5 L5.58578644,3 L2,3 L2,6 L14,6 Z M14,7 L2,7 L2,13 L14,13 L14,7 Z M1,2 L6,2 L8,4 L15,4 L15,14 L1,14 L1,2 Z'
}`"
stroke-width="1"
fill="#8a8e99"
></path>
</svg>
<small>{
{ nodeData.label }}</small>
</template>
</DevTree>
</div>
</template>
<script setup lang="ts">
import {
onMounted, ref, getCurrentInstance, toRaw, useCssModule, provide } from 'vue'
import {
Tree as DevTree } from "vue-devui"
import 'vue-devui/style.css'
// 代理对象
const proxy = getCurrentInstance()
// 树型DOM引用
const treeRef = ref(null);
// 树型数据
const data = ref(
[
{
label: '香烟 WiFi 啤酒',
expanded: true,
children: [
{
label: '香烟',
children: [
{
label: '煊赫门'
},
{
label: 'ESSE双爆珠'
}
],
},
{
label: '后端开发技术',
children: [
{
label: 'Java编程技术'
},
{
label: 'Python编程技术'
}
],
},
{
label: '数据库',
children: [
{
label: '关系型数据库',
children: [
{
label: 'MySQL'
},
{
label: 'Oracle'
}
],
},
{
label: '非关系型数据库',
children: [
{
label: 'Redis'
},
{
label: 'Elasticsearch'
}
],
}
],
},
{
label: 'AI人工智能'
},
],
},
{
label: '火腿 iPad 泡面'
},
]
)
onMounted(() => {
// 展开全部节点
if (treeRef && treeRef.value) {
const treeFactory: any = treeRef.value['treeFactory']
treeFactory.expandAllNodes()
}
})
</script>
<style lang="less" scoped>
.vue-devui-tree {
padding: 100px;
:deep(.devui-tree) {
.devui-tree__node-leaf {
// display: none;
position: relative;
width: 16px;
height: 16px;
margin-right: 6px;
// background-color: #ccc;
// border: 1px solid #ccc;
// border-radius: 2px;
}
.devui-tree__node-vline {
margin-left: 4.5px;
background-color: #cccccc !important;
}
.devui-tree__node-hline {
margin-left: 4.5px;
background-color: #cccccc !important;
}
.devui-tree__node {
.devui-tree__node-content {
&.active {
// background-color: unset;
small {
color: #5e7ce0;
}
}
// &:hover {
// background-color: unset;
// }
small {
font-size: 13px;
transition: all ease 0.3s;
&:hover {
color: #5e7ce0;
}
}
}
}
}
}
</style>
(2)效果如下~