Quasar Tree 是一个高度可定制的用于展示带层级数据的组件,例如树状结构的目录。
用法
基础
无障碍 v2.25+
QTree 遵循 WAI-ARIA 树形视图模式:组件对外暴露 role="tree",每个节点头部为 role="treeitem",在父节点上携带 aria-expanded,可选中节点上携带 aria-selected,可勾选节点上携带 aria-checked(包括部分勾选父节点的 mixed 状态),禁用时携带 aria-disabled,同时嵌套的子组传达层级关系。在 virtual-scroll 模式下,行以扁平方式渲染,因此每一行通过 aria-level、aria-setsize 和 aria-posinset 来补偿层级信息。请通过在组件上设置 aria-label(或 aria-labelledby)来为树提供无障碍名称。
勾选复选框仅作为指针操作的视觉呈现——键盘路径是在节点头部按 Space(见下文),状态通过 aria-checked 通告。"无节点"和"无结果"消息使用 Quasar 语言包 中的本地化字符串。
用户可见的每个节点都参与漫游 Tab 焦点,这是树模式对 role="treeitem" 的要求。这包括什么也不做的节点(没有选中和勾选的纯叶子节点)和禁用节点:禁用节点仍然可到达并通过 aria-disabled 通告自身,但不会触发任何操作——不选中、不展开、不懒加载,甚至不触发自身的 handler。
键盘导航
当树节点获得焦点时:
- Arrow Up 和 Arrow Down 在可见节点之间移动焦点。
- Arrow Right 展开已折叠的父节点,或将焦点移至其第一个可见子节点。
- Arrow Left 折叠已展开的父节点,或将焦点移至其父节点。
- Home 和 End 将焦点移到第一个和最后一个可见节点。
- Enter 执行节点的默认操作;Space 切换其展开/折叠状态——对于可勾选节点(使用了
tick-strategy时)则切换复选框。两者在禁用节点上均不生效。
没有连接线
紧凑的 v2.2.4+
强制暗色模式
性能考虑 v2.25+
从 Quasar v2.25 开始,QTree 只为屏幕上可见的部分付出渲染代价:折叠节点的子节点在首次展开前不会渲染(之后它们会保留在 DOM 中但隐藏,以便折叠/展开时仍能执行动画),状态变化(展开、勾选、选中、筛选、键盘导航)只重新渲染受影响的节点。因此渲染成本随 可见 节点数量线性增长,而非总树大小——大多数树完全不需要额外调优。如果你的代码之前通过查询 DOM 获取未展开节点的子元素,现在需要先展开这些节点。
当大量节点同时可见时,DOM 体量本身成为瓶颈。按效果递增,有两种应对手段:
no-transition布尔 prop 关闭展开/折叠动画,同时允许 QTree 将折叠的子树从 DOM 中移除,而非为了动画而保留(在旧版 Quasar 中这是避免渲染折叠内容的唯一方式)。推荐在数据量较大时使用。
<q-tree no-transition ...virtual-scroll布尔 prop(参见下方虚拟滚动章节)只将滚动视口附近的行保留在 DOM 中。这是应对超大型树的模式:无论树展开了多少,挂载、全部展开和筛选操作的成本都保持恒定。
虚拟滚动 v2.25+
virtual-scroll 布尔 prop 会将可见节点渲染为扁平化的虚拟列表:DOM 中只存在滚动视口附近的行(加上可配置的缓冲区——参见 virtual-scroll-* 相关 prop),因此无论展开了多少节点,渲染成本始终恒定。下面的示例运行了一棵完全展开的 4,680 节点的树;即使是最大的树,在此模式下挂载、全部展开和筛选都只需要毫秒级。
该模式需要注意:
- 树本身成为滚动容器,因此需要通过 CSS 给它一个高度——或者将
virtual-scroll-target指向一个滚动祖先元素。 - 展开和折叠是即时的:没有滑动过渡动画,因此
duration和no-transitionprop 以及@after-show/@after-hide事件不适用。 scrollTo方法可以将任何可见节点的行滚动到视图中;键盘导航会自动执行此操作。
集成示例
更多信息:QSplitter,QTabPanels。
自定义内容
注意(在下面的示例中)默认头部和正文插槽的自定义。
注意(在下面的示例中)自定义头部和正文插槽。
在自定义头部上点击或按下 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-filtered | 与 leaf 策略概念相同,只是此策略只应用于筛选后的节点(筛选后仍然可见的节点)。 |
| strict | 勾选节点独立于父节点或子节点的勾选状态。 |
您可以为 QTree 应用一个全局的勾选策略,也可以在 nodes 模型中指定 tickStrategy 来为特定节点本地更改勾选策略。
部分勾选节点 v2.25+
在 leaf 和 leaf-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 属性中指定的字段获取。 |
| label | String | 该项目没有标签 | 节点的标签。当设置了 labelKey 属性时,标签从该键获取。 |
| icon | String | 使用默认图标 | 节点的图标。 |
| iconColor | String | 使用继承的颜色 | 节点图标的颜色。取自 Quasar 调色板。 |
| img | String | 不显示图片 | 节点的图片。使用 /public 文件夹。示例:‘mountains.png’ |
| avatar | String | 不显示头像 | 节点的头像。使用 /public 文件夹。示例:‘boy-avatar.png’ |
| children | Array | 该节点没有子节点 | 子节点数组。 |
| disabled | Boolean | 节点为启用状态 | 节点是否禁用? |
| expandable | Boolean | 节点可展开 | 节点是否可展开? |
| selectable | Boolean | 节点可选择 | 节点是否可选择? |
| handler | Function | 不调用额外函数 | 点击节点时调用的自定义函数。接收 node 作为参数。 |
| tickable | Boolean | 根据勾选策略决定 | 使用勾选策略时,每个节点会显示复选框。该节点的复选框是否应被禁用? |
| noTick | Boolean | 节点显示复选框 | 使用勾选策略时,该节点是否应显示复选框? |
| tickStrategy | String | 使用 ‘none’ 勾选策略 | 仅覆盖此节点的全局勾选策略。可选值:‘leaf’、‘leaf-filtered’、‘strict’、‘none’。 |
| lazy | Boolean | 子节点不进行懒加载 | 子节点是否应该懒加载?使用时也不要指定 ‘children’ 属性。 |
| header | String | 使用 ‘default-header’ 插槽 | 节点头部的作用域插槽名称,不含必需的 ‘header-’ 前缀。示例:‘story’ 对应 ‘header-story’ 作用域插槽。 |
| body | String | 使用 ‘default-body’ 插槽 | 节点正文的作用域插槽名称,不含必需的 ‘body-’ 前缀。示例:‘story’ 对应 ‘body-story’ 作用域插槽。 |