简体中文
智能表单
约 3646 字大约 12 分钟
2026-07-14
当智能表单的默认展示、交互或提交流程无法满足业务诉求时,可以通过 JS 脚本在现有表单能力之上补充字段处理、交互控制、业务判断和提交逻辑。
典型场景例如:
- 根据字段值动态调整其他字段的文案、样式或显隐
- 监听输入、点击、选择等事件并补充页面交互
- 在提交前增加业务校验或调整本次提交数据
- 根据提交结果执行通知或清理逻辑
适用场景
适用于“智能表单已经能够完成基础数据采集,但默认字段行为、页面交互或提交流程不足,需要在现有表单上增强功能”的场景。
什么时候适合使用智能表单
如果遇到下面这些需求,通常可以优先考虑使用智能表单 JS 脚本:
- 想修改已有字段的值、文案、样式或显隐逻辑
- 想根据其他字段值动态控制页面行为
- 想监听按钮、输入框等页面元素的交互事件
- 想在系统校验通过后、提交请求前增加同步业务校验或数据处理
- 想在提交成功、提交失败或页面销毁时执行补充逻辑
开发思路
智能表单功能增强通常可以拆成三步理解:
- 明确要增强的是字段、按钮、页面展示还是提交流程
- 选择标准 DOM API 或智能表单生命周期钩子作为扩展方式
- 在目标表单中编写脚本,并验证扩展逻辑不会影响原有表单流程
开发流程
配置入口操作步骤
进入管理后台,在左侧搜索框输入“智能表单”。
点击搜索结果中的“智能表单”,进入智能表单列表。
在列表中点击已有表单的“编辑”,或点击右上角“新建”创建智能表单。
进入智能表单编辑页后,打开右侧“全局设置”。
在“JS脚本”配置项下点击“编辑脚本”。
在“JS脚本”弹窗中输入或粘贴脚本内容。
点击弹窗中的“确定”,将脚本配置回写到表单配置中。
回到表单编辑页后,点击页面顶部“保存”,保存当前智能表单。
操作注意:
- 脚本保存后,需要通过预览或真实填写页验证执行效果。
- 如果脚本中使用字段选择器,优先使用字段 API Name 对应的
data-key。 - 如果脚本使用生命周期钩子,确认当前运行环境已经包含
window.smartForm.register。 - 弹窗“确定”只表示脚本内容写回当前编辑态;最终是否持久化,以表单编辑页顶部“保存”为准。
明确增强目标
在开始之前,先确认以下问题:
- 要调整的是字段、按钮、页面展示还是提交流程
- 逻辑依赖的是页面 DOM、字段值、提交数据还是接口响应
- 逻辑应该在页面渲染后、提交前、提交后还是页面销毁时执行
编写增强逻辑
开发时,优先关注以下内容:
- 使用稳定的字段 API Name 定位字段,避免依赖易变化的 DOM 层级
- 根据目标行为选择正确的执行时机和生命周期钩子
- 保持条件判断和业务规则清晰,避免影响系统原有校验与提交逻辑
- 对异步渲染元素、空值和脚本异常做好必要处理
调试与验证
- 在测试表单中验证页面加载、字段交互和提交主流程。
- 同时覆盖系统校验失败、提交成功、提交失败和页面销毁等相关场景。
- 页面升级后 DOM 可能变化,脚本上线前需要重新验证依赖的元素和事件。
- 脚本报错不会阻断表单基础展示,但错误脚本本身不会继续生效。
- 不要在脚本中保存账号、密码或密钥,也不要把客户填写内容发送到未经确认的第三方地址。
智能表单关注点
字段与数据增强
适合:
- 修改字段值、文案、样式或显隐状态
- 监听字段输入、点击和选择事件
- 根据其他字段值处理简单联动
- 在提交前校验或调整本次提交数据
重点关注:
- 优先使用字段的
data-key定位元素 - 页面字段值与本次提交数据的修改方式不同
- 目标元素是否由异步交互触发后才渲染
按钮与交互增强
提交按钮可以通过 name="button_submit" 查找:
var submitButton = document.querySelector('[name="button_submit"]');重置按钮可以通过 name="button_reset" 查找:
var resetButton = document.querySelector('[name="button_reset"]');监听提交按钮点击:
var submitButton = document.querySelector('[name="button_submit"]');
if (submitButton) {
submitButton.addEventListener('click', function () {
console.log('用户点击了提交按钮');
});
}当前填写页不是标准原生 <form> 提交。如需在提交前校验、阻断提交或修改提交数据,优先使用 beforeSubmit 生命周期。
监听提交按钮 click 只适合记录用户点击、修改页面提示等轻量场景。点击提交按钮不等于最终提交成功,系统校验、验证码、附件上传等流程仍可能阻断提交。
如果只是记录或提示,不要调用 preventDefault()、stopPropagation(),避免影响系统校验和提交。
提交前提示示例:
var submitButton = document.querySelector('[name="button_submit"]');
if (submitButton) {
submitButton.addEventListener('click', function () {
var telInput = document.querySelector('[data-key="tel"] input');
if (telInput && telInput.value) {
console.log('即将提交,手机号:', telInput.value);
}
});
}重点关注:
- 按钮事件是否会影响系统原有校验和提交
- 点击按钮与最终提交成功是两个不同的状态
- 交互发生后是否向用户提供了明确反馈
页面行为增强
适合:
- 在表单基础 DOM 渲染后执行初始化逻辑
- 在提交成功或失败后执行通知逻辑
- 在页面销毁时清理脚本绑定的事件或定时器
重点关注:
- 扩展逻辑的触发时机是否正确
- 页面行为是否影响原有表单稳定性
- 是否需要处理异步渲染、空值和异常场景
附录
Part 1:扩展能力说明
执行时机
脚本会在表单基础 DOM 渲染完成后执行。
如果目标元素是异步渲染的,例如地址、定位、附件、弹窗类组件,可以用等待函数:
(function () {
function waitFor(selector, callback, timeout) {
var start = Date.now();
var timer = setInterval(function () {
var el = document.querySelector(selector);
if (el) {
clearInterval(timer);
callback(el);
} else if (Date.now() - start > (timeout || 5000)) {
clearInterval(timer);
}
}, 100);
}
waitFor('[data-key="tel"] input', function (input) {
input.placeholder = '请输入手机号';
});
})();生命周期钩子 API
智能表单填写页会暴露全局对象 window.smartForm,可以通过 register 注册表单生命周期钩子:
window.smartForm.register('my-hook', {
beforeSubmit: function (ctx) {},
submitSuccess: function (ctx) {},
submitError: function (ctx) {},
destroy: function (ctx) {}
});register(id, hooks) 规则:
id必须是非空字符串。- 同一个
id重复注册时,后一次会覆盖前一次,避免脚本重复执行后重复绑定。 hooks只识别beforeSubmit、submitSuccess、submitError、destroy。register会返回取消注册函数。- 钩子函数内部报错会被隔离,不会阻断表单基础展示、系统校验、提交、支付和错误提示流程。
取消注册示例:
var unregister = window.smartForm.register('my-hook', {
submitSuccess: function () {
console.log('提交成功');
}
});
unregister();不提供 ready
脚本本身已经在表单主体 DOM 渲染完成后执行,因此不再单独提供 ready 生命周期。
如果只是做字段 DOM 初始化、修改提示文案、绑定输入事件,可以直接写在脚本顶层或自执行函数中:
(function () {
var nameField = document.querySelector('[data-key="name"]');
var nameInput = nameField && nameField.querySelector('input');
if (nameInput) {
nameInput.placeholder = '请输入主属性';
}
})();如果目标元素是弹窗、附件预览、定位地图等交互后或异步后才出现的 DOM,仍然建议配合 waitFor 查询。
生命周期触发时机
| 生命周期 | 触发时机 | 是否可阻断 |
|---|---|---|
beforeSubmit | 系统表单校验通过后、提交接口请求前触发 | 是 |
submitSuccess | 提交接口返回 code === 0 后触发 | 否 |
submitError | 提交接口返回非 0 code,或提交请求失败后触发 | 否 |
destroy | 表单页面卸载或重置销毁时触发 | 否 |
说明:
beforeSubmit在系统必填、格式等校验通过后触发。若系统校验未通过,例如必填字段为空,beforeSubmit不会执行。submitSuccess/submitError是通知型钩子,不改变系统原有成功页、支付跳转、错误提示和验证码刷新逻辑。destroy适合清理脚本自己绑定在window/document上的事件、定时器或外部状态。
ctx 上下文
每个生命周期函数都会收到 ctx 对象。ctx 是稳定的脚本上下文,不暴露 React 组件实例,也不暴露完整 rc-form 对象。
常用字段:
| 字段 | 说明 |
|---|---|
ctx.cardId | 当前表单卡片 ID |
ctx.root | 当前表单根 DOM |
ctx.describe | 当前表单描述对象 |
ctx.fields | 字段元数据 map |
ctx.components | 当前渲染字段列表 |
ctx.buttons | 当前按钮列表 |
ctx.data | 当前生命周期相关数据;beforeSubmit 中是待提交数据 |
ctx.response | submitSuccess / submitError 中的接口响应 |
常用方法:
| 方法 | 说明 |
|---|---|
ctx.getValue(apiName) | 读取字段当前值 |
ctx.setValue(apiName, value) | 设置字段当前值 |
ctx.getValues() | 读取全部字段当前值 |
ctx.getField(apiName) | 获取字段元数据 |
ctx.getFieldElement(apiName) | 通过 data-key 获取字段外层 DOM |
脚本顶层初始化逻辑没有 ctx 入参,如需操作 DOM 可直接使用标准 DOM API。
beforeSubmit
beforeSubmit 适合做提交前同步判断、阻断提交、修改本次提交数据。
阻断提交支持两种返回值:
return false;或:
return {
cancel: true,
message: '请先填写主属性'
};带 message 时,页面会展示该提示。
beforeSubmit 第一版只支持同步返回,不会等待 Promise、setTimeout 或异步接口结果。需要远程校验时,不要把接口请求写成异步返回后再决定是否提交;这类能力需要另行设计提交等待、loading、超时和重复提交处理。
提交前校验并修改提交数据示例:
window.smartForm.register('before-submit-demo', {
beforeSubmit: function (ctx) {
if (!ctx.data.name) {
return {
cancel: true,
message: '请先填写主属性'
};
}
ctx.data.name = String(ctx.data.name).trim() + '-hook';
}
});如果只是修改页面上的字段值,可以使用 ctx.setValue(apiName, value);如果要修改本次提交到接口的数据,建议直接修改 ctx.data。
submitSuccess、submitError、destroy
提交成功、提交失败、销毁清理示例:
window.smartForm.register('submit-result-demo', {
submitSuccess: function (ctx) {
console.log('提交成功:', ctx.response);
},
submitError: function (ctx) {
console.log('提交失败:', ctx.response);
},
destroy: function () {
console.log('表单已销毁,清理脚本事件或定时器');
}
});submitError 包括业务错误和请求失败两类场景。业务错误时通常可以拿到接口响应;请求失败时 ctx.response 可能为空或只包含底层错误信息,脚本需要自行做空值判断。
完整验证脚本
下面脚本可用于验证注册、提交前修改数据、提交成功、提交失败和销毁回调。示例字段 API Name 使用 name,实际使用时请替换成当前表单真实字段的 data-key。
(function () {
var HOOK_ID = 'verify-smart-form-hooks';
function log() {
var args = Array.prototype.slice.call(arguments);
console.log.apply(console, ['[smartForm hooks]'].concat(args));
}
var field = document.querySelector('[data-key="name"]');
log('top-level DOM check [data-key="name"]:', field);
window.smartForm.register(HOOK_ID, {
beforeSubmit: function (ctx) {
log('beforeSubmit data before:', JSON.stringify(ctx.data));
if (!ctx.data.name) {
return {
cancel: true,
message: '验证脚本:主属性为空,已阻断提交'
};
}
ctx.data.name = String(ctx.data.name).trim() + '-hook';
log('beforeSubmit data after:', JSON.stringify(ctx.data));
},
submitSuccess: function (ctx) {
log('submitSuccess response:', ctx.response);
},
submitError: function (ctx) {
log('submitError response:', ctx.response);
},
destroy: function () {
log('destroy called');
}
});
})();预期结果:
- 页面加载后能打印
[data-key="name"]对应字段 DOM,说明脚本在表单主体 DOM 渲染后执行。 - 必填字段为空时,先触发系统必填校验,
beforeSubmit不执行。 - 必填字段填写后提交,会触发
beforeSubmit,并将提交数据追加-hook。 - 提交接口返回
code === 0时触发submitSuccess。 - 提交接口返回非
0或请求失败时触发submitError。
字段选择器
每个字段外层会提供以下 data-* 属性:
<div
data-key="字段 API Name"
data-label="字段显示名"
data-type="字段类型"
data-required="true/false"
data-readonly="true/false"
>
</div>推荐优先级:
- 推荐:
[data-key="tel"] - 可用:
[data-type="text"]、[data-required="true"] - 谨慎:
[data-label="姓名"],字段改名或多语言会影响 - 不推荐:依赖复杂 DOM 层级,例如
div > div > input
示例:
var telField = document.querySelector('[data-key="tel"]');
var telInput = telField && telField.querySelector('input');常见字段操作
隐藏字段:
var company = document.querySelector('[data-key="company"]');
if (company) {
company.style.display = 'none';
}修改字段标题样式:
var nameField = document.querySelector('[data-key="name"]');
if (nameField) {
var label = nameField.querySelector('.input-label');
if (label) {
label.style.color = '#d93026';
}
}监听输入:
var telInput = document.querySelector('[data-key="tel"] input');
if (telInput) {
telInput.addEventListener('input', function () {
console.log('当前手机号:', telInput.value);
});
}输入框赋值:
function setInputValue(input, value) {
var descriptor = Object.getOwnPropertyDescriptor(input.__proto__, 'value');
descriptor.set.call(input, value);
input.dispatchEvent(new Event('input', { bubbles: true }));
input.dispatchEvent(new Event('change', { bubbles: true }));
}
var nameInput = document.querySelector('[data-key="name"] input');
if (nameInput) {
setInputValue(nameInput, '张三');
}字段联动示例
根据电话字段是否填写,控制公司字段显示:
(function () {
var telInput = document.querySelector('[data-key="tel"] input');
var companyField = document.querySelector('[data-key="company"]');
if (!telInput || !companyField) {
return;
}
function updateCompanyVisible() {
companyField.style.display = telInput.value ? '' : 'none';
}
telInput.addEventListener('input', updateCompanyVisible);
updateCompanyVisible();
})();根据来源字段变更,修改销售线索详情提示:
(function () {
var sourceField = document.querySelector('[data-key="source"]');
var remarkTextarea = document.querySelector('[data-key="remark"] textarea');
if (!sourceField || !remarkTextarea) {
return;
}
sourceField.addEventListener('click', function () {
remarkTextarea.placeholder = '请补充销售线索详情';
});
})();Part 2:CSP 安全策略说明
智能表单填写页会通过 meta 标签配置内容安全策略:
<meta http-equiv="Content-Security-Policy" content="default-src 'self'; script-src 'self' 'unsafe-eval' https://webapi.amap.com; connect-src 'self' https://webapi.amap.com; img-src 'self' data: https:; style-src 'self' 'unsafe-inline';">该策略主要限制:
- 默认资源只允许加载当前站点资源:
default-src 'self' - 脚本只允许当前站点脚本和高德地图脚本:
script-src 'self' 'unsafe-eval' https://webapi.amap.com - 网络请求只允许请求当前站点和高德地图接口:
connect-src 'self' https://webapi.amap.com - 图片允许当前站点、
data:图片和 HTTPS 图片:img-src 'self' data: https: - 样式允许当前站点样式和行内样式:
style-src 'self' 'unsafe-inline'
对用户脚本的影响:
- 可以操作当前页面 DOM。
- 不允许通过
fetch、XMLHttpRequest等方式请求未授权第三方接口。 - 不允许加载未授权第三方 JS 文件。
- 可以设置行内样式,例如
element.style.display = 'none'。 - 可以使用 HTTPS 图片或
data:图片。 - 如果脚本触发了 CSP 限制,浏览器会拦截对应行为。
