Tree 樹狀元件
扁平資料 → 樹的轉換與勾選傳播都在 core 純函式裡完成,元件只負責渲染與鍵盤。
基礎用法
基礎用法
Vue
React
後端
運維
vue
<script setup>
import { ref } from 'vue'
const treeData = [
{ key: 'a', label: '前端', children: [
{ key: 'a-1', label: 'Vue' },
{ key: 'a-2', label: 'React' },
] },
{ key: 'b', label: '后端', children: [{ key: 'b-1', label: 'Node' }] },
{ key: 'c', label: '运维', disabled: true },
]
</script>
<template>
<!-- 基礎用法 -->
<LhTree :data="treeData" :default-expanded-keys="['a']" />
</template>vue
<script>
export default {
data() {
return {
treeData: [
{ key: 'a', label: '前端', children: [
{ key: 'a-1', label: 'Vue' },
{ key: 'a-2', label: 'React' },
] },
{ key: 'b', label: '后端', children: [{ key: 'b-1', label: 'Node' }] },
{ key: 'c', label: '运维', disabled: true },
],
}
},
}
</script>
<template>
<!-- 基礎用法 -->
<LhTree :data="treeData" :default-expanded-keys="['a']" />
</template>可勾選(級聯)
勾選父級會級聯全部後代;子級全選時父級自動選中,部分選中顯示半選態(indeterminate)。
勾選級聯
Vue
React
Node
運維
vue
<script setup lang="ts">
const treeData = [
{ key: 'a', label: '前端', children: [
{ key: 'a-1', label: 'Vue' },
{ key: 'a-2', label: 'React' },
] },
{ key: 'b', label: '后端', children: [{ key: 'b-1', label: 'Node' }] },
{ key: 'c', label: '运维', disabled: true },
]
</script>
<template>
<!-- 勾選級聯 -->
<LhTree :data="treeData" show-checkbox :default-expanded-keys="['a', 'b']" />
</template>vue
<script>
export default {
data() {
return {
treeData: [
{ key: 'a', label: '前端', children: [
{ key: 'a-1', label: 'Vue' },
{ key: 'a-2', label: 'React' },
] },
{ key: 'b', label: '后端', children: [{ key: 'b-1', label: 'Node' }] },
{ key: 'c', label: '运维', disabled: true },
],
}
},
}
</script>
<template>
<!-- 勾選級聯 -->
<LhTree :data="treeData" show-checkbox :default-expanded-keys="['a', 'b']" />
</template>Lazy 載入
開啟 lazy 後,展開未載入的節點會呼叫 load 回呼(resolve 提交子節點並展開,reject 取消);載入中箭頭顯示旋轉態。 可載入節點須明確宣告 isLeaf: false(否則無 children 的節點會被推斷為葉子、不顯示展開箭頭); 載入下來的子節點如仍是待載入分支,同樣宣告 isLeaf: false,真葉子宣告 isLeaf: true。
Lazy 載入
點我加載
vue
<script setup>
import { ref } from 'vue'
const lazyData = ref([
{ key: 'lazy', label: '点我加载', isLeaf: false },
])
function loadNodes(node, resolve, reject) {
// 模擬非同步請求
setTimeout(() => {
if (node.key === 'lazy') {
resolve([
{ key: 'lazy-1', label: '子节点一', isLeaf: true },
{ key: 'lazy-2', label: '子节点二', isLeaf: true },
])
} else {
reject(new Error('nothing to load'))
}
}, 600)
}
</script>
<template>
<!-- Lazy 載入 -->
<LhTree :data="lazyData" lazy :load="loadNodes" />
</template>vue
<script>
export default {
data() {
return {
lazyData: [{ key: 'lazy', label: '点我加载', isLeaf: false }],
}
},
methods: {
// 模擬非同步請求
loadNodes(node, resolve, reject) {
setTimeout(() => {
if (node.key === 'lazy') {
resolve([
{ key: 'lazy-1', label: '子节点一', isLeaf: true },
{ key: 'lazy-2', label: '子节点二', isLeaf: true },
])
} else {
reject(new Error('nothing to load'))
}
}, 600)
},
},
}
</script>
<template>
<!-- Lazy 載入 -->
<LhTree :data="lazyData" lazy :load="loadNodes" />
</template>父子不聯動(checkStrictly)
開啟 checkStrictly 後勾選只作用於節點自身:不級聯後代、不回溯祖先、無半選態。
checkStrictly
Vue
React
Node
運維
vue
<script setup lang="ts">
const treeData = [
{ key: 'a', label: '前端', children: [
{ key: 'a-1', label: 'Vue' },
{ key: 'a-2', label: 'React' },
] },
{ key: 'b', label: '后端', children: [{ key: 'b-1', label: 'Node' }] },
{ key: 'c', label: '运维', disabled: true },
]
</script>
<template>
<!-- 父子不聯動 -->
<LhTree :data="treeData" show-checkbox check-strictly :default-expanded-keys="['a', 'b']" />
</template>vue
<script>
export default {
data() {
return {
treeData: [
{ key: 'a', label: '前端', children: [
{ key: 'a-1', label: 'Vue' },
{ key: 'a-2', label: 'React' },
] },
{ key: 'b', label: '后端', children: [{ key: 'b-1', label: 'Node' }] },
{ key: 'c', label: '运维', disabled: true },
],
}
},
}
</script>
<template>
<!-- 父子不聯動 -->
<LhTree :data="treeData" show-checkbox check-strictly :default-expanded-keys="['a', 'b']" />
</template>選中與受控鍵值
multiple 允許多選;expand-on-click-node 點擊節點即展開/收起;default-selected-keys / v-model:selected-keys 受控選中;default-checked-keys / v-model:checked-keys 受控勾選;default-expanded-keys / v-model:expanded-keys 受控展開;disabled 停用整棵樹。
受控選中與勾選
色彩
字體
工程
選中:[ "x" ] | 勾選:[ "x-1" ]
停用整棵樹
設計
工程
vue
<script setup>
import { ref } from 'vue'
const selKeys = ref(['x'])
const chkKeys = ref(['x-1'])
const ctrlData = [
{ key: 'x', label: '设计', children: [
{ key: 'x-1', label: '色彩' },
{ key: 'x-2', label: '字体' },
] },
{ key: 'y', label: '工程' },
]
</script>
<template>
<!-- 受控選中與勾選 -->
<LhTree
v-model:selected-keys="selKeys"
v-model:checked-keys="chkKeys"
:data="ctrlData"
multiple
expand-on-click-node
:default-expanded-keys="['x']"
:default-selected-keys="['x']"
:default-checked-keys="['x-1']"
/>
<p class="demo-tree-log">选中:{{ selKeys }} | 勾选:{{ chkKeys }}</p>
<!-- 停用整棵樹 -->
<LhTree :data="ctrlData" disabled />
</template>vue
<script>
export default {
data() {
return {
selKeys: ['x'],
chkKeys: ['x-1'],
ctrlData: [
{ key: 'x', label: '设计', children: [
{ key: 'x-1', label: '色彩' },
{ key: 'x-2', label: '字体' },
] },
{ key: 'y', label: '工程' },
],
}
},
}
</script>
<template>
<!-- 受控選中與勾選 -->
<LhTree
v-model:selected-keys="selKeys"
v-model:checked-keys="chkKeys"
:data="ctrlData"
multiple
expand-on-click-node
:default-expanded-keys="['x']"
:default-selected-keys="['x']"
:default-checked-keys="['x-1']"
/>
<p class="demo-tree-log">选中:{{ selKeys }} | 勾选:{{ chkKeys }}</p>
<!-- 停用整棵樹 -->
<LhTree :data="ctrlData" disabled />
</template>API
LhTree Props
| 屬性名 | 說明 | 類型 | 預設值 |
|---|---|---|---|
| data | 樹狀資料 | [LhTreeNode](#type-LhTreeNode)[] | — |
| showCheckbox | 顯示勾選框 | boolean | false |
| multiple | 多選(點擊節點切換選中而非替換) | boolean | false |
| expandOnClickNode | 點擊非葉子節點時展開/收起 | boolean | true |
| defaultExpandedKeys | 預設展開的鍵(非受控) | string[] | () => [] |
| expandedKeys | 受控展開的鍵 | string[] | — |
| defaultSelectedKeys | 預設選中的鍵(非受控) | string[] | () => [] |
| selectedKeys | 受控選中的鍵 | string[] | — |
| defaultCheckedKeys | 預設勾選的鍵(非受控) | string[] | () => [] |
| checkedKeys | 受控勾選的鍵 | string[] | — |
| checkStrictly | 父子勾選不聯動(勾選只作用於自身,無級聯與半選) | boolean | false |
| lazy | Lazy 載入模式(展開未載入節點時呼叫 load) | boolean | false |
| load | Lazy 載入回呼(resolve 提交子節點並展開,reject 取消) | (node: LhTreeNode, resolve: (children: LhTreeNode[]) => void, reject: (reason?: unknown) => void) => void | — |
| disabled | 整體停用 | boolean | false |
| sx | sx 快速版面配置屬性 | LhSxProps | — |
Events
| 事件名 | 說明 | 回呼參數 |
|---|---|---|
| update:expandedKeys | 展開鍵列表變化時觸發,配合 v-model:expanded-keys 使用 | (keys: string[]) |
| update:selectedKeys | 選中鍵列表變化時觸發,配合 v-model:selected-keys 使用 | (keys: string[]) |
| update:checkedKeys | 勾選鍵列表變化時觸發,配合 v-model:checked-keys 使用 | (keys: string[]) |
| select | 節點被點擊選中 / 切換選中後觸發 | (key: string, node: LhTreeNode) |
| check | 勾選變化後觸發 | (keys: string[], node: LhTreeNode) |
| expand | 節點展開 / 收起後觸發 | (key: string, expanded: boolean) |
| nodeLoad | Lazy 載入子節點傳回後觸發 | (node: LhTreeNode, children: LhTreeNode[]) |
插槽:node(作用域 { node, level, expanded, selected })、empty。
LhTreeNode
| 欄位 | 說明 | 類型 |
|---|---|---|
| key | 唯一鍵 | string |
| label | 顯示文字 | string |
| children | 子節點 | LhTreeNode[] |
| disabled | 停用 | boolean |
| isLeaf | 是否葉子(不傳按 children 推斷) | boolean |
無障礙
role="tree"+role="treeitem",aria-level/aria-expanded/aria-selected/aria-disabled全量暴露- 勾選框
role="checkbox"+aria-checked(半選為mixed) - 鍵盤(APG tree pattern):↑/↓ 項間移動(跳過停用、循環)、→ 展開或進首子、← 收起或回父級、Home/End 首末、Enter/Space 啟用;roving tabindex
類型定義
本元件對外匯出的 TypeScript 類型結構如下(自動提取自 packages/components/tree/src/types.ts 與 @lh-ui/share/core/* 原始碼,點選各條目展開檢視完整定義;屬性說明見上方 Props 表):
LhTreeProps
ts
export interface LhTreeProps {
/** 樹狀資料 */
data: LhTreeNode[]
/** 顯示勾選框 */
showCheckbox?: boolean
/** 多選(點擊節點切換選中而非替換) */
multiple?: boolean
/** 點擊非葉子節點時展開/收起 */
expandOnClickNode?: boolean
/** 預設展開的鍵(非受控) */
defaultExpandedKeys?: string[]
/** 受控展開的鍵 */
expandedKeys?: string[]
/** 預設選中的鍵(非受控) */
defaultSelectedKeys?: string[]
/** 受控選中的鍵 */
selectedKeys?: string[]
/** 預設勾選的鍵(非受控) */
defaultCheckedKeys?: string[]
/** 受控勾選的鍵 */
checkedKeys?: string[]
/** 父子勾選不聯動(勾選只作用於節點自身,無級聯與半選) */
checkStrictly?: boolean
/** Lazy 載入模式:展開未載入的非葉子節點時呼叫 load */
lazy?: boolean
/** Lazy 載入回呼:resolve 提交子節點並展開,reject(或拋錯)取消 */
load?: (
node: LhTreeNode,
resolve: (children: LhTreeNode[]) => void,
reject: (reason?: unknown) => void,
) => void
/** 整體停用 */
disabled?: boolean
/** sx 快速版面配置屬性 */
sx?: LhSxProps
}LhFlatTreeNode (來自 @lh-ui/share/core/tree)
ts
/** 扁平化後的節點(渲染用) */
export interface LhFlatTreeNode {
node: LhTreeNode
key: string
level: number
parentKey: string | null
/** 祖先鏈(不含自身) */
ancestorKeys: string[]
/** 是否葉子 */
isLeaf: boolean
}LhTreeNode (來自 @lh-ui/share/core/tree)
ts
/** 樹狀節點資料 */
export interface LhTreeNode {
/** 唯一鍵 */
key: string
/** 顯示文字 */
label: string
/** 子節點 */
children?: LhTreeNode[]
/** 停用 */
disabled?: boolean
/** 是否葉子(不傳按 children 推斷) */
isLeaf?: boolean
}LhTreeCheckState (來自 @lh-ui/share/core/tree)
ts
/** 勾選狀態 */
export type LhTreeCheckState = 'checked' | 'indeterminate' | 'unchecked'