# 一、接入配置

# 环境配置

在域名不同的情况下,我们可能会遇到iframe页面嵌入存在跨域的问题或者是嵌入的页面打不开的情况。此时第三方页面需要事先配置下nginx。(参考资料可以参考文末)

这个header主要用来配置哪些网站可以通过frame来加载资源 Access-Control-Allow-Origin 配置iframe页面允许父页面跨域访问

add_header X-Frame-Options "Allow-From domain.com"; 
add_header Access-Control-Allow-Origin: <具体域名>

# 外框配置

固定外框样式是手写样式,后续不会维护,为了满足客户样式多样化的需求,三方表单推荐使用节点权限绑定外框,建议三方表单自定义节点权限和流程外框。 1747015835362.png

# 二、前端接入过程

前端接入通信整体过程如下: 1747015878946.jpg

外框和中间通信建立成功的前置条件是需要页面加载完成且接入相关的代码来标识通信过程建立完成。

# 初始化建立通信

审批应用前端页面采用iframe的方式加载三方表单, 三方表单加载完成后通知审批外框,此操作必须,否则审批外框提交时,不会与三方表单进行交互。

❗ 注意:下面监听代码一定要在index.html的head标签下的script标签中增加这一行代码,最好是第一个script标签,这样能尽快建立起消息通知,保证页面通信正常。

在页面加载之前就发送这个消息,能避免页面未加载完成点击了按钮的情况。这个消息表示双方建立起了通信,此时点击按钮会发消息给中间表单,中间表单如果没有完全加载完成时,点击按钮就不会有效果。能避免页面还在加载中,点了提交按钮的情况。

window.parent?.postMessage({ 
    hasListener: true, 
}, '*');

# 消息监听

# 第三方监听外框的消息

点击流程操作按钮后,通过postMessage与监听message事件实现页面间通信。外框发送消息至表单页面,第三方监听外框消息。

第三方通过如下方式监听来自外框的消息:

注:后续文档介绍到的消息内容都是这里监听到的msgData。

window.addEventListener('message', (e: any) => {
  const msgData = e.data;
});

第三方接收到的数据msgData对象内容如下:

参数名称 参数类型 参数说明
needValidate boolean 是否需要校验表单数据 (BPM默认行为的校验逻辑,校验逻辑由第三方自行判断)
messageType string 消息类型,例如:GET_FORM_DATA流程行为点击获取表单数据
submitType string 当前流程行为具体操作类型,例如:
SEND:发送,
url string 父页面地址
isFromBPM boolean 标记是否为审批外框触发的消息。true:表示审批外框触发的消息
iframeId string 当前页面唯一id
{
  needValidate: true,
  messageType: 'GET_FORM_DATA',
  submitType: 'SEND',
  url: 'http://xxx.xxx.com',
  isFromBPM: true,
  iframeId: 'xxx',
}

注:当messageType为**GET_FORM_DATA**时,需要第三方业务系统提交表单数据(双方约定:哪些表单数据会对流程的流转产生影响,主要提交的是这部分数据;如果表单内的数据对流程全无影响,则只需传

<font style="background-color:#FBDE28;">formRecordId</font>即可)

建议:业务系统表单页面监听到messageType为‘GET_FORM_DATA’的消息时,进行表单数据保存操作。

# 加签并提交的消息内容

注意:GIVE、NODE_ADD类型如果后台系统参数配置为加签并提交的配置,点击按钮会先传递一个GIVE或NODE_ADD的消息到中间表单,中间表单回传消息后,父页面会再次传递提交动作的消息。

再次提交动作的消息内容:

{
  // 根据审批类型submitType取值:SUBMIT、AGREE、SEND
  submitType: "SUBMIT" 
  // 消息类型
  messageType: "GET_FORM_DATA",
  bpmInfo: {
    // 区分加签并提交的动作(可选取值判断)
    // actionType取值:SUBMIT_SUBMIT、AGREE_SUBMIT、SEND_SUBMIT
    actionType: 'SUBMIT_SUBMIT', //(2.6以后版本建议替换成extAction区分判断, 可根据submitType取具体的值)
    extAction: 'NODE_ADD_SUBMIT' // NODE_ADD_SUBMIT(加签并提交)、GIVE_SUBMIT(会签并提交)
  }
}

加签并提交消息通信过程 1747016120462.jpg

# 流程操作处理行为执行之后的消息

目前支持的操作功能:发送、提交、同意、已阅。这些动作在执行成功、失败、弹出选下一节点的情况下会接收到不同类型的消息,业务方根据自己的需求选择性接入。

# 监听处理成功

流程引擎提交完成后会发送成功和失败的消息

第三方监听接收到外框流程处理成功传来的消息内容如下:

