之前想着做一个树图,通过v-for套3层,实现了一个三层树图

后来突然开窍了,vue组件的形式,可以组件套组件,方便多了
先上图

[
{
"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>