DirectSurface UIDirectSurface UI
开始使用
组件/选择器组件

选择器组件 / COMPONENT

DropCheckTreeEdit

RenderDropCheckTreeEdit 是“触发输入框 + popup check tree”的组合控件。它用于从树形数据中选择多个叶子节点,例如多科室范围、多目录授权、多分类过滤。

文档 READY示例 1
PUBLIC APIRenderDropCheckTreeEdit

FUNCTION EXPLORER

可运行示例与完整源码。

这里始终保留组件的主运行入口;文档中的示例用于补充具体功能说明。

LIVE EXAMPLE CHECK TREE
全屏
正在启动 DirectSurface UI 运行时…

DropCheckTreeEdit 树形多选下拉

通用布局能力RenderDropCheckTreeEdit 实例统一支持 widthheight、min/max、margin 和槽位对齐;下拉 popup 的面板尺寸使用选择器浮层配置。详见组件通用布局属性

LIVE CANVAS可运行组件示例
ON-DEMAND RUNTIME交互式示例将在进入视区时启动避免文档首屏同时初始化多个 Canvas Runtime

RenderDropCheckTreeEdit 是“触发输入框 + popup check tree”的组合控件。它用于从树形数据中选择多个叶子节点,例如多科室范围、多目录授权、多分类过滤。

控件内部复用 TreeViewselectionMode: 'check' 行为。触发框负责展示摘要、搜索文本和清除按钮;popup 内负责搜索、展开折叠、checkbox 勾选和滚动。

API 总览

主类:

  • RenderDropCheckTreeEdit

相关 public API:

  • TreeNode
  • defaultDropCheckTreeEditQueryProcessor
  • DropCheckTreeEditQueryContext
  • DropCheckTreeEditQueryProcessor
  • DropCheckTreeEditQueryTextBuilder
  • DropCheckTreeEditExpandedKeysResolver
  • DropCheckTreeEditExpandOnOpenContext
  • DropCheckTreeEditSummaryBuilder
  • DropCheckTreeEditSummaryContext
  • DropCheckTreeEditDebugState
  • FormFieldStatus

导入:

import {  RenderDropCheckTreeEdit,  defaultDropCheckTreeEditQueryProcessor,  type DropCheckTreeEditDebugState,  type DropCheckTreeEditExpandOnOpenContext,  type DropCheckTreeEditExpandedKeysResolver,  type DropCheckTreeEditQueryContext,  type DropCheckTreeEditQueryProcessor,  type DropCheckTreeEditQueryTextBuilder,  type DropCheckTreeEditSummaryBuilder,  type DropCheckTreeEditSummaryContext,  type FormFieldStatus,  type TreeNode,} from 'ds-ui'

RenderDropCheckTreeEdit 的构造参数是公开 options 对象,但当前没有单独导出的 DropCheckTreeEdit options 类型。业务代码直接按本文参数表传入对象。

何时使用

使用 DropCheckTreeEdit:

  • 需要在树形目录中多选。
  • 选项有父子层级,父节点用于分组。
  • 需要搜索树节点。
  • 表单字段中只需要显示一个简短摘要。

不要使用:

最小示例

import { RenderDropCheckTreeEdit, type TreeNode } from 'ds-ui' const departmentCheckRoots: TreeNode[] = [  {    key: 'internal',    label: '内科系统',    children: [      { key: 'cardiology', label: '心内科' },      { key: 'respiratory', label: '呼吸科' },    ],  },] const checkTreeState = {  keys: [] as string[],  labels: [] as string[],} const field = new RenderDropCheckTreeEdit({  roots: departmentCheckRoots,  checkedKeys: checkTreeState.keys,  placeholder: '选择适用科室范围',  clearable: true,  onChange: (checkedKeys, nodes) => {    checkTreeState.keys = checkedKeys    checkTreeState.labels = nodes.map(node => node.label)  },})

checkedKeys 表示已勾选的叶子 key。onChange 返回当前 leaf checked keys 和对应节点。

