Skip to content

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
Unlock
<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.

Multi-select
  • 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
附加icon
  • 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.

Basic Drag Sort
  • 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
Custom Function Config
  • 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.

New增移除
  • parent 1
    • child 1-1
    • child 1-2
下拉menu
  • 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.

currentId:00102
currentNode:{}
checked:[]
<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.

currentId:00102
currentNode:{}
<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.

Data Count:

暂无数据

<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

ParameterDescriptionTypeOptionsDefault
dataNested array of node properties for generating tree dataArray[]
multipleWhether to support multi-selectBooleanfalse
show-checkboxWhether to show checkboxesBooleanfalse
empty-textText displayed when there is no dataStringis not data
load-dataMethod for async loading data, see examplesFunction
title-keyDefine the title key, defaults to titleStringtitle
children-keyDefine the children key, defaults to childrenStringchildren
check-strictlyIn checkbox mode, whether to strictly maintain parent-child independenceBooleanfalse
check-directlyWhen enabled, selection interaction in show-checkbox mode will also toggle checkboxesBooleanfalse
lock-selectLock tree selection. Commonly used in business scenarios, e.g., to disable tree selection when a modal is openBooleanfalse
title-ellipsisEnable title text ellipsis for overflowBooleantrue
draggableEnable drag-and-drop for tree nodesBooleantrue
allow-dragDetermines whether a node can be dragged. Return false to prevent draggingFunction(node)
allow-dropDetermines 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-methodFilter function for tree nodesFunction
highlight-filterHighlight search keywords in textBooleantrue
timeoutRefresh interval (<b-big-tree> extended component only)Number17
itemHeightNode height (<b-big-tree> extended component only)Number28
visibleCountNumber of nodes shown in the visible area (<b-big-tree> extended component only)Number15

Events

Event NameDescriptionReturn Value
select-changeTriggers when a tree node is clickedArray of currently selected nodes, current item, flatState
check-changeTriggers when a checkbox is clickedArray of currently checked nodes, current item, array including half-checked nodes, flatState
toggle-expandTriggers when expanding or collapsing child listData of the current node
node-drag-startTriggers when node drag startsNode being dragged, event
node-drag-enterTriggers when dragged node entersNode being dragged, Node being entered, event
node-drag-leaveTriggers when dragged node leavesNode being dragged, Node being left, event
node-drag-overTriggers on dragover eventNode being dragged, Node being entered, event
node-drag-endTriggers when node drag endsNode being dragged, target Node, drop type, event
node-dragTriggers when node drag endsNode being dragged, target Node, drop type, event

Methods

Event NameDescriptionReturn Value
getFlatStateFlattened array buffer containing hierarchy and indices
getCheckedNodesGet checked nodes
getSelectedNodesGet selected nodes
getCheckedAndIndeterminateNodesGet selected and half-checked nodes
filterTree node filter function; requires filter-node-method to be set
setCheckedSet node checked state. Parameters: keys (array of nodeKeys), flag (checked state, default: true)
setSelectedSet node selected state. Parameters: keys (array of nodeKeys), flag (selected state, default: true), expandParent (whether to expand ancestor nodes, default: true)
setExpandSet node expanded state. Parameters: keys (array of nodeKeys), flag (expanded state, default: true)
expandAllExpand all
collapseAllCollapse all
checkAllCheck all
unselectAllDeselect all
uncheckAllUncheck all

Children

PropertyDescriptionTypeDefault
iconNode iconString
titleNode titleString
expandWhether to expand direct child nodesBooleanfalse
disabledDisabledBooleanfalse
disableCheckboxDisable checkboxBooleanfalse
selectedWhether to select child nodesBooleanfalse
checkedWhether to check (if checked, all child nodes will also be checked)Booleanfalse
visibleWhether to show the node (nodes can be hidden by setting visible to false)Booleanfalse
childrenArray of child node properties. Use the children-key prop on the tree component to customize.Array
loadingCan be set for async loading. Requires loadData to be configured.Boolean
isLeafWhether it is a leaf node. Controls whether to show the arrow. Can be used with async loading.Boolean