更新记录

1.2.1(2026-11-06) 下载此版本

新增一项视觉优化:

  1. onlyRadioLeaf 生效时仍显示单选图标且父节点文字置灰,视觉上与「不可选中」不符。现开启 onlyRadioLeaf 后默认隐藏单选图标(显式传 showRadioIcon 可覆盖),父节点文字默认不变灰(新增 onlyRadioLeafGray 配置开启置灰)

1.2.0(2026-11-06) 下载此版本

新增四项交互与健壮性改进:

交互改进(统一为「选中失败则退化为展开/折叠」)

  1. 单选模式下开启 onlyRadioLeaf 后,点击非叶子节点既不选中也不展开,只能通过点击箭头展开折叠。现改为点击非叶子节点即展开/折叠该节点(等价于点击箭头),末级节点点击仍正常选中
  2. 禁用节点(disabled: true)点击后完全无反应,无法展开查看其下级。现禁用节点可点击展开/折叠浏览下级,但不触发 change;叶子节点点击仍无反应;开启 checkedDisabled 后禁用节点仍可选中,此时不展开
  3. 懒加载(loadMode)模式下开启 expandChecked 时,自动展开只修改了展开状态而没有请求数据,出现「箭头朝下但没有子节点」的空展开。现自动展开会同时触发 loadApi 把子节点取回来,箭头状态与实际数据保持一致

健壮性

  1. 搜索时的懒加载会逐层请求节点,接口若对任意节点都返回子节点会无限递归直到页面卡死。现新增 searchLoadLimit 配置(默认 1000)限制搜索加载总量,超限时给出提示并停止;同时请求改为分批并发(每批 6 个),避免一次性发起过多请求

1.1.9(2026-11-06) 下载此版本

新增两项交互改进,统一为「选中失败则退化为展开/折叠」:

  1. 单选模式下开启 onlyRadioLeaf 后,点击非叶子节点既不选中也不展开,只能通过点击箭头展开折叠。现改为点击非叶子节点即展开/折叠该节点(等价于点击箭头),末级节点点击仍正常选中
  2. 禁用节点(disabled: true)点击后完全无反应,无法展开查看其下级。现禁用节点可点击展开/折叠浏览下级,但不触发 change;叶子节点点击仍无反应;开启 checkedDisabled 后禁用节点仍可选中,此时不展开
查看更多

平台兼容性

uni-app(3.8.10)

Vue2 Vue3 Chrome Safari app-vue app-nvue Android iOS 鸿蒙
√ √ √ √ √ - √ - -
微信小程序 支付宝小程序 抖音小程序 百度小程序 快手小程序 京东小程序 鸿蒙元服务 QQ小程序 飞书小程序 小红书小程序 快应用-华为 快应用-联盟
√ - - - - - - - - - - -

lu-data-tree

uni-app 树形选择组件,支持单选、多选、父子级联、严格模式、节点禁用、懒加载、关键词筛选、默认展开及自定义节点插槽。

该组件基于 da-tree 树形选择 进行修改

特性

  • 支持 Vue 2 和 Vue 3
  • 支持单选和多选
  • 支持父子节点联动及严格模式
  • 支持半选状态
  • 支持默认选中和默认展开
  • 支持节点禁用
  • 支持异步懒加载
  • 支持关键词筛选
  • 支持自定义节点内容
  • 支持实例方法控制选中和展开状态
  • 支持大数据量增量渲染

安装

将 lu-data-tree 放入项目的 uni_modules 目录,并确保项目中已安装 uni-icons。

组件支持 easycom,通常无需手动导入:

<lu-data-tree :data="treeData" />

基础用法

<template>
  <lu-data-tree
    :data="treeData"
    labelField="name"
    valueField="id"
    @change="handleChange"
    @expand="handleExpand"
  />
</template>

<script setup>
import { ref } from 'vue';

const treeData = ref([
  {
    id: '1',
    name: '一级节点',
    children: [
      { id: '1-1', name: '二级节点' }
    ]
  }
]);

function handleChange(keys, currentNode) {
  console.log(keys, currentNode);
}

function handleExpand(expanded, currentNode) {
  console.log(expanded, currentNode);
}
</script>

属性