TreeNode 数据结构

DropCheckTreeEdit 使用 TreeView 的 TreeNode<T>

interface TreeNode<T = any> {  key: string  label: string  labelTokens?: readonly TreeLabelToken[]  tooltip?: string  icon?: string  selectable?: boolean  checkable?: boolean  data?: T  children?: TreeNode<T>[]}

常用字段:

字段 说明
key 节点唯一标识。必须稳定。
label 展示文本,也是默认搜索文本之一。
children 子节点。存在子节点时该节点可展开。
checkable 是否显示/响应 checkbox。false 时节点本身不可勾选,但子节点仍可勾选。
selectable TreeView 单选语义字段;check 模式下主要由 checkbox 控制。
data 业务数据,可用于搜索、摘要和回调。
tooltip TreeView 节点自身 tooltip。
labelTokens TreeView 支持的分段着色文本。

构造参数

参数 类型 默认值 说明
roots TreeNode<T>[] 必填 树根节点。
checkedKeys readonly string[] [] 初始勾选 key。会去重并过滤空值。
placeholder string '请选择...' 未选择时的提示文本。
onChange (checkedKeys, nodes) => void undefined 勾选变化和清除时触发。
searchable boolean true 是否显示 popup 搜索框,并允许触发框键入打开搜索。
maxVisibleItems number 8 popup 中最多按多少个可见树节点计算高度。
readonly boolean false 只读。不能聚焦、不能打开 popup、不能清除。
disabled boolean false 禁用。不能交互。
status FormFieldStatus 'default' 表单字段状态。
helperText string '' 字段下方辅助/错误文本。影响整体高度。
prefixText string '' 触发框前缀文本。
suffixText string '' 触发框后缀文本。
clearable boolean false 有值且非只读/禁用时显示清除按钮。
expandAllOnOpen boolean false 打开时展开所有可展开节点。
expandedKeysOnOpen readonly string[] | resolver undefined 打开时使用的展开 key。会自动合并已选节点祖先。
queryTextBuilder (node) => string[] key + label 默认查询处理器使用的节点文本构造器。
queryProcessor (context) => TreeNode<T>[] 默认处理器 自定义查询过滤逻辑。
summaryBuilder (context) => string 默认摘要 自定义触发框摘要。
maxSummaryItems number 2 默认摘要最多展示多少个标签,至少为 1。

属性和方法

API 类型 说明
roots TreeNode<T>[] 当前根节点。赋值后若 popup 打开会刷新 popup roots。
checkedKeys string[] 当前勾选 key 副本。赋值会同步 popup 并重绘。
placeholder string placeholder 文本。
onChange (checkedKeys, nodes) => void 变化回调。
searchable boolean 是否可搜索。
maxVisibleItems number popup 可见行数上限。
readonly boolean 只读状态。设为 true 会关闭 popup、清理查询展示并取消焦点。
disabled boolean 禁用状态。设为 true 会关闭 popup、清理查询展示并取消焦点。
status FormFieldStatus 字段状态。
helperText string 辅助文本。修改会请求布局。
prefixTextsuffixText string 触发框前后缀。
clearable boolean 是否显示清除按钮。
expandAllOnOpen boolean 打开时是否展开全部。
expandedKeysOnOpen readonly string[] | resolver | undefined 打开时展开策略。
queryTextBuilder DropCheckTreeEditQueryTextBuilder<T> 默认搜索文本构造器。
queryProcessor DropCheckTreeEditQueryProcessor<T> 自定义搜索处理器。
summaryBuilder DropCheckTreeEditSummaryBuilder<T> 自定义摘要。
maxSummaryItems number 默认摘要数量上限。
debugState() DropCheckTreeEditDebugState 返回 popup、查询、展示、勾选、展开等调试状态。
dispose() void 关闭并释放 popup,注销焦点。

checkedKeys getter 返回副本,不能通过 field.checkedKeys.push(...) 修改内部状态;应整体赋值。

