✅基本使用
<template>
<el-tree :data="data" :props="defaultProps" @node-click="handleNodeClick" />
</template>
<script>
export default {
data() {
return {
data: [
{
label: '一级 1',
children: [
{
label: '二级 1-1',
children: [{ label: '三级 1-1-1' }]
}
]
}
],
// 💡当前为defaultProps的默认值,可不写
defaultProps: {
label: 'label',
children: 'children'
}
}
},
methods: {
// 获取当前数据对象
// 比如 点击最后一项 输出 “{ label: '三级 1-1-1' }”
handleNodeClick(node) {
console.log(node)
}
}
}
</script>
✅复选+动态加载
<template>
<el-tree
:props="defaultProps"
lazy
:load="loadNode"
show-checkbox
@check-change="handleCheckChange"
/>
</template>
<script>
export default {
data() {
return {
defaultProps: {
label: 'name',
children: 'zones'
}
}
},
methods: {
// 加载子树数据
loadNode(node, resolve) {
// 往第1级添加数据
if (node.level === 0) { return resolve([{ name: '福建省' }, { name: '广东省' }]) }
// 第4级不再加载
if (node.level > 3) return resolve([])
// 接下来,实现动态加载节点数据
var hasChild
if (node.data.name === '福建省') {
hasChild = true
} else if (node.data.name === '广东省') {
hasChild = false
} else {
hasChild = Math.random() > 0.5
}
// 添加子元素进内部
setTimeout(() => {
var data
if (hasChild) {
data = [{ name: '厦门市' }]
} else {
data = []
}
resolve(data)
}, 500)
},
// 选中节点
handleCheckChange(data, checked, indeterminate) {
// data:原数据 / checked:选中状态 / indeterminate:原始选中状态
console.log(data, checked, indeterminate)
}
}
}
</script>
✅懒加载自定义叶子节点
提前告知 Tree 某个节点是否为叶子节点,从而避免在叶子节点前渲染下拉按钮。
<template>
<el-tree
:props="defaultProps"
lazy
:load="loadNode"
show-checkbox
/>
</template>
<script>
export default {
data() {
return {
defaultProps: {
label: 'name',
children: 'zones',
isLeaf: 'leaf'
}
}
},
methods: {
// 加载子树数据
loadNode(node, resolve) {
// 往1级添加数据
if (node.level === 0) return resolve([{ name: '福建省' }, { name: '广东省' }])
// 第2级(最后1级)不再加载
if (node.level > 1) return resolve([])
// 添加子元素进内部
setTimeout(() => {
const data = [{ name: '厦门市', leaf: 'true' }]
resolve(data)
}, 500)
}
}
}
</script>
✅默认展开和默认选中
default-expanded-keys
设置默认展开的节点;
default-checked-keys
设置默认选中的节点。
注意:此时必须设置 node-key
,其值为节点数据中的一个字段名,在整棵树中是唯一的。
<template>
<el-tree
:data="data"
show-checkbox
node-key="id"
:default-expanded-keys="[2, 3]"
:default-checked-keys="[5]"
/>
</template>
<script>
export default {
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'
}
]
}
]
}
}
}
</script>
✅禁用状态
在每一项,添加 disabled: true
,就可以实现了。
data: [
{
id: 1,
label: '一级',
children: [
{
id: 2,
label: '二级',
children: [
{
id: 3,
label: '三级',
disabled: true
}
]
}
]
}
]
✅树节点的选择
<template>
<div>
<el-tree
ref="tree"
:data="data"
show-checkbox
default-expand-all
node-key="id"
highlight-current
/>
<div class="buttons">
<el-button @click="getCheckedNodes">通过 node 获取</el-button>
<el-button @click="setCheckedNodes">通过 node 设置</el-button>
<el-button @click="getCheckedKeys">通过 key 获取</el-button>
<el-button @click="setCheckedKeys">通过 key 设置</el-button>
<el-button @click="resetChecked">清空</el-button>
</div>
</div>
</template>
<script>
export default {
data() {
return {
data: [
{
id: 1,
label: '一级',
children: [
{
id: 2,
label: '二级',
children: [
{
id: 3,
label: '三级'
}
]
}
]
}
]
}
},
methods: {
/* ⭐ 基于节点数据(少用❗) */
// 获取到选中的复选框的节点数据
getCheckedNodes() {
console.log(this.$refs.tree.getCheckedNodes())
},
// 根据节点数据回填复选框
setCheckedNodes() {
this.$refs.tree.setCheckedNodes([{ id: 2, label: '二级' }])
},
/* ⭐ 基于节点ID(常用👍) */
// 获取到选中的复选框的节点ID
getCheckedKeys() {
// 输出id组成的数组,比如[1,2,3]
console.log(this.$refs.tree.getCheckedKeys())
// 如果只想获取一级和二级的节点key集合
const allCheckedKeys = this.$refs.tree.getCheckedKeys()
const filteredKeys = allCheckedKeys.filter(key => {
// 判断是否是一级或二级节点
const node = this.$refs.tree.getNode(key)
return node.level === 1 || node.level === 2
})
console.log(filteredKeys) // 输出: [1, 2]
},
// 根据节点ID回填复选框
setCheckedKeys() {
this.$refs.tree.setCheckedKeys([3])
},
// 清空所有选中的状态
resetChecked() {
this.$refs.tree.setCheckedKeys([])
}
}
}
</script>
✅自定义节点内容
共两种方法
① render-content
<template>
<el-tree
:data="data"
show-checkbox
node-key="id"
default-expand-all
:expand-on-click-node="false"
:render-content="renderContent"
/>
</template>
<script>
let id = 1000
export default {
data() {
const 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'
}
]
}
]
return {
data: JSON.parse(JSON.stringify(data))
}
},
methods: {
// 新增(在Children的最后面)
append(data) {
const newChild = { id: id++, label: 'test', children: [] }
if (!data.children) this.$set(data, 'children', [])
data.children.push(newChild)
},
// 删除当前节点
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)
},
// 自定义 树节点内容
renderContent(h, { node, data, store }) {
return (
<span class='custom-tree-node'>
<span>{node.label}</span>
<span>
<el-button
size='mini'
type='text'
on-click={() => this.append(data)}
>
新增
</el-button>
<el-button
size='mini'
type='text'
on-click={() => this.remove(node, data)}
>
删除
</el-button>
</span>
</span>
)
}
}
}
</script>
<style lang="scss">
// ❗这里不能使用scoped,不然样式不生效
.custom-tree-node {
flex: 1;
display: flex;
align-items: center;
justify-content: space-between;
font-size: 14px;
padding-right: 8px;
}
</style>
② scoped slot
<template>
<el-tree
:data="data"
show-checkbox
node-key="id"
default-expand-all
:expand-on-click-node="false"
>
<span slot-scope="{ node, data }" class="custom-tree-node">
<span>{{ node.label }}</span>
<span>
<el-button type="text" size="mini" @click="() => append(data)">
新增
</el-button>
<el-button type="text" size="mini" @click="() => remove(node, data)">
删除
</el-button>
</span>
</span>
</el-tree>
</template>
<script>
let id = 1000
export default {
data() {
const 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'
}
]
}
]
return {
data: JSON.parse(JSON.stringify(data))
}
},
methods: {
// 新增(在Children的最后面)
append(data) {
const newChild = { id: id++, label: 'test', children: [] }
if (!data.children) this.$set(data, 'children', [])
data.children.push(newChild)
},
// 删除当前节点
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)
}
}
}
</script>
<style lang="scss">
// ❗这里不能使用scoped,不然样式不生效
.custom-tree-node {
flex: 1;
display: flex;
align-items: center;
justify-content: space-between;
font-size: 14px;
padding-right: 8px;
}
</style>
✅节点过滤
<template>
<div>
<el-input v-model="filterText" placeholder="输入关键字进行过滤" />
<el-tree
ref="tree"
:data="data"
default-expand-all
:filter-node-method="filterNode"
/>
</div>
</template>
<script>
export default {
data() {
return {
filterText: '',
data: [
{
id: 1,
label: '一级 1',
children: [
{
id: 2,
label: '二级 1-1',
children: [
{
id: 3,
label: '三级 1-1-1'
}
]
}
]
}
]
}
},
watch: {
// 过滤时调用Tree实例的filter方法
filterText(val) {
this.$refs.tree.filter(val)
}
},
methods: {
// 对树节点进行筛选
filterNode(value, data) {
// 如果输入为空,则全部显示,不进行过滤
if (!value) return true
// 否则,根据输入的内容进行过滤
return data.label.indexOf(value) !== -1
}
}
}
</script>
✅手风琴模式
通过 accordion
属性可让 Tree 树形控件 变为手风琴模式。
<el-tree accordion/>
✅可拖拽节点
通过 draggable
属性可让节点变为可拖拽。
<template>
<el-tree
:data="data"
node-key="id"
default-expand-all
draggable
:allow-drop="allowDrop"
:allow-drag="allowDrag"
@node-drag-start="handleDragStart"
@node-drag-enter="handleDragEnter"
@node-drag-leave="handleDragLeave"
@node-drag-over="handleDragOver"
@node-drag-end="handleDragEnd"
@node-drop="handleDrop"
/>
</template>
<script>
export default {
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',
children: [
{
id: 11,
label: '三级 3-2-1'
},
{
id: 12,
label: '三级 3-2-2'
},
{
id: 13,
label: '三级 3-2-3'
}
]
}
]
}
]
}
},
methods: {
// 节点开始拖拽时触发
handleDragStart(node, ev) {
console.log('drag start', node)
},
// 拖拽进入其他节点时触发
handleDragEnter(draggingNode, dropNode, ev) {
console.log('tree drag enter: ', dropNode.label)
},
// 拖拽离开某个节点时触发
handleDragLeave(draggingNode, dropNode, ev) {
console.log('tree drag leave: ', dropNode.label)
},
// 在拖拽节点时触发
handleDragOver(draggingNode, dropNode, ev) {
console.log('tree drag over: ', dropNode.label)
},
// 拖拽结束时
handleDragEnd(draggingNode, dropNode, dropType, ev) {
console.log('tree drag end: ', dropNode && dropNode.label, dropType)
},
// 拖拽成功完成时触发
handleDrop(draggingNode, dropNode, dropType, ev) {
console.log('tree drop: ', dropNode.label, dropType)
},
// 拖拽时判定目标节点能否被放置
allowDrop(draggingNode, dropNode, type) {
// type参数:'prev'、'inner'、'next'
// 放置在目标节点`前`、插入至`目标`节点、放置在目标节点`后`
if (dropNode.data.label === '二级 3-1') {
// '二级 3-1'节点只能放置在目标节点的前后
return type !== 'inner'
} else {
// 其它节点可任意放置
return true
}
},
// 判断节点能否被拖拽
allowDrag(draggingNode) {
// 当前'三级 3-2-2'节点不可拖拽
return draggingNode.data.label.indexOf('三级 3-2-2') === -1
}
}
}
</script>