属性 类型 默认值 说明
data Array [] 树形数据
themeColor String #007aff 选中状态主题色
showCheckbox Boolean false 是否启用多选;关闭时为单选
showCount Boolean false 是否显示当前节点的直接子节点数量
defaultCheckedKeys Array \| String \| Number null 默认选中节点;单选传单个 key,多选传 key 数组(单选模式下若传入数组,只取第一个)
defaultExpandAll Boolean false 是否默认展开全部节点
defaultExpandedKeys Array null 默认展开的节点 key 数组
expandChecked Boolean false 选中节点时是否自动展开到该节点,单选与多选均生效
checkStrictly Boolean false 多选时是否关闭默认父子联动
autoCheckAllChildren Boolean false 严格模式下,选中节点时是否选中全部子孙节点
autoUncheckAllChildren Boolean false 严格模式下,取消节点时是否取消全部子孙节点
autoCheckAllParent Boolean false 严格模式下,选中节点时是否选中全部祖先节点
autoUnCheckAllParent Boolean false 严格模式下,取消节点时是否取消全部祖先节点
showRadioIcon Boolean - 单选模式下是否显示单选图标;不传时 onlyRadioLeaf 生效则不显示,否则显示;显式传入 true / false 时以传入值为准
onlyRadioLeaf Boolean false 单选时是否只允许选中叶子节点;非叶子节点点击时改为展开/折叠
onlyRadioLeafGray Boolean false onlyRadioLeaf 生效时,非叶子节点的文本是否置灰,默认不变灰
indent Number 40 层级缩进距离,单位为 rpx
checkboxPlacement String left 选择图标位置,可选 left 或 right
loadMode Boolean false 是否启用懒加载模式;空子节点也显示展开图标
loadApi Function null 懒加载函数,接收当前节点并返回子节点数组;无子节点时可返回空数组
alwaysFirstLoad Boolean false 是否每次展开都重新调用懒加载函数
searchLoadLimit Number 1000 搜索时懒加载的节点数上限,防止接口对任意节点都返回子节点时无限递归
checkedDisabled Boolean false 是否允许处理禁用节点的选中状态;开启后禁用节点可直接点击选中,父子联动与父节点状态回算也会一并纳入禁用节点
packDisabledkey Boolean true 获取选中 key 时是否包含禁用节点
isLeafFn Function null 自定义叶子节点判断函数,接收原始节点并返回布尔值
filterValue String '' 筛选关键词,按节点标签进行包含匹配
labelField String label 节点标签字段名
valueField String value 节点唯一值字段名,要求全树唯一
childrenField String children 子节点字段名
disabledField String disabled 禁用状态字段名;禁用节点不可选中,但可点击展开/折叠
appendField String append 节点副标签字段名
leafField String leaf 叶子节点标记字段名
sortField String sort 同级节点排序字段名;排序按父节点分组进行,排序后每个节点的子树仍保持连续

选择模式

单选

默认关闭 showCheckbox,组件处于单选模式:

<lu-data-tree
  :data="treeData"
  defaultCheckedKeys="1-1"
  @change="handleChange"
/>

单选事件中的第一个参数为单个 key;取消选中时为 null。

多选

<lu-data-tree
  :data="treeData"
  showCheckbox
  :defaultCheckedKeys="['1-1']"
  @change="handleChange"
/>

默认情况下父子节点联动。选中父节点会联动选中可用子孙节点,子节点部分选中时父节点显示半选状态。

开启 expandChecked 后,无论是点击勾选还是调用 setCheckedKeys,选中节点都会自动展开到该节点(展开其祖先链并显示其子节点)。筛选状态下不触发自动展开,以免破坏筛选结果结构。

懒加载(loadMode)模式下,自动展开会同时触发 loadApi 把子节点数据取回来,箭头状态与实际数据保持一致;不开启 expandChecked 时,点击节点不会展开、也不会发起请求,只能点击箭头展开。

禁用节点

节点数据中设置 disabled: true 后,该节点不能被选中,但仍可点击展开/折叠以浏览其下级:

<lu-data-tree :data="treeData" labelField="name" valueField="id" />
  • 点击禁用节点 → 展开/折叠该节点(等价于点击箭头),触发 expand 事件,但不触发 change
  • 禁用节点若是叶子节点,点击无任何反应
  • 开启 checkedDisabled 后,禁用节点可以直接被选中,此时点击不再展开
  • 懒加载模式下禁用节点不显示展开箭头,也不会触发 loadApi

