之前对
Ant Design TreeSelect
树选择控件进行了基础封装,这一篇主要讲异步数据的处理,也顺便讲了一下深度优先遍历(DFS)和广度优先遍历(BFS)
封装原因:之前部门树数据是一次性加载的, 存在着性能问题,数据量很大的时候,加载很缓慢,而且组件可能崩溃;因此改成异步加载数据,系统中用到的地方很多,不可能每个地方都改一下,进行封装统一处理。
Ant Design TreeSelect 树选择控件二次封装及原理:
juejin.cn/post/700328…
departSource
:数据源,默认展示根部门和一级部门数据
onLoadData
:异步加载数据,根据部门 id 查询直接子部门数据,加载到数据源中
treeExpandedKeys
:展开的树节点,默认展开根部门
<template>
<a-tree-select
v-bind="$attrs"
v-on="selectListeners"
class="m-width"
placeholder="请选择"
searchPlaceholder="请输入部门名称"
:treeDefaultExpandAll="false"
:treeData="departSource"
:load-data="onLoadData"
@treeExpand="handleExpand"
:treeExpandedKeys="treeExpandedKeys"
:dropdownStyle="{ maxHeight: '400px' }"
show-search
treeNodeFilterProp="title"
</a-tree-select>
</template>
const tree = [
id: 1,
name: "根部门",
departNumber: 6,
children: [
id: 11,
pid: 1,
name: "一级部门1",
departNumber: 2,
children: [
{ id: 111, pid: 11, name: "二级部门1", departNumber: 0 },
{ id: 112, pid: 11, name: "二级部门2", departNumber: 0 },
{ id: 21, pid: 1, name: "一级部门2", departNumber: 0 },
{ id: 31, pid: 1, name: "一级部门3", departNumber: 0 },
{ id: 41, pid: 1, name: "一级部门4", departNumber: 0 },
{ id: 51, pid: 1, name: "一级部门5", departNumber: 0 },
id: 61,
pid: 1,
name: "一级部门6",
departNumber: 2,
children: [
{ id: 611, pid: 61, name: "二级部门3", departNumber: 0 },
{ id: 612, pid: 61, name: "二级部门4", departNumber: 0 },
深度优先遍历(DFS)
深度优先遍历(DFS -- Depth First Search
):从根节点出发,先访问一个完整的子树,再访问相邻未被访问的子树,直到所有子树都被访问完。访问子树的操作和之前一样,直到所有节点都被访问到为止。
简单的图形化展示:
函数代码展示:
* 深度优先遍历
* @params {Array} tree 树数据
* @params {Array} func 操作函数
const dfsTransFn = (tree, func) => {
tree.forEach((node) => {
func(node);
if (node.children && node.children.length) {
dfsTransFn(node.children, func);
dfsTransFn(tree, (node) => {
console.log(`${node.id}...${node.name}`);
let treeList = [];
dfsTransFn(tree, (node) => {
treeList.push(node);
console.log(treeList);
函数运行效果展示:
广度优先遍历(BFS)
广度优先遍历(BFS -- Breadth First Search
):从根节点出发,先访问根节点所在的初始队列,进行操作,并将该节点的所有子节点加入队列中;再依次访问队列中的第一个元素,进行操作,直到队列为空。即访问树结构的第 n+1 层前必须先访问完第 n 层,彻底搜索整个树结构,直到找到结果为止。
简单的图形化展示:
函数代码展示:
* 广度优先遍历
* @params {Array} tree 树数据
* @params {Array} func 操作函数
const bfsTransFn = (tree, func) => {
let node,
list = [...tree];
while ((node = list.shift())) {
func(node);
node.children && list.push(...node.children);
bfsTransFn(tree, (node) => {
console.log(`${node.id}...${node.name}`);
let treeList = [];
bfsTransFn(tree, (node) => {
treeList.push(node);
console.log(treeList);
函数运行效果展示:
深度优先:不需要记住所有节点,占用空间小;堆栈形式,先进后出
广度优先:需要记录所有节点,占用空间大;队列形式,先进先出
初始化处理
初始化时,需要请求根部门数据和一级部门数据
queryRootDepartDetail
:该接口用于获取根部门详情数据;
queryChildDeparts
:该接口通过部门 id 获取直接子部门数据;
isLeaf
:是否是叶子节点,通过是否存在子部门(o.departNumber>0
)判断
<script>
import * as R from "ramda";
const tranFn = (data) => {
if (R.type(data) === "Array" && R.length(data) > 0) {
return R.map(
(o) => ({
value: o.id,
key: o.id,
title: o.name,
children: [],
isLeaf: !o.departNumber,
} else {
return [];
export default {
created() {
this.queryAsyncDeparts();
methods: {
async queryAsyncDeparts() {
let res = await queryRootDepartDetail();
let departId = R.path(["data", "id"], res);
this.departSource = tranFn([res.data]);
this.treeExpandedKeys = [departId];
this.queryMoreData(departId);
queryMoreData(departId) {},
</script>
实现效果展示:
异步加载树节点
要在对应的部门树节点添加数据,有两种方式:深度优先遍历和广度优先遍历
<script>
* 异步加载数据(深度优先遍历)
* @params {Array} tree 树数据
* @params {number} departId 部门id
* @params {Array} childList 加载的数据
const transTreeFn = (tree, departId, childList) => {
return R.map((o) => {
if (o.key === departId) {
o.children = tranFn(childList);
} else if (o.children && o.children.length) {
transTreeFn(o.children, departId, childList);
return o;
}, tree);
export default {
methods: {
onLoadData(treeNode) {
const { key } = treeNode.dataRef;
this.queryMoreData(key);
queryMoreData(departId) {
queryChildDeparts(departId).then((res) => {
let childList = R.pathOr([], ["data"], res);
this.departSource = transTreeFn(
this.departSource,
departId,
childList
handleExpand(expandedKeys) {
this.treeExpandedKeys = expandedKeys;
</script>
复制代码