{
  needValidate: false; //是否需要验证表单内容,为true则需要验证表单  
  submitType: 'SUNMIT',
  messageType: 'PROCESS_ACTION_SUCCESS', 
  url: string, //父页面地址,放在postMessage第二个参数内  
  isFromBPM: true, //审批外框消息标识
}
# 监听失败的消息

第三方监听接收到外框流程处理失败传来的消息内容如下:

{
  needValidate: false; //是否需要验证表单内容,为true则需要验证表单  
  submitType: 'SUNMIT',
  messageType: 'PROCESS_ACTION_FAIL', 
  url: string, //父页面地址,放在postMessage第二个参数内  
  isFromBPM: true //审批外框消息标识
}
# 下节点弹框点取消按钮的回调

第三方监听接收到外框流程下节点选择取消的消息内容如下:

{
  needValidate: false; //是否需要验证表单内容,为true则需要验证表单  
  submitType: 'SUNMIT',
  messageType: 'PROCESS_ACTION_CANCEL', 
  url: string, //父页面地址,放在postMessage第二个参数内  
  isFromBPM: true //审批外框消息标识
}

# 消息发送(第三方通知外框)

三方表单监听到消息以后,需要自行验证表单内容 、数据保存,并且验证成功后将表单内容以json对象的形式回传给审批应用页面,如果表单验证失败,则自行进行错误提示,并通知审批应用页面。

参数名称 参数类型 参数说明
success boolean 第三方行为是否执行成功的标识,只有成功时,后续消息通知给流程外框才会继续往下执行
submitType string 回传当前流程行为具体操作类型,例如: SEND:发送,
messageType string 消息类型,例如:GET_FORM_DATA流程行为点击获取表单数据
formData JSON 1.流程流转需要的单据数据
2. formRecordId,单据数据id 必填
iframeId string 当前页面唯一id

代码示例:

window.parent?.postMessage({
    success: true, // 第三方自己业务执行成功的标识传递给外框
    submitType: 'SEND',//将此字段值回传
    messageType: 'GET_FORM_DATA', // 回传点击流程操作行为的消息
    //表单数据放这里
    formData: {
      //## 必传,统一流程平台维护流程实例ID和业务系统数据ID的关联关系
      formRecordId: 'xxxxxxxxxx',//表单记录id(Long) 最长32位
      //下面内容为表单数据
      applyNo: 'jaylu',
      applyName: '卢光杰',
      applyDate: '2022-05-12',
    },
    iframeId: 'xxx', // 上面监听到外框消息传递的唯一id
}, e.data.url); // 上面监听到外框消息传递的url

# 三、完整示例

第三方业务系统监听示例及回传消息示例:

demo.js (opens new window)

const validateForm = ()=>  {
  // 伪代码验证表单必填
  const formData = form.getFiledsValue();
  // 如果name值为空,则需要必填
  if(!formData?.name) {
    // 校验不通过
    return false;
  }
  // 校验通过
  return true;
}

window.addEventListener('message', (e: any) => {
  if(e.data.messageType ==='GET_FORM_DATA') {
    //监听了流程的发送,处理等操作,操作完成后,需要发送postMessage通知父页面
    if(e.data.submitType === 'SEND' || e.data.submitType === 'SUBMIT'){
      // 校验是否通过
      const isValid = validateForm();
      // 校验失败回传消息
      if(!isValid) {
        window.parent.postMessage({
          messageType: 'VALIDATE_FAIL',
          submitType: e.data.submitType, // 当前点击按钮的行为类型
        },'*');
      }
      //三方表单自己逻辑处理完后,通过以下方式回传父页面参数
      window.parent?.postMessage({
        success: true, //表单数据验证成功或不需要验证时传true,否则传false
        submitType: e.data.submitType,//将此字段值回传
        messageType: 'GET_FORM_DATA', //获取表单数据消息
        //表单数据放这里
        formData: {
          //## 必传,统一流程平台维护流程实例ID和业务系统数据ID的关联关系
          formRecordId: 'xxxxxxxxxx',//表单记录id(Long) 
          //下面内容为表单数据
          applyNo: 'jaylu',
          applyName: '卢光杰',
          applyDate: '2022-05-12',
        }
      }, e.data.url);
    }else{
      //其他什么都不做的情况下也原样把消息通知到父页面,表单数据可以拿到就传,取不到就不传
      // 注意:一定要回传消息,不然会出现按钮点击无效果的情况
      window.parent?.postMessage({
        success: true, //传true
        submitType: e.data.submitType,//将此字段值回传
        messageType: e.data.messageType, //获取表单数据消息
        //表单数据放这里
        formData: {
          //## 必传,统一流程平台维护流程实例ID和业务系统数据ID的关联关系
          formRecordId: 'xxxxxxxxxx',//表单记录id(Long) 
          .......
        }
      }, e.data.url);
    }
  }
});

