Tree 组件的使用
https://www.antdv.com/components/tree-cn#treenode
2024年7月19日
该组件有几个功能场景:
- 递归展示
Tree结构, 支持全部数据加载, 懒加载. - 结合文本框, 和响应式数据, 允许搜索
Tree节点 (高亮, 或控制显示). - 允许拖拽调整
Tree节点. - 支持右键菜单.
基本用法
组件:
Tree组件. 树控件容器.TreeNode组件. 用于构造节点类型.
Tree 组件属性:
selectedKeys, 控制选中项, 受控属性 (用v-model绑定).expandedKeys, 需要展开的节点, 受控属性 (用v-model绑定).treeData, 绑定的数据源. 类型为:TreeNode[].auto-expand-parent, 在指定展开某节点后, 是否自动展开其父节点. 与expandedKeys连用. 主要控制只选择一个节点时, 自动处理父节点的key(不太喜欢使用该属性). 并且该属性与expandedKeys连用后, 会导致树无法折叠. 因此需要结合expand事件, 关闭autoExpandParent, 并为expandedKeys赋值.
DataNode 类型:
export interface BasicDataNode {
checkable?: boolean;
disabled?: boolean;
disableCheckbox?: boolean;
icon?: IconType;
isLeaf?: boolean;
selectable?: boolean;
switcherIcon?: IconType;
/** 设置 TreeNode 样式. 不推荐使用 */
class?: string;
style?: CSSProperties;
slots?: Record<string, string>;
[key: string]: any;
}
export interface DataNode extends BasicDataNode {
children?: DataNode[];
key: string | number;
title?: any;
}
示例:
<Tree :tree-data="treeDatas">
</Tree>
const treeDatas = ref<DataNode[]>([
{ title: '标题1', key: '值1', children: [
{ title: '子标题', key: '子值1', children: [] }
] },
{ title: '标题2', key: '值2', children: [] },
])

如果要控制展开, 使用 expandedKeys 属性. 要获取选中的值, 可以使用 selectedKeys 属性, 或使用 select 事件.
如果需要多选, 使用 multiple 属性.
使用 title 插槽, 可以自定义每一个节点的显示.
要使得节点占据一整行, 使用 blockNode 属性.

树右侧按钮, 不建议使用
title插槽来实现, 当tree过深是布局会混乱. 建议使用右键菜单.
异步加载数据
所谓异步加载数据, 即点击节点前的展开按钮, 触发 loadData 方法 (事件), 在节点中添加子节点.
涉及属性:
Tree组件的load-data属性, 该属性是一个方法, 点击展开按钮时, 若节点不是叶子节点, 则触发该方法.isLeaf描述节点是否为叶子节点, 若为叶子节点, 则不显示前置展开按钮. 即不会再次加载数据.
load-data 方法签名:
(node: EventDataNode) => Promise<void>
EventDataNode 类型
export interface EventDataNode extends DataNode {
expanded?: boolean;
selected?: boolean;
checked: boolean;
loaded?: boolean;
loading?: boolean;
halfChecked?: boolean;
dragOver?: boolean;
dragOverGapTop?: boolean;
dragOverGapBottom?: boolean;
pos?: string;
active?: boolean;
dataRef?: DataNode;
parent?: DataNode;
eventKey?: Key;
}
重点注意 dataRef 属性.
Tree 初始化后, 传入的节点树对象会被包装成内部对象, 并提供一些附加属性, 从而构造出 EventDataNode 节点. 而其中的 dataRef 则表示其原始节点 (受控的 DataNode 数据).
需要注意的是, 必须提供
children属性, 除非节点为叶子节点 (即isLeaf为true), 否则在
使用步骤:
- 准备一个根节点.
- 定义一个
loadData方法, 在方法中为dataRef.children添加子节点, 或必要的设置isLeaf为true.
<Tree
v-model:selected-keys="selectedKeys"
v-model:expanded-keys="expandedKeys"
:tree-data="treeDatas"
:load-data="loadDataHandler"
></Tree>
const selectedKeys = ref<string[]>([])
const expandedKeys = ref<string[]>([])
const treeDatas = ref<DataNode[]>([
{ title: '根节点', key: 'root', children: [] },
])
const loadDataHandler = async (node: EventDataNode) => {
log('加载数据: %O', node)
// log('受控节点与 dataRef 的比较: %s', treeDatas.value[0] == node.dataRef)
node.dataRef.children.push({
title: node.dataRef.title + '-p',
key: node.dataRef.key + `-1`,
children: [],
// isLeaf: true,
})
}
注释中表示, 在根节点上点击展开, 会提示
dataRef的比较为true.

借助于 uuid 这个包, 生成不重复 key, 然后使用线性化的 cache 存储节点, 可以方便的生成需要的 Tree 结构.
关于自动展开的补充说明
原则上, 展开节点, 需要从被展开的节点开始, 向上找到根节点, 依次将 key 赋值给 expandedKeys 属性.
但是为了简单控制, 可以只将 展开节点的 key 赋值给 expandedKeys, 然后设置 autoExpandParent 为 true.
但此时会造成点击 被展开节点的 父节点时, 不再折叠树. 因此需要结合 expand 事件进行一些修整:
const expandHandler = (keys: string[]) => {
log('展开事件: %o', keys)
expandedKeys.value = keys // 保证基于受控属性自动填充 `expandedKeys`
autoExpandParent.value = false // 不再一直展开, 将展开的控制交给受控属性
}
因此自动展开父节点, 一般只在初始化的时候使用.
简单总结: 自动展开节点值用在组件展开状态初始化的时候, 需要配合
expand事件, 和受控的expandedKeys属性, 与autoExpandParent属性来来同步后续交互.
托拉拽功能说明
实现:
- 添加
draggable属性, 来启用拖拽功能 (浏览器的功能). - 注册
drop事件. 该事件的逻辑是核心. 该事件参数中会记录, 拖拽的节点, 以及放置时的目标节点. 通过调整引用的节点对象的位置, 达到托拉拽功能 (改变节点顺序等信息后, 重现渲染页面).
其核心是拖拽最后放在哪里,
drop事件会提供属性用于描述最终放在哪里.实现的逻辑是通过找到放在哪里, 从而调整绑定节点数据的构成, 来重新渲染
Tree节点.
右键菜单的说明
右键菜单实际上利用 title 插槽, 在 title 上实现右键下拉菜单.
