目录
- 起因
- 具体的需求如下
- 处理
- 需求一
- 需求二
- 需求三,四,五
- 其他知识点
- 默认展开节点
- 横纵向滚动条
- 刷新某一个节点
起因
事情是这样的,项目最近有个需求。服务器有个图片空间,说白了就是个文件夹。文件夹的结构大家都知道,一层一层的。然后需要在前端以树形展示。
具体的需求如下
- 可以新建子目录,但是只能新建二级,三级的时候新建按钮置灰。
- 在页面上展示父级目录名称和当前所在路径,用 ‘/’ 分隔。
- 根目录固定为 图片空间 。
- 页面首次打开的时候,请求接口,只返回一级目录,默认展示所有一级目录。
- 用户点击一级目录,根据所点击的一级目录对应的 id 请求接口返回二级目录,以此类推。
我当时看到这个需求的时候,心里想,这玩意我在 ElementUI 的官网见过,好说。结果当我去开发的时候,发现还是挺复杂的,于是我就花了点时间了研究一下,以防下次用的时候忘了。
处理
先把 elementUI 的官方例子拷下来。然后又随便添加了点内容。
<template> | |
<div> | |
<el-input v-model="input" | |
style="width:px" | |
placeholder="请输入内容"></el-input> | |
<el-button type="primary">添加节点</el-button> | |
<span>当前目录:</span> | |
<span>当前目录的父级目录:</span> | |
<el-tree :data="data" | |
:props="defaultProps" | |
accordion | |
@node-click="handleNodeClick"></el-tree> | |
</div> | |
</template> | |
<script> | |
export default { | |
data() { | |
return { | |
input: '', | |
data: [{ | |
label: '图片空间', | |
children: [{ | |
label: '蔬菜', | |
children: [{ | |
label: '菠菜', | |
}, | |
{ | |
label: '生菜', | |
}, | |
], | |
}, | |
{ | |
label: '水果', | |
children: [{ | |
label: '香蕉', | |
}], | |
}, | |
{ | |
label: '美女', | |
children: [{ | |
label: '秦岚', | |
}], | |
}], | |
}], | |
defaultProps: { | |
children: 'children', | |
label: 'label', | |
}, | |
}; | |
}, | |
methods: { | |
handleNodeClick(data) { | |
console.log(data); | |
}, | |
}, | |
}; | |
</script> |
现在的界面是这样的。
需求一
先从最简单的开始吧。在任意目录下添加分类(子节点)。
然后我们去看文档怎么说。嗯,就是它了。
接收两个参数 (data, parentNode)
- 要追加的子节点的 data
- 子节点的 parent 的 data、key 或者 node
第一个参数 data 在当前的例子中就是一个名字而已。第二个参数要传的东西怎么获取呢?
要给目录添加子节点,肯定要先点一下这个目录,那我们看看点击事件都能给我们返回什么东西。
可以看到第二个回调参数就是 节点对应的node , 这不就有了么!那就来试一下。
修改代码如下,我们点击节点的时候,把点击事件回调的参数 node 先存在变量 nowClickNode 中,然后我们调用 append 方法的时候,再把 nowClickNode 给传过去。
// 为了节省篇幅,省略template中的代码,文末会贴出整体代码 | |
methods: { | |
// 点击节点 | |
handleNodeClick(nodes, node, self) { | |
this.nowClickNode = node; | |
}, | |
// 添加节点 | |
addSort() { | |
const data = { | |
label: '草莓', | |
}; | |
this.$refs.tree.append(data, this.nowClickNode); | |
}, | |
}, |
成功啦!
那么怎么实现只可以建二级目录,三级的时候置灰呢,我们点击的时候不是保存了一个变量 nowClickNode 么。
这个变量里面有个 level 属性,代表的就是层级。我通过计算属性来完成这个需求。
//template | |
<el-button type="primary" | |
:disabled='isDisabled' | |
'addSort'>添加节点</el-button> | =|
//js | |
computed: { | |
// 是否可以新建分类 | |
isDisabled() { | |
if (this.nowClickNode.level >) { | |
return true; | |
} | |
return false; | |
}, | |
}, |
可以看到我们点击到三级的时候按钮就禁用了。
需求二
接下来我们实现第二个需求,显示当前所在路径和父级目录。这个示例图看上面那张就可以了,已经实现了。
// 点击节点的时候触发事件 | |
handleNodeClick(nodes, node, self) { | |
this.nowClickNode = node; | |
this.nowPathArr = []; | |
// 如果不是根节点 | |
if (node.parent.parent) { | |
this.faterName = node.parent.data.label; | |
// 递归找路径 | |
this.findParentName(node); | |
// 拼接路径 | |
this.nowPath = `图片空间 / ${this.nowPathArr.join(' / ')}`; | |
} else { | |
// 如果是根节点 直接赋值 | |
this.nowPath = '图片空间'; | |
this.faterName = '- -'; | |
} | |
}, | |
// 查找父节点的分类名称 | |
findParentName(node) { | |
if (node.parent.parent) { | |
this.nowPathArr.unshift(node.data.label); | |
this.findParentName(node.parent); | |
} | |
}, |
这个不知道怎么回事,我的根目录也有 parent 属性,所以只能再向上找一层,通过 parent.parent 存不存在来判断是不是根节点。
我是在点击的时候,递归查找点击节点的父元素,然后把所有祖代元素放在数组里,最后进行拼接。
需求三,四,五
剩下的三个需求放在一起讲,这个地方我练习的时候用的项目真实数据,不方便演示,代码也是经过脱敏处理的。重要的是知道怎么用就可以了。
//template | |
<el-tree | |
:data="treeData" //数据源 | |
:props="defaultProps" //配置项 | |
accordion //每次打开一个节点 手风琴模式 | |
ref="tree" //用来获取dom | |
lazy //节点懒加载 | |
:highlight-current='true' //高亮当前节点 | |
:load='treeLoad' //懒加载时调用的方法 | |
node-key="cateId" //唯一标识 | |
:default-expanded-keys='defaultOpen' //默认打开的节点数组 | |
:expand-on-click-node='true' //是否在点击节点的时候展开或者收缩节点, 默认值为 true,如果为 false,则只有点箭头图标的时候才会展开或者收缩节点。 | |
"handleNodeClick" //点击节点的回调函数 | -click=|
> | |
</el-tree> | |
//js | |
// 加载树形结构 就是上面懒加载绑定的函数 | |
treeLoad(node, resolve) { | |
// 判断节点等级 | |
if (node.level ==) { | |
// 如果是根节点,添加一个固定的图片空间节点 这是实现需求三 | |
resolve([{ | |
cateName: '图片空间', | |
cateId: '', | |
children: [], | |
}]); | |
} | |
// 不是根节点,调取接口加载列表 这里实现需求四,五的树结构懒加载,点完一级请求一级的数据 | |
if (node.level >=) { | |
this.getCategoryListNew(node, resolve); | |
// 防止子节点为空时一直出现加载动画 | |
return resolve([]); | |
} | |
}, | |
// 获取树形结构 上面掉用的请求接口 data处理完之后要调用resolve方法 | |
getCategoryListNew(node, resolve) { | |
Service.getCategoryListNew({ parentId: node.data.cateId }).then(({ data }) => { | |
resolve(data); | |
}).catch((e) => { | |
console.log(e); | |
}); | |
}, | |
其他知识点
另外记录几个知识点
默认展开节点
默认展开使用的是 :default-expanded-keys='defaultOpen' 属性, defaultOpen 里面存放的是 node-key 的数组。
横纵向滚动条
首先要给 tree 组件的外层添加 宽高 ,以及 overflow 属性。然后要修改 el-tree 的属性。
.tree-box{ | |
width:px; | |
height: calc(% - 30px); | |
overflow:auto; | |
} | |
.el-tree { | |
display: inline-block; | |
min-width:%; | |
} |
刷新某一个节点
// 通过节点id找到对应树节点对象 | |
const node = this.$refs.tree.getNode(this.nodeId); | |
// 把loaded手动置为false,也就是告诉tree这个节点没有加载过 | |
node.loaded = false; | |
// 主动调用展开节点方法,重新查询该节点下的所有子节点 | |
node.expand(); |
模拟点击一级目录中第一个节点
// 选取dom | |
const root = document.querySelector('.el-tree-node__children'); | |
// 找到目标节点 | |
const firstNode = root.firstChild; | |
// 模拟点击 | |
firstNode.click(); |