首页
学习
活动
专区
圈层
工具
发布
社区首页 >专栏 >从平台自带示例入手:一个 CRUD 模块的完整拆解

从平台自带示例入手:一个 CRUD 模块的完整拆解

原创
作者头像
技术挖掘官
发布于 2026-11-11 10:09:58
发布于 2026-11-11 10:09:58
270
举报

登录后的平台首页。左侧是「开发套件 / 管理工具 / 我的应用 / 示例」四项入口,右侧欢迎页把上手路径写得很直白:打开示例 → 打开文档 → 打开集成开发环境 → 打开数据库配置 → 打开用户管理。这张图也说明了一件事——平台自己就把「示例」放在第一步。

一、为什么读示例比读文档更快

学一个快速开发平台,最容易走的弯路是从 API 手册第一页开始啃。API 手册告诉你「有什么」,示例告诉你「这些东西怎么凑在一起能跑」。

geejing WebBuilder 快速开发平台在 wb/modules/example/ 下内置了一整套可运行的示例,按主题分成 11 个目录:comps(组件)、basic(基础页面)、load(加载)、coding(脚本)、crud(增删改查)、tools(工具)、apps(完整小应用)、touch(移动端)、workflow(工作流)、common(通用)、ai(AI 生成示例)。每个目录里都是可以直接用 m?xwl=<路径> 打开的 .xwl 文件,且每个文件都有一份 .min.xwl 压缩版。

这批示例的价值在于三点:能跑(不是代码片段)、能改(IDE 里打开即编辑)、能抄(服务端脚本、事件绑定、属性配置全都是真实可复用的写法)。本文不打算泛泛讲概念,只做一件事:把 example/ai/crud-dialog.xwl 这一个文件从打开到跑通完整拆一遍,让你看完就能照着改出自己的模块——这也是我们推荐给新团队上手 geejing WebBuilder 快速开发平台的第一条路径。

环境说明:本文所有画面与代码基于 WebBuilder 11 本地部署,浏览器直接访问,无需额外构建工具;截图为真实界面,示例数据为平台自带演示数据。

二、进 IDE:先看清楚工作区

从首页左侧点「开发套件」中的集成开发环境,或直接访问 m?xwl=dev/ide,进入下图这个界面。

集成开发环境(IDE)初始画面。顶部是「文件 / 编辑 / 导航 / 搜索 / 运行 / 工具 / 帮助」菜单与一排快捷图标,左侧是文件浏览器,底部是「标记 / 调试 / 搜索 / 线程 / AI Gen」标签页,右上角显示当前登录用户 admin。

第一眼容易觉得"右侧怎么是空的"——这是正常的:IDE 是一个多文档工作区,没有打开任何模块时,右侧设计区保持空白。左侧文件浏览器把模块按根目录分组:dev 开发套件、admin 管理工具、my 我的应用、sys 系统、example 示例,下面还有「应用」与「系统」两个资源分组。

顶部菜单里几个和日常开发最相关的动作:导航负责在模块间跳转,搜索做全模块文本检索(找一个 SQL 片段或某个事件名特别好用),运行直接在 IDE 里预览当前模块,而不必切回浏览器手输地址。底部那排标签里,「调试」和「线程」用于排查服务端脚本,「AI Gen」则是 AI 生成入口——本文第三篇会专门讲它。

这里有个容易被忽略但很关键的细节:IDE 操作的对象就是磁盘上的文本文件。你在 IDE 里改一个属性,落盘的是 .xwl 里的一行 YAML;这意味着模块能进 Git、能做 diff、能批量替换、能被 AI 直接读写。这跟"只能在某个设计器里点、导出成一坨二进制"的低代码平台是两条路。

三、找到示例:展开 example 目录树

在文件浏览器里展开 example 示例,会看到上面提到的 11 个分类目录。

展开 example 后的目录树:comps、basic、load、coding、crud、tools、apps、touch、workflow、common、ai,共 11 类。右侧仍是空的,因为还没有打开具体模块。

