vue3-super-tree
v1.4.3
Published
一个功能强大、高度可定制的 Vue 3 树形组件,完全复刻 PrimeVue Tree 的功能特性,支持拖拽、多选、键盘导航等高级功能
Maintainers
Readme
Vue3 Super Tree
一个功能强大、高度可定制的 Vue 3 树形组件 npm 包,完全复刻 PrimeVue Tree 的功能特性,支持拖拽、多选、键盘导航等高级功能。
📄 协议
本项目采用 MIT 协议 开源,您可以自由使用、修改和分发本项目。
🆕 v1.4.3 更新亮点
🔍 拖拽功能调试和诊断增强
- 新增拖拽测试页面 - 添加了专门的 DragTestPage.vue,提供完整的拖拽功能测试和调试环境
- 增强事件日志记录 - 改进了拖拽事件的详细日志记录,包含完整的事件数据和时间戳
- 实时调试信息 - 提供实时的拖拽状态显示,包括 isDragDropEnabled 状态和拖拽配置信息
- 操作指导优化 - 添加了详细的拖拽操作指导和最佳实践说明
📚 文档和集成支持完善
- 集成指南文档 - 新增 INTEGRATION_GUIDE.md,提供详细的外部项目集成指南
- 故障排除文档 - 新增 TROUBLESHOOTING.md,包含常见问题和解决方案
- 外部项目示例 - 新增 EXTERNAL_PROJECT_EXAMPLE.md,提供完整的外部项目使用示例
- 发布说明文档 - 新增 RELEASE_NOTES_1.4.3.md,详细记录版本更新内容
🛠️ 开发体验提升
- 调试工具增强 - 提供更好的调试工具和测试按钮,帮助开发者快速验证拖拽配置
- 错误诊断改进 - 增强了拖拽功能的错误诊断能力,提供更准确的问题定位
- 配置验证 - 添加了配置参数的实时验证和反馈机制
🆕 v1.4.2 更新亮点
🔧 跨树拖拽功能重大修复
- 修复跨树拖拽核心问题 - 解决了
performCrossTreeDrop返回值检查逻辑错误,确保跨树拖拽操作正确执行 - 完善拖拽属性配置 - 为 Tree 组件添加了缺失的
dragdrop-scope属性支持,修复了isDragDropEnabled计算属性逻辑 - 简化演示代码架构 - 重构 TreeDemo.vue 中的跨树拖拽演示,移除复杂的手动数据处理逻辑
- 组件内部自动化 - 现在跨树拖拽完全由组件内部自动处理,用户只需简单配置即可使用
- 提升开发体验 - 大幅简化了跨树拖拽的使用方式,减少了样板代码和配置复杂度
🆕 v1.4.1 更新亮点
🐛 演示页面拖拽功能修复
- 修复 TreeDemo.vue 拖拽排序功能 - 为拖拽排序的 Tree 组件添加了缺少的
:dragdrop="true"属性 - 修复 TreeDemo.vue 跨树拖拽功能 - 为跨树拖拽的两个 Tree 组件添加了
:dragdrop="true"和:crossTreeAutoUpdate="false"属性 - 确保演示功能正常工作 - 修复了演示页面中拖拽功能的配置问题,用户现在可以正常体验所有拖拽特性
- 配置一致性 - 统一了拖拽相关属性的配置,确保文档示例与实际代码保持一致
🆕 v1.4.0 更新亮点
🔧 构建稳定性大幅提升
- 44个构建错误全部修复 - 完成了大规模的 TypeScript 类型系统重构,确保构建过程零错误
- 生产就绪状态 - 库文件构建稳定,支持 ES 模块和 UMD 两种格式,完整的类型定义文件
- TypeScript 类型安全 - 完善的类型定义,确保开发时的类型安全和 IDE 智能提示
🚀 功能完整性和稳定性
- 跨树拖拽自动更新功能完整集成 - 将 TreeDemo 中的高级跨树拖拽功能完全集成到插件核心,提供开箱即用的跨树拖拽体验
- CrossTreeDataProvider 接口 - 新增跨树数据提供者接口,支持灵活的数据源管理和自定义数据更新逻辑
- 增强的拖拽状态管理 - 新增 useCrossTreeDragState 全局状态管理,确保跨树拖拽状态的一致性和可靠性
- 完善的错误处理 - 修复所有关键运行时错误,提升组件稳定性和可靠性
🏗️ 架构优化
- 插件化架构优化 - 所有跨树拖拽功能现已完全集成到插件核心,符合插件规范,无需额外配置
- 性能优化 - 生产构建自动移除调试日志,优化包体积和运行性能
- 开发体验提升 - 完整的开发工具支持,热重载,类型检查等
🚀 特性
核心功能
- 完整的树形结构支持 - 支持无限层级的树形数据展示,经过大规模测试验证
- 多种选择模式 - 支持单选、多选、复选框选择,状态管理稳定可靠
- 拖拽功能 - 支持节点拖拽排序和跨树拖拽,包含完整的错误处理和状态恢复
- 键盘导航 - 完整的键盘快捷键支持,符合无障碍访问标准
- 懒加载 - 支持大数据量的懒加载展示,性能优化到位
- 过滤搜索 - 内置过滤器,支持自定义过滤逻辑,搜索性能优异
高级功能
- 跨树拖拽自动更新 - 全新的自动更新模式,支持多个树组件之间的智能数据同步
- CrossTreeDataProvider 接口 - 灵活的数据提供者模式,支持自定义数据源和更新逻辑
- 全局状态管理 - useCrossTreeDragState 提供一致的跨组件状态管理
- 调试和诊断工具 - 内置的拖拽测试页面和实时调试信息,帮助快速定位问题
- 自定义模板 - 支持节点内容、图标、展开/折叠图标的自定义
- 事件系统 - 丰富的事件回调,包含完整的错误处理和状态追踪
- 主题定制 - 支持自定义样式和主题,CSS 变量支持
技术特性
- Vue 3 Composition API - 基于最新的 Vue 3 技术栈,充分利用响应式系统
- TypeScript 完全支持 - 100% TypeScript 编写,完整的类型安全支持,零类型错误
- 生产就绪 - 经过严格测试,44个构建错误全部修复,稳定可靠
- Tree Shaking - 支持按需引入,优化包体积,生产构建自动优化
- SSR 友好 - 支持服务端渲染,同构应用兼容
- 零外部依赖 - 除 Vue 3 外无其他依赖,减少包体积和潜在冲突
📋 版本信息
当前最新版本:v1.4.3
本版本是一个功能增强版本,主要改进内容:
- 新增了专门的拖拽测试页面,提供完整的调试和诊断环境
- 增强了拖拽事件的日志记录和实时状态显示功能
- 完善了集成指南、故障排除和外部项目示例文档
- 提升了开发体验,添加了更好的调试工具和配置验证
- 改进了错误诊断能力,帮助开发者快速定位和解决问题
v1.4.2 版本主要修复:
- 修复了跨树拖拽功能的核心问题,解决了
performCrossTreeDrop返回值检查逻辑错误 - 完善了 Tree 组件的拖拽属性配置,添加了缺失的
dragdrop-scope属性支持 - 修复了
isDragDropEnabled计算属性的逻辑,确保拖拽功能正确启用 - 重构并简化了 TreeDemo.vue 中的跨树拖拽演示代码
- 移除了复杂的手动数据处理逻辑,现在完全由组件内部自动处理
- 大幅提升了跨树拖拽功能的易用性和稳定性
v1.4.1 版本主要改进:
- 修复了 TreeDemo.vue 中拖拽排序功能的配置问题
- 修复了 TreeDemo.vue 中跨树拖拽功能的配置问题
- 为 Tree 组件添加了缺少的
:dragdrop="true"属性 - 为跨树拖拽添加了
:crossTreeAutoUpdate="false"属性 - 确保演示页面中的拖拽功能正常工作
v1.4.0 版本主要改进:
- 完整集成跨树拖拽自动更新功能到插件核心
- 新增 CrossTreeDataProvider 接口,支持灵活的数据源管理
- 增强拖拽状态管理,新增全局状态管理机制
- 修复关键错误,提升组件稳定性和可靠性
- 优化插件化架构,所有功能完全集成到核心
🏗️ 构建状态
构建信息
- 构建状态: ✅ 通过 (44个错误已修复)
- 类型检查: ✅ 完全通过
- 生产就绪: ✅ 是
- 包格式: ES 模块 + UMD
- 类型定义: 完整的 .d.ts 文件
性能指标
- 包体积: 优化后的生产构建
- Tree Shaking: ✅ 支持
- 调试日志: 生产环境自动移除
- 依赖: 零外部依赖 (除 Vue 3)
质量保证
- TypeScript: 100% 类型安全
- 错误处理: 完整的错误边界
- 状态管理: 稳定的响应式状态
- 测试覆盖: 核心功能全覆盖
📦 安装
# 使用 npm
npm install vue3-super-tree
# 使用 yarn
yarn add vue3-super-tree
# 使用 pnpm
pnpm add vue3-super-tree🚀 快速开始
基础使用
<template>
<div>
<Tree
:value="nodes"
:selectionMode="'single'"
v-model:selectionKeys="selectedKeys"
/>
</div>
</template>
<script setup>
import { ref } from 'vue'
import { Tree } from 'vue3-super-tree'
import 'vue3-super-tree/style.css'
const nodes = ref([
{
key: '0',
label: '根节点',
children: [
{
key: '0-0',
label: '子节点 1'
},
{
key: '0-1',
label: '子节点 2'
}
]
}
])
const selectedKeys = ref({})
</script>完整功能示例
<template>
<div>
<Tree
:value="nodes"
:selectionMode="'multiple'"
:filter="true"
:dragdrop="true"
:loading="loading"
v-model:selectionKeys="selectedKeys"
v-model:expandedKeys="expandedKeys"
@node-select="onNodeSelect"
@node-unselect="onNodeUnselect"
@node-expand="onNodeExpand"
@node-collapse="onNodeCollapse"
/>
</div>
</template>
<script setup>
import { ref } from 'vue'
import { Tree } from 'vue3-super-tree'
import 'vue3-super-tree/style.css'
const nodes = ref([...]) // 你的树形数据
const selectedKeys = ref({})
const expandedKeys = ref({})
const loading = ref(false)
const onNodeSelect = (node) => {
console.log('节点选中:', node)
}
const onNodeUnselect = (node) => {
console.log('节点取消选中:', node)
}
const onNodeExpand = (node) => {
console.log('节点展开:', node)
}
const onNodeCollapse = (node) => {
console.log('节点折叠:', node)
}
</script>TypeScript 支持
import { ref } from 'vue'
import { Tree } from 'vue3-super-tree'
import type { TreeNode, TreeSelectionKeys, TreeExpandedKeys } from 'vue3-super-tree/types'
import 'vue3-super-tree/style.css'
const nodes = ref<TreeNode[]>([
{
key: '0',
label: '根节点',
children: [...]
}
])
const selectedKeys = ref<TreeSelectionKeys>({})
const expandedKeys = ref<TreeExpandedKeys>({})📖 API 文档
Tree 组件属性
| 属性 | 类型 | 默认值 | 描述 |
|------|------|--------|------|
| value | TreeNode[] | [] | 树形数据,支持无限层级嵌套 |
| modelValue | TreeSelectionKeys | null | 选中的节点(v-model),支持双向绑定 |
| selectionMode | 'single' \| 'multiple' \| 'checkbox' | null | 选择模式:单选、多选或复选框模式 |
| selectionKeys | TreeSelectionKeys | {} | 选中状态对象,键为节点 key,值为选中状态 |
| expandedKeys | TreeExpandedKeys | {} | 展开状态对象,键为节点 key,值为展开状态 |
| dragdrop | boolean | false | 是否启用拖拽功能 |
| dragdropScope | string | undefined | 拖拽作用域,相同作用域的树可以互相拖拽 |
| autoUpdate | boolean | false | 同树拖拽时是否自动更新数据源 |
| crossTreeAutoUpdate | boolean | false | 🆕 v1.4.0 跨树拖拽时是否自动更新数据源 |
| crossTreeDataProvider | CrossTreeDataProvider | undefined | 🆕 v1.4.0 跨树数据提供者,用于获取和更新不同树的数据 |
| filter | boolean | false | 是否启用过滤功能 |
| filterMode | 'lenient' \| 'strict' | 'lenient' | 过滤模式:宽松模式显示匹配节点的父节点,严格模式只显示匹配节点 |
| filterBy | string | 'label' | 过滤字段,指定按哪个字段进行过滤 |
| lazy | boolean | false | 是否启用懒加载,适用于大数据量场景 |
| loading | boolean | false | 加载状态,显示加载指示器 |
| indent | number | 1 | 缩进级别,控制子节点的缩进距离 |
| metaKeySelection | boolean | true | 是否支持 Ctrl/Cmd 键多选 |
Tree 组件事件
| 事件 | 参数 | 描述 |
|------|------|------|
| node-select | TreeNodeSelectEvent | 节点选中时触发 |
| node-unselect | TreeNodeUnselectEvent | 节点取消选中时触发 |
| node-expand | TreeNodeExpandEvent | 节点展开时触发 |
| node-collapse | TreeNodeCollapseEvent | 节点折叠时触发 |
| node-focus | TreeNodeFocusEvent | 节点获得焦点时触发 |
| node-blur | TreeNodeBlurEvent | 节点失去焦点时触发 |
| node-click | TreeNodeClickEvent | 节点点击时触发 |
| node-drop | TreeNodeDropEvent | 节点拖拽放置时触发 |
| node-drag-start | { originalEvent, node } | 开始拖拽时触发 |
| node-drag-end | { originalEvent, node } | 拖拽结束时触发 |
| cross-tree-drag-start | CrossTreeDragStartEvent | 跨树拖拽开始时触发 |
| cross-tree-drag-enter | CrossTreeDragEnterEvent | 拖拽节点进入目标树时触发 |
| cross-tree-drag-over | CrossTreeDragOverEvent | 拖拽节点在目标树上方移动时触发 |
| cross-tree-drag-leave | CrossTreeDragLeaveEvent | 拖拽节点离开目标树时触发 |
| cross-tree-drop | CrossTreeDropEvent | 跨树拖拽放置时触发 |
| cross-tree-drag-end | CrossTreeDragEndEvent | 跨树拖拽结束时触发(无论成功或失败) |
| cross-tree-drag-cancel | CrossTreeDragCancelEvent | 跨树拖拽取消时触发(如按 ESC 键) |
TreeNode 接口
interface TreeNode {
key: string | number // 节点唯一标识
label: string // 节点显示文本
data?: any // 节点关联数据
icon?: string | Component // 节点图标
children?: TreeNode[] // 子节点
leaf?: boolean // 是否为叶子节点
expanded?: boolean // 是否展开
selectable?: boolean // 是否可选择
draggable?: boolean // 是否可拖拽
droppable?: boolean // 是否可作为拖拽目标
styleClass?: string // 自定义样式类
style?: Record<string, any> // 自定义样式
}CrossTreeDataProvider 接口
🆕 v1.4.0 新增功能
CrossTreeDataProvider 是跨树拖拽自动更新功能的核心接口,它提供了统一的数据访问和更新机制。
interface CrossTreeDataProvider {
/**
* 获取指定树的数据
* @param treeId 树的唯一标识符(通常是组件的 id 属性)
* @returns 树的数据数组,如果树不存在则返回 null 或 undefined
*/
getTreeData: (treeId: string) => TreeNode[] | null | undefined
/**
* 更新指定树的数据
* @param treeId 树的唯一标识符
* @param newData 新的树数据
*/
updateTreeData: (treeId: string, newData: TreeNode[]) => void
}使用说明
- 必须为每个树组件设置唯一的
id属性 - 实现
getTreeData方法:根据treeId返回对应树的数据 - 实现
updateTreeData方法:根据treeId更新对应树的数据 - 将同一个
CrossTreeDataProvider实例传递给所有参与跨树拖拽的树组件
错误处理
- 如果
getTreeData返回null或undefined,跨树拖拽将被阻止 - 确保
updateTreeData方法能正确处理数据更新,避免响应式丢失 - 建议在方法中添加适当的错误处理和日志记录
使用示例
1. 基础树形展示
<template>
<Tree :value="basicData" />
</template>
<script setup>
import { Tree } from 'vue3-super-tree'
import 'vue3-super-tree/style.css'
const basicData = [
{
key: '1',
label: '文档',
children: [
{ key: '1-1', label: '安装指南.md' },
{ key: '1-2', label: '快速开始.md' }
]
}
]
</script>2. 多选模式
<template>
<Tree
:value="data"
v-model:selectionKeys="selectedKeys"
selectionMode="multiple"
:metaKeySelection="false"
/>
</template>
<script setup>
import { ref } from 'vue'
import { Tree } from 'vue3-super-tree'
import 'vue3-super-tree/style.css'
const selectedKeys = ref({})
</script>3. 复选框选择
<template>
<Tree
:value="data"
v-model:selectionKeys="selectedKeys"
selectionMode="checkbox"
/>
</template>
<script setup>
import { Tree } from 'vue3-super-tree'
import 'vue3-super-tree/style.css'
</script>4. 拖拽功能
自动更新模式(推荐)
<template>
<Tree
:value="data"
:autoUpdate="true"
dragdropScope="demo"
@node-drop="onNodeDrop"
/>
</template>
<script setup>
import { ref } from 'vue'
import { Tree } from 'vue3-super-tree'
import 'vue3-super-tree/style.css'
const data = ref([...]) // 你的树形数据
const onNodeDrop = (event) => {
// 自动更新模式下,数据会自动更新,只需要接受拖拽
event.accept()
console.log('拖拽完成,数据已自动更新')
}
</script>手动处理模式
<template>
<Tree
:value="data"
:autoUpdate="false"
dragdropScope="demo"
@node-drop="onNodeDrop"
/>
</template>
<script setup>
import { ref } from 'vue'
import { Tree } from 'vue3-super-tree'
import 'vue3-super-tree/style.css'
const data = ref([...]) // 你的树形数据
const onNodeDrop = (event) => {
// 手动处理拖拽逻辑
const { dragNode, dropNode, dropIndex } = event
// 自定义拖拽处理逻辑
if (dropNode) {
// 拖拽到节点上
if (!dropNode.children) dropNode.children = []
dropNode.children.push(dragNode)
} else {
// 拖拽到根级别
data.value.splice(dropIndex, 0, dragNode)
}
event.accept()
}
</script>5. 跨树拖拽自动更新
推荐:自动更新模式
<template>
<div class="flex gap-4">
<!-- 源树 -->
<div class="w-1/2">
<h3>源树</h3>
<Tree
id="source-tree"
:value="sourceData"
:dragdrop="true"
dragdropScope="cross-tree"
:crossTreeAutoUpdate="true"
:crossTreeDataProvider="crossTreeDataProvider"
@cross-tree-drop="onCrossTreeDrop"
/>
</div>
<!-- 目标树 -->
<div class="w-1/2">
<h3>目标树</h3>
<Tree
id="target-tree"
:value="targetData"
:dragdrop="true"
dragdropScope="cross-tree"
:crossTreeAutoUpdate="true"
:crossTreeDataProvider="crossTreeDataProvider"
@cross-tree-drop="onCrossTreeDrop"
/>
</div>
</div>
</template>
<script setup>
import { ref } from 'vue'
import { Tree } from 'vue3-super-tree'
import 'vue3-super-tree/style.css'
const sourceData = ref([
{
key: 'source-1',
label: '源节点 1',
children: [
{ key: 'source-1-1', label: '源子节点 1-1' },
{ key: 'source-1-2', label: '源子节点 1-2' }
]
}
])
const targetData = ref([
{
key: 'target-1',
label: '目标节点 1',
children: []
}
])
// 跨树数据提供者
const crossTreeDataProvider = {
getTreeData: (treeId) => {
if (treeId === 'source-tree') return sourceData.value
if (treeId === 'target-tree') return targetData.value
return null
},
updateTreeData: (treeId, newData) => {
if (treeId === 'source-tree') {
sourceData.value = newData
} else if (treeId === 'target-tree') {
targetData.value = newData
}
}
}
// 可选:监听跨树拖拽事件
const onCrossTreeDrop = (event) => {
console.log('跨树拖拽完成:', {
sourceTreeId: event.sourceTreeId,
targetTreeId: event.targetTreeId,
dragNode: event.dragNode,
dropNode: event.dropNode,
dropIndex: event.dropIndex
})
}
</script>6. 跨树拖拽手动模式
基础跨树拖拽
<template>
<div class="flex gap-4">
<Tree
ref="sourceTree"
:value="sourceData"
dragdropScope="cross-tree"
@node-drop="onCrossTreeDrop"
/>
<Tree
ref="targetTree"
:value="targetData"
dragdropScope="cross-tree"
@node-drop="onCrossTreeDrop"
/>
</div>
</template>
<script setup>
import { Tree } from 'vue3-super-tree'
import 'vue3-super-tree/style.css'
const onCrossTreeDrop = (event) => {
// 处理跨树拖拽
if (event.isCrossTree) {
// 跨树拖拽逻辑
event.accept()
}
}
</script>完整跨树拖拽事件监听
<template>
<div class="flex gap-4">
<Tree
id="tree1"
:value="sourceData"
dragdropScope="cross-tree"
@node-drop="onCrossTreeDrop"
@cross-tree-drag-start="onCrossTreeDragStart"
@cross-tree-drag-enter="onCrossTreeDragEnter"
@cross-tree-drag-over="onCrossTreeDragOver"
@cross-tree-drag-leave="onCrossTreeDragLeave"
@cross-tree-drop="onCrossTreeDropEvent"
@cross-tree-drag-end="onCrossTreeDragEnd"
@cross-tree-drag-cancel="onCrossTreeDragCancel"
/>
<Tree
id="tree2"
:value="targetData"
dragdropScope="cross-tree"
@node-drop="onCrossTreeDrop"
@cross-tree-drag-start="onCrossTreeDragStart"
@cross-tree-drag-enter="onCrossTreeDragEnter"
@cross-tree-drag-over="onCrossTreeDragOver"
@cross-tree-drag-leave="onCrossTreeDragLeave"
@cross-tree-drop="onCrossTreeDropEvent"
@cross-tree-drag-end="onCrossTreeDragEnd"
@cross-tree-drag-cancel="onCrossTreeDragCancel"
/>
</div>
</template>
<script setup>
import { Tree } from 'vue3-super-tree'
import 'vue3-super-tree/style.css'
// 跨树拖拽开始
const onCrossTreeDragStart = (event) => {
console.log('跨树拖拽开始:', {
sourceTreeId: event.sourceTreeId,
dragNode: event.dragNode,
isCrossTree: event.isCrossTree,
timestamp: event.timestamp
})
}
// 拖拽节点进入目标树
const onCrossTreeDragEnter = (event) => {
console.log('进入目标树:', {
sourceTreeId: event.sourceTreeId,
targetTreeId: event.targetTreeId,
dragNode: event.dragNode
})
}
// 拖拽节点在目标树上方移动(频繁触发)
const onCrossTreeDragOver = (event) => {
// 由于频繁触发,通常只做必要的处理
console.log('在目标树上方移动')
}
// 拖拽节点离开目标树
const onCrossTreeDragLeave = (event) => {
console.log('离开目标树:', {
targetTreeId: event.targetTreeId,
dragNode: event.dragNode
})
}
// 跨树拖拽放置
const onCrossTreeDropEvent = (event) => {
console.log('跨树拖拽放置:', {
sourceTreeId: event.sourceTreeId,
targetTreeId: event.targetTreeId,
dragNode: event.dragNode,
dropNode: event.dropNode,
dropPosition: event.dropPosition
})
}
// 跨树拖拽结束
const onCrossTreeDragEnd = (event) => {
console.log('跨树拖拽结束:', {
success: event.success,
sourceTreeId: event.sourceTreeId,
targetTreeId: event.targetTreeId
})
}
// 跨树拖拽取消(如按 ESC 键)
const onCrossTreeDragCancel = (event) => {
console.log('跨树拖拽取消:', {
sourceTreeId: event.sourceTreeId,
dragNode: event.dragNode
})
}
// 传统的 node-drop 事件处理
const onCrossTreeDrop = (event) => {
if (event.isCrossTree) {
event.accept()
}
}
</script>跨树拖拽自动更新
crossTreeAutoUpdate 参数控制跨树拖拽时是否自动更新数据源。v1.3.0 版本完善了跨树拖拽的数据同步机制,确保在两种模式下都能正确更新数据:
- 自动更新模式 (
crossTreeAutoUpdate: true):拖拽操作会自动调用accept()方法并同步更新源树和目标树的数据 - 手动确认模式 (
crossTreeAutoUpdate: false):需要手动调用accept()或reject()方法,确认后才会更新数据
<template>
<div class="flex gap-4">
<!-- 自动更新模式 -->
<Tree
id="auto-tree1"
:value="autoData1"
:crossTreeAutoUpdate="true"
:dragdrop="true"
dragdropScope="auto-cross-tree"
@cross-tree-drop="onAutoUpdateDrop"
/>
<Tree
id="auto-tree2"
:value="autoData2"
:crossTreeAutoUpdate="true"
:dragdrop="true"
dragdropScope="auto-cross-tree"
@cross-tree-drop="onAutoUpdateDrop"
/>
<!-- 手动控制模式 -->
<Tree
id="manual-tree1"
:value="manualData1"
:crossTreeAutoUpdate="false"
:dragdrop="true"
dragdropScope="manual-cross-tree"
@cross-tree-drop="onManualControlDrop"
/>
<Tree
id="manual-tree2"
:value="manualData2"
:crossTreeAutoUpdate="false"
:dragdrop="true"
dragdropScope="manual-cross-tree"
@cross-tree-drop="onManualControlDrop"
/>
</div>
</template>
<script setup>
import { ref } from 'vue'
import { Tree } from 'vue3-super-tree'
import 'vue3-super-tree/style.css'
const autoData1 = ref([...]) // 自动更新模式的源树数据
const autoData2 = ref([...]) // 自动更新模式的目标树数据
const manualData1 = ref([...]) // 手动控制模式的源树数据
const manualData2 = ref([...]) // 手动控制模式的目标树数据
// 自动更新模式:拖拽会自动完成数据同步
const onAutoUpdateDrop = (event) => {
console.log('自动更新拖拽:', event)
// v1.3.0 版本已完善数据同步机制,无需手动处理数据更新
// 组件会自动调用 accept() 并更新源树和目标树的数据
}
// 手动控制模式:需要手动决定是否接受拖拽
const onManualControlDrop = (event) => {
console.log('手动控制拖拽:', event)
// 根据业务逻辑决定是否接受拖拽
const shouldAccept = validateDragOperation(event)
if (shouldAccept) {
event.accept() // 接受拖拽,自动更新源树和目标树数据
} else {
event.reject() // 拒绝拖拽,不更新数据
}
}
const validateDragOperation = (event) => {
// 自定义验证逻辑
return event.dragNode.label !== '禁止拖拽的节点'
}
</script>使用场景:
- 自动更新模式 (
crossTreeAutoUpdate: true):适用于简单的拖拽操作,无需复杂验证 - 手动控制模式 (
crossTreeAutoUpdate: false):适用于需要验证、确认或异步处理的拖拽操作
注意事项:
crossTreeAutoUpdate只影响跨树拖拽,不影响同树内的拖拽操作- 手动控制模式下,必须调用
accept()或reject()方法,否则拖拽状态不会清理 - 可以与
autoUpdate参数配合使用,分别控制同树和跨树的拖拽行为
7. 节点过滤
<template>
<Tree
:value="data"
:filter="true"
filterBy="label"
filterPlaceholder="搜索节点..."
/>
</template>
<script setup>
import { Tree } from 'vue3-super-tree'
import 'vue3-super-tree/style.css'
</script>8. 懒加载
<template>
<Tree
:value="data"
:lazy="true"
@node-load="onNodeLoad"
/>
</template>
<script setup>
import { Tree } from 'vue3-super-tree'
import 'vue3-super-tree/style.css'
const onNodeLoad = async (event) => {
// 异步加载子节点
const children = await fetchChildren(event.node.key)
event.node.children = children
}
</script>键盘操作
| 按键 | 功能 |
|------|------|
| ↑/↓ | 上下导航 |
| ←/→ | 展开/折叠节点 |
| Enter | 选择节点 |
| Space | 切换复选框状态 |
| Home | 跳转到第一个节点 |
| End | 跳转到最后一个节点 |
| Ctrl/Cmd + A | 全选(多选模式) |
样式定制
导入样式
// 导入默认样式
import 'vue3-super-tree/style.css'原生焦点样式
组件使用浏览器原生的 :focus 伪类来管理焦点样式,提供更好的性能和无障碍访问体验:
/* 组件内置的焦点样式 */
.tree-node:focus {
outline: 2px solid #3b82f6;
outline-offset: -2px;
background-color: #eff6ff;
}
/* 自定义焦点样式 */
.tree-node:focus {
outline: 2px solid #10b981;
background-color: #ecfdf5;
border-radius: 0.375rem;
}主题变量
组件支持通过 CSS 变量进行主题定制:
:root {
--tree-node-padding: 0.5rem;
--tree-node-border-radius: 0.375rem;
--tree-selected-bg: #3b82f6;
--tree-selected-color: white;
--tree-focus-bg: #eff6ff;
--tree-focus-color: #1e40af;
--tree-focus-outline: #3b82f6;
}自定义样式类
<Tree
:value="data"
class="custom-tree"
:selectedBackgroundColor="#10b981"
:selectedTextColor="white"
:focusBackgroundColor="#ecfdf5"
:focusTextColor="#059669"
/>相关链接
- npm 包: https://www.npmjs.com/package/vue3-super-tree
- GitHub 仓库: https://github.com/zuoanCo/vue3-super-tree
- 在线演示: https://zuoanCo.github.io/vue3-super-tree
- API 文档: https://github.com/zuoanCo/vue3-super-tree#api-文档
- 问题反馈: https://github.com/zuoanCo/vue3-super-tree/issues
技术栈
- Vue 3.4+ - 渐进式 JavaScript 框架
- TypeScript - 类型安全的 JavaScript 超集
- Vite - 快速的前端构建工具
- Tailwind CSS - 实用优先的 CSS 框架
- Lucide Icons - 美观的图标库
性能优化
生产构建优化
组件在生产环境下自动进行以下优化:
- 调试日志移除 - 自动移除所有
console.log调试信息 - 代码压缩 - 使用 Terser 进行代码压缩和混淆
- Tree Shaking - 移除未使用的代码,减小包体积
- 原生焦点管理 - 使用浏览器原生
:focus伪类,减少 JavaScript 开销
运行时性能
- 虚拟滚动 - 大数据量时自动启用虚拟滚动
- 智能更新 - 只更新变化的节点,避免全量重渲染
- 内存优化 - 及时清理事件监听器和引用
项目结构
vue3-super-tree/
├── src/
│ ├── components/ # 组件目录
│ │ ├── Tree.vue # 主树组件
│ │ ├── TreeNode.vue # 树节点组件
│ │ └── index.ts # 组件导出
│ ├── lib/ # 核心库
│ │ ├── types.ts # TypeScript 类型定义
│ │ ├── utils.ts # 工具函数
│ │ ├── themes.ts # 主题配置
│ │ └── index.ts # 库导出
│ ├── composables/ # 组合式函数
│ │ ├── useDragDrop.ts # 拖拽功能
│ │ ├── useSelection.ts # 选择功能
│ │ ├── useFilter.ts # 过滤功能
│ │ └── index.ts # 组合函数导出
│ └── style.css # 样式文件
├── dist/ # 构建输出
├── package.json # 包配置
├── vite.lib.config.ts # Vite 库构建配置
└── README.md # 项目文档🔧 故障排除
常见问题
1. 构建错误
问题: TypeScript 构建失败,出现类型错误
error TS2742: The inferred type of 'xxx' cannot be named without a reference to '@vue/shared'解决方案:
- 确保使用的是 v1.4.0 或更高版本,该版本已修复所有构建错误
- 检查 TypeScript 版本是否兼容(推荐 4.9+)
- 清理缓存:
rm -rf node_modules/.cache && pnpm install
2. 跨树拖拽不工作
问题: 跨树拖拽功能无法正常工作
解决方案:
- 确保所有参与跨树拖拽的树组件都设置了相同的
dragdropScope - 检查是否为每个树组件设置了唯一的
id属性 - 确保
CrossTreeDataProvider正确实现了getTreeData和updateTreeData方法 - 检查控制台是否有错误信息
3. 自动更新不生效
问题: 设置了 autoUpdate 或 crossTreeAutoUpdate 但数据没有自动更新
解决方案:
- 确保在拖拽事件处理器中调用了
event.accept() - 检查数据是否为响应式对象(使用
ref或reactive) - 对于跨树拖拽,确保
CrossTreeDataProvider的updateTreeData方法正确更新了响应式数据
4. 性能问题
问题: 大数据量时性能较差
解决方案:
- 启用懒加载:设置
lazy="true" - 使用虚拟滚动(如果数据量特别大)
- 避免在节点模板中使用复杂的计算
- 考虑分页或分批加载数据
5. 样式问题
问题: 样式显示不正确
解决方案:
- 确保导入了样式文件:
import 'vue3-super-tree/style.css' - 检查 CSS 优先级,避免样式被覆盖
- 使用浏览器开发者工具检查样式应用情况
调试建议
1. 开启调试模式
在开发环境中,组件会输出详细的调试信息:
// 在浏览器控制台中查看调试信息
console.log('Vue3 Super Tree Debug Mode: ON')2. 检查事件流
监听所有相关事件来调试问题:
<template>
<Tree
@node-drop="onNodeDrop"
@cross-tree-drop="onCrossTreeDrop"
@cross-tree-drag-start="onDragStart"
@cross-tree-drag-end="onDragEnd"
/>
</template>
<script setup>
const onNodeDrop = (event) => {
console.log('Node drop:', event)
}
const onCrossTreeDrop = (event) => {
console.log('Cross tree drop:', event)
}
const onDragStart = (event) => {
console.log('Drag start:', event)
}
const onDragEnd = (event) => {
console.log('Drag end:', event)
}
</script>3. 数据结构验证
确保树形数据结构正确:
// 正确的数据结构
const treeData = [
{
key: 'unique-key', // 必须:唯一标识
label: 'Node Label', // 必须:显示文本
children: [ // 可选:子节点数组
{
key: 'child-key',
label: 'Child Node'
}
]
}
]性能优化建议
1. 大数据量优化
<template>
<Tree
:value="data"
:lazy="true"
:loading="loading"
@node-expand="loadChildren"
/>
</template>
<script setup>
const loadChildren = async (node) => {
loading.value = true
try {
const children = await fetchChildren(node.key)
node.children = children
} finally {
loading.value = false
}
}
</script>2. 减少重渲染
<script setup>
import { computed, shallowRef } from 'vue'
// 使用 shallowRef 减少深度响应式开销
const treeData = shallowRef([])
// 使用 computed 缓存计算结果
const filteredData = computed(() => {
return filterTreeData(treeData.value, searchTerm.value)
})
</script>3. 事件处理优化
<script setup>
// 使用防抖避免频繁触发
import { debounce } from 'lodash-es'
const handleSearch = debounce((searchTerm) => {
// 搜索逻辑
}, 300)
</script>版本升级指南
从 v1.3.x 升级到 v1.4.0
新增功能:
- 跨树拖拽自动更新功能
CrossTreeDataProvider接口- 全局状态管理
破坏性变更:
- 无破坏性变更,完全向后兼容
推荐更新:
- 使用新的跨树拖拽自动更新功能替代手动处理
- 更新 TypeScript 类型定义
🤝 贡献指南
欢迎贡献代码!vue3-super-tree 是一个开源 npm 包,我们欢迎社区的贡献。
开发环境设置
- 克隆项目
git clone https://github.com/zuoanCo/vue3-super-tree.git
cd vue3-super-tree- 安装依赖
pnpm install- 启动开发服务器
pnpm run dev- 构建库文件
pnpm run build:lib贡献流程
- Fork 项目
- 创建特性分支 (
git checkout -b feature/AmazingFeature) - 提交更改 (
git commit -m 'Add some AmazingFeature') - 推送到分支 (
git push origin feature/AmazingFeature) - 开启 Pull Request
开发规范
- 使用 TypeScript 编写代码
- 遵循 Vue 3 Composition API 最佳实践
- 添加适当的类型注解
- 编写清晰的注释
- 确保代码通过 ESLint 检查
- 添加相应的测试用例
代码规范
项目使用 ESLint 进行代码检查:
# 检查代码
pnpm run lint
# 自动修复
pnpm run lint:fix许可证
本项目采用 MIT 许可证 - 查看 LICENSE 文件了解详情。
致谢
- Vue.js - 渐进式 JavaScript 框架
- PrimeVue - 功能参考来源
- Tailwind CSS - 样式框架
- Lucide - 图标库
- Vite - 构建工具
📊 包信息
版本信息
- 当前版本: v1.4.0 🆕
- 发布状态: 生产就绪 ✅
- 构建状态: 通过 (44个错误已修复) ✅
技术规格
- 包大小: ~220KB (生产优化后)
- 支持的 Vue 版本: 3.4+
- Node.js 版本: 16.0+
- TypeScript 支持: 100% 类型安全 ✅
- Tree Shaking: 完全支持 ✅
- SSR 支持: 服务端渲染友好 ✅
功能特性
- 原生焦点管理: 基于浏览器原生 :focus 伪类 ✅
- 自动拖拽更新: 同树拖拽自动更新 ✅
- 跨树拖拽自动更新: v1.4.0 新增功能 🆕
- 全局状态管理: useCrossTreeDragState ✅
- 错误处理: 完整的错误边界和恢复机制 ✅
- 性能优化: 生产构建自动优化 ✅
构建产物
- ES 模块:
dist/index.es.js - UMD 版本:
dist/index.umd.js - 类型定义:
dist/index.d.ts - 样式文件:
dist/style.css
如果这个项目对你有帮助,请给它一个 ⭐️!
