el-tree
el-tree
是 Element Plus 提供的树形控件,用于展示具有层级关系的数据,如组织架构、文件目录、分类菜单等。
一、基本用法
<template>
<el-tree :data="treeData" :props="defaultProps" @node-click="handleNodeClick" />
</template>
<script setup>
import { ref } from 'vue'
// 树形数据
const treeData = ref([
{
label: '一级 1',
children: [
{
label: '二级 1-1',
children: [
{ label: '三级 1-1-1' },
{ label: '三级 1-1-2' }
]
}
]
},
{
label: '一级 2',
children: [
{ label: '二级 2-1' },
{ label: '二级 2-2' }
]
}
])
// 节点渲染配置
const defaultProps = {
children: 'children',
label: 'label'
}
const handleNodeClick = (data, node, component) => {
console.log('节点被点击:', data)
}
</script>
✅ 说明:
data
:树的数据源,数组格式。props
:配置节点的label
和children
字段名。- 默认情况下,节点可展开/折叠。
二、核心属性(Props)
属性 | 类型 | 说明 | 默认值 |
---|---|---|---|
data |
array | 树的数据源 | [] |
props |
object | 配置节点的 label 、children 、disabled 等字段 |
{ label: 'label', children: 'children' } |
node-key |
string | 每个节点的唯一标识字段名(用于勾选、展开等状态持久化) | - |
default-expand-all |
boolean | 是否默认展开所有节点 | false |
expand-on-click-node |
boolean | 是否在点击节点时展开/折叠节点 | true |
check-on-click-node |
boolean | 是否在点击节点时选中节点 | false |
default-expanded-keys |
string[] | 默认展开的节点 key 数组 | [] |
auto-expand-parent |
boolean | 展开子节点时是否自动展开父节点 | true |
show-checkbox |
boolean | 节点是否可被选择(显示复选框) | false |
check-strictly |
boolean | 是否严格遵循父子不互相关联 | false |
default-checked-keys |
string[] | 默认勾选的节点 key 数组 | [] |
highlight-current |
boolean | 是否高亮当前选中节点 | false |
draggable |
boolean | 是否开启拖拽节点 | false |
allow-drag |
Function | 判断节点能否被拖拽 | - |
allow-drop |
Function | 拖拽时判定目标节点能否被放置 | - |
三、重要事件(Events)
事件名 | 说明 | 回调参数 |
---|---|---|
node-click |
节点被点击时触发 | (data, node, component) |
node-contextmenu |
右键节点时触发 | (event, data, node, component) |
check-change |
节点选中状态变化时触发 | (data, checked, indeterminate) |
check |
勾选节点时触发(父子联动) | (data, checkedStatus) |
current-change |
当前选中节点变化时触发 | (data, node) |
node-expand |
节点被展开时触发 | (data, node, component) |
node-collapse |
节点被折叠时触发 | (data, node, component) |
node-drag-start |
拖拽开始时触发 | (node, event) |
node-drag-end |
拖拽结束时触发 | (node, endDropNode, dropType, event) |
node-drop |
拖拽放置时触发 | (node, dragNode, dropType, event) |
四、常用方法(通过 ref 调用)
const treeRef = ref(null)
// 获取当前选中节点
treeRef.value.getCurrentKey()
// 设置当前选中节点
treeRef.value.setCurrentKey(key)
// 获取当前选中节点数据
treeRef.value.getCurrentNode()
// 获取被勾选节点(show-checkbox 时)
treeRef.value.getCheckedKeys() // 返回 key 数组
treeRef.value.getCheckedNodes() // 返回节点数据数组
// 设置节点勾选
treeRef.value.setCheckedKeys([key1, key2])
// 获取半选中节点(父子不联动时)
treeRef.value.getHalfCheckedKeys()
treeRef.value.getHalfCheckedNodes()
// 手动展开/折叠节点
treeRef.value.getNode(key).expanded = true
五、高级功能
1. 带复选框的树(可多选)
<el-tree
:data="treeData"
show-checkbox
node-key="id"
:default-checked-keys="[2]"
@check-change="handleCheckChange"
/>
const handleCheckChange = (data, checked, indeterminate) => {
console.log(`${data.label} 选中状态: ${checked}, 半选: ${indeterminate}`)
}
🔍
check-strictly
:设为true
时,父子节点选中状态不互相关联。
2. 异步加载树节点
适用于大数据量或按需加载场景。
<el-tree
:data="treeData"
node-key="id"
:props="asyncProps"
:load="loadNode"
lazy
/>
const asyncProps = {
children: 'children',
label: 'label',
isLeaf: 'leaf' // 标记是否为叶子节点
}
const loadNode = (node, resolve) => {
if (node.level === 0) {
return resolve([{ label: '根目录', id: 1 }])
}
if (node.level > 3) return resolve([])
// 模拟异步请求
setTimeout(() => {
const data = Array.from({ length: 3 }, (_, i) => ({
label: `子节点 ${node.data.label}-${i + 1}`,
id: Math.random(),
leaf: node.level >= 2 // 2层以下为叶子
}))
resolve(data)
}, 500)
}
3. 自定义节点内容
使用 #default
插槽自定义节点渲染。
<el-tree :data="treeData" :props="defaultProps">
<template #default="{ node, data }">
<span class="custom-tree-node">
<span>{{ node.label }}</span>
<span>
<el-button size="small" @click="() => append(data)">新增</el-button>
<el-button size="small" type="danger" @click="() => remove(node, data)">删除</el-button>
</span>
</span>
</template>
</el-tree>
4. 拖拽排序
<el-tree
:data="treeData"
node-key="id"
draggable
:allow-drop="allowDrop"
:allow-drag="allowDrag"
@node-drop="handleDrop"
/>
const allowDrop = (draggingNode, dropNode, type) => {
// 不允许将父节点拖拽到子节点下
if (type === 'inner' && draggingNode.data.children) {
return false
}
return true
}
const allowDrag = (draggingNode) => {
return draggingNode.data.label.indexOf('不可拖拽') === -1
}
const handleDrop = (dragNode, dropNode, dropType, ev) => {
ElMessage.success(`拖拽成功: ${dropType}`)
}
六、 完整案例
<script setup>
import { ref, onMounted } from 'vue'
import { ElMessage, ElMessageBox } from 'element-plus'
// 树引用
const treeRef = ref(null)
// 树数据
const treeData = ref([
{
id: 1,
label: '部门A',
children: [
{ id: 2, label: '小组A1' },
{ id: 3, label: '小组A2' }
]
},
{
id: 4,
label: '部门B',
children: [
{ id: 5, label: '小组B1' }
]
}
])
// 配置
const defaultProps = {
children: 'children',
label: 'label'
}
// 事件
const handleNodeClick = (data) => {
ElMessage.info(`点击了: ${data.label}`)
}
const handleCheckChange = (data, checked) => {
console.log(`${data.label} 勾选状态: ${checked}`)
}
const getCheckedKeys = () => {
const keys = treeRef.value.getCheckedKeys()
ElMessage.info('已勾选: ' + keys.join(', '))
}
const append = (data) => {
const newChild = { id: Date.now(), label: '新节点' }
if (!data.children) data.children = []
data.children.push(newChild)
}
const remove = (node, data) => {
const parent = node.parent
const children = parent.data.children || parent.data
const index = children.findIndex(d => d.id === data.id)
children.splice(index, 1)
}
onMounted(() => {
// 可在此处加载异步数据
})
</script>
<template>
<div class="p-4">
<h3 class="mb-4">组织架构树</h3>
<el-button size="small" @click="getCheckedKeys" class="mb-4">获取勾选节点</el-button>
<el-tree
ref="treeRef"
:data="treeData"
:props="defaultProps"
node-key="id"
show-checkbox
default-expand-all
highlight-current
@node-click="handleNode-click"
@check-change="handleCheckChange"
>
<template #default="{ node, data }">
<span class="flex items-center">
<span>{{ node.label }}</span>
<span class="ml-2">
<el-button type="primary" link size="small" @click="append(data)">新增</el-button>
<el-button type="danger" link size="small" @click="remove(node, data)">删除</el-button>
</span>
</span>
</template>
</el-tree>
</div>
</template>
<style scoped>
.custom-tree-node {
flex: 1;
display: flex;
align-items: center;
justify-content: space-between;
font-size: 14px;
padding-right: 8px;
}
</style>
✅ 最佳实践建议
- 必须设置
node-key
:用于状态持久化,否则勾选/展开状态会错乱。 - 大数据量使用
lazy
懒加载:避免一次性渲染卡顿。 - 合理使用
check-strictly
:根据业务决定父子是否联动。 - 拖拽功能谨慎使用:需配合后端保存新顺序。