功能描述:
项目要求el-tree懒加载, 因为树结构中的数据太多, 所以每层节点需要使用分页多次加载,具体的实现效果如下图:
实现思路:
- 懒加载很简单, 参照element-ui文档即可
- 根节点的加载更多也简单, 记录初始的node及resolve即可
- 其他节点的分页加载逻辑就稍微复杂一点, 需要复写el-tree自带的load方法,load方法内携带两个返回参数,一个是node,一个是resolve,即懒加载数据必须是通过resolve就是return 将数据带回,所以在初次加载的时候就需要记录下当前的node和resolve, 其次就是记录当前节点下数据的总数并再每次点击加载更多的时候对当前节点下的页码进行累加
实现代码
<template>
<div v-dee-loading="loading" class="demo">
<el-tree
ref="treeRef"
v-dee-loading="list.loading"
accordion
node-key="id"
:load="loadNode"
lazy
:props="defaultProps"
:expand-on-click-node="false"
@node-click="handleNodeClick"
>
<div slot-scope="{ node, data }" class="custom-tree-node">
<!-- 查看更多 -->
<span
v-if="data.id ==='loadmore'"
class="tree-node loadmore"
@click="loadMoreNode(node,data)"
>
<el-link>{{ node.label }}</el-link>
</span>
<!-- 普通节点 -->
<span v-else class="span-tree-node">
{{ node.label }}
</span>
</div>
</el-tree>
<!-- 根节点数据加载更多(分页)-->
<div class="more-bar">
<span v-if="curpage[0]* pageSize < total[0]" class="more" @click="loadMoreOut">加载更多</span>
<span v-else>已全部加载!</span>
</div>
</div>
</template>
<script>
export default {
name: 'Demo',
components: {},
props: {},
data() {
return {
loading: false,
list: {
loading: false,
isExpand: false
},
defaultProps: {
label: 'name',
children: 'children',
isLeaf: 'leaf'
},
curNode: {},
resolveFunc: [],
node0: {},
resolveFunc0: function() {},
total: {},
curpage: {},
pageSize: 100
}
},
created() {},
mounted() {
},
methods: {
async loadNode(node, resolve, parentNode = []) {
const id = node?.data?.id || 0
// 记录当前节点的当前页码
!this.curpage[id] && (this.curpage[id] = 1)
this.curNode = node
// 节点各自的resolve
this.resolveFunc.push({ id: id, resolve: resolve })
this.resolveFunc = this.resolveFunc.filter((item, index, self) => {
return self.findIndex(x => x.id === item.id) === index
})
if (node.level === 0) {
this.node0 = node
this.resolveFunc0 = resolve
return resolve(await this.getTreeData(null, node.level))
} else if (node.level >= 1) {
const data = await this.getTreeData(node.data, node.level)
// 当前界面的数据小于总数据
if (this.curpage[id] * this.pageSize < this.total[id]) {
const nearName = data[data.length - 1].name
data.push({ name: '查看更多', id: 'loadmore', nearName: nearName,leaf: true, disabled: true })
}
// 查看更多加载需根据各自的resolve加载数组数据
if (parentNode && parentNode.length > 0) {
parentNode.push(...data)
return resolve(parentNode)
} else {
return resolve(data)
}
} else {
return resolve([]) // 防止该节点没有子节点时一直转圈的问题出现
}
},
getTreeData(nodeData, nodeLevel, node) {
const curId = nodeData?.id || 0
this.list.loading = true
let tags = []
const params = {xxx:xxx} // 此处为请求所需参数, 已省略
return matchingQuery(params).then(res => {
if (!res.items || !res.items.content) return
tags = res.items.content || []
tags.forEach((item, index) => { // 节点需要构建为 tree 的结构
item.name = `${item.name}-${item.code}`
item.id = parseInt(Math.random() * 1000000000000, 10)
item.leaf = false
})
this.list.loading = false
// 存储当前节点下的数据总数
this.total[curId] = res.items.totalElements
return tags
}).catch(err => {
console.log('err: ', err)
}).finally(() => {
this.list.loading = false
return tags
})
},
loadMoreNode(node, data) {
if (data.id === 'loadmore') {
const nodeParentKey = node.parent.key ? node.parent.key : ' '
const childNode = {
data: {
id: nodeParentKey,
name: data.nearName,
},
level: node.level - 1
}
let resolve = ''
let parentNode = node.parent.childNodes.map(({ key: id, label: name }) => {
return { name, id }
})
// 剔除自定义的“查看更多”项的数据
if (parentNode.length > 0) {
parentNode = parentNode.slice(0, -1)
}
// 选取resolve返回
if (parentNode.length <= this.total[nodeParentKey]) {
resolve = this.resolveFunc.filter((item) => {
return item.id === nodeParentKey
})
// 点击更多时, 当前节点的当前页+1
this.curpage[nodeParentKey] += 1
// 调用原生Tree load方法
this.$refs.treeRef.load(childNode, resolve[0].resolve, parentNode)
}
}
},
// 根节点加载更多 (为了效果更好一点, 所以根节点的加载方式单独设置样式)
loadMoreOut(node) {
if (this.curpage[0] * this.pageSize < this.total[0]) {
this.curpage[0] += 1
this.loadNode(this.node0, this.resolveFunc0)
}
},
// 此处为重新加载树结构的方法
reloadTree() {
this.curpage = {}
this.node0.childNodes = []
this.loadNode(this.node0, this.resolveFunc0)
},
handleNodeClick(){}
}
}
</script>
<style lang="scss">
.demo{
height: 100%;
box-sizing: border-box;
background-color: #F1F4F5;
.more-bar {
margin: 10px auto;
text-align: center;
font-size: 12px;
color: #999;
}
.more {
margin: auto;
margin-top: 20px;
color: #2f90e2;
height: 26px;
line-height: 26px;
padding: 0 20px;
background: rgba(231, 242, 255, 1);
border-radius: 13px;
display: inline-block;
cursor: pointer;
}
}
</style>