对新手,建议按这个顺序读:basic/layout.xwl 看清容器与布局 → basic/grid1-form.xwl 看清表格与表单联动 → crud/ 下看清增删改查 → apps/oa/leave.xwl 看清工作流与多表 → ai/crud-dialog.xwl 看清 AI 生成出来的完整模块长什么样。

我们今天直接跳到 example/ai/——这个目录下的 5 个文件(comp.xwl、crud-dialog.xwl、crud-edit-grid.xwl、detail-page.xwl、order.xwl)是平台用 AI 生成能力产出的示例,结构上最"标准",也最适合当模板。

双击 crud-dialog.xwl,工作区立刻变了。

四、设计视图:四栏工作区逐个拆开

打开 crud-dialog.xwl 后的设计视图。自左至右:文件浏览器、属性面板、对象浏览器、控件库;顶部出现模块页签与「设计 / 源码」切换。这是 geejing WebBuilder 快速开发平台最核心的一张开发界面。

4.1 文件浏览器(最左)

展开状态保留了 example/ai/,当前选中的 crud-dialog.xwl 高亮。旁边同级的 crud-edit-grid.xwl、order.xwl、comp.xwl、detail-page.xwl 可以直接双击切换,边改边对照很方便。

4.2 属性面板(左二)

上半部分是属性,按字母排序列出当前选中对象的可配置项。截图中能看到 bodyTag、cid、cls、description、head、language、links、loginRequired、moduleType、obfuscate、remark、serverMethod、serverScript、style、theme、title 等;其中 cls 的值是 module,serverScript 一栏显示 let action = Params.xaction; if ... 的开头,description 写着 "Dialog based database CRUD"。

