使用ElTree组件实现懒加载+可拖动

前提说明

  1. 绝对不能用load方式实现所描述之功能,此方式问题极多

实现方式

实现思路

  1. 在数据库表当中添加hasChildren等标记来表示当前节点是否有子节点(可以通过触发器等方式来确保数据一致性)
  2. 定义一个数组来存储已经加载子节点(其中也包括没有子节点)的节点id列表(这个数组的作用是为了防止特殊情况下的重复加载)
  3. 在加载页面的时候添加第一级的所有节点,并根据hasChildren标记来添加伪元素(这个元素的作用是让ElTree组件可以显示下拉按钮,因为ElTree组件通过子节点数组来渲染下拉按钮),同时将没有子元素的节点id添加至数组
  4. 在点击展开按钮后,判断这个节点的子节点数据是否已经加载(通过数组some方法来实现),若未加载则加载相关层级数据,继续按照3所示思路设计,并维护数组(将此节点id加入到数组当中)

一个比较简单(没有优化过)的实现

  1. 初次加载
1
2
3
4
5
6
7
8
9
10
11
12
13
onMounted(async () => {
let data = await loadFirstLevel();
data.forEach((item) => {
if (item.isLeaf === 0) {
item.children = [{
jobName: ''
}]
} else {
loadedNodeIds.push(item.id);
}
})
treeData.value = data;
})

注意:

  • 上面程序的
1
2
3
{
jobName: ''
}

表示伪元素

  1. 点击按钮
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
const nodeExpand = async (node) => {
if (node.isLeaf > 0) {
return;
}
if (loadedNodeIds.some(n => n === node.id)) {
return;
}
// 注意遍历删除元素一定要用倒序遍历!!!
for (let i = node.children.length - 1; i >= 0; i--) {
if (node.children[i].jobName === "") {
node.children.splice(i, 1);
}
}
let children = await loadChildLevel(node);
children.forEach((item) => {
if (item.isLeaf === 0) {
item.children = [{
jobName: ''
}]
} else {
loadedNodeIds.push(item.id);
}
});
node.children.unshift(...children);
loadedNodeIds.push(node.id);
}

注意:

  • 上面循环部分的作用是将伪元素删除,否则将影响正常显示(如果不进行过滤则会显示空元素)

关键程序说明

  1. 点击展开按钮后,判断是否已经加载子元素(通过数组判断),如果没有加载,则将加载后的结果填充到节点的children中
  2. 此处填充节点使用的是unshift,作用是前插数组,此处不直接替换的原因是拖动进来的子节点也需要变成这个节点的子元素,如果直接替换则会导致拖动进来的节点消失