简介
虽然 element-plus 和 ant-design 等 UI 库中都提供了 tree 组件,但是当前开源组件库中,无法将所有方法和属性集中到单一的树形组件中,所以我希望写一个功能齐全的业务通用组件。
安装与引入
安装依赖
# NPM
$ npm install vxe-tree --save
# Yarn
$ yarn add vxe-tree
# pnpm
$ pnpm install vxe-tree
引入组件
import Vue from "vue";
import vxeTree from "vxe-tree";
import "vxe-tree/dist/index.css";
Vue.use(vxeTree);
基础用法
最简单的树形结构
<template>
<div>
<vxe-tree :data="treeData" :config="treeConfig" />
</div>
</template>
<script>
export default {
data() {
return {
treeData: [
{
id: "1",
label: "根节点1",
children: [
{
id: "1-1",
label: "子节点1-1",
children: [
{ id: "1-1-1", label: "子节点1-1-1" },
{ id: "1-1-2", label: "子节点1-1-2" },
],
},
{ id: "1-2", label: "子节点1-2" },
],
},
{
id: "2",
label: "根节点2",
children: [
{ id: "2-1", label: "子节点2-1" },
{ id: "2-2", label: "子节点2-2" },
],
},
],
treeConfig: {
children: "children",
hasChild: "hasChild",
id: "id",
label: "label",
},
};
},
};
</script>
数据格式
标准数据格式
const treeData = [
{
id: "1", // 节点唯一标识
label: "节点名称", // 显示文本
children: [], // 子节点数组
hasChild: false, // 是否有子节点(用于异步加载)
disabled: false, // 是否禁用
checked: false, // 是否选中
expanded: false, // 是否展开
loading: false, // 是否加载中
icon: "icon-class", // 自定义图标
},
];
自定义数据格式
const customTreeData = [
{
nodeId: "1",
nodeName: "自定义节点",
childNodes: [],
hasChildren: true,
isDisabled: false,
isSelected: false,
isExpanded: false,
},
];
const customConfig = {
children: "childNodes",
hasChild: "hasChildren",
id: "nodeId",
label: "nodeName",
disabled: "isDisabled",
checked: "isSelected",
expanded: "isExpanded",
};
配置选项
基础配置
const treeConfig = {
// 数据字段映射
children: "children", // 子节点字段名
hasChild: "hasChild", // 是否有子节点字段名
id: "id", // 节点ID字段名
label: "label", // 显示文本字段名
disabled: "disabled", // 禁用状态字段名
checked: "checked", // 选中状态字段名
expanded: "expanded", // 展开状态字段名
loading: "loading", // 加载状态字段名
icon: "icon", // 图标字段名
// 显示配置
showLine: true, // 是否显示连接线
showIcon: true, // 是否显示图标
showCheckbox: false, // 是否显示复选框
showRadio: false, // 是否显示单选框
showExpand: true, // 是否显示展开按钮
// 交互配置
accordion: false, // 是否手风琴模式
checkStrictly: false, // 是否严格模式(父子节点不关联)
checkOnClickNode: false, // 点击节点时是否选中
expandOnClickNode: false, // 点击节点时是否展开
// 样式配置
nodeKey: "id", // 节点唯一标识字段
defaultExpandAll: false, // 是否默认展开所有节点
defaultExpandedKeys: [], // 默认展开的节点
defaultCheckedKeys: [], // 默认选中的节点
highlightCurrent: true, // 是否高亮当前选中节点
};
高级配置
const advancedConfig = {
// 异步加载配置
lazy: false, // 是否启用异步加载
load: null, // 异步加载函数
// 拖拽配置
draggable: false, // 是否启用拖拽
allowDrop: null, // 拖拽放置判断函数
allowDrag: null, // 拖拽开始判断函数
// 搜索配置
filterNodeMethod: null, // 节点过滤方法
// 渲染配置
renderContent: null, // 自定义节点内容渲染
renderIcon: null, // 自定义图标渲染
// 事件配置
onNodeClick: null, // 节点点击事件
onNodeExpand: null, // 节点展开事件
onNodeCollapse: null, // 节点收起事件
onCheckChange: null, // 选中状态变化事件
};
事件处理
基础事件
<template>
<vxe-tree
:data="treeData"
:config="treeConfig"
@node-click="handleNodeClick"
@node-expand="handleNodeExpand"
@node-collapse="handleNodeCollapse"
@check-change="handleCheckChange"
/>
</template>
<script>
export default {
methods: {
// 节点点击事件
handleNodeClick(data, node, event) {
console.log("点击节点:", data);
console.log("节点信息:", node);
console.log("事件对象:", event);
},
// 节点展开事件
handleNodeExpand(data, node) {
console.log("展开节点:", data);
// 可以在这里进行异步加载
this.loadChildNodes(data, node);
},
// 节点收起事件
handleNodeCollapse(data, node) {
console.log("收起节点:", data);
},
// 选中状态变化事件
handleCheckChange(data, checked, indeterminate) {
console.log("选中状态变化:", data, checked, indeterminate);
},
},
};
</script>
异步加载示例
// 异步加载子节点
async loadChildNodes(data, node) {
try {
// 设置加载状态
this.$set(data, 'loading', true)
// 模拟API请求
const response = await this.fetchChildNodes(data.id)
// 更新子节点数据
this.$set(data, 'children', response.data)
this.$set(data, 'hasChild', false)
} catch (error) {
console.error('加载子节点失败:', error)
} finally {
this.$set(data, 'loading', false)
}
},
// 模拟API请求
async fetchChildNodes(parentId) {
return new Promise((resolve) => {
setTimeout(() => {
resolve({
data: [
{ id: `${parentId}-1`, label: `子节点${parentId}-1` },
{ id: `${parentId}-2`, label: `子节点${parentId}-2` }
]
})
}, 1000)
})
}
高级功能
搜索过滤
<template>
<div>
<vxe-input
v-model="searchKeyword"
placeholder="搜索节点"
@input="handleSearch"
/>
<vxe-tree ref="tree" :data="filteredTreeData" :config="treeConfig" />
</div>
</template>
<script>
export default {
data() {
return {
searchKeyword: "",
originalTreeData: [], // 原始数据
filteredTreeData: [], // 过滤后的数据
treeConfig: {
filterNodeMethod: this.filterNode,
},
};
},
methods: {
// 节点过滤方法
filterNode(value, data, node) {
if (!value) return true;
return data.label.toLowerCase().includes(value.toLowerCase());
},
// 搜索处理
handleSearch() {
this.filteredTreeData = this.filterTreeData(
this.originalTreeData,
this.searchKeyword
);
},
// 递归过滤树数据
filterTreeData(data, keyword) {
return data.filter((item) => {
const match = item.label.toLowerCase().includes(keyword.toLowerCase());
if (item.children && item.children.length > 0) {
item.children = this.filterTreeData(item.children, keyword);
return match || item.children.length > 0;
}
return match;
});
},
},
};
</script>
拖拽排序
<template>
<vxe-tree :data="treeData" :config="treeConfig" @node-drop="handleNodeDrop" />
</template>
<script>
export default {
data() {
return {
treeConfig: {
draggable: true,
allowDrop: this.allowDrop,
allowDrag: this.allowDrag,
},
};
},
methods: {
// 允许拖拽判断
allowDrag(node) {
// 不允许拖拽根节点
return node.level !== 0;
},
// 允许放置判断
allowDrop(draggingNode, dropNode, type) {
// 不允许放置到禁用节点
if (dropNode.data.disabled) return false;
// 不允许放置到自己或自己的子节点
if (draggingNode.data.id === dropNode.data.id) return false;
return true;
},
// 拖拽完成事件
handleNodeDrop(draggingNode, dropNode, dropType, ev) {
console.log("拖拽完成:", {
draggingNode: draggingNode.data,
dropNode: dropNode.data,
dropType, // 'prev', 'inner', 'next'
event: ev,
});
// 这里可以调用API更新数据
this.updateNodePosition(draggingNode.data.id, dropNode.data.id, dropType);
},
},
};
</script>
多选功能
<template>
<div>
<vxe-tree
ref="tree"
:data="treeData"
:config="treeConfig"
@check-change="handleCheckChange"
/>
<div class="actions">
<vxe-button @click="getCheckedNodes">获取选中节点</vxe-button>
<vxe-button @click="setCheckedNodes">设置选中节点</vxe-button>
<vxe-button @click="clearChecked">清空选中</vxe-button>
</div>
</div>
</template>
<script>
export default {
data() {
return {
treeConfig: {
showCheckbox: true,
checkStrictly: false, // 父子节点关联
defaultCheckedKeys: ["1-1"], // 默认选中节点
},
};
},
methods: {
// 获取选中的节点
getCheckedNodes() {
const checkedNodes = this.$refs.tree.getCheckedNodes();
console.log("选中的节点:", checkedNodes);
},
// 设置选中节点
setCheckedNodes() {
this.$refs.tree.setCheckedKeys(["1-1", "1-2"]);
},
// 清空选中
clearChecked() {
this.$refs.tree.setCheckedKeys([]);
},
// 选中状态变化
handleCheckChange(data, checked, indeterminate) {
console.log("选中状态变化:", {
node: data,
checked,
indeterminate,
});
},
},
};
</script>
总结
vxeTree 是一个功能强大、配置灵活的树形组件,主要特点包括:
- 丰富的配置选项:支持自定义数据格式、显示样式、交互行为等
- 强大的交互功能:支持拖拽排序、搜索过滤、多选等
- 异步加载支持:可以轻松实现懒加载功能
- 完善的事件系统:提供丰富的事件回调
- 良好的扩展性:支持自定义渲染和样式
Vue3 vxeTree树形组件完全指南:从入门到精通的完整使用教程 - 高质量源码分享平台-免费下载各类网站源码与模板及前沿技术分享