下半部分是事件,能看到 beforeUnload、finalize、initialize,initialize 的值是 Wb.apply(app, { checkExists(resp)... ——这是模块初始化时把校验函数挂到 app 上的写法。

属性面板最实用的地方是:你不需要记住每个组件有哪些属性,选中对象,能改的都列在这里,值直接显示当前配置。改完即时生效,不经过编译。

4.3 对象浏览器(中间,面积最大)

这是理解 XWL 结构的关键。它把模块展开成一棵组件树,截图中可见:

代码语言:txt
复制
module  serverScript  initialize
└─ editWin
   ├─ code  Code
   ├─ full_name  Full name
   ├─ photo  Photo
   ├─ user_name  @xpath + @action=selectUsers...
   ├─ dept_name  @xpath + @action=selectDeptT...
   ├─ birth_date  Birth date
   ├─ gender  Gender
   ├─ salary  Salary
   ├─ cvTitle  Personal Resume
   └─ cv
└─ viewport1
   └─ tbar
      ├─ addBtn  添加 click
      ├─ editBtn  编辑 click
      ├─ delBtn   删除 click
      ├─ divider1
      └─ search  change
   └─ grid1  @xpath + @action=select  itemdblclick
      └─ columns
         ├─ rowNumCol
         ├─ codeCol  Code
         ├─ fullNameCol  Full name
         └─ ...

几个值得停下来看的点:

  • editWin 是一个 Window,表单字段都挂在它下面,所以点「添加」弹出的是对话框,不是新页面;
  • user_name / dept_name 这两个 Select 绑定了远程 action(selectUsers / selectDeptTree),下拉数据由服务端脚本提供,而不是写死枚举;
  • 工具栏 tbar 是 viewport1 的属性,里面是 addBtn / editBtn / delBtn / search 等 Wb.Item,每个按钮后面标着 click 事件;
  • grid1 绑定 action=select,双击行触发 itemdblclick(通常是打开编辑);列通过 columns 数组节点表达。

一句话概括:XWL 用一棵组件树描述界面,用属性描述行为,用事件属性挂接服务端动作。看懂这棵树,就看懂了平台里绝大多数模块。

4.4 控件库(最右)

右侧是可视化拖拽用的控件清单,General 分组里能看到 viewport、array、window、panel、container、splitter、tab、card、cardCt、fieldset、component、viewer、menu、toolbar、item、line、title、divider、gap、fill、script、button、splitButton、label;往下还有 layout 分组的 text、number、textArea、select、date、time 等。

这份清单有个很实际的作用:它界定了"什么组件是真的"。写 XWL 时凭印象造一个 cls: Wb.Form 或 Wb.Combo,设计器不会认;正确写法是 fieldset + Select,属性用 text / keyName / required 而不是 fieldLabel / dict / allowBlank。后面第二篇会专门把这张控件表展开讲。

五、切到源码:XWL 到底长什么样

点顶部「源码」标签,同一个模块换成代码视角。

源码」标签下的真实画面:左侧文件树不变,中间是带行号与语法高亮的源码编辑器,右侧是整份代码缩略图(minimap),底部仍是 AI Gen 等标签。

这里要澄清一个很容易混淆的点,也是很多教程没讲清楚的:

磁盘上的 .xwl 文件是 YAML。 用文本编辑器打开 wb/modules/example/ai/crud-dialog.xwl,开头是这样的(原文节选):

代码语言:txt
复制
_expanded: true
_icon: module
cls: Wb.Module
events:
  initialize: |-
    Wb.apply(app, {
      checkExists(resp) {
        if (resp?.toLowerCase().includes('wb_staff_code')) {
          Wb.error(Str.alreadyExists.format(app.code.value), f => app.code.focus(false, true));
        }
      }
    });
hideInMenu: 'false'
items:
- _expanded: true
  _icon: window
  cls: Wb.Window
  items:
  - _icon: title
    cls: Wb.Title
    properties:
      cid: generalTitle
      title: General Information
    text: generalTitle
  - _icon: text
    cls: Wb.Text
    properties:
      cid: code
      maxLength: '5'
      required: 'true'
      text: Code
    text: code

注意 `events.initialize:

-与后文properties.serverScript:

- 用 YAML 块标量承载多行 JavaScript;所有属性值写成带引号的字符串('false'、'5'`),这是平台强制约定,AI 生成模块时也严格遵守。

IDE 的「源码」标签则是另一种视角:截图里显示的是 {"name":"module", "serverScript(){ let action = Params.action; ... }...} 这样的 JS 对象字面量,把模块定义与服务端脚本作为对象方法呈现,便于直接编辑脚本逻辑。两种表示指向同一份模块定义——YAML 适合版本管理与批量处理,JS 视图适合写逻辑。理解这一点,就不会在"到底该按哪种格式写"上纠结。

5.1 完整 serverScript 逐段读

下面是 crud-dialog.xwl 中 properties.serverScript 的真实原文(节选,未改写),它是整个模块的心脏:

代码语言:txt
复制
let action = Params.xaction;
if (!action) return;
let actions = {
  select() {
    let sql;
    sql = `
        select a.*,b.user_name,c.dept_name,c.dept_code from wb_staff a, wb_user b, wb_dept c
        where a.user_id=b.sid and a.dept_id=c.sid
        `;
    if (Params.search) {
      Wb.setLike('search');
      sql += ' and (a.full_name like {?search?} or a.code like {?search?} or b.user_name like {?search?})';
    }
    sql += ' order by a.code';
    Wb.sendRowx({ sql, kv: { gender: 'gender' } });
  },
  add() {
    let rec = { sid: Wb.getId() };
    Wb.set(rec);
    Wb.sync({ tableName: 'wb_staff', insert: Params });
    Wb.send(rec);
  },
  edit() {
    // Params contains blob/photo fields
    Wb.sync({ tableName: 'wb_staff', update: Params });
  },
  del() {
    let del = Wb.getObject('del');
    Wb.sync({ tableName: 'wb_staff', del });
  },
  getPhoto() {
    let row = Wb.getRow({
      sql: 'select photo from wb_staff where sid={?sid?}',
      blob: true
    });
    if (row?.photo)
      Wb.exportData(row.photo);
    else
      Wb.exportData(new Wb.File(true, 'wb/images/null.png').stream);
  },
  selectUsers() {
    let sql = 'select sid,user_name from wb_user';
    if (Params.query) {
      Wb.setLike('query');
      sql += ' where user_name like {?query?}';
    }
    sql += ' order by user_name';
    Wb.sendRows(sql);
  },
  selectDeptTree() {
    let searchClause = '', query = Params.query, firstItem, sql, rows;
    // ... 构造部门树查询
    rows = Wb.getRows({ sql, rs: query ? 100 : -1 });
    Wb.send(rows);
  }
};
actions[action]();

这段代码信息密度很高,值得逐条对照:

写法

作用

值得注意的点

Params.xaction

前端请求自带的动作名

一个模块只用一个服务端入口,靠 actions 对象分派,不必为每个按钮建一个接口

Wb.setLike('search')

把参数加工成模糊匹配值

SQL 里写 {?search?} 占位符,平台自动绑定,不用手工拼 like '%...%'

Wb.sendRowx({sql, kv:{...}})

查询结果连列元数据一起回写前端

kv 指定用哪个字典翻译字段,gender 会自动从 0/1 变成"男/女"

Wb.getId()

生成主键

新增记录先生成 sid,再 Wb.set(rec) 把它放回上下文

Wb.sync({tableName, insert/update/del})

一行完成 CRUD

前端提交的 Params 直接作为数据对象,字段与表列名一致即可

Wb.getObject('del')

取前端提交的删除数据

配合 Wb.sync 的 del 参数

Wb.getRow({sql, blob:true}) + Wb.exportData

读取并输出照片二进制

没有照片时回退到 wb/images/null.png,边界处理很完整

Wb.sendRows(sql) / Wb.send(rows)

输出查询结果

下拉框、树控件的数据源就是这么来的

Wb.getRows({sql, rs:-1})

取全部行

rs 控制返回行数,-1 表示不限制

如果你之前按 API 手册写过 Wb.getRows + Wb.send 的组合,会发现在真实示例里平台惯用 sendRowx / sendRows 这类"一步到位"的封装:查询与输出合并成一次调用,少写中间变量。这不是两种互相矛盾的 API,而是不同抽象层级——读示例的意义,就是学到这一层地道的写法。

还有一点很关键:整个文件没有一行 Wb.send({success:true})。平台约定"无异常即成功",失败用 Wb.raise 或 Wb.error 抛出,正常路径只输出业务数据。这跟很多自研后端"每个接口都包一层 {code:0,msg:'ok'}"的习惯不同,初次接手时要适应。

六、跑起来:一次 CRUD 的完整闭环

在 IDE 里点「运行」,或浏览器直接打开 m?xwl=example/ai/crud-dialog:

示例运行画面。一张 100 条记录的人员表格,列含 Code、Full name、Username、Department、Birth date、Gender、Email、Salary、CV、Photo;左上角是「添加 / 编辑 / 删除 / 搜索」工具栏,底部有分页(1 - 100 / 100)。

点「添加」,弹出表单对话框:

点击「添加」弹出的 editWin 窗口。表单分「General Information」与「Personal Resume」两段,必填项带红星,含 Code、Full name、Username、Dept name、Birth date、Gender、Salary、Photo 上传、CV 多行文本,底部「保存 / 取消」。

把前面的组件树和这两张图对起来看,闭环就清楚了:

  1. 页面加载 → grid1 触发 action=select → 服务端 select() 用三表联查 + Wb.sendRowx 输出数据 → 表格渲染 100 行;
  2. gender 字段通过 kv:{gender:'gender'} 走字典翻译,所以库里存 0/1、界面显示"男/女";
  3. 点「添加」→ addBtn 的 click 打开 editWin 窗口 → 其中 Username、Dept name 两个 Select 分别请求 selectUsers / selectDeptTree 拿下拉/树数据;
  4. 填完点「保存」→ 触发 action=add → 服务端 Wb.getId() 生成主键 + Wb.sync({insert: Params}) 落库 → Wb.send(rec) 把新记录回写,前端表格即时刷新;
  5. 编辑、删除、双击行、搜索、照片上传,分别对应 edit / del / itemdblclick / search / getPhoto 分支。

一个 .xwl 文件 + 一段 serverScript,覆盖了一整套增删改查、字典翻译、下拉联动、树选择、图片存取、模糊搜索。 这就是"快速开发"四个字最具体的样子:不是少写业务,而是把重复的工程装配压缩掉了。在 geejing WebBuilder 快速开发平台里,这种装配复用是默认行为,不需要项目组另外搭一层框架。

七、照着示例改一个字段(十分钟实操)

读完之后最该做的事是动手改一处,验证自己真的懂了。以"给人员表加一个手机号字段"为例,最短路径是五步:

  1. 改表:在业务库 wb_staff 加列 mobile varchar(20)(平台的数据库浏览器或 init 模块里执行均可);
  2. 改弹窗:IDE 文件浏览器打开 crud-dialog.xwl,在 editWin 下新增一个 Wb.Text,cid 设 mobile、text 设"手机号"、required 按需设置;
  3. 改表格列:在 grid1 → columns 下加一个 Wb.Column,fieldName 填 mobile、text 填"手机号";
  4. 通常不用改服务端:select() 用的是 select a.*,新增列自动带出;add() / edit() 用的是整包 Params,字段与列名一致即可自动写入;
  5. 点运行验证:新增一条带手机号的记录,确认列表显示、编辑回显、删除都正常。

第 4 步是最能体现平台设计取舍的地方——因为服务端脚本面向"数据对象"而非"固定字段列表",加字段这类高频变更往往只动界面定义。如果换成手写 Controller + DTO + SQL 的传统结构,同样的改动要碰三到四个文件。

改完记得看一眼磁盘上的 .xwl diff:YAML 里多出来的就是两个组件节点,清晰可评审,可以直接提交版本库。

八、从示例到自己的模块:三条可复用经验

第一,先找最像的示例,再从它复制。 example 的 11 个分类本质是一张"需求 → 模板"索引:表单联动找 basic/grid1-form、可编辑表格找 ai/crud-edit-grid、工作流多表找 apps/oa/leave、图表大屏找 basic/dashboard、AI 生成结果参考 ai/order。比起从空白模块起步,改一个已经跑通的示例能省掉大量结构调试。

第二,把 serverScript 的 actions 分派模式当成默认骨架。 一个模块一个入口、Params.xaction 分派、每个分支只做一件业务动作,这套结构在平台的示例里高度一致,也最便于 AI 理解和续写。

第三,把"字典 + 列名一致"当成约定,而不是每次临时处理。 示例里 kv 翻译、整包 Params 落库、Select 走独立 action,都建立在字段命名统一这个前提上。前期把命名规范定好,后面加字段、加页面几乎不需要动服务端。

在 geejing WebBuilder 快速开发平台里,模块就是可复制的文本文件,示例就是官方维护的最佳实践模板。所谓"快速",很大程度上来自不必每次重新发明这套骨架。

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

目录
  • 一、为什么读示例比读文档更快
  • 二、进 IDE:先看清楚工作区
  • 三、找到示例:展开 example 目录树
  • 四、设计视图:四栏工作区逐个拆开
    • 4.1 文件浏览器(最左)
    • 4.2 属性面板(左二)
    • 4.3 对象浏览器(中间,面积最大)
    • 4.4 控件库(最右)
  • 五、切到源码:XWL 到底长什么样
    • 5.1 完整 serverScript 逐段读
  • 六、跑起来:一次 CRUD 的完整闭环
  • 七、照着示例改一个字段(十分钟实操)
  • 八、从示例到自己的模块:三条可复用经验
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档