仅选中末级节点

单选模式下开启 onlyRadioLeaf 后,只有末级(叶子)节点可以被选中:

<lu-data-tree
  :data="treeData"
  labelField="name"
  valueField="id"
  onlyRadioLeaf
  @change="handleChange"
/>
  • 非叶子节点不可选中,点击时改为展开/折叠该节点(等价于点击箭头)
  • 末级节点点击正常选中,触发 change 事件
  • 该属性仅对单选模式生效,多选模式下不产生任何影响
  • 懒加载模式下未加载的节点无法预知是否为末级,默认按「非末级」处理:可以展开加载,但不能选中

配套行为:

  • 单选图标:开启 onlyRadioLeaf 后默认不再显示单选图标(非叶子节点本就选不了,显示反而误导)。需要保留图标时显式传 :showRadioIcon="true" 即可
  • 文本置灰:非叶子节点的文本默认不变灰,保持与普通节点一致。需要提示「只能选末级」时传 :onlyRadioLeafGray="true"
<!-- 默认:隐藏单选图标,父节点文字正常 -->
<lu-data-tree :data="treeData" onlyRadioLeaf />

<!-- 保留单选图标 + 父节点文字置灰 -->
<lu-data-tree :data="treeData" :showRadioIcon="true" onlyRadioLeaf onlyRadioLeafGray />

严格模式

开启 checkStrictly 后,默认父子联动关闭。此时每个节点只能处于未选中或选中状态,不会产生半选状态。需要联动时,可按需开启以下配置:

<lu-data-tree
  :data="treeData"
  showCheckbox
  checkStrictly
  autoCheckAllChildren
  autoUncheckAllChildren
  autoCheckAllParent
  autoUnCheckAllParent
/>

懒加载

<lu-data-tree
  :data="treeData"
  loadMode
  :loadApi="loadChildren"
/>
async function loadChildren(node) {
  const children = await fetchChildren(node.key);
  return children || [];
}

loadApi 接收处理后的当前节点。返回值应为子节点数组;没有子节点时返回 []。组件会根据 childrenField 合并并插入新节点。

传入的节点是内部节点的副本,字段与内部节点一致。其中 children、originItem、parents 采用惰性深拷贝——只有真正被访问时才会复制一次并缓存,未访问时不会产生复制开销;其余字段为浅拷贝。因此可以安全地读取和修改 originItem,不会影响组件内部状态。

筛选

<template>
  <input v-model="keyword" placeholder="请输入关键词" />
  <lu-data-tree :data="treeData" :filterValue="keyword" />
</template>

<script setup>
import { ref } from 'vue';

const keyword = ref('');
</script>

匹配节点及其祖先节点会显示。清空关键词后,组件会恢复筛选前的展开状态。

懒加载模式下,筛选范围仅包含已经加载到组件中的节点;为保证匹配完整,组件会在筛选时自动逐层加载尚未加载的节点,加载总量受 searchLoadLimit 限制(默认 1000),超限时停止加载并在控制台提示,此时可能存在未加载的匹配项。

事件

事件 回调参数 说明
change (checkedKeys, currentNode) 选中状态变化时触发;单选取消时 checkedKeys 为 null;调用 setCheckedKeys 时同样触发
expand (expanded, currentNode) 节点展开或收起时触发;expanded 为布尔值;调用 setExpandedKeys 时同样触发

currentNode 为组件处理后的内部节点,其中 originItem 保存原始数据节点。

内部节点中:

  • disabled:节点被禁用,无法选中(但可点击展开/折叠)
  • radioDisabled:单选模式且开启 onlyRadioLeaf 时,非叶子节点不可选中(仍可展开)

数据刷新与选中态

当 data 属性发生变化时,组件会重新构建内部数据,并按以下优先级决定选中态:

  1. defaultCheckedKeys 发生变化 → 以 defaultCheckedKeys 为准
  2. 用户手动操作过选中状态(点击或调用 setCheckedKeys)→ 保留操作后的运行时状态
  3. 以上都不满足 → 使用 defaultCheckedKeys

因此异步加载数据、或列表数据被整体替换时,用户的选中结果不会被重置;而主动取消选中后,数据刷新也不会让默认选中「复活」。如需强制恢复到默认选中,重新赋值 defaultCheckedKeys 即可。

