# IFrame控件设计指引

# 概述

# 为什么需要IFrame控件

如果表单上需要显示比较复杂的UI界面,在平台中找不到合适的通用表单控件使用时,可以考虑使用IFrame控件,开发者可以自行定制控件的UI以及传递到表单中的数据。

# 数据类型

IFrame控件只支持一种数据类型anyURI,也就是一个链接地址,这个链接地址对应的就是开发出来的控件部署地址。

# 权限控制

表单字段在当前步骤是否可写可通过infoplus.sdk.js (opens new window)中封装的方法获取,可根据是否可写决定页面里交互规则,后面会有详述


以下介绍开发制作一个IFrame控件的步骤以及注意事项

# 创建主窗口

制作一个IFrame控件,至少需要完成一个主窗口页面,根据需求可能还需要完成一个弹出(flyout)窗口页面。先介绍一下主窗口页面制作步骤。

# 引入前端sdk

因为主窗口页面需要与流程平台前端通信,所以首先需要在主窗口页面引入infoplus.sdk.js (opens new window),该sdk负责窗口间通信,以及封装了一些方法供开发者调用。

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>iframe控件主窗口</title>
    <script type="text/javascript" src="https://form.sjtu.edu.cn/infoplus/static/js/sdk/infoplus.sdk.js"></script>
</head>
<body>
    <div id="app"></div>
</body>
</html>
1
2
3
4
5
6
7
8
9
10
11
12
13

引入脚本后会在window对象上产生一个全局对象InfoPlusSDK.iframe,后序将调用这个对象里的一些方法完成各项功能。

# 初始化

在window的onload事件中需要初始化一下主窗口,调用InfoPlusSDK.iframe上的init方法即可。

window.onload = function () {
    InfoPlusSDK.iframe.init('https://form.sjtu.edu.cn');
};
1
2
3

init方法需要传递一个参数,内容是相应表单系统的访问地址,这个参数也可以填"*",但基于安全考虑并不推荐在生产环境中使用"*",如果只是为了本地开发方便那么可以使用参数*,但请记得在生产环境部署时调用参数改为"https://form.sjtu.edu.cn"(或您的表单系统访问地址)

window.onload = function () {
    InfoPlusSDK.iframe.init('*');
};
1
2
3

# 主窗口加载

