之前想着做一个树图,通过v-for套3层,实现了一个三层树图
后来突然开窍了,vue组件的形式,可以组件套组件,方便多了
仿windows文件管理器的文件树
先上图
定义数据结构
[
{
"path":"/快速访问",
"name":"快速访问",
"icon":"quick_access",
"type":"dir",
"child":[]
},
]
通过child无线嵌套。
基础结构
当前节点中包含,展开图标,合并图标,空白图标,文件夹图标,文件名。
子节点就是遍历当前节点的child数组生成。
需要注意两个问题
(1)树图不是一下子就把全部叶子节点都给展开的,未展开前,需不需要提前渲染好。
(2)叶子节点多次展开合并,子节点不应该每次都重复生成渲染。
这里使用两个变量来控制。
isExtend: false, 是否展开节点,通过v-show开控制
isCreatedChild: false,是否生成节点,通过v-if来控制
当第一次展开的时候,生成一次子节点。isCreatedChild变为true,后续不在变动。
后续展开合并通过控制isExtend即可。
这样也是实现了异步加载树节点。
无子节点的情况
添加如下变量,默认为true
isExistChild: true,
在第一次展开的时候判断一次,没有子节点时置为false,后续不在改动
当isExistChild为false时,将展开合并图标隐藏,不显示。
设置选中样式
如何实现只选中一个节点?
设置选中和未选中的样式
.file_tree_node_div {
white-space: nowrap;
border: 1px solid transparent;
}
.file_tree_node_div:hover {
background-color: #e9f3f3;
border: 1px solid #e9f3f3;
}
.file_tree_node_div_selected {
white-space: nowrap;
background-color: #cce6ee;
border: 1px solid #cce6ee;
}
.file_tree_node_div_selected:hover {
border: 1px solid #b3dce7;
}
节点个数不固定,隔代节点之间通信困难,当点击一个节点标记为选中,遍历其他节点,将选中的置为不选中,这种消耗太大。
不如直接操作document来的快,
setSelected(){
let arr=document.getElementsByClassName("file_tree_node_div_selected");
if(arr&&arr.length>0)arr[0].className='file_tree_node_div';
this.$refs.node.className='file_tree_node_div_selected';
},
完整效果
附:节点源码
<template>
<div>
<div ref="node" class="file_tree_node_div" >
<b-icon v-if="isExistChild&&!isExtend" @click="handle_extend_node()" class="file_tree_node_arrow"
local="arrow_thick_right" style="color: #adadad;"></b-icon>
<b-icon v-else-if="isExistChild&&isExtend" @click="handle_extend_node()" class="file_tree_node_arrow"
local="arrow_thick_bottom" style="color: #535353;"></b-icon>
<img v-else class="file_tree_node_arrow" src="" style="opacity: 0;">
<img class="file_tree_node_icon" :src="icon">
<button class="empty_button file_tree_node_name" @click="handle_change_path">{{ name }}</button>
</div>
<div v-if="isCreatedChild" v-show="isExtend">
<template v-for="(item,index) in child" :key="index+'b'">
<FileTreeNode v-if="item.type==='dir'" :data="item" style="margin-left: 10px;"></FileTreeNode>
</template>
</div>
</div>
</template>
<script>
export default {
name: "FileTreeNode",
props: {
data: Object,
},
data() {
return {
name: '',
icon: '',
isExistChild: true,
isExtend: false,
isCreatedChild: false,
isSelected:false,
child: [],
}
},
created() {
this.init(this.data);
},
methods: {
init(data) {
if (data) {
this.name = data.name;
if (!data.icon || data.icon === '') this.icon = require("@/assets/file/dir.png")
this.icon = require("@/assets/file/" + data.icon+'.png');
this.child = data.child;
this.path = data.path;
if(data.isExtend)this.handle_extend_node();
}
},
/**
* 展开节点或者关闭节点
*/
handle_extend_node() {
this.isExistChild = this.checkExistDir(this.child);
this.isExtend = !this.isExtend;
if (!this.isCreatedChild) {
this.isCreatedChild = true;
}
},
/**
* 检查路径下是否存在文件夹
*/
checkExistDir(child) {
if (child) {
for (let i = 0; i < child.length; i++) {
if (child[i].type === 'dir') return true;
}
}
return false;
},
/**
* 获取当前节点路径,每一个节点都将数组传递给父节点,等父节点添加好文件夹名称后,子节点在添加
*/
getPath(arr){
this.$parent.getPath(arr);
if(!this.$parent.isRoot){
arr.push(this.name);
}
return arr;
},
/**
* 点击树图节点
*/
handle_change_path(){
if(this.$refs.node.className!=='file_tree_node_div_selected')
this.setSelected();
let path=[];
if('此电脑 快速访问'.indexOf(this.name)>-1){
path.push(this.name);
}else{
path=this.getPath([]);
}
this.$event.$emit('FileManager_set_data',path);
},
setSelected(){
let arr=document.getElementsByClassName("file_tree_node_div_selected");
if(arr&&arr.length>0)arr[0].className='file_tree_node_div';
this.$refs.node.className='file_tree_node_div_selected';
},
},
}
</script>
<style>
.file_tree_node_arrow {
width: 25px;
height: 25px;
opacity: 0;
transition: all 2s ease-out;
}
.file_tree_node_icon {
width: 25px;
height: 25px;
margin-top: -4px;
padding: 2px;
}
.file_tree_node_div {
white-space: nowrap;
border: 1px solid transparent;
}
.file_tree_node_div:hover {
background-color: #e9f3f3;
border: 1px solid #e9f3f3;
}
.file_tree_node_div_selected {
white-space: nowrap;
background-color: #cce6ee;
border: 1px solid #cce6ee;
}
.file_tree_node_div_selected:hover {
border: 1px solid #b3dce7;
}
.file_tree_node_name {
padding-top: 2px;
padding-left: 3px;
cursor: pointer;
}
</style>