核心思路#
E9 表单提交前校验要从事件出发,不是从按钮出发。
用 WfForm.registerCheckEvent(WfForm.OPER_SUBMIT, fn) 拦截提交动作,在回调里异步调用接口。接口校验通过才调用 callback() 放行;接口没返回、返回结构不完整、返回业务失败,都不调用 callback(),提交就会被阻止。
关键链路是:
代码块 JS -> /api/esb/oa/execute -> 接口 -> 应用 -> 事件 -> data.responsebody -> 代码块 JS这里最容易踩坑的是:E9 环境下如果接口在 HTTP、OA 接口层、应用层或事件层直接失败,前端代码块通常拿不到正常的 data.responsebody。所以接口设计上要尽量“永远成功返回”,把业务失败写到 responsebody 里,再让代码块按业务结果拦截提交。
前端拦截模板#
jQuery(function () {
var submitting = false;
WfForm.registerCheckEvent(WfForm.OPER_SUBMIT, function (callback) {
if (submitting) {
WfForm.showMessage("正在校验中,请不要重复提交", 2, 5);
return;
}
submitting = true;
var payload = buildPayload();
if (!payload.lines.length) {
submitting = false;
WfForm.showMessage("没有可提交的数据", 2, 5);
return;
}
jQuery.ajax({
url: "/api/esb/oa/execute",
type: "POST",
dataType: "json",
timeout: 120000,
cache: false,
data: {
eventkey: "EVENT_KEY",
params: JSON.stringify(payload)
},
success: function (res) {
if (!hasValidResponseBody(res)) {
WfForm.showMessage("接口未返回有效校验结果,请联系管理员", 2, 8);
return;
}
if (hasBusinessError(res)) {
writeErrorToDetail(res);
WfForm.showMessage("校验未通过,请查看明细备注", 2, 8);
return;
}
WfForm.showMessage("校验通过,继续提交", 3, 5);
callback();
},
error: function () {
WfForm.showMessage("接口调用失败,请联系管理员", 2, 8);
},
complete: function () {
submitting = false;
}
});
});
});组装请求数据#
主表字段直接取值,明细表先取所有行标识,再拼成接口需要的数组。
function rowField(fieldId, rowIndex) {
return fieldId + "_" + rowIndex;
}
function getValue(fieldMark) {
return WfForm.getFieldValue(fieldMark) || "";
}
function getRows(detailMark) {
var rowStr = WfForm.getDetailAllRowIndexStr(detailMark) || "";
return rowStr ? rowStr.split(",").filter(Boolean) : [];
}
function buildPayload() {
var detailMark = "detail_1";
var mainType = WfForm.convertFieldNameToId("main_type");
var lineNo = WfForm.convertFieldNameToId("line_no", detailMark);
var itemCode = WfForm.convertFieldNameToId("item_code", detailMark);
var qty = WfForm.convertFieldNameToId("qty", detailMark);
var rows = getRows(detailMark);
var lines = [];
for (var i = 0; i < rows.length; i++) {
lines.push({
line: getValue(rowField(lineNo, rows[i])),
itemCode: getValue(rowField(itemCode, rows[i])),
qty: getValue(rowField(qty, rows[i]))
});
}
return {
type: getValue(mainType),
lines: lines
};
}解析响应结果#
代码块只认 data.responsebody。它可能是数组,也可能是单个对象,所以先统一成数组。
function normalizeList(value) {
if (!value) return [];
return jQuery.isArray(value) ? value : [value];
}
function getResponseBodyList(res) {
if (!res || !res.data || res.data.responsebody == null) {
return [];
}
return normalizeList(res.data.responsebody);
}
function hasValidResponseBody(res) {
return getResponseBodyList(res).length > 0;
}
function getBodyLine(item) {
return jQuery.trim(String(item.line || item.LINE_NO || item.WMS_LINE_ID || ""));
}
function getBodyMsg(item) {
return jQuery.trim(String(item.msg || item.message || item.PROCESS_MESSAGE || ""));
}
function getBodyStatus(item) {
return jQuery.trim(String(item.status || item.STATUS || "")).toUpperCase();
}
function isErrorItem(item) {
var msg = getBodyMsg(item);
var status = getBodyStatus(item);
return !!msg || (!!status && status !== "S");
}
function hasBusinessError(res) {
var list = getResponseBodyList(res);
for (var i = 0; i < list.length; i++) {
if (isErrorItem(list[i] || {})) {
return true;
}
}
return false;
}回写明细错误#
接口最好每条错误都带行号。前端用行号映射到当前 OA 明细行,把错误写回备注字段。
function writeErrorToDetail(res) {
var detailMark = "detail_1";
var lineNo = WfForm.convertFieldNameToId("line_no", detailMark);
var remark = WfForm.convertFieldNameToId("remark", detailMark);
var rows = getRows(detailMark);
var rowMap = {};
var data = {};
for (var i = 0; i < rows.length; i++) {
rowMap[getValue(rowField(lineNo, rows[i]))] = rows[i];
}
var list = getResponseBodyList(res);
for (var j = 0; j < list.length; j++) {
var item = list[j] || {};
if (!isErrorItem(item)) continue;
var rowIndex = rowMap[getBodyLine(item)];
if (rowIndex == null) continue;
data[rowField(remark, rowIndex)] = {
value: getBodyMsg(item) || "校验未通过"
};
}
WfForm.changeMoreField(data);
}接口返回约定#
不要让接口用异常、HTTP 500、事件执行失败来表达业务不通过。代码块需要的是一个能稳定进入 success 回调的结构。
推荐结构:
{
"code": "100",
"data": {
"responsebody": [
{
"line": "OA-202607030001-1",
"status": "S",
"msg": ""
},
{
"line": "OA-202607030001-2",
"status": "E",
"msg": "库存不足"
}
]
}
}判断规则:
- 能取到
data.responsebody,才算拿到接口校验结果。 status为空或S且没有msg,认为该行通过。status不是S,或存在msg,认为该行失败。- 任何结构缺失、接口错误、超时,都按失败处理,不放行提交。
注意点#
registerCheckEvent里是异步校验,只有业务通过时才能调用callback()。- 不要在接口失败的
error分支里尝试放行;此时响应链路已经不可靠。 - 接口、应用、事件三层都要尽量捕获异常并返回成功结构,把业务错误放进
responsebody。 - 明细错误要按稳定行号匹配,不要按数组顺序匹配,避免用户增删行后写错备注。
- 用
submitting这类标记防重复提交,防止用户连续点击。 - 写回明细后如依赖字段联动,可对目标字段补一次
WfForm.triggerFieldAllLinkage(fieldMark)。
