外包数字化平台 第02篇|加载部门树

目录

引言

上一篇,我们建好了部门数据模型 ct_dept,也搭建了「左树右详情」的页面布局。

但你现在预览页面会发现:左边的树还是组件自带的示例数据(什么行政中心、营销中心),跟我们自己的部门没有半点关系。页面只是个空壳

这一篇,我们就让这棵树「活」过来------把 ct_dept 表里的部门,真实地加载到树组件上。

做完后你会看到:

  • 页面一打开,树里显示的就是你在数据模型里录入的部门;
  • 有上下级的部门,会正确地嵌套、缩进、展开;

这件事看起来只是「查个数据、塞给组件」,但中间藏着一个所有树形功能都会遇到的核心问题

数据库里存的是一行一行的「扁平数据」,而树组件要的是一层套一层的「嵌套数据」。

怎么把「行」变成「树」?------这是本篇的重点。


本节目标

今天完成 4 件事:

  1. 给 AI 助手装上两个「微搭技能」,让它写出来的代码符合微搭的规矩;
  2. 搞懂树组件 WdTree 要什么格式的数据;
  3. 写一个自定义方法:查部门 → 行转树 → 塞进组件;
  4. 让页面打开时自动执行这个方法。

完成后,部门管理页的左半边就彻底能用了


准备:给 AI 装上「微搭技能」

我们后面大量的业务逻辑,都是用自然语言指挥 AI 生成的。但 AI 默认并不完全清楚微搭的 API 细节------比如查数据该调哪个方法、树组件的数据要什么 key。

所以我们先准备两份「AI 专用手册」(技能包):

技能 管什么 什么时候派上用场
weda-custom-method 自定义方法怎么写 :查数据用 $w.cloud.callDataSource、统一的 try/catch 骨架、成功失败怎么提示 写按钮事件、页面加载逻辑、表单提交
weda-component-api 组件有哪些属性和方法 :比如树组件的数据放在 data、选中值在 treeInfo.checked 要操作页面上某个组件时

安装方式:把这两个技能文件夹放到 AI 工具能识别的技能目录(本项目里是 技能/ 目录),然后让agent安装这两个技能。你可以理解为:

  • weda-custom-method 告诉 AI:微搭的代码长什么样
  • weda-component-api 告诉 AI:页面上的组件能怎么操作

安装方法,将文件夹拖入到agent对话窗口,提示词为安装这两个技能

安装完毕后下一次你就可以用斜杠来调用技能

这一篇,我们就会同时用到这两份手册里的知识。


步骤一:先看清楚------「行」和「树」是两种东西

1.1 数据库里:部门是一行一行的

回忆上一篇,我们在 ct_dept 表里录的数据,长这样(完全平级,谁也不套谁):

_id name(名称) parent_id(上级) sort
d01 总部 (空) 10
d02 销售部 d01 10
d03 交付中心 d01 20
d04 研发部 d03 10
d05 测试部 d03 20

这是企业软件里最经典的树形存法:每行只记自己的上级是谁parent_id),不存完整层级。好处是改上级、加下级都简单。

1.2 树组件要的:一层套一层的 JSON

而树组件 WdTree 根本不认这种平表。它要的是一个嵌套数组,每个节点必须长这样:

json 复制代码
[
  {
    "label": "总部",
    "value": "d01",
    "children": [
      { "label": "销售部", "value": "d02" },
      {
        "label": "交付中心",
        "value": "d03",
        "children": [
          { "label": "研发部", "value": "d04" },
          { "label": "测试部", "value": "d05" }
        ]
      }
    ]
  }
]

对比一下两者的差别:

数据库里的「行」 树组件要的「节点」
结构 平级,靠 parent_id 指上级 嵌套,靠 children 装下级
名称字段 name 必须叫 label
唯一标识 _id 必须叫 value
适合 存储、条件查询 一次性渲染成树

所以我们的任务链路非常清晰:

text 复制代码
ct_dept 表(平的行)
      │  ① 查出来
      ▼
  rows 行数组
      │  ② 行转树(本篇核心)
      ▼
 嵌套 JSON(label/value/children)
      │  ③ 赋给树组件的 data
      ▼
   页面上的组织树

千万别直接把查出来的行塞给树 ------字段名是 name/_id 而不是 label/value,组件会渲染出一片空白,而且不会报明显的错,特别难排查。


步骤二:认识树组件 WdTree

官方文档:https://docs.cloudbase.net/lowcode/components/wedaUI/src/docs/compsdocs/show/WdTree

WdTree(中文名就叫「树」)专门用来展示组织架构、分类这种多层级数据。我们只需要先记住下面几个点。

2.1 节点的三个必备 key

key 作用 我们用什么填
label 节点上显示的文字 部门名称 name
value 节点的唯一标识,选中/展开都靠它 部门记录的 _id
children 子节点数组 拼出来的下级部门

另外还有可选的 disabled(设为 true 可禁用某节点),本篇用不到。

2.2 组件的几个关键属性(在右侧属性面板配置)

属性名 标识 本篇怎么配
树节点 data 点右侧 fx 按钮,绑定页面变量(后面细讲)
开启多选 checkable 部门树是单选导航,保持关闭
节点展开方式 expandType 选「展开所有节点」,否则默认只显示根
展开的节点 expandCustom 展开方式选「自定义」时才用,填 value 数组
显示节点连接线 line 建议开启,层级更清楚

2.3 运行时能读到的信息(下一篇用,先混个脸熟)

用户在树上点了哪个节点,可以从 treeInfo 读到:

javascript 复制代码
$w.tree1.treeInfo.checked[0]   // 当前选中节点的 value(部门 _id)

这一篇先不处理点击,只负责把树显示出来


步骤三:写自定义方法 loadDeptTree

在微搭编辑器左侧代码区,新建一个自定义方法(JS 方法),命名为 loadDeptTree

在代码编辑器中输入如下代码:

javascript 复制代码
/**
 * 加载部门树
 * 查 ct_dept 全部启用部门 → 扁平行转嵌套树 → 写入页面变量 treeData
 * 绑定时机:页面「加载时」事件
 */
export default async function ({ event, data }) {
  try {
    $w.utils.showLoading({ title: '加载中', mask: true });

    // ========== 第一段:查(把平的行查出来) ==========
    const result = await $w.cloud.callDataSource({
      dataSourceName: 'ct_dept', // 数据模型的「标识」,不是显示名
      methodName: 'wedaGetRecordsV2',
      params: {
        filter: {
          where: { status: { $eq: '1' } }, // 只加载启用的部门
        },
        select: { $master: true }, // 返回主表全部字段
        pageSize: 200,             // 单次最多 200 条,必须显式写(默认只给 10 条!)
        pageNumber: 1,
        orderBy: [{ sort: 'asc' }],
      },
    });

    const rows = result?.records || [];
    if (rows.length === 0) {
      $w.page.dataset.state.treeData = []; // 一条部门都没有时给空树
      return;
    }

    // ========== 第二段:转(行 → 树,本篇核心) ==========
    // ------ 第一遍:给每个部门造一个节点,收进 map,key 是部门 _id ------
    const nodeMap = {};
    rows.forEach((row) => {
      nodeMap[row._id] = {
        label: row.name, // 树组件要求显示名必须叫 label
        value: row._id,  // 唯一标识必须叫 value
        children: [],    // 先统一放空数组,第二遍再往里挂下级
      };
    });

    // ------ 第二遍:按 parent_id,把每个节点挂到它上级的 children 里 ------
    const tree = [];
    rows.forEach((row) => {
      const node = nodeMap[row._id];
      // 兼容两种存法:上级直接存 ID(字符串)/ 存关联对象({ _id })
      const parentId = row.parent_id?._id || row.parent_id;

      if (parentId && nodeMap[parentId]) {
        nodeMap[parentId].children.push(node); // 有上级 → 挂进上级的 children
      } else {
        tree.push(node); // 没上级(或上级被停用、不在本次结果里)→ 作为根节点
      }
    });

    // ========== 第三段:塞(写入页面变量,组件通过 fx 绑定它) ==========
    $w.page.dataset.state.treeData = tree;
  } catch (error) {
    console.error('加载部门树失败:', error);
    $w.utils.showToast({
      title: error?.message || '部门树加载失败',
      icon: 'error',
      duration: 2000,
    });
  } finally {
    $w.utils.hideLoading();
  }
}