实例方法

通过 ref 获取组件实例:

<lu-data-tree ref="treeRef" :data="treeData" showCheckbox />
const treeRef = ref(null);

treeRef.value?.setCheckedKeys(['1-1'], true);
treeRef.value?.setExpandedKeys(['1'], true);

const checkedKeys = treeRef.value?.getCheckedKeys();
const expandedKeys = treeRef.value?.getExpandedKeys();
方法 参数 说明
setCheckedKeys (keys, checked) 设置节点选中状态;单选 keys 为 key,多选为 key 数组;设置后触发 change 事件,事件参数为按实际选中状态校正后的结果
setExpandedKeys (keys, expand) 设置节点展开状态;keys 为 key 数组或 'all';设置后触发 expand 事件
getCheckedKeys - 获取已选中的 key,无选中时返回 []
getHalfCheckedKeys - 获取半选中的 key,无半选时返回 [];单选模式下表示选中节点的祖先链
getUncheckedKeys - 获取未选中的 key,无未选中时返回 []
getCheckedNodes - 获取已选中的内部节点,无选中时返回 []
getHalfCheckedNodes - 获取半选中的内部节点,无半选时返回 []
getUncheckedNodes - 获取未选中的内部节点,无未选中时返回 []
getExpandedKeys - 获取已展开的 key,无展开时返回 [];叶子节点不计入展开状态
getUnexpandedKeys - 获取未展开的 key,无未展开时返回 []
getExpandedNodes - 获取已展开的内部节点
getUnexpandedNodes - 获取未展开的内部节点
getNodeByKey (key) 根据 key 获取内部节点;找不到时返回 undefined

自定义节点内容

组件提供默认插槽,插槽参数如下:

  • node:组件处理后的内部节点
  • data:原始数据节点
<lu-data-tree :data="treeData">
  <template #default="{ node, data }">
    <view class="custom-node">
      <text>{{ node.label }}</text>
      <text v-if="data.description">{{ data.description }}</text>
    </view>
  </template>
</lu-data-tree>

## 空状态插槽

组件提供两个空状态插槽,插槽内容均完全由调用方控制,不会自动附加组件空状态样式:

- `empty`:传入的树形数据为空时使用
- `search-empty`:搜索完成且没有匹配节点时使用

未提供插槽时,分别显示“暂无数据”和“未匹配到数据”。

```vue
<lu-data-tree :data="treeData" :filterValue="filterValue">
  <template #empty>
    <view class="custom-empty">暂无组织数据</view>
  </template>
  <template #search-empty>
    <view class="custom-search-empty">没有找到相关节点</view>
  </template>
</lu-data-tree>

节点数据结构

默认字段结构如下:

[
  {
    value: '1',
    label: '一级节点',
    disabled: false,
    leaf: false,
    children: [
      {
        value: '1-1',
        label: '二级节点'
      }
    ]
  }
]

使用自定义字段时:

<lu-data-tree
  :data="treeData"
  labelField="name"
  valueField="id"
  childrenField="nodes"
/>

valueField 对应的值必须在整棵树中唯一,否则节点选中、展开和实例查询可能产生冲突。

注意事项

  1. 组件依赖 uni-icons。
  2. loadMode 必须同时传入有效的 loadApi 函数。
  3. loadApi 应返回数组;无子节点时返回 []。
  4. onlyRadioLeaf 与懒加载组合时,未加载的节点无法确定是否为叶子,默认按「非叶子」处理:可以正常展开并加载子节点,但不能被选中。
  5. loadApi 需在数据初始化前传入。若在 data 就绪之后才赋值,节点会被当作叶子节点处理(不显示展开图标)。
  6. 父容器应设置明确高度,否则 scroll-view 的百分比高度可能无法正常显示。
  7. 当前组件适用于普通 uni-app Vue2/Vue3 项目,不适用于 uni-app x。
  8. 组件声明支持的平台以 package.json 为准。

隐私、权限声明

1. 本插件需要申请的系统权限列表:

无

2. 本插件采集的数据、发送的服务器地址、以及数据用途说明:

无

3. 本插件是否包含广告,如包含需详细说明广告表达方式、展示频率:

无

许可协议

MIT协议