Tree
Basic Usage
lockSelect locks the tree selection event, useful for controlling tree menu behavior in different scenarios.
- Level 1-1
- Level 2-1-1
- Level 3-1-1-1 Very Long Field Text Very Long Field Text
- Level 3-1-1-2
- Level 2-1-2
- Level 3-1-2-1
- Level 3-1-2-2
<template>
<div flex>
<div style="width: 300px">
<b-tree
:data="data"
:lock-select="lockSelect"
title-ellipsis
@select-change="handleSelect"
></b-tree>
</div>
<div style="width: 80px">
<b-switch v-model="lockSelect" size="large">
<template #open>Lock</template>
<template #close>Unlock</template>
</b-switch>
</div>
</div>
</template>
<script setup lang="ts">
import { ref } from 'vue'
const data = ref([
{
title: 'Level 1-1',
expand: true,
children: [
{
title: 'Level 2-1-1',
expand: true,
children: [
{ title: 'Level 3-1-1-1 Very Long Field Text Very Long Field Text' },
{ title: 'Level 3-1-1-2' }
]
},
{
title: 'Level 2-1-2',
expand: true,
children: [{ title: 'Level 3-1-2-1' }, { title: 'Level 3-1-2-2' }]
}
]
}
])
const lockSelect = ref(false)
function handleSelect(selected, node) {
console.log(selected, node)
}
</script>Additional Parameters
You can configure additional parameters such as id, code, etc. You can also specify the display field name for title, which defaults to title.
暂无数据
<template>
<div>
<b-button @click="initData">初始化数据并Defaultselect前端组</b-button>
<div flex class="mt-10">
<div style="width: 300px">
<b-tree ref="treeRef" :data="data" title-key="text" @select-change="handleSelect"></b-tree>
</div>
</div>
</div>
</template>
<script setup lang="ts">
import { ref, nextTick } from 'vue'
const data = ref([])
const defaultId = ref('00102')
const treeRef = ref(null)
function initData() {
data.value = [
{
id: '001',
text: '研发部',
deptCode: 'yfb',
status: '1',
desc: '研发中心',
parentId: null,
children: [
{
id: '00101',
text: '后端组',
deptCode: 'hd',
status: '1',
desc: '后端研发中心',
parentId: '001'
},
{
id: '00102',
text: '前端组',
deptCode: 'qd',
status: '1',
desc: '前端研发中心',
parentId: '001'
},
{
id: '00103',
text: 'UI设计',
deptCode: 'sj',
status: '1',
desc: '交互、ui设计中心',
parentId: '001'
},
{
id: '00104',
text: '测试组',
deptCode: 'cs',
status: '1',
desc: '测试组',
parentId: '001'
},
{
id: '00105',
text: '运维组',
deptCode: 'yw',
status: '1',
desc: '运维、服务、巡检',
parentId: '001'
}
]
},
{
id: '002',
text: '项目部',
deptCode: 'xmb',
status: '1',
desc: '项目服务部',
parentId: null,
children: [
{
id: '00201',
text: '开发组',
deptCode: 'kf',
status: '1',
desc: '后端项目开发',
parentId: '002'
},
{
id: '00202',
text: '交付服务组',
deptCode: 'jf',
status: '1',
desc: '交付项目,技术服务支持',
parentId: '002'
}
]
}
]
nextTick(() => {
if (!treeRef.value) return
// 获取树结构的拍平数据,查找当前需要select的节点值
const flatState = treeRef.value.getFlatState()
const current = flatState.find(node => node.node.id === defaultId.value)
if (current && current) {
treeRef.value.setSelected([current.nodeKey])
}
})
}
function handleSelect(selected, node) {
console.log(selected, node)
}
</script>Other Properties
Set show-checkbox to enable checkboxes, and configure default checked state in the data format.
expand, selected, checked, and disabled control expansion, selection, check state, and disabled state. multiple enables multi-selection.
- Level 1 1
- Level 2 1-1
- Level 3 1-1-1 我是超长字段我是超长字段我是超长字段
- Level 3 1-1-2
- Level 2 1-2
- Level 3 1-2-1
- Level 3 1-2-2
- navigation
- navigationmenu
- 图钉
- anchor
- breadcrumb
- Tab
<template>
<div flex>
<div class="p10" style="width: 300px; border-right: 1px solid #eeeeee">
<b-tag type="primary">Multi-select</b-tag>
<b-divider style="margin: 8px 0"></b-divider>
<b-tree
:data="data"
show-checkbox
multiple
@select-change="handleSelect"
@check-change="handleChecked"
></b-tree>
</div>
<div class="p10" style="width: 300px; border-right: 1px solid #eeeeee">
<b-tag type="primary">附加icon</b-tag>
<b-divider style="margin: 8px 0"></b-divider>
<b-tree :data="data1"></b-tree>
</div>
</div>
</template>
<script setup lang="ts">
import { ref } from 'vue'
const data = ref([
{
title: 'Level 1 1',
expand: true,
children: [
{
title: 'Level 2 1-1',
expand: true,
children: [
{ title: 'Level 3 1-1-1 我是超长字段我是超长字段我是超长字段' },
{ title: 'Level 3 1-1-2' }
]
},
{
title: 'Level 2 1-2',
expand: true,
children: [{ title: 'Level 3 1-2-1' }, { title: 'Level 3 1-2-2' }]
}
]
}
])
const data1 = ref([
{
title: 'navigation',
icon: 'apartment',
expand: true,
children: [
{ title: 'navigationmenu', icon: 'menu' },
{ title: '图钉', icon: 'pushpin' },
{ title: 'anchor', icon: 'attachment' },
{ title: 'breadcrumb', icon: 'right' },
{ title: 'Tab', icon: 'project' }
]
}
])
function handleSelect(selected, node) {
console.log(selected, node)
}
function handleChecked(checked, node) {
console.log(checked, node)
}
</script>Node Operations
The tree internally flattens all nodes into an array and assigns a unique nodeKey to each node, making it easy to get and set tree state.
- Level 1 1
- Level 1 2
<template>
<div>
<div class="mb-16">
<b-button size="small" @click="expandAll">Expand All</b-button>
<b-button size="small" @click="collapseAll">Collapse All</b-button>
<b-button size="small" @click="setExpand">Expand Level 3</b-button>
<b-button size="small" @click="checkAll">Select All</b-button>
<b-button size="small" @click="uncheckAll">Deselect All</b-button>
<b-button size="small" @click="setChecked">Select 1-2 and below</b-button>
<b-button size="small" @click="setSelected">Single select 1-2-1</b-button>
<b-button size="small" @click="clear">Clear single select and multi-select</b-button>
</div>
<div style="width: 300px">
<b-tree ref="treeRef" :data="data" show-checkbox></b-tree>
</div>
</div>
</template>
<script setup lang="ts">
import { ref } from 'vue'
const data = ref([
{
title: 'Level 1 1',
children: [
{
title: 'Level 2 1-1',
children: [{ title: 'Level 3 1-1-1' }, { title: 'Level 3 1-1-2' }]
},
{
title: 'Level 2 1-2',
children: [{ title: 'Level 3 1-2-1' }, { title: 'Level 3 1-2-2' }]
}
]
},
{
title: 'Level 1 2',
children: [
{
title: 'Level 2 2-1'
},
{
title: 'Level 2 2-2'
}
]
}
])
const treeRef = ref(null)
function expandAll() {
treeRef.value.expandAll()
}
function collapseAll() {
treeRef.value.collapseAll()
}
function checkAll() {
treeRef.value.checkAll()
}
function uncheckAll() {
treeRef.value.uncheckAll()
}
function setChecked() {
// Default gets the nodeKey index. For special cases like id, you can search all nodeKey values from flatState
treeRef.value.setChecked([5, 6])
}
function setSelected() {
treeRef.value.setSelected([5])
}
function clear() {
treeRef.value.uncheckAll()
treeRef.value.unselectAll()
}
function setExpand() {
treeRef.value.setExpand([0, 4])
}
</script>Async Child Node Loading
- root
- leaf
<template>
<div style="width: 300px">
<b-tree :data="data" show-checkbox :load-data="loadData"></b-tree>
</div>
</template>
<script setup lang="ts">
import { ref } from 'vue'
const data = ref([
{
title: 'root',
children: []
},
{
title: 'leaf',
isLeaf: true,
children: []
}
])
function loadData(item, callback) {
console.log(item)
setTimeout(() => {
let data = [
{
title: 'have-child',
loading: false,
children: []
},
{
title: 'no-child',
loading: false,
children: []
}
]
// Simulate requesting data when there are child items
if (item.title === 'have-child') {
data = [
{
title: 'child1',
loading: false,
isLeaf: false,
children: [],
visible: true
},
{
title: 'leaf node',
isLeaf: true,
children: []
}
]
} else if (item.title === 'no-child') {
// Set whether it's a leaf node on demand, or leave it unset
item['isLeaf'] = true
// If not set, the arrow is retained and can be clicked again
// data = []
}
callback(data)
}, 1000)
}
</script>Filterable
If the tree has many levels, use a filter function to filter nodes.
- Jiangsu Province
- Hebei Province
<template>
<div>
<b-input
v-model="query"
search
placeholder="Enter filter criteria and press Enter to search"
style="width: 230px"
@search="handleFilter"
></b-input>
<b-divider style="margin: 14px 0"></b-divider>
<div style="width: 300px">
<b-tree ref="treeRef" :data="data" :filter-node-method="filterNode" highlight-filter></b-tree>
</div>
</div>
</template>
<script setup lang="ts">
import { ref } from 'vue'
const data = ref([
{
id: '1',
title: 'Jiangsu Province',
children: [
{
id: '1-1',
title: 'Nanjing',
children: [
{ id: '1-1-1', title: 'Xuanwu District' },
{ id: '1-1-2', title: 'Gulou District' },
{ id: '1-1-3', title: 'Jianye District' },
{ id: '1-1-4', title: 'Qinhuai District' }
]
},
{
id: '1-2',
title: 'Wuxi',
children: [
{ id: '1-2-1', title: 'Xishan District' },
{ id: '1-2-2', title: 'Huishan District' },
{ id: '1-2-3', title: 'Binhu District' },
{ id: '1-2-4', title: 'Liangxi District' }
]
},
{
id: '1-3',
title: 'Xuzhou',
children: [
{ id: '1-3-1', title: 'Gulou District' },
{ id: '1-3-2', title: 'Yunlong District' },
{ id: '1-3-3', title: 'Quanshan District' },
{ id: '1-3-4', title: 'Tongshan District' },
{ id: '1-3-5', title: 'Jiawang District' },
{ id: '1-3-6', title: 'Pei County' },
{ id: '1-3-7', title: 'Feng County' },
{ id: '1-3-8', title: 'Suining County' },
{ id: '1-3-9', title: 'Xinyi City' },
{ id: '1-3-10', title: 'Pizhou City' }
]
}
]
},
{
id: '2',
title: 'Hebei Province',
children: [
{
id: '2-1',
title: 'Shijiazhuang',
children: [
{ id: '2-1-1', title: "Chang'an District" },
{ id: '2-1-2', title: 'Xinhua District' },
{ id: '2-1-3', title: 'Gulou District' }
]
}
]
}
])
const query = ref('')
const treeRef = ref(null)
const handleFilter = value => {
treeRef.value.filter(value)
}
const filterNode = (value, node) => {
if (!value) return true
// return node.title===value.trim()
return node.title.indexOf(value) !== -1
}
</script>Drag to Reorder
Set draggable to enable drag-and-drop for tree nodes. You will need to listen for several events to handle data update.
- Level 1 1
- Level 2 1-1
- Level 3 1-1-1
- Level 3 1-1-2
- Level 2 1-2
- Level 3 1-2-1
- Level 3 1-2-2
- Level 1 2
- Level 2 2-1
- Level 3 2-1-1
- Level 3 2-1-2
- Level 2 2-2
- Level 3 2-2-1
- Level 3 2-2-2
- Data Dimensions
- Country Hierarchy
- Country
- Province
- City
- Department
- Department Code
- Department Name
- Data Measures
- Default
- Statistics
- Department
- Parent Department
- Level
<template>
<div flex>
<div class="p10" style="width: 300px; border-right: 1px solid #eeeeee">
<b-tag type="primary">Basic Drag Sort</b-tag>
<b-divider style="margin: 8px 0"></b-divider>
<b-tree
ref="treeRef"
:data="data"
draggable
default-expand
@node-drag-start="handleDragStart"
@node-drag-enter="handleDragEnter"
@node-drag-leave="handleDragLeave"
@node-drag-end="handleDragEnd"
@node-drop="handleDrop"
></b-tree>
</div>
<div class="p10" style="width: 300px; border-right: 1px solid #eeeeee">
<b-tag type="primary">Custom Function Config</b-tag>
<b-divider style="margin: 8px 0"></b-divider>
<b-tree
:allow-drop="allowDrop"
:allow-drag="allowDrag"
:data="data1"
:render="renderContent1"
draggable
lock-select
default-expand
></b-tree>
</div>
</div>
</template>
<script setup lang="ts">
import { ref, h } from 'vue'
import { Message, BDropdown, BDropdownMenu, BDropdownItem } from 'bin-ui-design'
const data = ref([
{
title: 'Level 1 1',
children: [
{
title: 'Level 2 1-1',
children: [{ title: 'Level 3 1-1-1' }, { title: 'Level 3 1-1-2' }]
},
{
title: 'Level 2 1-2',
children: [{ title: 'Level 3 1-2-1' }, { title: 'Level 3 1-2-2' }]
}
]
},
{
title: 'Level 1 2',
children: [
{
title: 'Level 2 2-1',
children: [{ title: 'Level 3 2-1-1' }, { title: 'Level 3 2-1-2' }]
},
{
title: 'Level 2 2-2',
children: [{ title: 'Level 3 2-2-1' }, { title: 'Level 3 2-2-2' }]
}
]
}
])
const data1 = ref([
{
title: 'Data Dimensions',
nodeType: 'root',
children: [
{
title: 'Country Hierarchy',
nodeType: 'hierarchy',
children: [
{
field: 'country',
title: 'Country',
dataType: 'STRING',
type: 'dimension',
nodeType: 'attribute',
tableId: '0001'
},
{
field: 'province',
title: 'Province',
dataType: 'STRING',
type: 'dimension',
nodeType: 'attribute',
tableId: '0001'
},
{
field: 'city',
title: 'City',
dataType: 'STRING',
type: 'dimension',
nodeType: 'attribute',
tableId: '0001'
}
]
},
{
title: 'Department',
nodeType: 'folder',
children: [
{
field: 'dept_code',
title: 'Department Code',
dataType: 'STRING',
type: 'dimension',
nodeType: 'attribute',
tableId: '0002'
},
{
field: 'dept_name',
title: 'Department Name',
dataType: 'STRING',
type: 'dimension',
nodeType: 'attribute',
tableId: '0002'
}
]
}
]
},
{
title: 'Data Measures',
nodeType: 'root',
children: [
{
title: 'Default',
nodeType: 'folder',
children: [
{
field: 'count',
title: 'Statistics',
dataType: 'NUMBER',
type: 'measure',
nodeType: 'attribute',
tableId: '0001'
}
]
},
{
title: 'Department',
nodeType: 'folder',
children: [
{
field: 'parent_dept',
title: 'Parent Department',
dataType: 'NUMBER',
type: 'measure',
nodeType: 'attribute',
tableId: '0002'
},
{
field: 'level',
title: 'Level',
dataType: 'NUMBER',
type: 'measure',
nodeType: 'attribute',
tableId: '0002'
}
]
}
]
}
])
const treeRef = ref(null)
function allowDrop(draggingNode, dropNode, type) {
if (dropNode.nodeType === 'attribute') {
return type !== 'inner'
} else {
return dropNode.nodeType !== 'root'
}
}
function allowDrag(draggingNode) {
// Restrict drag nodes
return draggingNode.nodeType === 'attribute'
}
function renderContent1({ root, node, data }) {
const iconMap = {
root: '',
hierarchy: 'cluster',
folder: 'folder',
dimension: 'deploymentunit',
measure: 'linechart'
}
const colorMap = {
root: '#1089ff',
hierarchy: '#1089ff',
folder: '#35495e',
dimension: '#1089ff',
measure: '#52c41a'
}
const iconType = data.nodeType === 'attribute' ? data.type : data.nodeType
const inline = [
h(
'span',
{
class: 't-ellipsis',
style: { width: 'calc(100% - 24px)' },
title: `${data.title}-(${data.field ?? ''})`
},
[
h('i', {
class: ['b-iconfont', `b-icon-${iconMap[iconType]}`],
style: { fontSize: '16px', marginRight: '4px', color: colorMap[iconType] }
}),
data.title
]
),
h(
BDropdown,
{
trigger: 'click',
appendToBody: true,
placement: 'bottom-start',
onCommand: name => {
console.log(name, data)
Message(`${name} node: [${data.title}]`)
}
},
{
default: () => h('i', { class: ['b-iconfont', 'b-icon-setting', 'setting-action'] }),
dropdown: () =>
h(BDropdownMenu, () => [
h(BDropdownItem, { name: 'edit' }, () => [
h('i', { class: 'b-iconfont b-icon-edit-square' }),
'Edit'
]),
h(BDropdownItem, { name: 'delete' }, () => [
h('i', { class: 'b-iconfont b-icon-delete' }),
'Delete'
])
])
}
)
]
return h('span', { style: { width: '100%', fontSize: '12px' }, flex: 'main:justify' }, inline)
}
function handleDragStart(node, ev) {
console.log('drag start', node)
}
function handleDragEnter(draggingNode, dropNode, ev) {
console.log('tree drag enter: ', dropNode.title)
}
function handleDragLeave(draggingNode, dropNode, ev) {
console.log('tree drag leave: ', dropNode.title)
}
function handleDragEnd(draggingNode, dropNode, dropType, ev) {
console.log('tree drag end: ', dropNode && dropNode.title, dropType)
}
function handleDrop(draggingNode, dropNode, dropType, ev) {
console.log('tree drop: ', dropNode.title, dropType)
}
</script>Render Function
Use the render function for more advanced custom effects.
- parent 1
- child 1-1
- child 1-2
- navigation
- navigationmenu
- 图钉
- anchor
- breadcrumb
- Tab
<template>
<div flex>
<div class="p10" style="width: 300px; border-right: 1px solid #eeeeee">
<b-tag type="primary">New增移除</b-tag>
<b-divider style="margin: 8px 0"></b-divider>
<b-tree ref="tree" :data="data" :render="renderContent"></b-tree>
</div>
<div class="p10" style="width: 300px; border-right: 1px solid #eeeeee">
<b-tag type="primary">下拉menu</b-tag>
<b-divider style="margin: 8px 0"></b-divider>
<b-tree :data="data1" :render="renderContent1"></b-tree>
</div>
</div>
</template>
<script setup lang="ts">
import { ref, h } from 'vue'
import { Message, BDropdown, BDropdownMenu, BDropdownItem } from 'bin-ui-design'
const data = ref([
{
title: 'parent 1',
expand: true,
render: ({ root, node, data }) => {
return h(
'span',
{
style: {
display: 'inline-flex',
justifyContent: 'space-between',
width: '100%'
}
},
[
h('span', data.title),
h('i', {
class: ['b-iconfont', 'b-icon-plus-square-fill'],
style: { fontSize: '16px', color: '#1089ff', marginRight: '4px' },
onClick: () => {
append(data)
}
})
]
)
},
children: [{ title: 'child 1-1' }, { title: 'child 1-2' }]
}
])
const data1 = ref([
{
title: 'navigation',
icon: 'apartment',
expand: true,
children: [
{ title: 'navigationmenu', icon: 'menu' },
{ title: '图钉', icon: 'pushpin' },
{ title: 'anchor', icon: 'attachment' },
{ title: 'breadcrumb', icon: 'right' },
{ title: 'Tab', icon: 'project' }
]
}
])
function append(node) {
const children = node.children || []
node.expand = true
children.push({ title: 'new node' })
node.children = children
data.value = [...data.value]
}
function remove(root, node, data) {
console.log(root, node, data)
const parentKey = root.find(el => el === node).parent
const parent = root.find(el => el.nodeKey === parentKey).node
const index = parent.children.indexOf(data)
parent.children.splice(index, 1)
}
function renderContent({ root, node, data }) {
return h(
'span',
{
style: { display: 'inline-flex', justifyContent: 'space-between', width: '100%' }
},
[
h('span', { class: 't-ellipsis', style: { width: 'calc(100% - 36px)' } }, data.title),
h('span', { style: { width: '36px' } }, [
h('i', {
class: ['b-iconfont', 'b-icon-plus-square-fill'],
style: { fontSize: '16px', color: '#5d6d7e' },
onClick: e => {
e.stopPropagation()
append(data)
}
}),
h('i', {
class: ['b-iconfont', 'b-icon-minus-square-fill'],
style: { fontSize: '16px', color: '#f5222d' },
onClick: e => {
e.stopPropagation()
remove(root, node, data)
}
})
])
]
)
}
function renderContent1({ root, node, data }) {
const inline = [
h(
'span',
{
class: 't-ellipsis',
style: { width: 'calc(100% - 24px)' },
title: `${data.title}`
},
[
h('i', {
class: ['b-iconfont', `b-icon-${data.icon}`],
style: { marginRight: '4px' }
}),
data.title
]
),
h(
BDropdown,
{
trigger: 'click',
appendToBody: true,
placement: 'bottom-start',
onCommand: name => {
console.log(name, data)
Message(`${name} node: [${data.title}]`)
}
},
{
default: () => h('i', { class: ['b-iconfont', 'b-icon-setting', 'setting-action'] }),
dropdown: () =>
h(BDropdownMenu, () => [
h(BDropdownItem, { name: 'edit' }, () => [
h('i', { class: 'b-iconfont b-icon-edit-square' }),
'Edit'
]),
h(BDropdownItem, { name: 'delete' }, () => [
h('i', { class: 'b-iconfont b-icon-delete' }),
'Delete'
])
])
}
)
]
return h('span', { style: { width: '100%', fontSize: '12px' }, flex: 'main:justify' }, inline)
}
</script>Tree Select
Due to the many tree selection scenarios, a separate tree-select component is also provided for use in forms.
<template>
<div>
<div flex>
<div flex>
<b-tree-select
v-model="currentId"
style="width: 300px"
:data="data"
title-key="text"
@change="handleChange"
></b-tree-select>
<b-button @click="defaultSelect">Set Default: Frontend Team</b-button>
</div>
<div flex class="ml-16">
<b-tree-select
v-model="currentId"
v-model:checked="checked"
style="width: 300px"
:data="data"
title-key="text"
show-checkbox
@change="handleChange"
></b-tree-select>
<b-button @click="setChecked">Set Default: Project Dept</b-button>
</div>
</div>
<div class="pt-8 pb-8">
<b-button @click="clear">Clear Selection</b-button>
</div>
<div>currentId:{{ currentId }}</div>
<div>currentNode:{{ currentNode }}</div>
<div>checked:{{ checked }}</div>
</div>
</template>
<script setup lang="ts">
import { ref } from 'vue'
const data = ref([
{
id: '001',
text: 'R&D Department',
deptCode: 'yfb',
status: '1',
desc: 'R&D Center',
parentId: null,
children: [
{
id: '00101',
text: 'Backend Team',
deptCode: 'hd',
status: '1',
desc: 'Backend R&D Center',
parentId: '001'
},
{
id: '00102',
text: 'Frontend Team',
deptCode: 'qd',
status: '1',
desc: 'Frontend R&D Center',
parentId: '001'
},
{
id: '00103',
text: 'UI Design',
deptCode: 'sj',
status: '1',
desc: 'Interaction & UI Design Center',
parentId: '001'
},
{
id: '00104',
text: 'Testing Team',
deptCode: 'cs',
status: '1',
desc: 'Testing Team',
parentId: '001'
},
{
id: '00105',
text: 'Operations Team',
deptCode: 'yw',
status: '1',
desc: 'Operations, Services, and Inspections',
parentId: '001'
}
]
},
{
id: '002',
text: 'Project Department',
deptCode: 'xmb',
status: '1',
desc: 'Project Services Department',
parentId: null,
children: [
{
id: '00201',
text: 'Development Team',
deptCode: 'kf',
status: '1',
desc: 'Backend Project Development',
parentId: '002'
},
{
id: '00202',
text: 'Delivery Service Team',
deptCode: 'jf',
status: '1',
desc: 'Project Delivery and Technical Service Support',
parentId: '002'
}
]
}
])
const currentId = ref('00102')
const currentNode = ref({})
const checked = ref([])
function defaultSelect() {
currentId.value = '00102'
}
function handleChange(val, node) {
currentNode.value = node ? { id: node.id, text: node.text } : {}
}
function setChecked() {
checked.value = ['002', '00201', '00202']
}
function clear() {
currentId.value = ''
currentNode.value = {}
checked.value = []
}
</script>Tree Validation
Can be used with form validation.
<template>
<div>
<div flex>
<b-form ref="ruleFormRef" :model="obj" label-width="85px" :rules="ruleValidate">
<b-form-item prop="currentId" label="树结构">
<b-tree-select
v-model="obj.currentId"
style="width: 300px"
:data="data"
title-key="text"
clearable
@change="handleChange"
></b-tree-select>
</b-form-item>
<b-form-item>
<b-button type="primary" @click="submitForm">Submit</b-button>
<b-button @click="resetForm">Reset</b-button>
</b-form-item>
</b-form>
</div>
<div>currentId:{{ obj.currentId }}</div>
<div>currentNode:{{ currentNode }}</div>
</div>
</template>
<script setup lang="ts">
import { ref } from 'vue'
const data = ref([
{
id: '001',
text: '研发部',
deptCode: 'yfb',
status: '1',
desc: '研发中心',
parentId: null,
children: [
{
id: '00101',
text: '后端组',
deptCode: 'hd',
status: '1',
desc: '后端研发中心',
parentId: '001'
},
{
id: '00102',
text: '前端组',
deptCode: 'qd',
status: '1',
desc: '前端研发中心',
parentId: '001'
},
{
id: '00103',
text: 'UI设计',
deptCode: 'sj',
status: '1',
desc: '交互、ui设计中心',
parentId: '001'
},
{
id: '00104',
text: '测试组',
deptCode: 'cs',
status: '1',
desc: '测试组',
parentId: '001'
},
{
id: '00105',
text: '运维组',
deptCode: 'yw',
status: '1',
desc: '运维、服务、巡检',
parentId: '001'
}
]
},
{
id: '002',
text: '项目部',
deptCode: 'xmb',
status: '1',
desc: '项目服务部',
parentId: null,
children: [
{
id: '00201',
text: '开发组',
deptCode: 'kf',
status: '1',
desc: '后端项目开发',
parentId: '002'
},
{
id: '00202',
text: '交付服务组',
deptCode: 'jf',
status: '1',
desc: '交付项目,技术服务支持',
parentId: '002'
}
]
}
])
const obj = ref({
currentId: '00102'
})
const ruleFormRef = ref(null)
const currentNode = ref({})
const ruleValidate = {
currentId: [{ required: true, message: '树Cannot be empty', trigger: 'change' }]
}
function handleChange(val, node) {
// console.log(val, node)
currentNode.value = node ? { id: node.id, text: node.text } : {}
}
function submitForm() {
ruleFormRef.value.validate(valid => {
if (valid) {
alert('submit!')
} else {
console.log('error submit!!')
return false
}
})
}
function resetForm() {
ruleFormRef.value.resetFields()
}
</script>Big-Tree (Large Data)
To render extremely large tree structures, use the extended component <b-big-tree>. It reuses all tree APIs and optimizes performance by filtering and operating on nodes within the visible viewport. However, to maintain performance, some user experience trade-offs are necessary. Large data trees do not support animated expand/collapse effects.
Default node height is 28px. Use visible-count to specify the number of nodes shown in the visible area (default: 15, i.e., 420px). Since this is optimized for viewport scrolling, you must specify the container height.
暂无数据
<template>
<div>
<div style="margin-bottom: 8px">
<b-space>
Data Count:
<b-input-number v-model="number" style="width: 120px"></b-input-number>
<b-button @click="init">render</b-button>
<b-input
v-model="query"
search
placeholder="Enter filter criteria and press Enter to filter"
style="width: 230px"
@search="handleFilter"
></b-input>
<b-button-group>
<b-button @click="expandAll">Expand All</b-button>
<b-button @click="collapseAll">Collapse All</b-button>
</b-button-group>
</b-space>
</div>
<b-divider style="margin: 14px 0"></b-divider>
<b-big-tree
ref="treeRef"
:data="treedata"
:visible-count="10"
:filter-node-method="filterNode"
@select-change="handleSelected"
></b-big-tree>
</div>
</template>
<script setup lang="ts">
import { ref } from 'vue'
const treedata = ref([])
const number = ref(5000)
const query = ref('')
const treeRef = ref(null)
let maxNode = 10000 // Maximum node count
const childNodesNumber = [2, 5] // Child node count
const maxLevel = 3 // Maximum nesting level
const childRate = 0.4 // Probability of having child nodes
const label = 'Node' // Node label
let index = 0
const data = []
const randomInteger = function (min, max) {
let result = min - 0.5 + Math.random() * (max - min + 1)
result = Math.round(result)
return result
}
const generateId = function () {
++index
return Math.random().toString().slice(3) * 1
}
const generateNode = function () {
const id = generateId()
return {
id: id,
title: `${label}_${index}-id:${id}`
}
}
const generateChild = function (tree, level = 1) {
if (index > maxNode - 1) return
tree.children = []
const childNumber = randomInteger(childNodesNumber[0], childNodesNumber[1])
for (let i = 0; i < childNumber; i++) {
if (index > maxNode - 1) break
const obj = generateNode()
if (Math.random() < childRate && level < maxLevel) {
generateChild(obj, ++level)
}
tree.children.push(obj)
}
}
const generate = function (number) {
// eslint-disable-next-line no-const-assign
maxNode = number
// eslint-disable-next-line no-unmodified-loop-condition
while (index < maxNode) {
let obj = generateNode()
generateChild(obj)
data.push(obj)
}
return data
}
function init() {
treedata.value = generate(number.value)
}
function handleSelected(allSelected, node) {
console.log(allSelected, node)
}
function expandAll() {
treeRef.value.expandAll()
}
function collapseAll() {
treeRef.value.collapseAll()
}
function handleFilter(value) {
treeRef.value.filter(value)
}
function filterNode(value, node) {
if (!value) return true
// return node.title===value.trim()
return node.title.indexOf(value) !== -1
}
</script>Props
| Parameter | Description | Type | Options | Default |
|---|---|---|---|---|
| data | Nested array of node properties for generating tree data | Array | — | [] |
| multiple | Whether to support multi-select | Boolean | — | false |
| show-checkbox | Whether to show checkboxes | Boolean | — | false |
| empty-text | Text displayed when there is no data | String | — | is not data |
| load-data | Method for async loading data, see examples | Function | — | — |
| title-key | Define the title key, defaults to title | String | — | title |
| children-key | Define the children key, defaults to children | String | — | children |
| check-strictly | In checkbox mode, whether to strictly maintain parent-child independence | Boolean | — | false |
| check-directly | When enabled, selection interaction in show-checkbox mode will also toggle checkboxes | Boolean | — | false |
| lock-select | Lock tree selection. Commonly used in business scenarios, e.g., to disable tree selection when a modal is open | Boolean | — | false |
| title-ellipsis | Enable title text ellipsis for overflow | Boolean | — | true |
| draggable | Enable drag-and-drop for tree nodes | Boolean | — | true |
| allow-drag | Determines whether a node can be dragged. Return false to prevent dragging | Function(node) | — | — |
| allow-drop | Determines whether the target can be a drop position. Return false to prevent. The type parameter has three values: 'prev', 'inner', and 'next' (before, inside, and after the target node) | Function(draggingNode, dropNode, type) | — | — |
| filter-node-method | Filter function for tree nodes | Function | — | — |
| highlight-filter | Highlight search keywords in text | Boolean | — | true |
| timeout | Refresh interval (<b-big-tree> extended component only) | Number | — | 17 |
| itemHeight | Node height (<b-big-tree> extended component only) | Number | — | 28 |
| visibleCount | Number of nodes shown in the visible area (<b-big-tree> extended component only) | Number | — | 15 |
Events
| Event Name | Description | Return Value |
|---|---|---|
| select-change | Triggers when a tree node is clicked | Array of currently selected nodes, current item, flatState |
| check-change | Triggers when a checkbox is clicked | Array of currently checked nodes, current item, array including half-checked nodes, flatState |
| toggle-expand | Triggers when expanding or collapsing child list | Data of the current node |
| node-drag-start | Triggers when node drag starts | Node being dragged, event |
| node-drag-enter | Triggers when dragged node enters | Node being dragged, Node being entered, event |
| node-drag-leave | Triggers when dragged node leaves | Node being dragged, Node being left, event |
| node-drag-over | Triggers on dragover event | Node being dragged, Node being entered, event |
| node-drag-end | Triggers when node drag ends | Node being dragged, target Node, drop type, event |
| node-drag | Triggers when node drag ends | Node being dragged, target Node, drop type, event |
Methods
| Event Name | Description | Return Value |
|---|---|---|
| getFlatState | Flattened array buffer containing hierarchy and indices | |
| getCheckedNodes | Get checked nodes | — |
| getSelectedNodes | Get selected nodes | — |
| getCheckedAndIndeterminateNodes | Get selected and half-checked nodes | — |
| filter | Tree node filter function; requires filter-node-method to be set | — |
| setChecked | Set node checked state. Parameters: keys (array of nodeKeys), flag (checked state, default: true) | — |
| setSelected | Set node selected state. Parameters: keys (array of nodeKeys), flag (selected state, default: true), expandParent (whether to expand ancestor nodes, default: true) | — |
| setExpand | Set node expanded state. Parameters: keys (array of nodeKeys), flag (expanded state, default: true) | — |
| expandAll | Expand all | — |
| collapseAll | Collapse all | — |
| checkAll | Check all | — |
| unselectAll | Deselect all | — |
| uncheckAll | Uncheck all | — |
Children
| Property | Description | Type | Default |
|---|---|---|---|
| icon | Node icon | String | — |
| title | Node title | String | — |
| expand | Whether to expand direct child nodes | Boolean | false |
| disabled | Disabled | Boolean | false |
| disableCheckbox | Disable checkbox | Boolean | false |
| selected | Whether to select child nodes | Boolean | false |
| checked | Whether to check (if checked, all child nodes will also be checked) | Boolean | false |
| visible | Whether to show the node (nodes can be hidden by setting visible to false) | Boolean | false |
| children | Array of child node properties. Use the children-key prop on the tree component to customize. | Array | — |
| loading | Can be set for async loading. Requires loadData to be configured. | Boolean | — |
| isLeaf | Whether it is a leaf node. Controls whether to show the arrow. Can be used with async loading. | Boolean | — |