我们来自己搞一个树组件
1. 创建 Vue 项目
如果你还没有创建 Vue 项目,可以使用 Vue CLI 快速创建:
vue create my-tree-component
cd my-tree-component
2. 安装依赖
确保安装了 Vue Router 和 Vuex(如果需要状态管理):
npm install vue-router vuex --save
3. 创建 Tree 组件
在 src/components
目录下创建 Tree.vue
文件:
<template>
<div class="tree">
<ul>
<tree-node v-for="node in data" :key="node.id" :node="node"></tree-node>
</ul>
</div>
</template>
<script>
import TreeNode from './TreeNode.vue';
export default {
name: 'Tree',
components: {
TreeNode
},
props: {
data: {
type: Array,
required: true
}
}
};
</script>
<style scoped>
.tree ul {
list-style-type: none;
padding-left: 20px;
}
</style>
4. 创建 TreeNode 组件
在 src/components
目录下创建 TreeNode.vue
文件:
<template>
<li>
<span @click="toggle">{{ node.name }}</span>
<ul v-if="isOpen">
<tree-node v-for="child in node.children" :key="child.id" :node="child"></tree-node>
</ul>
</li>
</template>
<script>
export default {
name: 'TreeNode',
props: {
node: {
type: Object,
required: true
}
},
data() {
return {
isOpen: false
};
},
methods: {
toggle() {
this.isOpen = !this.isOpen;
}
}
};
</script>
<style scoped>
li {
cursor: pointer;
}
</style>
5. 使用 Tree 组件
在 src/App.vue
中使用 Tree
组件:
<template>
<div id="app">
<tree :data="treeData"></tree>
</div>
</template>
<script>
import Tree from './components/Tree.vue';
export default {
name: 'App',
components: {
Tree
},
data() {
return {
treeData: [
{
id: 1,
name: 'Node 1',
children: [
{ id: 2, name: 'Child Node 1' },
{ id: 3, name: 'Child Node 2' }
]
},
{
id: 4,
name: 'Node 2',
children: [
{ id: 5, name: 'Child Node 3' }
]
}
]
};
}
};
</script>
<style>
#app {
font-family: Avenir, Helvetica, Arial, sans-serif;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
text-align: center;
color: #2c3e50;
margin-top: 60px;
}
</style>
6. 运行项目
运行项目以查看效果:
npm run serve
7. 功能扩展
- 搜索功能:添加一个搜索框,根据输入过滤树节点。
- 拖拽排序:使用第三方库如
vuedraggable
实现节点的拖拽排序。 - 懒加载:对于大型树结构,可以实现懒加载,仅在展开节点时加载子节点数据。
功能扩展的实现
1. 搜索功能
修改 Tree.vue
组件
在 Tree.vue
中添加一个搜索框,并根据输入过滤树节点。
<template>
<div class="tree">
<input type="text" v-model="searchQuery" placeholder="Search..." />
<ul>
<tree-node v-for="node in filteredData" :key="node.id" :node="node"></tree-node>
</ul>
</div>
</template>
<script>
import TreeNode from './TreeNode.vue';
import { filterTree } from '@/utils/treeUtils';
export default {
name: 'Tree',
components: {
TreeNode
},
props: {
data: {
type: Array,
required: true
}
},
data() {
return {
searchQuery: ''
};
},
computed: {
filteredData() {
if (!this.searchQuery) return this.data;
return filterTree(this.data, this.searchQuery);
}
}
};
</script>
<style scoped>
.tree ul {
list-style-type: none;
padding-left: 20px;
}
</style>
创建 treeUtils.js
工具文件
在 src/utils
目录下创建 treeUtils.js
文件,用于实现树节点的过滤功能。
export function filterTree(data, query) {
const lowerCaseQuery = query.toLowerCase();
return data.filter(node => {
if (node.name.toLowerCase().includes(lowerCaseQuery)) {
return true;
}
if (node.children && node.children.length > 0) {
node.children = filterTree(node.children, query);
return node.children.length > 0;
}
return false;
});
}
2. 拖拽排序
安装 vuedraggable
首先,安装 vuedraggable
库:
npm install vuedraggable
修改 Tree.vue
组件
在 Tree.vue
中引入 vuedraggable
并使用它来实现拖拽排序。
<template>
<div class="tree">
<input type="text" v-model="searchQuery" placeholder="Search..." />
<draggable v-model="filteredData" @change="onDragChange">
<tree-node v-for="node in filteredData" :key="node.id" :node="node"></tree-node>
</draggable>
</div>
</template>
<script>
import TreeNode from './TreeNode.vue';
import draggable from 'vuedraggable';
import { filterTree } from '@/utils/treeUtils';
export default {
name: 'Tree',
components: {
TreeNode,
draggable
},
props: {
data: {
type: Array,
required: true
}
},
data() {
return {
searchQuery: '',
localData: [...this.data]
};
},
computed: {
filteredData: {
get() {
if (!this.searchQuery) return this.localData;
return filterTree(this.localData, this.searchQuery);
},
set(value) {
this.localData = value;
}
}
},
methods: {
onDragChange(event) {
console.log('Drag change:', event);
// 处理拖拽后的变化,例如更新父组件的数据
this.$emit('update:data', this.localData);
}
}
};
</script>
<style scoped>
.tree ul {
list-style-type: none;
padding-left: 20px;
}
</style>
3. 懒加载
修改 TreeNode.vue
组件
在 TreeNode.vue
中实现懒加载功能。
<template>
<li>
<span @click="toggle">{{ node.name }}</span>
<ul v-if="isOpen">
<draggable v-model="node.children" @change="onDragChange">
<tree-node v-for="child in node.children" :key="child.id" :node="child"></tree-node>
</draggable>
</ul>
</li>
</template>
<script>
import draggable from 'vuedraggable';
export default {
name: 'TreeNode',
components: {
draggable
},
props: {
node: {
type: Object,
required: true
}
},
data() {
return {
isOpen: false,
hasLoadedChildren: false
};
},
methods: {
toggle() {
if (!this.hasLoadedChildren && this.node.children && this.node.children.length === 0) {
this.loadChildren();
}
this.isOpen = !this.isOpen;
},
loadChildren() {
// 模拟异步加载子节点
setTimeout(() => {
this.node.children = [
{ id: 101, name: 'Lazy Child 1' },
{ id: 102, name: 'Lazy Child 2' }
];
this.hasLoadedChildren = true;
}, 1000);
},
onDragChange(event) {
console.log('Drag change:', event);
// 处理拖拽后的变化,例如更新父组件的数据
this.$emit('update:node', { ...this.node, children: this.node.children });
}
}
};
</script>
<style scoped>
li {
cursor: pointer;
}
</style>
4. 更新 App.vue
确保 App.vue
中的 Tree
组件能够接收和处理数据更新。
<template>
<div id="app">
<tree :data="treeData" @update:data="updateTreeData"></tree>
</div>
</template>
<script>
import Tree from './components/Tree.vue';
export default {
name: 'App',
components: {
Tree
},
data() {
return {
treeData: [
{
id: 1,
name: 'Node 1',
children: [
{ id: 2, name: 'Child Node 1' },
{ id: 3, name: 'Child Node 2' }
]
},
{
id: 4,
name: 'Node 2',
children: [
{ id: 5, name: 'Child Node 3' }
]
}
]
};
},
methods: {
updateTreeData(newData) {
this.treeData = newData;
}
}
};
</script>
<style>
#app {
font-family: Avenir, Helvetica, Arial, sans-serif;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
text-align: center;
color: #2c3e50;
margin-top: 60px;
}
</style>
5. 运行项目
运行项目以查看效果:
npm run serve
基本的功能就都具备了,当然实际生产中可能会碰到各种新的需求,慢慢来扩展即可。