Skip to page content

树形控件

Quasar Tree 是一个高度可定制的用于展示带层级数据的组件,例如树状结构的目录。

用法

基础

基础



无障碍
v2.25+

QTree 遵循 WAI-ARIA 树形视图模式:组件对外暴露 role="tree",每个节点头部为 role="treeitem",在父节点上携带 aria-expanded,可选中节点上携带 aria-selected,可勾选节点上携带 aria-checked(包括部分勾选父节点的 mixed 状态),禁用时携带 aria-disabled,同时嵌套的子组传达层级关系。在 virtual-scroll 模式下,行以扁平方式渲染,因此每一行通过 aria-levelaria-setsizearia-posinset 来补偿层级信息。请通过在组件上设置 aria-label(或 aria-labelledby)来为树提供无障碍名称。

勾选复选框仅作为指针操作的视觉呈现——键盘路径是在节点头部按 Space(见下文),状态通过 aria-checked 通告。"无节点"和"无结果"消息使用 Quasar 语言包 中的本地化字符串。

用户可见的每个节点都参与漫游 Tab 焦点,这是树模式对 role="treeitem" 的要求。这包括什么也不做的节点(没有选中和勾选的纯叶子节点)和禁用节点:禁用节点仍然可到达并通过 aria-disabled 通告自身,但不会触发任何操作——不选中、不展开、不懒加载,甚至不触发自身的 handler

键盘导航

当树节点获得焦点时:

  • Arrow UpArrow Down 在可见节点之间移动焦点。
  • Arrow Right 展开已折叠的父节点,或将焦点移至其第一个可见子节点。
  • Arrow Left 折叠已展开的父节点,或将焦点移至其父节点。
  • HomeEnd 将焦点移到第一个和最后一个可见节点。
  • Enter 执行节点的默认操作;Space 切换其展开/折叠状态——对于可勾选节点(使用了 tick-strategy 时)则切换复选框。两者在禁用节点上均不生效。

没有连接线

紧凑的
v2.2.4+

强制暗色模式

性能考虑
v2.25+

从 Quasar v2.25 开始,QTree 只为屏幕上可见的部分付出渲染代价:折叠节点的子节点在首次展开前不会渲染(之后它们会保留在 DOM 中但隐藏,以便折叠/展开时仍能执行动画),状态变化(展开、勾选、选中、筛选、键盘导航)只重新渲染受影响的节点。因此渲染成本随 可见 节点数量线性增长,而非总树大小——大多数树完全不需要额外调优。如果你的代码之前通过查询 DOM 获取未展开节点的子元素,现在需要先展开这些节点。

当大量节点同时可见时,DOM 体量本身成为瓶颈。按效果递增,有两种应对手段:

  1. no-transition 布尔 prop 关闭展开/折叠动画,同时允许 QTree 将折叠的子树从 DOM 中移除,而非为了动画而保留(在旧版 Quasar 中这是避免渲染折叠内容的唯一方式)。推荐在数据量较大时使用。
<q-tree no-transition ...
  1. virtual-scroll 布尔 prop(参见下方虚拟滚动章节)只将滚动视口附近的行保留在 DOM 中。这是应对超大型树的模式:无论树展开了多少,挂载、全部展开和筛选操作的成本都保持恒定。

虚拟滚动
v2.25+

virtual-scroll 布尔 prop 会将可见节点渲染为扁平化的虚拟列表:DOM 中只存在滚动视口附近的行(加上可配置的缓冲区——参见 virtual-scroll-* 相关 prop),因此无论展开了多少节点,渲染成本始终恒定。下面的示例运行了一棵完全展开的 4,680 节点的树;即使是最大的树,在此模式下挂载、全部展开和筛选都只需要毫秒级。

该模式需要注意:

  • 树本身成为滚动容器,因此需要通过 CSS 给它一个高度——或者将 virtual-scroll-target 指向一个滚动祖先元素。
  • 展开和折叠是即时的:没有滑动过渡动画,因此 durationno-transition prop 以及 @after-show/@after-hide 事件不适用。
  • scrollTo 方法可以将任何可见节点的行滚动到视图中;键盘导航会自动执行此操作。

集成示例

更多信息:QSplitterQTabPanels

自定义内容

注意(在下面的示例中)默认头部和正文插槽的自定义。

注意(在下面的示例中)自定义头部和正文插槽。

WARNING

在自定义头部上点击或按下 ENTER 会选中树的选项(并且自定义头部会失焦)。按下 SPACE 则会切换其展开/折叠状态。

