1、起因
全网找了很多相关资料,写的都不是很明白,需要一定的摸索,尽管有着10年开发经验的我,还是很看不惯,还是自己写一个吧!
2、思路
1.用el-tree
2.用lazy 和 load 来做异步
3.要有初始化(很多人不写这一步,导致一头雾水)
3、实践
1.先上代码(本人项目真实代码)
<!--
ref:相当于html的ID
props:数据转换(如果你接口返回的数据格式和el-tree 要求的格式不一致时,用这个,否则不用)( :props="props" )
load:加载子树数据的【这是一个方法】,仅当 lazy 属性为true 时生效。
lazy:是否懒加载子节点,需与 load 方法结合使用。
node-key:是所有节点之前的唯一标识(在这里设置节点的唯一键)
highlight-current:点击时是否高亮显示
@node-click:点击事件,每次点击节点是都会触发,返回当前节点的node
default-expanded-keys:默认展开节点数组,从第一个根节点到最后展开节点的id,例如这样:[1, 2, 12]
-->
<el-tree ref="tree"
:data="treeData"
:props="treeProps"
node-key="value"
lazy :load="loadNode"
:highlight-current="true"
@node-click="treeClick"
:default-expanded-keys="expandIds"
:filter-node-method="treeFilterNode" >
</el-tree>
export default {
data: () => ({
treeProps: {//树的配置选项
children: 'children',
label: 'label',
isLeaf: 'isLeaf'
},
treeData: [], //树的全部数据
expandIds: [],//默认树节点展开IDs
treeFilterText: '',
}),
methods: {
//14.获取节点数据
async getTreeData(node, resolve, isFirst = false) {
let fid = 0
if (!isFirst) {
fid = node.data.value
}
this.showGrouploading = true;
const res = await this.$API.system.dept.viewerasyn.get(fid);
this.showGrouploading = false;
if (res.data.length > 0) {
if (isFirst) {
_.forEach(res.data, item => {
if (item.children === null) {
item.children = [];
}
// (指定节点是否为叶子节点,仅在指定了 lazy 属性的情况下生效)
// isLeaf 为true时,是叶子节点,false时非叶子节点
item.isLeaf = !item.children || item.children.length === 0;
//this.expandIds.push(item.value)
});
let def = res.data[0].value
this.formData.FDepartID = def
this.getCustomGrade(def)
this.getRole(def)
this.getData()
this.treeData = res.data;
//渲染
resolve(this.treeData);
//设置默认展开
this.expandIds.push(def);
// 这一步为了懒加载中等节点渲染完毕后点击到我们需要的节点上去
// 通过 key 设置某个节点的当前选中状态,使用此方法必须设置 node-key 属性
// this.$refs.tree.setCurrentKey(def);
} else {
_.forEach(res.data, item => {
if (item.children === null) {
item.children = [];
}
// (指定节点是否为叶子节点,仅在指定了 lazy 属性的情况下生效)
//isLeaf 为true时,是叶子节点,false时非叶子节点
item.isLeaf = !item.children || item.children.length === 0;
item.children = [];
});
node.data.children = res.data;
//渲染
resolve(res.data);
}
}
},
//15.异步树叶子节点懒加载逻辑(返回当前节点的数据,渲染节点数(用来获取到节点数据后渲染使用的))
//第一个是树节点对象,第二个是一个解析函数resolve,你需要在数据加载完成后调用它,并传入子节点数据。
//树渲染完成后自动加载一次。
async loadNode(node, resolve) {
let isFirst = false
//一级节点处理(根节点)
if (node.level === 0) {
//这里也可以写两个方法分别请求数据。
isFirst = true
} else {
// 其余节点处理
console.log(node)
isFirst = false
}
await this.getTreeData(node, resolve, isFirst);
},
//16.树点击事件
treeClick(data, node) {
//console.log('data', data, node);
this.nodeId = node.data.value;
this.formData.FDepartID = data.value
this.upsearch()
},
}
2.代码讲解
1.html部分
里面有注释
2.过程(核心)
2.1 数据量太大,请求卡,前端渲染时间漫长,用懒加载其实就是(吃多少,装多少),点击树枝,才会加载叶子,而不是直接给一棵树。
2.2 loadNode方法,在页面加载完成后会主动调用一次,也就是说:data="treeData"这个方法会自动运行一次 ,其实treeData为[]时,它的level=0,并且 data是空对象{}。如图:
有的人不写treeData,也是可以的,就是方法【14】里面渲染的时候写正确,其实到了这里就是请求数据会来了,第一次请求和第n次请求只是后端的处理而已。
我的代码有一点不好的是,后端没有isLeaf,是否是子级,所以要前端去处理,数据库设计的不合理导致的,不然代码看起来不会那么长,我的业务逻辑也写在了代码里,没有去掉。
2.3 一定要写isLeaf,还有树的配置,否则可能显示不出叶子来【切记】。
treeProps: {//树的配置选项
children: 'children',
label: 'label',
isLeaf: 'isLeaf'
},
2.4我想要进页面默认展示1.2级,只有一级就一个,还要去点击,不合理,用expandIds来设置默认展开。(就像下图这样,第一级和第二级展开,其实就是望果总部展开,后续的不需要再去追加了,好像这个组件自己会运作,只是首次需要设置)没有过多验证这块!!!
3.后端接口返回格式
/// <summary>
/// Int 树形选择器(element)
/// </summary>
public class SelectorIntDto
{
/// <summary>
/// 显示
/// </summary>
public string label { get; set; }
/// <summary>
/// 值(注意这个在前端需要转换)也就是:props
/// </summary>
public int value { get; set; }
/// <summary>
/// 父级ID (本人项目自用)
/// </summary>
public int parentId { get; set; }
/// <summary>
/// 子集
/// </summary>
public List<SelectorIntDto> children { get; set; }
}
4、总结
大概这些,代码里也是满满的注释,老板喊我去搬砖了。