使用过程描述:文件目录树的操作要求是,树的结构数据不能一次性吐出,点击树的节点,加载该节点下的数据,除了数据加载的情况外,还有相关子节点的新建。当时没有看elementUI关于el-tree组件的详细文档,就是感觉文件数在数据子节点的递归上会有麻烦;所以试过几个方法,尝试过自己写组件,利用递归的方法一次性将文件数加载出来,最后勉强能够加载,但是新增子节点这块又遇到麻烦,就是还要根据递归将新增的子节点加载到选中的节点下,还是用递归,整个过程写下来很痛苦,使用的体验也不是很完美。最后又倒腾到el-tree上,回到官网老老实实滴看了各种函数使用说明。归结起来找到el-tree的扩展方法:新增子节点和删除子节点。
<el-tree
:data="data"
show-checkbox
node-key="id"
ref="tree"
default-expand-all
:expand-on-click-node="false">
<span class="custom-tree-node" slot-scope="{ node, data }">
<span>{{ node.label }}</span>
<span>
<el-button
type="text"
size="mini"
@click="() => append(data)">
Append
</el-button>
<el-button
type="text"
size="mini"
@click="() => remove(node, data)">
Delete
</el-button>
</span>
</span>
</el-tree>
append(data) {
const newChild = { id: id++, label: 'testtest', children: [] };
if (!data.children) {
this.$set(data, 'children', []);
}
data.children.push(newChild);
},
remove(node, data) {
const parent = node.parent;
const children = parent.data.children || parent.data;
const index = children.findIndex(d => d.id === data.id);
children.splice(index, 1);
}
通过尝试,的确可以满足大部分的使用需求,但是依然存在问题。新增节点静态数据插入到目标节点上是没问题,但是实际情况是需要先查询当前目标节点的children的数据,然后将新增的data数据push到children,最后将children利用$set方法改变目标节点。实际操作中逻辑没有问题,但就是不成功,通过控制台打印出来的结果也是新增了一条的数据children,可是文件树却不能更新。使用了官网提供的updateKeyChildren(通过 keys 设置节点子元素,使用此方法必须设置 node-key 属性)也没有用。后来通过控制台打印观察,发现每次操作更新节点数据之前children数据已经被更新,但是感觉就是有一个点没有抓住,网上查找了很多资料,找到一个关于date数据深拷贝的说法,感觉很有道理,就试了试,没想到竟然真的成功了。一个问题困扰了自己好多天,终于被解决的感觉真是太美妙了。
完整代码如下,供下次参考。
<el-tree ref="tree" :data="dataTree"
:props="defaultProps"
node-key="id"
:default-expand-all="true" // 目录树默认是否全部展开
:highlight-current="true" // 选中节点是否高亮(加背景色区分)
:expand-on-click-node="false"
@node-click="handleNodeClick">
<span class="custom-tree-node" slot-scope="{ node, data }">
<span v-if="!data.isedit">
<i class="el-icon-folder"></i>
{{ node.label }}</span>
<span v-else>
<el-input v-model="data.dirName" size="mini" @keyup.enter.native="saveDir(data)">
<!-- <template slot="append" style="padding: 0 10px;">
<i class="el-icon-check" @click.stop="saveDir(data)"></i></template> -->
</el-input>
</span>
<span style="font-size:20px;">
<el-button type="text" size="mini" @click.stop="() => append(data,node)">
<i class="el-icon-plus"></i>
</el-button>
<el-button type="text" size="mini" v-if="node.level !== 1"
@click="() => editDir(node,data)">
<i class="el-icon-edit"></i>
</el-button>
<el-button type="text" size="mini" v-if="node.level !== 1" @click="() => remove(node, data)">
<i class="el-icon-delete"></i>
</el-button>
</span>
</span>
</el-tree>
// JS部分
// 这步的操作是解决问题的关键
// let dataObj = JSON.parse(JSON.stringify(data)) // 深度拷贝
async append (data,node) {
// 这步的操作是解决问题的关键
let dataObj = JSON.parse(JSON.stringify(data)) // 深度拷贝
// 先查询当前文件夹是否有子目录
if (dataObj.children.length === 0) {
let result = await this.$axios.getMyDirectoryList({directorypath: dataObj.dirPath})
let res = this.$formatdata(result)
if (res.code === 10000 && res.data.length > 0) {
res.data.forEach(item=>{
item.isedit=false;
item.isUpdate=false;
item.children=[]
dataObj.children.push(item)
})
this.setNewFolder(dataObj, node)
} else {
this.setNewFolder(dataObj, node)
}
} else {
this.setNewFolder(dataObj, node)
}
},
setNewFolder (data,node) {
let dataChild = data.children
let dirName = '新建文件夹'
if (dataChild) {
let count=1
dataChild.forEach(item => {
if (item.dirName.indexOf('新建文件夹')>-1) {
count++
}
})
dirName += count
}
// 新增节点对象
const newChild = {
id: data.dirPath+'/'+dirName,
dirName,
dirPath:data.dirPath+'/'+dirName,
parentId: data.dirPath,
isedit: true,
isUpdate: false,
children: []
}
if (!data.children) {
this.$set(data, 'children', []);
}
dataChild.push(newChild);
this.$nextTick(() => {
// this.$set(data, 'children', data.children);
this.$refs['tree'].updateKeyChildren(data.id,dataChild)
})
},
this.$refs['tree'].updateKeyChildren(data.id,dataChild)
// 这个方法可以脱离函数对象流操作文件树,只要知道目标对象,以及新增之后的对象数组即可使用此方法更新el-tree对象
// 使用注意点:通过 keys 设置节点子元素,使用此方法必须设置 node-key 属性
记录下这段文字希望为遇到同样问题的童鞋们提供点参考,记得深拷贝试试,哈哈!!!
el-tree 官网扩展:
props
参数 | 说明 | 类型 | 可选值 | 默认值 |
label | 指定节点标签为节点对象的某个属性值 | string, function(data, node) | — | — |
children | 指定子树为节点对象的某个属性值 | string | — | — |
disabled | 指定节点选择框是否禁用为节点对象的某个属性值 | boolean, function(data, node) | — | — |
isLeaf | 指定节点是否为叶子节点,仅在指定了 lazy 属性的情况下生效 | boolean, function(data, node) | — | — |
//props的使用举例
<template>
<el-tree :data="data" :props="defaultProps"></el-tree>
</template>
<script>
export default{
data () {
return {
data: [
{title:'一级目录', list: [
{
title: '二级目录',
list: [...]
}
]}
],
defaultProps:{
label: 'title', // label代替data里面的title显示在目录树上
children: 'list' // children代替data里面的list显示子节点目录树
}
}
}
}
</script>