下面把这段代码里的两个关键知识点讲透。

3.1 查数据:wedaGetRecordsV2

这是微搭数据源「查多条」的标准方法,有两个坑必须提醒:

  1. pageSize 一定显式写。默认只返回 10 条------你建了 15 个部门,树上永远只显示一部分,还以为是拼树代码写错了。单次上限 200 条,对部门来说绰绰有余。
  2. dataSourceName 填模型标识 ct_dept,不是你在界面上起的中文显示名。

3.2 行转树:Map 两遍法(重点)

这是本篇最值钱的一段逻辑,所有树形加载(部门、分类、物料分组)都是同一个套路。

第一遍:建索引。 遍历所有行,每行造一个 { label, value, children: [] } 节点,放进一个以 _id 为 key 的「字典」nodeMap 里:

text 复制代码
nodeMap = {
  d01: { label:'总部',   value:'d01', children:[] },
  d02: { label:'销售部', value:'d02', children:[] },
  d03: { label:'交付中心', value:'d03', children:[] },
  d04: { label:'研发部', value:'d04', children:[] },
  d05: { label:'测试部', value:'d05', children:[] },
}

第二遍:挂载。 再遍历一次,看每一行的 parent_id

  • 研发部(d04)的上级是 d03,而 nodeMap['d03'] 一定存在(第一遍已经全部建好),于是把研发部节点 push 进交付中心的 children
  • 总部(d01)没有上级,放进根数组 tree

走完第二遍,嵌套关系就自然形成了:

text 复制代码
tree = [ 总部 ]
          └─ children: [ 销售部, 交付中心 ]
                                  └─ children: [ 研发部, 测试部 ]

为什么要先全部建进 map,再挂载? 因为查询结果的顺序是不确定的------万一「研发部」这行排在「交付中心」前面,挂载时它的上级还没出生。有了 map 做索引,无论行的顺序怎么乱,第二遍都能找到上级。整个算法每个部门只处理两次,性能也好。

对照看原型是怎么做的:我们外包平台原型(Next.js 版)里用的是「递归过滤」------每遇到一个部门,就把全部部门过滤一遍找它的孩子,再递归往下:

typescript 复制代码
function buildTree(rows, parentId = null) {
  return rows
    .filter((row) => row.parentId === parentId)
    .map((row) => ({
      id: row.id,
      name: row.name,
      children: buildTree(rows, row.id), // 递归找下级
    }));
}

两种写法结果完全一样。递归写法直观,但每个节点都要全表扫描一次;Map 两遍法在前端自定义方法里更简洁、也不怕数据乱序,所以本篇用它。

parent_id 的两种形态 也要注意:如果上一篇你把「上级部门」建成了普通文本字段,它直接就是上级 ID;如果建成了关联字段 ,查回来会是个对象 { _id: 'd03' }。代码里 row.parent_id?._id || row.parent_id 这一句两种情况都兼容。若是关联字段,还要在查询参数里把它显式带回来:

javascript 复制代码
select: { $master: true, parent_id: true },

步骤四:建页面变量,接住树数据

自定义方法里我们把结果写到了 $w.page.dataset.state.treeData,这个变量需要先在编辑器里创建:

在代码区点击+,在右侧面板点击新建自定义变量

变量名填 treeData(和代码里完全一致)

类型选「数组」,初始值设为空数组


步骤五:把树组件绑到变量上

选中树组件,找到属性「树节点 」(标识 data),点击右侧的 fx 按钮,打开表达式编辑器

绑定表达式:

javascript 复制代码
$w.page.dataset.state.treeData

这样组件和数据就接上了:方法往变量里写 → 组件通过 fx 读变量 → 自动渲染。这是微搭里最常用的数据流模式,后面表格、详情都这么玩。


步骤六:让页面打开时自动加载

最后一步,告诉页面「一加载就执行 loadDeptTree」:

在大纲树点选最外层的页面 节点

右侧下拉到「事件 」面板;

找到「加载时 」(页面生命周期事件),新增动作 → 调用自定义方法 → 选择 loadDeptTree

保存,点右上角「预览」。如果数据模型里已经录了总部、销售部、交付中心这些部门,树上就会按层级显示出来。


常见问题排查

现象 原因与处理
树一片空白,也不报错 90% 是字段名不对:节点 key 必须是小写的 label/value/children,不能直接用行里的 name/_id
只显示根节点,下级没展开 展开方式停留在默认值,改成「展开所有节点」
所有部门都平铺在最外层 parent_id 没取到值。关联字段要用 row.parent_id._id,并在 select 里显式带上该字段
部门总是少几个 pageSize 没写,默认只返回 10 条;显式设为 200
明明有数据却显示空树 检查数据源标识是不是 ct_dept;检查筛选条件是不是把部门过滤光了
停用的部门也冒出来了 确认 where 里带了 status: { $eq: '1' }

一个调试小技巧:在方法里临时加一句 console.log(rows, tree),预览时打开浏览器控制台,可以分别看到「查回来的行」和「拼好的树」,两层数据一对比,问题立刻定位。


本节成果

今天我们做完了什么?

维度 成果
数据链路 打通了「ct_dept 表 → 自定义方法 → 页面变量 → 树组件」
核心能力 掌握了扁平行转嵌套树的 Map 两遍法(以后所有树都复用)
组件 学会了 WdTreedata / label / value / children / expandType
工程习惯 数据源查询显式写 pageSize、try/catch + toast、页面加载事件

至此,部门管理页的左半边(组织树)已经是真实数据了。


下一篇预告

树虽然能显示了,但现在点树上的部门,右边毫无反应------还是死的。

下一篇,我们给树挂上「选中节点改变 」事件(selectNodeChange):

  • 点击某个部门,从 treeInfo.checked 拿到部门 _id
  • 右侧表单容器显示这个部门的名称、编码、负责人;
  • 下方子部门表格也跟着只显示它的下级。

让左右两侧真正联动起来。

相关推荐
华无丽言2 天前
如何在宜搭中实现获取子表中的字段值赋值到父表中?
前端·javascript·低代码
烛之武2 天前
低代码大模型
低代码
快乐非自愿2 天前
2026企业低代码选型对比:制造、政务、供应链转型硬核评测
人工智能·低代码·架构·制造·政务
快乐非自愿2 天前
低代码落地实战:场景拆解+企业数字化转型完整框架
人工智能·低代码·架构
梦梦代码精4 天前
《回收租赁系统技术选型避坑指南:业务闭环与二开自由度详解》
开发语言·低代码·docker·开源·代码规范
蜘蛛小助理5 天前
AI 自动推导业务自动化规则实战教程|多维表格低代码自动化落地
大数据·人工智能·低代码·自动化·多维表格·蜘蛛表格
希艾席帝恩6 天前
数字孪生平台与数据内容工具对比:山海鲸可视化VS镝数
大数据·人工智能·物联网·低代码·信息可视化·数字化转型
泡海椒7 天前
低代码规则开发:JQuick-Java声明式配置驱动开发落地教程
java·低代码·rxjava