如果您不想要这个行为,只需要使用 <div @click.stop @keydown.stop> 包裹自定义头部的内容即可(或者添加监听事件到相应的组件/元素)。

手风琴、筛选和可选中的

在下面的示例中,当一个节点扩展时,兄弟节点会收缩。

懒加载

选中 vs 打勾,展开

  • 选中(通过 QTree 的 selected 属性)指向当前选中的节点(背景颜色会有不同的高亮)。
  • 打勾(通过 QTree 的 ticked 属性)指向与每个节点关联的复选框。
  • 展开(通过 QTree 的 expanded 属性)指向已经展开的节点。

为了保证组件正确工作,上述属性都需要使用 v-model:<prop_name> 指令进行动态绑定(示例:v-model:expanded)。

勾选策略

有三种勾选策略:‘leaf’、‘leaf-filtered’、‘strict’,另外一个(默认)‘none’ 将禁用勾选。

策略描述
leaf勾选的节点只有叶子节点。勾选一个节点会影响其父节点的勾选状态(父节点会变成部分勾选或完全勾选),以及其子节点(所有可勾选的子节点都会变成勾选状态)。
leaf-filteredleaf 策略概念相同,只是此策略只应用于筛选后的节点(筛选后仍然可见的节点)。
strict勾选节点独立于父节点或子节点的勾选状态。

您可以为 QTree 应用一个全局的勾选策略,也可以在 nodes 模型中指定 tickStrategy 来为特定节点本地更改勾选策略。

部分勾选节点
v2.25+

leafleaf-filtered 策略下,如果一个父节点的可勾选子节点只有部分被勾选,那么该父节点既不是勾选也不是未勾选状态,因此不会出现在 ticked model 数据中。使用 getIndeterminateNodes() 方法可以获取这些节点(按 nodes model 数据中的顺序),或使用 isIndeterminate(key) 检查某个具体节点。

这两个方法在 strict 策略下始终返回空结果,因为在该策略中节点的勾选状态独立于其子节点。

当你需要同时获取三种状态时,getTickState(key) 会以 QCheckbox 接受的三态形式返回:勾选时为 true,部分勾选时为 null,未勾选时为 false。这正是节点自身勾选框所获取的值,因此可以直接绑定到你自己的 QCheckbox 上。

头部和正文插槽的作用域中也会包含一个 indeterminate 布尔值,与 ticked 并列。它是只读的——节点通过其子节点变为部分勾选状态,所以应该勾选子节点来改变它。

自定义筛选方法

您可以通过指定 filter-method 属性来自定义筛选方法。下面的方法会筛选输入内容,如果它还包含 ‘(*)’:

节点模型结构

以下描述了 QTree 的 v-model 中所使用的节点属性。

节点属性类型未提供时的行为描述
<nodeKey>String, Number会产生错误节点的键。键从 nodeKey 属性中指定的字段获取。
labelString该项目没有标签节点的标签。当设置了 labelKey 属性时,标签从该键获取。
iconString使用默认图标节点的图标。
iconColorString使用继承的颜色节点图标的颜色。取自 Quasar 调色板。
imgString不显示图片节点的图片。使用 /public 文件夹。示例:‘mountains.png’
avatarString不显示头像节点的头像。使用 /public 文件夹。示例:‘boy-avatar.png’
childrenArray该节点没有子节点子节点数组。
disabledBoolean节点为启用状态节点是否禁用?
expandableBoolean节点可展开节点是否可展开?
selectableBoolean节点可选择节点是否可选择?
handlerFunction不调用额外函数点击节点时调用的自定义函数。接收 node 作为参数。
tickableBoolean根据勾选策略决定使用勾选策略时,每个节点会显示复选框。该节点的复选框是否应被禁用?
noTickBoolean节点显示复选框使用勾选策略时,该节点是否应显示复选框?
tickStrategyString使用 ‘none’ 勾选策略仅覆盖此节点的全局勾选策略。可选值:‘leaf’、‘leaf-filtered’、‘strict’、‘none’。
lazyBoolean子节点不进行懒加载子节点是否应该懒加载?使用时也不要指定 ‘children’ 属性。
headerString使用 ‘default-header’ 插槽节点头部的作用域插槽名称,不含必需的 ‘header-’ 前缀。示例:‘story’ 对应 ‘header-story’ 作用域插槽。
bodyString使用 ‘default-body’ 插槽节点正文的作用域插槽名称,不含必需的 ‘body-’ 前缀。示例:‘story’ 对应 ‘body-story’ 作用域插槽。