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
上实现右键下拉菜单.