七巧低代码服务端脚本语法教学(原理详解版)
📘 基于官方文档核对,重点讲清"为什么这样写、能拿到什么、方法之间有什么差异"
一、先搞懂几个核心概念(不看会一直懵)
写脚本之前,必须先分清这几个对象,否则后面所有代码都是"抄了不知道为什么"。
1.1 应用 / 表单 / 文档 的关系
- 应用(application):七巧里一个独立的应用,每个应用有自己的ID(appId)。同一套表单名在不同应用里可能都存在,所以几乎所有的数据操作API都要求传 appId,用来定位"在哪个应用里操作"。
- 表单(form):表单设计器里建的那个表单模型,比如"采购订单"、"采购明细"。它是一个"模板/结构定义"。
- 文档(document):表单的一条实际数据记录。比如张三提交的那一张采购订单,就是一个 document。脚本里操作的大部分对象都是 document,不是 form。
一句话:表单是结构,文档是数据。你读字段、改字段,操作的都是某一条文档(document),不是表单模型本身。
1.2 主表文档 vs 子表单文档
- 主表文档:一张主表里的某一条数据,比如一条采购订单。
- 子表单文档:主表里"子表单控件"里的某一行数据,比如采购订单里的某一行采购明细。
- 子表单文档也是 document,只是它附属于某个主表文档,通过外键字段关联。
1.3 ⚠️ "当前文档" vs "原始文档"(最容易搞混,必须看懂)
这是新手最容易踩坑的地方。七巧里有两个API都能拿到 document,但它们拿到的根本不是同一个东西:
| API | 拿到的是什么 | 典型场景 |
|---|---|---|
$.context.getCurrentDocument() |
当前正在编辑/操作的那张表单文档 | 表单事件里改自己表单的字段 |
$.context.getHttpRequest().getAttribute("document") |
触发这次按钮点击的那条原始数据 | 列表按钮:从A表生成B表,A表那条就是原始文档 |
举个具体场景帮你看懂:
场景:在"采购订单"列表页,每条订单后面有个"生成入库单"按钮。点这个按钮后,七巧会打开"入库单"的新增表单。
- 此时
$.context.getCurrentDocument()拿到的是 "入库单"(你正在填的那张表)。 - 而
$.context.getHttpRequest().getAttribute("document")拿到的是 "采购订单"(你刚才点按钮的那条数据)。
如果你要改入库单的字段,用 getCurrentDocument。如果你要读采购订单的数据复制过来,必须用 getHttpRequest().getAttribute("document")。用错了就拿到错误的数据。
记忆方法:getCurrentDocument = "我正在编辑的表";getHttpRequest().getAttribute("document") = "谁触发了我"。
二、脚本入口:为什么是 (function(){ ... })()
七巧服务端脚本必须包在这个立即执行函数里:
javascript
(function(){
// 你的代码
})()
为什么这样写? 这是平台要求的执行容器。七巧在服务端执行你的脚本时,是把这整段代码当作一个"任务"来跑的,IIFE 格式能保证你的变量不会污染全局作用域,也保证脚本一加载就立即执行。这是固定写法,所有服务端脚本都必须这样开头和结尾,不能改成别的形式。
三、$.context ------ 拿到当前环境信息
$.context 的作用是"拿到当前这次脚本执行的环境信息"。下面逐个讲清每个方法能拿到什么、什么时候用。
3.1 $.context.getCurrentApplicationId()
- 能拿到什么:当前应用的ID(字符串)。
- 为什么需要 :后面几乎所有
$.form的操作都要传 appId,因为七巧是多应用架构,必须告诉系统在哪个应用里查/改数据。 - 用法:
javascript
var appId = $.context.getCurrentApplicationId();
3.2 $.context.getCurrentDocument()
- 能拿到什么:当前正在编辑/操作的那张表单的 document 对象。
- 什么时候用:表单的按钮事件、字段校验、系统任务里,你要读/改当前表单的字段时。
- 注意:在"列表按钮"场景里,这个拿到的是新打开的那张表单(比如入库单),不是你点的那条原始数据。原始数据要用下面的方法。
javascript
var doc = $.context.getCurrentDocument();
var name = doc.getElementByName("姓名").getValue();
3.3 $.context.getHttpRequest().getAttribute("document")
- 能拿到什么:触发这次操作的那条原始数据(document 对象)。
- 什么时候用:只在"数据管理列表的自定义按钮"场景里用。当你需要从A表复制数据到B表时,A表那条就是用这个拿。
- 和 getCurrentDocument 的区别:见第一章 1.3,这是两份不同的数据。
javascript
var originalDocument = $.context.getHttpRequest().getAttribute("document");
var originalId = originalDocument.getId(); // 原始数据的ID
3.4 $.context.getCurrentUser() / $.context.getCurrentUserId()
- 能拿到什么:getCurrentUser 拿到用户对象(含姓名、电话等),getCurrentUserId 只拿到用户ID字符串。
- 区别:只想要ID就用后者更轻量;想要姓名、电话等详细信息就用前者。
javascript
var userId = $.context.getCurrentUserId(); // 只要ID
var user = $.context.getCurrentUser(); // 要完整对象
var userName = user.getName(); // 从对象里取姓名
四、Document 对象 ------ 读字段和改字段
拿到 document 之后,最常用的操作就是读字段值和改字段值。这里讲清各种读取方法的差异和修改方法的差异。
4.1 读取字段值:getValue / getIntValue / getDoubleValue / getFloatValue
所有读取方法都是 doc.getElementByName("字段名").xxxValue(),区别在返回类型:
| 方法 | 返回类型 | 什么时候用 |
|---|---|---|
getValue() |
String 或 JSONArray | 通用方法。文本、人员、部门等所有控件都能用。多选控件返回的是数组 |
getIntValue() |
int | 数字控件,且你需要整数参与运算时 |
getDoubleValue() |
double | 数字控件,且你需要小数(金额类)参与运算时 |
getFloatValue() |
float | 数字控件,float 类型 |
为什么数字要分三种? 因为数字控件在表单设计时可以配置成不同精度类型,对应后端不同数据类型。用错类型可能导致精度丢失或类型转换异常。如果拿不准,金额、数量类一律用 getDoubleValue() 最安全。
getValue 返回什么?
- 单行文本、人员单选、部门单选 → 返回 String
- 人员多选、部门多选 → 返回 JSONArray(数组),里面是用户ID/部门ID
javascript
var doc = $.context.getCurrentDocument();
// 文本字段
var name = doc.getElementByName("姓名").getValue(); // "张三"
// 数字字段(金额用double)
var amount = doc.getElementByName("金额").getDoubleValue(); // 100.50
// 人员单选 → 用户ID字符串
var leader = doc.getElementByName("队长").getValue(); // "abc123..."
// 人员多选 → 用户ID数组
var members = doc.getElementByName("成员").getValue(); // ["id1","id2"]
4.2 ⚠️ 判空:为什么必须先判空再取值
getElementByName("字段名") 如果字段不存在或没值,会返回 null。如果直接 .getValue() 就会报空指针异常,整个脚本中断。
javascript
// ❌ 错误:字段可能为null,直接取值会崩
var value = doc.getElementByName("可能不存在的字段").getValue();
// ✅ 正确:先判空
var element = doc.getElementByName("可能不存在的字段");
var value = element != null ? element.getValue() : null;
4.3 读取子表单:getSubDocuments
- 能拿到什么 :子表单控件里的所有行,返回
List<Document>,每一行又是一个 document。 - 注意 :这里拿到的是"当前文档自己的子表单"。如果你要拿"别的文档"的子表单,要用
$.form.getSubFormDocumentsByName(见第五章)。
javascript
var doc = $.context.getCurrentDocument();
var subDocs = doc.getElementByName("明细").getSubDocuments(); // List<Document>
// 必须判空!子表单可能没数据
if (subDocs != null) {
for (var i = 0; i < subDocs.size(); i++) {
var row = subDocs.get(i);
var productName = row.getElementByName("产品名称").getValue();
}
}
4.4 修改字段值:addElement vs setValue
两种方式都能改字段值,区别:
| 方法 | 写法 | 区别 |
|---|---|---|
doc.addElement("字段名", 值) |
直接在 document 上调用 | 官方文档里更常见的写法 |
doc.getElementByName("字段名").setValue(值) |
先取字段元素再设值 | 适合"先读再改"的场景 |
两者效果一样,选哪个看习惯。但注意:在按钮执行前事件、系统任务里改了字段值,不需要自己调 saveFormDocument 保存,七巧会自动保存。手动再保存反而会报错(见下章)。
javascript
var doc = $.context.getCurrentDocument();
// 方式一:addElement
doc.addElement("姓名", "张三");
doc.addElement("金额", 100);
// 方式二:setValue(效果一样)
doc.getElementByName("姓名").setValue("张三");
五、$.form ------ 表单数据的增删改查
这是最常用也最容易混乱的一组API。重点讲清四种查询方法的区别、保存时机的坑、子表单获取的参数含义。
5.1 创建空文档:createEmptyDocument
- 能拿到什么:一张指定表单的空白文档对象,里面还没有任何数据。
- 参数为什么这样传 :
- 参数1 appId:在哪个应用里建。
- 参数2 表单名:建哪种表单的数据。
- 什么时候用:要往某张表单里新增一条数据时,先建空文档,再 addElement 填字段,最后 saveFormDocument 保存。
javascript
var appId = $.context.getCurrentApplicationId();
var doc = $.form.createEmptyDocument(appId, "学生表"); // 建一张空白的学生表数据
doc.addElement("姓名", "张三");
doc.addElement("年龄", 24);
$.form.saveFormDocument(doc, appId); // 必须保存才会真正入库
5.2 保存文档:saveFormDocument
- 能做什么:把文档真正写入数据库。
- 参数:doc(要保存的文档)、appId(在哪个应用里)。
- ⚠️ 什么时候该调 / 什么时候不该调(这个坑很多人踩):
| 场景 | 要不要调 saveFormDocument | 原因 |
|---|---|---|
| 自己 createEmptyDocument 新建的数据 | 要调 | 新建的文档不会自动入库 |
| 按钮执行前事件里改当前表单字段 | 不要调 | 七巧会自动保存,手动调会报错(表单有版本机制) |
| 系统任务里改当前表单字段 | 不要调 | 同上,自动保存 |
javascript
// ❌ 错误:按钮执行前事件里手动保存
var doc = $.context.getCurrentDocument();
doc.addElement("姓名", "张三");
$.form.saveFormDocument(doc, appId); // 报错!系统已经会自动保存
// ✅ 正确:只改值,不保存
var doc = $.context.getCurrentDocument();
doc.addElement("姓名", "张三");
// 不调 saveFormDocument,系统自动保存
5.3 ⚠️ 四种查询方法的区别(重点看这个)
七巧有四个API都能查数据,新手完全分不清该用哪个。对比一下:
| 方法 | 怎么传条件 | 适用场景 |
|---|---|---|
getFormDocumentsByFieldNameAndValue(appId, 表单名, fieldMap) |
HashMap(字段名→值,等值匹配) | 简单的"字段=值"查询 |
getDocumentsByCondition(appId, 表单名, conditionMap) |
HashMap(字段名→值) | 和上面类似,等值查询 |
getDocumentsByFilterParam(appId, 表单名, filterParamList) |
FilterParam 列表(支持大于/小于/like等) | 需要 >、<、like、between 等复杂条件 |
getDocumentsByFormModelAndCondition(appId, formModel, fieldMap) |
表单模型对象 + HashMap | 先拿到表单模型再查,某些场景下更稳 |
怎么选?
- 只需要"某字段等于某值"→ 用前两个,简单。
- 需要"日期在某范围"、"金额大于某值"、"姓名模糊匹配" → 必须用
getDocumentsByFilterParam,因为它支持操作符。 getFormDocumentsByFieldNameAndValue和getDocumentsByCondition功能基本一样,任选一个即可。
四种查询的代码对比:
javascript
var appId = $.context.getCurrentApplicationId();
// 方法1:getFormDocumentsByFieldNameAndValue(等值查询)
var fieldMap = new Packages.java.util.HashMap();
fieldMap.put("状态", "已审批"); // 状态=已审批
var docs = $.form.getFormDocumentsByFieldNameAndValue(appId, "报销单", fieldMap);
// 方法2:getDocumentsByCondition(等值查询,和上面效果一样)
var conditionMap = new Packages.java.util.HashMap();
conditionMap.put("请假人", "张三");
var docs = $.form.getDocumentsByCondition(appId, "请假表", conditionMap);
// 方法3:getDocumentsByFilterParam(支持大于/小于/like等复杂条件)
var filterParamList = new Packages.java.util.ArrayList();
filterParamList.add(new Packages.cn.com.do1.do1cloud.runtime.dto.FilterParam("金额", "gt", 1000)); // 金额>1000
filterParamList.add(new Packages.cn.com.do1.do1cloud.runtime.dto.FilterParam("姓名", "like", "张%")); // 姓名以张开头
var docs = $.form.getDocumentsByFilterParam(appId, "报销单", filterParamList);
// 方法4:getDocumentsByFormModelAndCondition(先拿表单模型再查)
var formModel = $.form.getFormModel(appId, "报销单");
var fieldMap = new Packages.java.util.HashMap();
fieldMap.put("状态", "已审批");
var docs = $.form.getDocumentsByFormModelAndCondition(appId, formModel, fieldMap);
FilterParam 操作符一览(只有方法3支持这些):
| 操作符 | 含义 | 值的格式 |
|---|---|---|
gt / ge |
大于 / 大于等于 | 直接传值 |
lt / le |
小于 / 小于等于 | 直接传值 |
eq / ne |
等于 / 不等于 | 直接传值 |
like |
模糊匹配 | 字符串,可用% |
between |
在区间内 | 字符串,逗号隔开:"(开始,结束)" |
isNull / isNotNull |
为空 / 非空 | - |
in / notIn |
包含 / 不包含 | 数组 |
⚠️ 所有查询方法返回的都可能为 null,遍历前必须判空:
javascript
var docs = $.form.getFormDocumentsByFieldNameAndValue(appId, "报销单", fieldMap);
if (docs != null) { // 必须判空
for (var i = 0; i < docs.size(); i++) {
var doc = docs.get(i);
// 处理每条数据
}
}
5.4 获取子表单:getSubFormDocumentsByName(参数详解)
这个就是你在问题里举的那个API,重点讲清楚每个参数为什么必须传。
javascript
$.form.getSubFormDocumentsByName(docId, 外键字段名, 子表单名, appId)
- 获取谁的子表单? 获取 docId 对应的那条主表数据的子表单。
- 参数1 docId:主表文档的ID。告诉系统查哪条主表数据的子表单。不传这个,系统不知道查谁的子表单。
- 参数2 外键字段名:主表里"关联子表单"的那个字段名。子表单是通过主表的某个字段关联的,必须告诉系统通过哪个字段去找。
- 参数3 子表单名:子表单的表单名称。一张主表可能有多个子表单控件,要指定是哪一个。
- 参数4 appId:在哪个应用里查。
完整示例 + 业务场景:
场景:在采购订单列表点"生成入库单"按钮,要拿到这条采购订单的采购明细。
javascript
(function(){
var appId = $.context.getCurrentApplicationId();
// 拿到触发按钮的那条采购订单(不是当前正在编辑的入库单!)
var purchaseOrder = $.context.getHttpRequest().getAttribute("document");
// 拿这条采购订单的子表单"采购明细"
// 参数1:采购订单的文档ID
// 参数2:"采购编号"是采购订单表里关联子表单的字段名
// 参数3:"采购明细"是子表单的表单名
// 参数4:应用ID
var subDocuments = $.form.getSubFormDocumentsByName(
purchaseOrder.getId(),
"采购编号",
"采购明细",
appId
);
// 判空后遍历每一行明细
if (subDocuments != null) {
for (var i = 0; i < subDocuments.size(); i++) {
var row = subDocuments.get(i);
var productName = row.getElementByName("产品名称").getValue();
}
}
return subDocuments;
})()
和 doc.getElementByName("子表单").getSubDocuments() 的区别:
| 方法 | 拿谁的子表单 | 什么时候用 |
|---|---|---|
doc.getElementByName("明细").getSubDocuments() |
拿当前文档自己的子表单 | 当前表单有子表单,直接读 |
$.form.getSubFormDocumentsByName(...) |
拿任意指定文档的子表单 | 要读别的文档(如原始数据)的子表单 |
5.5 删除数据:两种方法
| 方法 | 能做什么 | 区别 |
|---|---|---|
deleteAllDocumentsByFormName(appId, 表单名) |
删整张表的所有数据 | 不加任何条件,全删 |
deleteFormDocumentsByFieldNameAndValue(appId, 表单名, fieldMap) |
按条件删 | 只删满足条件的 |
javascript
var appId = $.context.getCurrentApplicationId();
// 全删(慎用!)
$.form.deleteAllDocumentsByFormName(appId, "临时数据表");
// 条件删:只删"数量=100"的
var fieldMap = new Packages.java.util.HashMap();
fieldMap.put("数量", 100);
$.form.deleteFormDocumentsByFieldNameAndValue(appId, "采购明细", fieldMap);
六、$.validate ------ 校验:四种消息的区别
校验脚本返回的是一个 message 对象,用 $.validate.create() 创建。四种添加消息的方法,区别在"会不会阻止提交":
| 方法 | 效果 | 什么时候用 |
|---|---|---|
addSuccess("信息") |
显示成功提示 | 校验通过时给个正面反馈 |
addInfo("信息") |
显示普通提示 | 给提示但不影响提交 |
addWarn("信息") |
显示警告 | 警告但仍可提交 |
addError("信息") |
阻止提交 | 校验不通过,拦住 |
关键点 :只有 addError 会阻止用户提交,其他三个只是提示。所以拦截场景必须用 addError。
javascript
(function(){
var doc = $.context.getCurrentDocument();
var amount = doc.getElementByName("金额").getDoubleValue();
if (amount > 10000) {
var message = $.validate.create();
message.addError("金额超过10000,不允许提交"); // 阻止提交
return message; // 必须 return 才生效
}
})()
七、$.message ------ 发消息:站内信 vs 邮件
| 方法 | 能做什么 | 参数 |
|---|---|---|
sendGeneralStationMessage(subject, context, userId) |
发七巧站内信 | 标题、内容、接收人用户ID |
sendEmail(subject, context, email) |
发邮件 | 标题、内容、接收人邮箱 |
javascript
// 发站内信给指定用户
var userId = doc.getElementByName("队长").getValue();
$.message.sendGeneralStationMessage("通知标题", "通知内容", userId);
// 发邮件
var email = doc.getElementByName("客户邮箱").getValue();
$.message.sendEmail("报价通知", "您的报价已处理", email);
八、$.log / $.json / $.date ------ 辅助工具
8.1 $.log ------ 日志(调试用)
四种级别,都支持 {} 占位符:
javascript
$.log.info("处理文档:{}", docId); // 提示日志
$.log.error("出错了:{}", errorMsg); // 异常日志
$.log.warn("警告:{}", warnMsg); // 警告日志
$.log.debug("调试:{}", detail); // 调试日志
8.2 $.json ------ 对象转JSON字符串(调试神器)
拿到一个对象不知道里面有什么?转成JSON打印出来看:
javascript
var doc = $.context.getCurrentDocument();
var jsonStr = $.json.objectToJsonString(doc);
$.log.info("文档完整内容:{}", jsonStr); // 看看里面到底有什么
8.3 $.date ------ 日期处理
| 方法 | 能做什么 | 返回值 |
|---|---|---|
getCurrentDate() |
拿当前时间 | Date对象 |
dateToString(date, format) |
Date转字符串 | String |
stringToDate(str, format) |
字符串转Date | Date |
timestampToDate(ts) |
时间戳转Date | Date |
javascript
var now = $.date.getCurrentDate();
var dateStr = $.date.dateToString(now, "yyyy-MM-dd"); // "2024-01-15"
var date = $.date.stringToDate("2024-01-15", "yyyy-MM-dd");
九、$.contact ------ 通讯录(常用方法)
通讯录有33个方法,这里只讲最常用的几个,讲清参数和返回值。完整列表见附录。
9.1 拿用户
javascript
// 按ID拿单个用户
var user = $.contact.getUserById("用户ID"); // 返回 UserDTO
var name = user.getName(); // 取姓名
var phone = user.getTelephone(); // 取电话
// 按姓名关键字模糊查
var users = $.contact.listUserByKeyword("张"); // 返回 List,所有姓张的
9.2 拿部门下的人
javascript
// 只拿本部门的人
var users = $.contact.listUsersByDepartmentId("部门ID");
// 拿本部门+所有子部门的人
var allUsers = $.contact.listAllUsersByDepartmentId("部门ID");
9.3 拿用户的领导
javascript
// includeAllLeader: true=所有上级领导, false=只拿直接领导
var userConfig = $.contact.getUserLeadersByUserId("用户ID", true);
var directLeader = userConfig.getLeaderUser(); // 直接领导
var allLeaders = userConfig.getLeaderUsers(); // 所有领导
十、实战案例(每个都讲清为什么这样写)
10.1 从采购订单生成入库单(数据复制)
业务场景:采购订单列表有"生成入库单"按钮,点击后把采购订单的主表+子表数据复制到入库单。
为什么这样写:这是列表按钮场景,要拿原始数据(采购订单)必须用 getHttpRequest,要改当前表单(入库单)用 getCurrentDocument。
javascript
(function(){
var appId = $.context.getCurrentApplicationId();
// 拿触发按钮的那条采购订单(不是入库单!)
var purchaseOrder = $.context.getHttpRequest().getAttribute("document");
// 拿当前正在编辑的入库单
var storageDoc = $.context.getCurrentDocument();
// 把采购订单的主表字段复制到入库单
storageDoc.addElement("采购编号", purchaseOrder.getId());
storageDoc.addElement("供应商", purchaseOrder.getElementByName("供应商名称").getValue());
// 拿采购订单的子表单(采购明细)
var purchaseDetails = $.form.getSubFormDocumentsByName(
purchaseOrder.getId(), // 哪条采购订单
"采购编号", // 主表外键字段名
"采购明细", // 子表单名
appId // 哪个应用
);
// 把每行明细复制成入库明细
var storageDetails = new Packages.java.util.ArrayList();
if (purchaseDetails != null) {
for (var i = 0; i < purchaseDetails.size(); i++) {
var row = purchaseDetails.get(i);
var detailDoc = $.form.createEmptyDocument(appId, "入库明细");
detailDoc.addElement("产品名称", row.getElementByName("产品名称").getValue());
detailDoc.addElement("数量", row.getElementByName("待入库数量").getValue());
storageDetails.add(detailDoc);
}
}
// 把明细列表塞到入库单的子表单字段
storageDoc.addElement("入库明细", storageDetails);
// 注意:不调 saveFormDocument!按钮执行前事件里系统自动保存
})()
10.2 金额校验(拦截提交)
业务场景:报销金额超过10000不让提交。
为什么这样写:校验脚本必须 return message 对象,且必须用 addError 才能阻止提交。
javascript
(function(){
var doc = $.context.getCurrentDocument();
var amount = doc.getElementByName("金额").getDoubleValue();
if (amount > 10000) {
var message = $.validate.create();
message.addError("金额超过10000,不允许提交"); // 只有Error会拦截
return message; // 必须 return
}
})()
10.3 重复录入校验(查历史数据)
业务场景:今天已经登记过体温,不能再登记。
为什么这样写:要先查今天有没有记录,用 getDocumentsByFilterParam 因为要按"日期=今天"且"用户=当前用户"两个条件查。查到有数据就用 addError 拦截。
javascript
(function(){
var appId = $.context.getCurrentApplicationId();
var userId = $.context.getCurrentUserId();
var today = $.date.dateToString($.date.getCurrentDate(), "yyyy-MM-dd");
// 构建查询条件:日期=今天 且 人员=当前用户
var filterList = new Packages.java.util.ArrayList();
filterList.add(new Packages.cn.com.do1.do1cloud.runtime.dto.FilterParam("日期", "eq", today));
filterList.add(new Packages.cn.com.do1.do1cloud.runtime.dto.FilterParam("登记人", "eq", userId));
var docs = $.form.getDocumentsByFilterParam(appId, "体温登记表", filterList);
// 查到了就拦截
if (docs != null && docs.size() > 0) {
var message = $.validate.create();
message.addError("今天已登记过,不能重复登记");
return message;
}
})()
10.4 子表单关联必填校验
业务场景:出行方式选了"飞机",航班信息必填。
为什么这样写:要遍历子表单每一行,检查每行的"出行方式"和"航班信息"。
javascript
(function(){
var doc = $.context.getCurrentDocument();
var subDocs = doc.getElementByName("出行明细").getSubDocuments();
if (subDocs != null) {
for (var i = 0; i < subDocs.size(); i++) {
var row = subDocs.get(i);
var way = row.getElementByName("出行方式").getValue();
var flight = row.getElementByName("航班信息").getValue();
// 出行方式=2表示飞机,且航班信息为空 → 拦截
if (way == 2 && (flight == null || flight == "")) {
var message = $.validate.create();
message.addError("选择飞机出行时,航班信息不能为空");
return message;
}
}
}
})()
10.5 调试:不知道对象里有什么
为什么这样写:拿到一个对象不确定结构时,转成JSON打印或发站内信给自己看。
javascript
(function(){
var doc = $.context.getCurrentDocument();
var jsonStr = $.json.objectToJsonString(doc);
// 方式1:日志输出
$.log.info("文档内容:{}", jsonStr);
// 方式2:发站内信给自己看(日志看不到时用这招)
var userId = $.context.getCurrentUserId();
$.message.sendGeneralStationMessage("调试信息", jsonStr, userId);
})()
十一、Java 包使用
七巧脚本里能用 Java 的集合类,通过 Packages 访问。常用的是 HashMap(查询条件)、ArrayList(列表)、HashSet(去重)。
javascript
// HashMap:用于查询条件(字段名→值)
var fieldMap = new Packages.java.util.HashMap();
fieldMap.put("字段名", "值");
// ArrayList:用于列表(如子表单列表、FilterParam列表)
var list = new Packages.java.util.ArrayList();
list.add("item");
// HashSet:用于去重
var set = new Packages.java.util.HashSet();
set.add("value1");
set.add("value1"); // 重复的不会加进去
// FilterParam:过滤查询参数
var filterParam = new Packages.cn.com.do1.do1cloud.runtime.dto.FilterParam("字段名", "eq", "值");
附录:完整 API 速查表
$.context
| API | 能拿到什么 | 和谁容易混 |
|---|---|---|
getCurrentDocument() |
当前正在编辑的表单文档 | 别和 getHttpRequest().getAttribute("document") 搞混 |
getHttpRequest().getAttribute("document") |
触发按钮的那条原始数据 | 见上 |
getCurrentUser() |
当前用户对象(含姓名电话) | 和 getCurrentUserId 区别:一个拿对象一个拿ID |
getCurrentUserId() |
当前用户ID字符串 | 见上 |
getCurrentApplicationId() |
当前应用ID | - |
Document 读取
| API | 返回类型 | 区别 |
|---|---|---|
getValue() |
String/JSONArray | 通用,多选返回数组 |
getIntValue() |
int | 整数 |
getDoubleValue() |
double | 小数(金额推荐) |
getFloatValue() |
float | float类型 |
getSubDocuments() |
List<Document> | 拿当前文档自己的子表单 |
Document 修改
| API | 区别 |
|---|---|
addElement("字段名", 值) |
直接改 |
getElementByName("字段名").setValue(值) |
先取再改,效果一样 |
$.form 查询(四种对比)
| API | 条件类型 | 适用场景 |
|---|---|---|
getFormDocumentsByFieldNameAndValue |
HashMap 等值 | 简单等值查询 |
getDocumentsByCondition |
HashMap 等值 | 和上面一样 |
getDocumentsByFilterParam |
FilterParam 支持操作符 | 复杂条件(大于/小于/like) |
getDocumentsByFormModelAndCondition |
表单模型+HashMap | 先拿模型再查 |
$.form 其他
| API | 能做什么 | 注意 |
|---|---|---|
createEmptyDocument(appId, 表单名) |
建空文档 | 建完要save才入库 |
saveFormDocument(doc, appId) |
保存文档 | 按钮事件里不要调! |
getSubFormDocumentsByName(docId, 外键, 子表单名, appId) |
拿指定文档的子表单 | 和 getSubDocuments 区别见5.4 |
deleteAllDocumentsByFormName(appId, 表单名) |
全删 | 慎用 |
deleteFormDocumentsByFieldNameAndValue(appId, 表单名, fieldMap) |
条件删 | - |
$.validate
| API | 会阻止提交吗 |
|---|---|
addSuccess |
不会 |
addInfo |
不会 |
addWarn |
不会 |
addError |
会 |
$.message
| API | 能做什么 |
|---|---|
sendGeneralStationMessage(标题, 内容, userId) |
发站内信 |
sendEmail(标题, 内容, email) |
发邮件 |
$.date
| API | 能做什么 |
|---|---|
getCurrentDate() |
拿当前时间 |
dateToString(date, format) |
Date转字符串 |
stringToDate(str, format) |
字符串转Date |
timestampToDate(ts) |
时间戳转Date |
$.contact(常用)
| API | 能拿到什么 |
|---|---|
getUserById(id) |
单个用户对象 |
listUserByKeyword(关键字) |
模糊查用户列表 |
listUsersByDepartmentId(deptId) |
部门下的人(不含子部门) |
listAllUsersByDepartmentId(deptId) |
部门下的人(含子部门) |
getUserLeadersByUserId(userId, includeAll) |
用户的领导 |
官方文档参考
📘 文档版本 :v7.0(原理详解版)
📅 更新日期 :2026-08-06
🎯 改进重点:每个API都讲清"为什么这样写、能拿到什么、和相似方法的区别"