vue文件树实现
Vue 文件树实现方法
使用递归组件实现文件树
递归组件是 Vue 中实现树形结构的常用方法。通过组件自身调用自身,可以轻松处理嵌套的树形数据。
<template>
<ul>
<li v-for="item in treeData" :key="item.id">
{{ item.name }}
<file-tree v-if="item.children && item.children.length" :treeData="item.children" />
</li>
</ul>
</template>
<script>
export default {
name: 'FileTree',
props: {
treeData: {
type: Array,
required: true
}
}
}
</script>
添加交互功能
为文件树添加展开/折叠功能,增强用户体验。

<template>
<ul>
<li v-for="item in treeData" :key="item.id">
<div @click="toggle(item)">
<span v-if="item.children && item.children.length">
{{ isExpanded(item) ? '▼' : '▶' }}
</span>
{{ item.name }}
</div>
<file-tree
v-if="item.children && item.children.length && isExpanded(item)"
:treeData="item.children"
/>
</li>
</ul>
</template>
<script>
export default {
name: 'FileTree',
props: {
treeData: {
type: Array,
required: true
}
},
data() {
return {
expandedItems: []
}
},
methods: {
toggle(item) {
const index = this.expandedItems.indexOf(item.id)
if (index === -1) {
this.expandedItems.push(item.id)
} else {
this.expandedItems.splice(index, 1)
}
},
isExpanded(item) {
return this.expandedItems.includes(item.id)
}
}
}
</script>
使用第三方组件库
对于快速开发,可以使用现成的 Vue 树形组件库,如 Element UI 的 Tree 组件。
<template>
<el-tree
:data="treeData"
:props="defaultProps"
@node-click="handleNodeClick"
/>
</template>
<script>
export default {
data() {
return {
treeData: [{
label: '一级 1',
children: [{
label: '二级 1-1'
}]
}],
defaultProps: {
children: 'children',
label: 'label'
}
}
},
methods: {
handleNodeClick(data) {
console.log(data)
}
}
}
</script>
动态加载数据
对于大型文件树,可以实现按需加载数据的功能。

<template>
<ul>
<li v-for="item in treeData" :key="item.id">
<div @click="loadChildren(item)">
{{ item.name }}
<span v-if="item.children && item.children.length">
({{ item.children.length }})
</span>
</div>
<file-tree
v-if="item.children && item.children.length"
:treeData="item.children"
/>
</li>
</ul>
</template>
<script>
export default {
name: 'FileTree',
props: {
treeData: {
type: Array,
required: true
}
},
methods: {
async loadChildren(item) {
if (!item.children || item.children.length === 0) {
const children = await this.fetchChildren(item.id)
this.$set(item, 'children', children)
}
},
fetchChildren(id) {
return new Promise(resolve => {
// 模拟API请求
setTimeout(() => {
resolve([
{ id: `${id}-1`, name: `子节点 ${id}-1` },
{ id: `${id}-2`, name: `子节点 ${id}-2` }
])
}, 500)
})
}
}
}
</script>
样式优化
为文件树添加基本样式,使其更美观。
ul {
list-style-type: none;
padding-left: 20px;
}
li {
margin: 5px 0;
cursor: pointer;
}
li > div {
padding: 3px 5px;
}
li > div:hover {
background-color: #f0f0f0;
}
完整示例
结合上述功能,创建一个完整的文件树组件示例。
<template>
<div class="file-tree">
<ul>
<li v-for="item in treeData" :key="item.id">
<div
@click="handleItemClick(item)"
:class="{ 'has-children': item.children && item.children.length }"
>
<span class="toggle-icon" v-if="item.children && item.children.length">
{{ isExpanded(item) ? '−' : '+' }}
</span>
<span class="item-name">{{ item.name }}</span>
</div>
<file-tree
v-if="item.children && item.children.length && isExpanded(item)"
:treeData="item.children"
/>
</li>
</ul>
</div>
</template>
<script>
export default {
name: 'FileTree',
props: {
treeData: {
type: Array,
required: true
}
},
data() {
return {
expandedItems: []
}
},
methods: {
handleItemClick(item) {
if (item.children && item.children.length) {
this.toggleExpand(item)
}
this.$emit('item-click', item)
},
toggleExpand(item) {
const index = this.expandedItems.indexOf(item.id)
if (index === -1) {
this.expandedItems.push(item.id)
} else {
this.expandedItems.splice(index, 1)
}
},
isExpanded(item) {
return this.expandedItems.includes(item.id)
}
}
}
</script>
<style scoped>
.file-tree {
font-family: Arial, sans-serif;
user-select: none;
}
ul {
list-style-type: none;
padding-left: 20px;
}
li {
margin: 5px 0;
}
li > div {
padding: 5px 8px;
display: flex;
align-items: center;
}
li > div:hover {
background-color: #f5f5f5;
}
.toggle-icon {
display: inline-block;
width: 15px;
text-align: center;
margin-right: 5px;
}
.has-children {
font-weight: bold;
}
.item-name {
margin-left: 5px;
}
</style>
这些方法提供了从基础到高级的文件树实现方案,可以根据项目需求选择适合的方式或组合使用多种技术。