简单的原生js示例:

示例 (opens new window)

# 四、流程按钮加锁(防重复点击)

功能效果:为了防重复点击,避免流程操作功能被执行了两次

默认三方表单是没有启用按钮点击加锁的状态,所以需要第第三方在页面初始化完成时自行启用加锁。

加锁含义:点击按钮后,传递消息给第三方表单,第三方表单未回传消息过来时,此时按钮不可点击。避免重复点击按钮的情况。

❗ 下面加锁的代码消息传递一定要在页面完全加载完后再发消息到外框。

启用加锁的消息:

window.parent.postMessage({
  enableLock: true // true表示启用,false表示不启用。
},'*');

请注意以下关键点:在表单包含必填项验证的情况下,若第三方验证服务未能通过校验或接口报错等错误场景,则必须回传失败的消息以解除锁定状态。否则,系统将保持锁定状态,导致用户点击按钮时无任何响应

校验失败回传消息:

window.parent.postMessage({
  messageType: 'VALIDATE_FAIL',
  submitType: 'SEND', // 当前点击按钮的行为类型
},'*');

按钮加锁及解锁过程: 1747016735995.jpg

# 五、第三方iframe页面

业务方可以直接通过url获取到流程相关的信息,如下:

# 从url上获取流程相关信息

参数属性 说明
affairId 事项id
caseId 实例id
formRecordId 表单数据id (字符串null时表示无值)
state 事项状态
editable 页面编辑状态(只有当值为字符串true时才可编辑,页面上面无这个属性或等于其他值时都不可编辑)。
false或属性不存在的情况表示不可编辑状态。
isStart 是否是开始节点
locale 当前国际化语种
formPermissionCode 三方表单操作权限code
permissionType 当前节点的节点权限类型
SEND:发送
APPROVE:审批
COLLABORATION:协同
NOTICE:只会
JOINTLY:会签
activityCode 节点编码
activityGroupId 动态节点分组id
extendParam 第三方自定义的扩展参数

如何查看iframe上具体的url信息: 通过chrome检查元素可以查看到iframe的src属性。在表单页面内部可以通过获取url信息获取到对应参数。

# 六、流程扩展功能

流程按钮操作相应的扩展功能,如:关闭页面、刷新页面等等

# 阻止流程操作默认关闭逻辑

提交的时候选人选分支弹框点确定后,不立即关闭处理页面,由三方系统控制什么时候关闭

三方系统保存完数据后通知流程引擎需要增加不关闭页面的参数

window.parent?.postMessage({
    success: true,
    submitType: 'SUNMIT',
    formData: values,
    messageType: 'GET_FORM_DATA', 
    actionInfo: {
      // 阻止页面关闭
      preventClose: true
    }
  },
  * //父页面地址
);

# 关闭页面

三方系统通知流程引擎关闭处理页面

window.parent?.postMessage({
  messageType: 'CLOSE_WINDOW',
}, '*');

# 刷新页面

三方系统通知流程引擎刷新页面

window.parent?.postMessage({
  messageType: 'REFRESH',
}, '*');

# 动态更改iframe高度

业务系统页面加载完成后发送postMessage更新iframe的高度

window.parent?.postMessage({
  messageType: 'CHANGE_IFRAME_HEIGHT', // 更改iframe高度的消息类型
  height: 600, // 指定iframe高度,数字类型
}, '*');

# 三方系统隐藏外框工具栏

window.parent.postMessage({
  toolbarState: 'hide', //隐藏固定的工具栏流程按钮(隐藏用'hide',展示用'show')
  messageType: 'CHANGE_INPARAMS', // 消息类型,约定使用CHANGE_INPARAMS 更新页面入参
  code: 'toolbarState', //设计态定义的页面入参编码
  value: 'hide', // 需要写入的值,需要参照表单式的配置,请使用文本格式(隐藏用'hide',展示用'show')
},'*',);

# 三方系统通知父页面loading

window.parent.postMessage({
  messageType: 'SHOW_LOADING',
  showLoading: true // true表示loading, false取消loading
},'*');

# 三方系统通知父页面打开新的url

window.parent.postMessage({
  messageType: 'OPEN_URL', // 必传
  url: 'https://xxx.xxx.com', // 必传
  isHref: false, // 默认false, 是否是浏览器原生的window.location.href(可不传)
}, '*');

# 七、流程外框添加交互(选择性接入)

如果有对融合流程有如下需求,可以根据需求进行选择适配。支持如下功能:

  1. 插入按钮功能
  2. 控制显隐外层蒙层
  3. 控制显隐外层外框区域

首先与外框交互需要先引用指定的js文件,然后调用js中的方法选择所需要的功能

引入下方js资源:

udc-sdk-esm.zip (opens new window)

# 第三方自定义按钮(插入按钮)