勾选规则

勾选逻辑由 TreeView check 模式提供:

  • checkedKeys 存储的是已勾选叶子节点 key。
  • 点击叶子 checkbox 会勾选/取消该叶子。
  • 点击父节点 checkbox 会切换其下所有可勾选目标叶子。
  • 父节点根据子节点状态自动显示全选或半选。
  • checkable: false 的节点本身不作为勾选目标,但其可勾选子孙仍参与父子联动。
  • 勾选父节点不会自动展开折叠节点,也不会改变 popup 高度。

如果父节点只是分组,建议显式设置:

const roots: TreeNode[] = [  {    key: 'medical-tech',    label: '医技系统',    checkable: false,    children: [      { key: 'lab', label: '检验科' },      { key: 'imaging', label: '影像科' },    ],  },]

这样用户只能勾选子节点,父节点只承载展开和分组语义。

点击区域

当前交互边界:

区域 行为
触发框 打开/关闭 popup。
触发框清除按钮 清空 checked keys,触发 onChange([], [])
popup 搜索框 输入查询文本。
节点展开箭头 展开/折叠节点。
节点 checkbox 切换勾选。
节点 label 按 TreeView 行交互处理,不等同于 checkbox。
popup 外部点击 关闭 popup。点击触发框本身也会关闭 popup。

因此,不能假设“点击 label 等于勾选”。如果业务要求 label 也勾选,应先扩展 TreeView 的 check 模式交互,而不是在 DropCheckTreeEdit 里单独处理。

搜索

默认搜索处理器:

const processor = defaultDropCheckTreeEditQueryProcessor()

默认搜索规则:

  • 查询文本会 trim 并转小写。
  • 默认匹配 node.keynode.label
  • 父节点命中时保留该节点。
  • 子节点命中时保留祖先路径,并只保留匹配子树。
  • 搜索时 popup 会展开过滤结果中的所有可展开节点。

自定义搜索文本:

const field = new RenderDropCheckTreeEdit({  roots,  queryTextBuilder: node => [    node.key,    node.label,    node.data?.py ?? '',  ],})

自定义搜索处理器:

const field = new RenderDropCheckTreeEdit({  roots,  queryProcessor: ({ query, roots, checkedKeys }) => {    state.lastQuery = query    state.checked = [...checkedKeys]    return roots.filter(node => node.label.includes(query))  },})

debugState() 不会执行 queryProcessor,避免调试读取触发昂贵查询。

展开策略

打开 popup 时展开 key 计算规则:

  1. expandAllOnOpen === true:展开所有可展开节点。
  2. expandedKeysOnOpen 是函数:使用函数返回值。
  3. expandedKeysOnOpen 是数组:使用数组。
  4. 上述结果都会合并已勾选节点的祖先 key。
  5. 搜索中会展开过滤结果的所有可展开节点。
  6. 用户手动展开/折叠会在非搜索状态下记忆到 popup 内部;下次打开会沿用。

示例:

const field = new RenderDropCheckTreeEdit({  roots,  checkedKeys: ['lab'],  expandedKeysOnOpen: ({ checkedKeys }) => {    return checkedKeys.length > 0 ? ['medical-tech'] : []  },})

摘要显示

默认摘要使用已勾选节点 label:

  • 勾选数量小于等于 maxSummaryItems:显示全部,例如 心内科、呼吸科
  • 勾选数量超出上限:显示前 N 项加数量,例如 心内科、呼吸科 +1

自定义摘要:

const field = new RenderDropCheckTreeEdit({  roots,  summaryBuilder: ({ checkedNodes }) => {    return checkedNodes.length === 0      ? ''      : `已选择 ${checkedNodes.length} 项`  },})

当摘要被折叠或触发框宽度不足时,控件会把完整已选 label 列表放入 tooltip。placeholder 和搜索展示状态不会生成该 tooltip。

键盘行为