将部署的(或开发中的)主窗口url地址赋值给表单中的IFrame控件(例如,您的主窗口页面部署地址是https://www.test.com/iframe.html,那么将这个地址赋值给表单中的IFrame控件作为初值,或者用动态公式的方式根据表单上用户的一些输入产生这个IFrame控件的值,比如'https://www.test.com/iframe.html?param='+$fieldParam),即可在表单中看到该主窗口页面,在表单中该页面会嵌在一个iframe元素中,请根据需求调节控件大小适配页面显示效果。表单渲染地址是https协议的,所以部署的主窗口url也需要是https协议,否则浏览器会报Mixed Content错误,拒绝加载,这也就意味着IFrame控件主窗口页面的生产环境必须使用https协议。然而,在本地开发时一般开发者都使用http协议,此时可以在https://form.dev.sjtu.edu.cn/上进行开发测试,并用http方式访问测试环境的服务器地址,如您需要开发IFrame控件并采用http方式测试调试,请联系leiyan。如果您开发时可以直接使用https地址测试主窗口页面,那么可以直接在正式环境https://form.sjtu.edu.cn开发即可。

# 实现UI

在表单中加载主窗口都成功后,您就可以按需求实现主窗口的UI了,这里需要注意的是:

  • 请调节表单上IFrame控件尺寸适配主窗口UI,并添加样式控制主窗口的滚动条尽量不显示,除非真的页面过大有滚动需求。
  • 字体设置推荐如下样式
html,
body {
    font-family: "PingFang SC", "Heiti SC", "Songti SC", "Microsoft YaHei", SimSun, SimHei, "WenQuanYi Micro Hei", sans-serif;
}
1
2
3
4
  • 请调节主窗口页面中的字体大小和表单字体适配,移动端注意字体需要稍大一些,按照交大的规范非移动端字体一般为11.5pt,移动端为12.5pt,可用navigator.userAgent是否包含TaskCenterApp来判断是否在交我办中,字体大小也可根据实际情况自行调节。

# 数据传递

# 表单到主窗口

IFrame控件的值就是其url,从表单往主窗口传值比较简单,只需要用动态公式给IFrame控件赋值,将表单上的值作为查询参数传入主窗口页面即可,IFrame控件动态值改变(不包括url上的hash部分)的同时,主窗口页面就会刷新,页面加载后可从window.location.search获取表单传递过来的参数。

# 主窗口到表单

从主窗口向表单传递数据是通过修改主窗口url上的hash值来实现的,即修改window.location.hash属性,修改hash属性页面是不会刷新的,InfoPlusSDK.iframe.init方法里会开启监听hash值的变化事件,在hash变化时将数据传回表单控件。 例如,现在有一个IFrame控件部署在地址https://www.test.com/iframe.html,在用户在该页面进行了一番交互后需要将交互结果{a:100,b:200}传递回表单,此时只需按如下代码给window.location.hash赋值,a,b值就会作为控件值的属性被传回。

window.location.hash = "#a=100&b=200";
1

假设表单中这个IFrame控件名称为fieldIframe,为了接收a,b值,表单中需要放两个hidden控件,比如fieldHiddenA,fieldHiddenB,可以通过配置fieldHiddenA动态公式为$fieldIframe.a,配置fieldHiddenB动态公式为$fieldIframe.b,就可以接收 这2个属性值了。

# 使用sdk传递数据

通过以上代码可完成数据从主窗口到表单的传递,另外infoplus.sdk.js (opens new window)也封装了两个方法setPropertyValue以及setData来传递数据,两者的区别是前者按单个属性传值,后者会把参数对象上每一个值类型的数据都传递回去。

# setPropertyValue
InfoPlusSDK.iframe.setPropertyValue("a",100);
InfoPlusSDK.iframe.setPropertyValue("b",200);
1
2
# setData
InfoPlusSDK.iframe.setData({ a:100, b:200 });
1

# 保持数据状态

根据实际需求,IFrame的主窗口有时会要保持上一次交互的结果数据,在用户存盘或者提交到下一步后主窗口界面能正常的按照上一次的用户选择进行显示。由于IFrame控件值就是其url地址,那么为保持数据状态,只需要从hash上取出上次交互结果数据,按照这个数据显示主窗口即可。当然,也有些场景下无须保持数据状态,那么不处理hash上的数据即可。

infoplus.sdk.js (opens new window)封装了从hash上获取数据的方法getProperty,可以在window.onload事件中执行完init方法后,调用getProperty方法获取初始的数据,然后可以根据初始数据修改界面。

// 假设原先iframe控件值为https://example.sjtu.edu.cn/iframe/index.html#key1=value1&key2=value2
// 可指定名称获取特定的属性值
let value = InfoPlusSDK.iframe.getProperty("key1");
// 可不指定名称获取所有属性值对象,该例子会返回{"key1":"value1","key2":"value2"}
let obj = InfoPlusSDK.iframe.getProperty();
1
2
3
4
5

# 创建flyout窗口

有时候表单上主窗口页面能占用的空间有限,主窗口可以只展现一个按钮或者链接,用户点击时再弹出一个窗口完成操作,这时需要在主窗口中调用InfoPlusSDK.iframe.openFlyout方法。

# openFlyout方法

openFlyout方法会在表单上弹出一个窗口,展现详细的操作界面,该方法支持一个选项参数,具体说明如下

参数名 类型 说明
src string flyout窗口的地址,生产环境也需要是https地址
width number 弹出窗口的宽度,移动端无效
height number 弹出窗口高度,移动端无效
shadow boolean 是否显示阴影
position string 窗口显示位置,支持top|bottom|left|right|center|modal。其中center表示当前窗口居中;top,bottom,left,right分别表示flyout窗口和IFrame控件的上边缘对齐,下边缘对齐,左边缘对齐,右边缘对齐。如果这个参数不写,系统会根据当前iframe控件的位置自动选择上边缘对齐或者下边缘对齐。 modal表示会显示一个模式窗口,其他position属性打开的flyout窗口在窗口外点击都会造成窗口关闭,只有modal模式是不会关闭的。
title string 弹出窗口标题,仅当position为modal时有效
onClose function 关闭时候的回调函数,回调函数支持参数data。在flyout窗口需要将自己关闭时候可以调用InfoPlusSDK.iframe.closeFlyout(data)关闭,该data可以是一个js对象字面量{},flyout窗口关闭后将调用onClose函数,data将成为其唯一参数,这个onClose里面可以对flyout窗口传递过来的数据进行处理。另外当主窗口中调用InfoPlusSDK.iframe.closeFlyout()时,以及用户通过交互关闭flyout窗口时(pc端用户点窗口右上方关闭图标,移动端用户点关闭按钮) ,该onClose方法都会回调,只是此时data为undefined,onClose函数中需要处理data这两种情况
示例 openFlyout
InfoPlusSDK.iframe.openFlyout({
    src: "https://www.test.com/flyout.html",
    width: 800,
    height: 600,
    title: "标题",
    position: "modal",
    onClose: (data) => {
        if (data) {
            InfoPlusSDK.iframe.setData(data);
        } else {
            // User close flyout, do something else...
        }
    },
});
1
2
3
4
5
6
7
8
9
10
11
12
13
14

# 初始化

接下来讲一下flyout窗口页面的创建,首先也需要初始化,和主窗口有所不同的是,flyout窗口初始化需要调用的是initFlyout方法。

window.onload = function () {
    InfoPlusSDK.iframe.initFlyout("https://form.sjtu.edu.cn");
};
1
2
3

# 数据传递

在flyout窗口向表单传递数据只能通过infoplus.sdk.js (opens new window),有三个方法可传递数据。其中setPropertyValue和setData和主窗口调用方法一致。

# setPropertyValue

以下代码执行后a,b都会出现在主窗口url的hash上

InfoPlusSDK.iframe.setPropertyValue("a",100);
InfoPlusSDK.iframe.setPropertyValue("b",200);
1
2

# setData

InfoPlusSDK.iframe.setData({ a:100, b:200 });
1

# closeFlyout

flyout窗口页中可以主动调用closeFlyout方法关闭弹出窗口,并且将当前页面用户操作产生的数据作为参数传递到主窗口openFlyout时选项中的onClose方法里,由主窗口的openFlyout时选项中的onClose方法决定是否将该数据传回表单。

InfoPlusSDK.iframe.closeFlyout({ a:100, b:200 });
1

# 关闭flyout窗口

关闭flyout窗口有三种方法

  • 主窗口通过调用InfoPlusSDK.iframe.closeFlyout()关闭,openFlyout中的onClose会回调,但是data为undefined
  • 用户通过交互关闭(pc端点击了右上方关闭按钮或者移动端点击了关闭按钮),openFlyout中的onClose会回调,但是data为undefined
  • flyout窗口通过调用InfoPlusSDK.iframe.closeFlyout(data)关闭,同时数据传回主窗口交由onClose方法处理

# 权限控制

表单上的iframe控件可能在某些步骤上是只读的,如果主窗口或flyout窗口中有用户交互操作,需要考虑当前控件字段是否有写权限,如果没写权限,需要在页面上屏蔽一些有写权限时候才该有的交互。在主窗口或者是flyout窗口中都可以调用InfoPlusSDK.iframe.isWritable()来获取是否有写权限。

const isWritable = InfoPlusSDK.iframe.isWritable();
1

# sdk方法列表

所有方法封装在InfoPlusSDK.iframe对象上

方法名 描述
init 初始化主窗口
initFlyout 初始化flyout窗口
openFlyout 打开flyout窗口
closeFlyout 关闭flyout窗口
setPropertyData 设置单个属性数据
setData 设置多个属性数据
isWritable 当前iframe控件是否具有写权限
getProperty 获取属性值