三方表单自定义按钮代码如下:

// 根据上面提供的js包在项目中放入的位置自行调整路径

import{ready} from './udc-sdk-esm';


ready({
  // 固定值,不允许修改
  mode: 'iframe',
  //固定值,不允许修改
  tenant: 'CMI',  
},(instance) => {
  //  调用iframeSdk为CMI定制API insertBtnForToolbar, 不设置id,默认为当前页面第一个iframe控件插入按钮
  instance.getCustomApi().insertBtnForToolbar({
    // 按钮插入位置
    position: 1,
    btns: [{
      // 按钮名称
      name: '红色按钮',
      // 按钮类型 primary | ghost | dashed | link | text | default
      buttonType: 'primary',
      customEvents: [
        {
          type: 'click',
          // 按钮点击执行的方法
          func: () => {
            alert('你点击了红色按钮')
          }
        }
      ]
    }]
  });
});

注意:插入的按钮是批量插入的按钮,插入的安置目前仅支持批量按钮插入指定的位置,批量按钮中单独的按钮不能指定位置插入。

# 控制显隐外层蒙层

changeLayoutMaskStatus(params)

params入参:

status,Boolean类型,控制外层蒙层显示状态,true为显示,false为不显示

bgColor,string类型,控制iframe内部背景颜色,如不传则默认为白色('#ffffff')

入参示例:

{
  status: true,  //状态值
  bgColor: '#f00'  //背景颜色值,可不传
}

控制蒙层显隐需要在ready回调或者ready完成后调用,这样发出的蒙层消息才会生效。比如我们可以先把之前ready返回中的instance做个存储后,在ready函数之外再调用。

instance?.changeLayoutMaskStatus({
  status: true,
  bgColor: '#f7f7f7', //可不传,根据实际情况处理(只支持这种格式的色值,不支持rgba)
});

# 控制显隐外层外框区域

changeLayoutAreaStatus(params)

params入参:

hiddenArea,Array类型,控制外层蒙层哪些区域需要隐藏,主要的值有:

'header',
'left',
'right',
'footer',

入参示例:

instance?.changeLayoutAreaStatus({
  hiddenArea: ['header','left','right','footer'], //需要隐藏的外框区域的值
});

注意:控制显隐外框区域需要在ready回调或者ready完成后调用,这样发出的外框区域显隐消息才会生效。

另外,介于显隐蒙层与显隐区域需要同时调用情况,可一次性调用两个方法。

示例:

instance.changeLayoutMaskStatus({
  status: true
});

instance.changeLayoutAreaStatus({
  hiddenArea: ['header', 'footer']
});

# 八、消息通信参数查询表

参数名称 参数类型 参数说明
success boolean 第三方行为是否执行成功的标识,只有成功时,后续消息通知给流程外框才会继续往下执行
submitType string 处理前传过来的操作类型: SEND:发送, SUBMIT :提交 , READ:已阅, TEMPORARY:暂存, DRAFT_HANDLE:保存 BACK:回退, UNDO:撤销, TERMINATION:终止 , TRANSFER:移交, AFFAIR_CONSULT:咨询, RETRIEVE:取回, NOTICE:知会,GIVE:会签, _PROCESS_SHOW:查看流程图 DELETE_DRAFT: 删除待发, MULTI_COOPERATE:多人填写, PAUSE: 挂起, CANCEL_PAUSE: 取消挂起, TRACK:关注
messageType string GET_FORM_DATA: 获取表单数据消息; PROCESS_ACTION_SUCCESS:流程处理成功的消息PROCESS_ACTION_FAIL: 流程处理失败的消息 PROCESS_ACTION_CANCEL:下一节点弹窗取消的消息 CLOSE_WINDOW:关闭页面CHANGE_IFRAME_HEIGHT: 动态更改iframe高度 SHOW_LOADING:显示loading效果 CHANGE_INPARAMS: 页面入参变量; OPEN_URL: 打开指定url
formData JSON 1.流程流转需要的单据数据 2. formRecordId,单据数据id 必填

# 九、常见问题

常见问题总结 (opens new window)

# 十、运行效果

融合流程demo (opens new window)

# 十一、参考资料

X-Frame-Options参考资料:

https://www.cnblogs.com/goloving/p/14891370.html (opens new window)

https://stackoverflow.com/questions/47405597/x-frame-options-in-nginx-to-allow-all-domains (opens new window)

https://juejin.cn/post/6990302070695788552 (opens new window)

Access-Control-Allow-Origin参考资料:

https://developer.mozilla.org/zh-CN/docs/Web/HTTP/Headers/Access-Control-Allow-Origin (opens new window)

https://blog.csdn.net/cw_hello1/article/details/121540023 (opens new window)

编撰人:xuxyyf、xusx、wxju