操作 行为
Enter / Space / ArrowDown 触发框聚焦且 popup 未打开时打开 popup。
输入普通字符 searchable === true 且 popup 未打开时,以该字符作为初始查询打开 popup。
Backspace / Delete 有清除按钮时清空。
Escape popup 打开时关闭 popup 并清理查询展示。
Tab popup 打开时关闭 popup,返回 false 让焦点继续移动。
popup 内方向键 / Enter 转交给 TreeView。

只读或禁用状态不会注册焦点,也不会打开 popup。

popup 宽度:

panelW = clamp(max(280, anchorWidth), viewportWidth - 16)

popup 高度:

  • 按 TreeView 可见节点数计算。
  • 可见节点数最多为 maxVisibleItems
  • 搜索框开启时额外占用一行输入框高度和 padding。
  • 位置会限制在 viewport 8px inset 内。

当节点数量超过可见高度时,内部 TreeView 自己处理滚动和滚动条。popup 内滚轮会被消费,即使当前树没有溢出,也不会把滚动传递到底层页面。

状态、只读和禁用

状态 行为
readonly 显示锁图标和只读底色,不可聚焦、不可打开、不可清除。
disabled 禁用视觉,不可聚焦、不可打开、不可清除。
status 影响触发框边框/状态色,适合校验状态。
helperText 显示在字段下方,并影响组件布局高度。

设置 readonlydisabledtrue 时,已打开 popup 会立即关闭,查询展示会清空。

DebugState

const debug = field.debugState()

返回字段:

字段 说明
popupVisible popup 是否打开。
queryText 当前查询文本。
showQueryInField 触发框是否临时展示查询文本。
displayText 当前触发框显示文本。
focused 触发框是否聚焦。
disabled 是否禁用。
readonly 是否只读。
checkedKeys 当前勾选 key。
checkedLabel 当前摘要文本。
filteredCount popup 打开时为过滤树节点数;关闭时为完整树节点数。
expandedKeys popup 当前展开 key。

debugState() 适合测试和诊断,不应作为业务数据源。

生命周期

dispose() 会:

  • 关闭 popup。
  • 释放 popup 内 TreeView 和搜索输入会话。
  • 注销焦点。
  • 清理 hover/focus 状态。

当页面卸载、表单销毁或字段不再使用时,应 dispose 该控件。roots 中的业务对象仍由业务持有,不由控件释放。

性能边界

  • popup 内 TreeView 使用固定行虚拟范围绘制可见行。
  • 搜索过滤会遍历当前 roots,复杂度和树节点数量相关。
  • debugState() 不执行查询处理器,避免调试面板触发昂贵过滤。
  • 大树应提供轻量 queryTextBuilder,避免每个节点构造大字符串。
  • 远程查询或懒加载应放在业务层更新 roots,不要在 queryProcessor 中阻塞主线程。

常见问题

为什么点击 label 没有勾选?

当前勾选动作绑定在 TreeView check 模式的 checkbox 上。label 点击不是 checkbox 点击。需要改变这个交互应扩展 TreeView 的 check 行为。

为什么勾选父节点后没有展开?

这是预期行为。父节点 checkbox 会切换子孙可勾选目标,但不会改变展开状态,也不会改变折叠区域高度。

checkedKeys 为什么只包含叶子?

TreeView check 模式以叶子目标作为真实值。父节点的全选/半选是根据叶子状态推导出来的展示状态。

搜索时为什么会展开很多节点?

搜索状态下会展开过滤结果中的所有可展开节点,方便用户看到匹配子节点。清空搜索后恢复非搜索展开策略。

如何清空?

设置 clearable: true 后,触发框显示清除按钮。点击后会设置 checkedKeys = [] 并触发 onChange([], [])。也可以直接赋值 field.checkedKeys = [],但直接赋值不会触发 onChange

如何显示完整已选内容?

默认摘要被折叠或宽度不足时会设置 tooltip,内容是完整 label 列表。也可以通过 summaryBuilder 自定义摘要。

相关文档