七巧低代码前端表单JS脚本教学(原理详解版)
📘 基于官方文档核对,重点讲清"为什么这样写、能拿到什么、方法之间有什么差异"
一、先搞懂几个核心概念(不看会一直懵)
1.1 ⚠️ 这里的 this 和 Vue 里的 this 不是一回事
这是新手最容易踩的第一个坑。你如果写过 Vue,会习惯性地以为 this 能访问组件里的响应式变量。在七巧页面JS里不行。
官方文档原话:页面JS事件扩展的 this 区别于 Vue 中 this 的概念,不能访问到任何应用组件中的响应式变量,与系统 Vue 组件是解耦的。
那要访问/修改应用数据怎么办? 必须通过七巧暴露的 API(this.context、this.business、this.utils)来访问,不能直接 this.某变量。
javascript
// ❌ 错误:Vue习惯,这里访问不到
export function mounted() {
this.表单数据 = '123'; // 没用,访问不到
}
// ✅ 正确:通过API访问
export function mounted() {
this.context.form.setFormData({ '单行文本1': '123' });
}
1.2 为什么函数前面必须有 export
官方文档明确:若希望编辑器中的脚本能被动作面板引用到,必须要保证函数前使用了 export 标识,否则配置时会看不到对应的函数出现在动作面板中,实际运行时也会报错。
简单说:不写 export,你在动作面板里就选不到这个函数,配了也不执行。这是固定要求。
javascript
// ❌ 没加export,动作面板里看不到,配了也不执行
function mounted() { ... }
// ✅ 加了export,动作面板里能选到
export function mounted() { ... }
1.3 三个核心API模块的分工
七巧把前端API分成三块,各管各的,不要混用:
| 模块 | 能做什么 | 例子 |
|---|---|---|
this.utils |
工具类 + UI交互(和业务无关) | dayjs处理日期、axios发请求、Message弹提示 |
this.business |
业务接口(深度交互七巧应用) | 调后端自定义函数、打开弹窗 |
this.context |
访问当前页面的系统数据 | 读表单数据、改表单数据、拿流程信息 |
记忆方法:utils是"工具箱"(通用工具),business是"业务通道"(和后端打交道),context是"当前环境"(拿系统数据)。
1.4 ⚠️ 表单类API不能在所有地方用(第二个大坑)
官方文档明确:表单类的上下文都必须要在表单相关的场景 中使用,不能在页面的 mounted 中使用该API,否则会返回空。
什么意思?
- 在"表单新增/编辑/详情页"里 → 可以用
this.context.form.getFormData()拿到表单数据。 - 在"普通列表页/自定义页面"的
mounted里 → 用getFormData()会返回空,因为此时根本没有打开表单。
所以在写脚本前,先想清楚:这个脚本运行在什么场景?这个场景下能不能拿到表单数据?
二、脚本入口和事件函数
2.1 几个核心事件:什么时候触发、能拿到什么参数
七巧前端JS就是写一堆 export function 事件名(参数) 的函数,由系统在对应时机调用。每个事件的参数不同,下面逐个讲清。
mounted() ------ 页面/表单加载完成时触发
- 参数:无
- 能做什么:初始化数据、设置默认值、调用后端接口
- 注意:如果是在普通页面(非表单)的mounted里,不能调表单API,会返回空
javascript
export function mounted() {
// 表单加载完成,设置默认值
this.context.form.setFormData({ '创建日期': this.utils.dayjs().format('YYYY-MM-DD') });
}
onChange({field, value}) ------ 表单字段值变化时触发
- 参数为什么这样写 :用解构
{field, value}是因为系统传入的是一个对象,里面有两个属性。field是变化字段的名字,value是新值。 - 能做什么:监听某字段变化,触发联动计算、调用后端
javascript
export function onChange({field, value}) {
// field = 变化的字段名,value = 新值
if (field === '单价') {
// 单价变了,重新计算小计
}
}
onClick({button, selectedIds}) vs onClick({button, row}) ------ 列表按钮点击
这里有个容易混的点 :同样是 onClick,但参数不同,取决于按钮位置:
| 按钮位置 | 参数 | 能拿到什么 |
|---|---|---|
| 列表顶部操作栏按钮 | {button, selectedIds} |
button是按钮配置,selectedIds是选中的行ID数组 |
| 列表行内按钮 | {button, row} |
button是按钮配置,row是当前点击那行的完整数据 |
为什么不同? 顶部按钮是"先勾选多行再批量操作",所以给你的是ID数组;行内按钮是"点哪行操作哪行",所以直接给你那一行数据。
javascript
// 顶部按钮:批量操作
export function onClick({button, selectedIds}) {
// selectedIds = ['id1', 'id2', 'id3'] 勾选的行ID
if (selectedIds.length === 0) {
this.utils.Message({ message: '请先选择数据' });
return;
}
}
// 行内按钮:单行操作
export function onClick({button, row}) {
// row = { 字段1: '值1', 字段2: '值2' } 当前行的完整数据
console.log('点了这行:', row);
}
三、this.context ------ 访问和修改系统数据
这是最常用、也最容易出错的模块。核心要分清:哪些是只读的、哪些能改、改了怎么生效。
3.1 表单数据的三件套:getFormData / getFormInfo / setFormData
这三个是配套的,关系如下:
| 方法 | 能做什么 | 可读写 | 返回什么 |
|---|---|---|---|
getFormData() |
拿当前表单的所有字段值 | 只读 | 对象 {字段名: 值} |
getFormInfo() |
拿当前表单的配置信息 | 只读 | 对象 {id, formDefinitionId, ...} |
setFormData(数据) |
修改当前表单的字段值 | 可写 | - |
getFieldData() |
拿到当前表单的字段值 | 只读 | -值 |
⚠️ getFormData 拿到的是只读的,不能直接改! 官方文档明确:直接修改会抛 Error。要改必须用 setFormData。
javascript
export function mounted() {
let formData = this.context.form.getFormData();
// formData = { 单行文本1: '1', 数字1: '1', 人员多选1: ['id1','id2'] }
let valueText = this.context.form.getFieldData('单行文本1');
// valueText = '1'
// ❌ 错误:直接改会报错
// formData['单行文本1'] = '2'; // throw Error
// ✅ 正确:用setFormData改
this.context.form.setFormData({ '单行文本1': '2' });
}
setFormData 的一个重要特性 :它会自动过滤表单里不存在的字段。你传了一个表单里没有的字段名,不会报错,但也不会生效。所以字段名必须和表单设计器里的完全一致。
setFormData 数据类型要匹配:字符串字段就传字符串,数组字段(如多选)就传数组,否则可能不生效。
javascript
export function mounted() {
// ✅ 正确:类型匹配
this.context.form.setFormData({
'单行文本1': '张三', // 文本字段传字符串
'数字1': 100, // 数字字段传数字
'人员多选1': ['id1','id2'] // 多选字段传数组
});
}
3.2 getFormData vs getFormInfo:拿数据 vs 拿配置
新手经常分不清这两个:
| 方法 | 拿到的是 | 里面有啥 | 什么时候用 |
|---|---|---|---|
getFormData() |
表单的业务数据 | 各字段的值(姓名、金额等) | 要读字段值时 |
getFormInfo() |
表单的配置/元信息 | 文档ID、表单建模ID、按钮类型等 | 要拿文档ID、判断新增/编辑状态时 |
javascript
export function mounted() {
// 拿业务数据(字段值)
let formData = this.context.form.getFormData();
let name = formData['姓名']; // "张三"
// 拿配置信息
let formInfo = this.context.form.getFormInfo();
let docId = formInfo.id; // 当前数据ID
let btnType = formInfo.btnType; // 按钮类型(判断新增/编辑/详情)
}
3.3 context.inputs ------ 拿控件配置(只读)
- 能拿到什么:表单里所有控件的配置信息数组,每个控件一项,包含字段id、字段名、权限等。
- 什么时候用:需要根据字段权限做逻辑判断时。
javascript
export function mounted() {
let inputs = this.context.inputs;
// [
// { id: 'xxx', filedName: '姓名', permission: 'w' },
// { id: 'yyy', filedName: '金额', permission: 'r' }
// ]
}
3.4 context.processInfo ------ 拿流程信息(只读)
- 能拿到什么:流程实例ID、流程定义ID等流程配置。
- 什么时候用:在流程详情页做流程相关逻辑时。
- 注意:只有在流程相关场景才有值,普通表单里拿不到。
javascript
export function mounted() {
let processInfo = this.context.processInfo;
// { processInstanceId: '流程实例id', definitionId: '流程定义id' }
}
3.5 this.params ------ 动作面板传参
- 能拿到什么:在动作面板里配置的参数。
- 为什么需要:一个函数可以被多个按钮复用,用 params 区分是哪个按钮触发的。
javascript
// 在动作面板里给不同按钮配不同的 params,比如 {type: 'top'} 和 {type: 'inline'}
export function onClick() {
let params = this.params;
if (params.type === 'top') {
// 顶部按钮的逻辑
} else if (params.type === 'inline') {
// 行内按钮的逻辑
}
}
四、this.business ------ 调后端接口(三个方法的区别)
这里有三个方法都能调后端,新手完全分不清。对比一下:
| 方法 | 调什么 | 推荐程度 | 区别 |
|---|---|---|---|
excuteCustomAPI |
自定义页面的服务端接口 | ❌ 不推荐 | 官方说很快会被替换 |
executeServiceAPI |
应用级自定义函数 | ✅ 推荐 | 最常用,调当前应用里的自定义函数 |
executeGlobalServiceAPI |
全局自定义函数 | ✅ 推荐 | 调基础设置里定义的全局函数 |
openDialog |
打开弹窗 | - | PC端专用 |
excuteCustomAPI 和 executeServiceAPI 的区别:
- excuteCustomAPI 调的是"自定义页面"里定义的接口(旧方式,要被淘汰)
- executeServiceAPI 调的是"自定义函数"里定义的接口(新方式,推荐)
- 两者参数也不同:前者要传 businessId,后者要传 customJsId
4.1 executeServiceAPI(最常用,重点讲)
- 能做什么:调用当前应用里"自定义函数"中定义的后端方法。
- 参数为什么这样传:
| 参数 | 类型 | 为什么需要 |
|---|---|---|
applicationId |
String | 告诉系统在哪个应用里找自定义函数 |
customJsId |
String | 告诉系统调哪个自定义函数脚本(一个应用可能有多个脚本) |
methodName |
String | 告诉系统调这个脚本里的哪个方法 |
params |
Array | 传给后端方法的参数,按顺序对应后端函数的形参 |
customJsId 怎么拿? 官方文档说:需要通过F12控制台获取。在表单脚本里打印 this 查看。
javascript
export async function mounted() {
let result = await this.business.executeServiceAPI({
applicationId: '当前应用id', // 哪个应用
customJsId: '自定义函数脚本id', // 哪个脚本(F12获取)
methodName: 'getData', // 调哪个方法
params: ['参数1', '参数2'] // 传什么参数(数组,按顺序对应)
});
console.log(result); // 后端方法的返回值
}
后端自定义函数怎么写(这是配套的):
javascript
// 在"自定义函数"里定义
var API = {
// 方法名要和前端的 methodName 对应
// 形参要和前端 params 数组里的顺序对应
getData: function(param1, param2) {
// 这里能用服务端API($.context、$.form等)
var doc = $.context.getCurrentDocument();
// ...
return { success: true, data: '结果' }; // 返回值就是前端 result
}
};
4.2 executeGlobalServiceAPI ------ 调全局函数
- 和 executeServiceAPI 的区别 :全局函数定义在"基础设置"里,不属于某个应用,所以不需要传 applicationId 和 customJsId。
- 什么时候用:调跨应用通用的函数(如统一的用户查询、统一的审批逻辑)。
javascript
export async function mounted() {
let result = await this.business.executeGlobalServiceAPI({
methodName: 'hello', // 只需要方法名
params: ['参数1', '参数2'] // 和参数
});
}
4.3 openDialog ------ 打开弹窗(PC端专用)
javascript
export function onClick() {
this.business.openDialog({
// 弹窗配置
});
}
五、this.utils ------ 工具箱
这些是通用工具,和业务无关。重点讲清 Message 和 MessageBox 的区别。
5.1 Message vs MessageBox:提示 vs 模态框
这两个最容易混:
| 方法 | 是什么 | 交互方式 | 适用场景 |
|---|---|---|---|
this.utils.Message(配置) |
顶部提示条 | 自动消失,不阻塞 | 简单提示"操作成功" |
this.utils.MessageBox(配置) |
模态对话框 | 需要用户点确认/取消 | 需要用户确认的操作 |
javascript
// Message:简单提示,自动消失
this.utils.Message({ message: '保存成功' });
// MessageBox:需要用户确认(返回Promise)
this.utils.MessageBox({
title: '确认操作',
message: '确定要删除吗?'
}).then(() => {
// 用户点了确认
}).catch(() => {
// 用户点了取消
});
5.2 dayjs ------ 日期处理
完整的dayjs功能都有,参考 https://dayjs.fenxianglu.cn/
javascript
let today = this.utils.dayjs().format('YYYY-MM-DD');
let tomorrow = this.utils.dayjs().add(1, 'day').format('YYYY-MM-DD');
5.3 axios ------ 发HTTP请求
- 特性:请求七巧内部接口时会自动带 token,无需额外鉴权;请求外部接口要自己处理鉴权。
javascript
export async function mounted() {
// 内部接口(自动带token)
let res1 = await this.utils.axios({
method: 'get',
url: 'bpms-runtime/form',
params: { id: 123 }
});
// 外部接口(自己鉴权)
let res2 = await this.utils.axios({
method: 'get',
url: 'https://外部接口地址'
});
}
5.4 loadScript ------ 加载第三方JS库
用Promise封装,async/await 调用:
javascript
export async function mounted() {
await this.utils.loadScript('https://cdn地址/库.js');
// 加载完成后可以用这个库
}
5.5 Vue ------ Vue引用
暴露Vue引用,用于高级定制(如用createElement渲染自定义内容):
javascript
const createVueElement = new this.utils.Vue().$createElement;
this.utils.MessageBox({
message: createVueElement('div', {}, '自定义内容')
});
六、实战案例(每个都讲清为什么这样写)
6.1 新增时设置默认值
为什么这样写:要先判断是不是新增状态(通过 formInfo.btnType),只有新增才设默认值,否则编辑时也会覆盖已有数据。
javascript
export function mounted() {
let formInfo = this.context.form.getFormInfo();
// 判断新增状态(PC端是addbtn,移动端是add)
if (formInfo.btnType === 'addbtn' || formInfo.btnType === 'add') {
this.context.form.setFormData({
'创建日期': this.utils.dayjs().format('YYYY-MM-DD'),
'状态': '草稿'
});
}
}
6.2 字段联动计算
为什么这样写:onChange 触发时,value 是变化后的新值,但其他字段值要用 getFormData 拿。
javascript
export function onChange({field, value}) {
if (field === '单价' || field === '数量') {
let formData = this.context.form.getFormData();
let price = parseFloat(formData['单价']) || 0;
let quantity = parseFloat(formData['数量']) || 0;
this.context.form.setFormData({
'小计': price * quantity
});
}
}
6.3 调后端并回填表单
为什么这样写:前端拿不到用户信息等系统数据,必须调后端获取。后端返回结果后用 setFormData 回填。
javascript
export async function onChange({field, value}) {
if (field === '客户选择') {
// 调后端拿客户详情
let result = await this.business.executeServiceAPI({
applicationId: '应用ID',
customJsId: '脚本ID',
methodName: 'getCustomerInfo',
params: [value] // value是选中的客户ID
});
if (result && result.data) {
// 回填到表单
this.context.form.setFormData({
'客户名称': result.data.name,
'客户电话': result.data.phone
});
}
}
}
6.4 列表批量操作(顶部按钮)
为什么这样写:顶部按钮给的是 selectedIds(ID数组),要先判断有没有勾选数据,确认后调后端批量处理。
javascript
export async function onClick({button, selectedIds}) {
if (selectedIds.length === 0) {
this.utils.Message({ message: '请先选择数据' });
return;
}
// 确认对话框
this.utils.MessageBox({
title: '确认',
message: `处理选中的${selectedIds.length}条数据?`
}).then(async () => {
let result = await this.business.executeServiceAPI({
applicationId: '应用ID',
customJsId: '脚本ID',
methodName: 'batchProcess',
params: [selectedIds] // 把ID数组传给后端
});
if (result && result.success) {
this.utils.Message({ message: '处理成功' });
}
});
}
6.5 调试:不知道表单数据长什么样
javascript
export function mounted() {
let formData = this.context.form.getFormData();
console.log('表单数据:', formData);
let formInfo = this.context.form.getFormInfo();
console.log('表单配置:', formInfo);
console.log('按钮类型:', formInfo.btnType);
}
附录:完整 API 速查表
事件
| 事件 | 参数 | 触发时机 |
|---|---|---|
mounted() |
无 | 页面/表单加载完成 |
onChange({field, value}) |
field:字段名, value:新值 | 表单字段变化 |
onClick({button, selectedIds}) |
button:按钮配置, selectedIds:ID数组 | 列表顶部按钮 |
onClick({button, row}) |
button:按钮配置, row:行数据 | 列表行内按钮 |
this.context
| API | 能拿到什么 | 可读写 |
|---|---|---|
form.getFormData() |
表单字段值 | 只读(直接改会报错) |
form.getFormInfo() |
表单配置(ID、按钮类型等) | 只读 |
form.setFormData(数据) |
修改表单字段值 | 可写 |
inputs |
控件配置列表 | 只读 |
processInfo |
流程信息 | 只读 |
this.params |
动作面板配置的参数 | 只读 |
this.business
| API | 能做什么 | 和谁区别 |
|---|---|---|
excuteCustomAPI |
调自定义页面接口 | ❌不推荐,要被替换 |
executeServiceAPI |
调应用级自定义函数 | ✅推荐,要传applicationId+customJsId |
executeGlobalServiceAPI |
调全局自定义函数 | 不需要传applicationId |
openDialog |
打开弹窗 | PC端专用 |
this.utils
| API | 能做什么 | 和谁区别 |
|---|---|---|
Message(配置) |
顶部提示,自动消失 | 简单提示用这个 |
MessageBox(配置) |
模态框,需确认 | 需要确认用这个 |
dayjs() |
日期处理 | - |
axios(配置) |
HTTP请求 | 内部接口自动带token |
loadScript(url) |
加载第三方库 | Promise封装 |
Vue |
Vue引用 | 高级定制用 |
官方文档参考
📘 文档版本 :v4.0(原理详解版)
📅 更新日期 :2026-08-06
🎯 改进重点:每个API都讲清"为什么这样写、能拿到什么、和相似方法的区别"