开发常见问题及解答

# 1 表单

## 1.1 表单的类型

说明：

表单类型分为普通，查询表单，普通（映射）表单。

(1)普通，常规的表单配置

(2)查询表单，主要为视图列表配置查询头，要与视图配置结合使用

(3)映射表单，可以调用应用软件在同一个数据库中不属于平台生成的数据表，通过字段的映射，对该表进行数据查询，修改，添加，删除操作

## 1.2 控件属性

### 1.2.1 刷新

说明：

控件刷新属性，是指当控件的内容发生改变时，触发包含当前控件的表单的刷新事件，以通知表单中其它具有重计算属性的控件进行刷新。

### 1.2.2 重计算

说明：

控件重计算属性，是指当控件配置选择重计算时，则包含当前控件的表单，其刷新事件被触发时，当前控件的脚本将被重新运算，以实现与最新数据同步

### 1.2.3 手机

说明：

控件手机属性，是指当控件配置选择手机时，通过 MYAPPS 智能客户端访问平台应用，则此控件将在智能客户端上显示，否则不显示。

### 1.2.4 值脚本

说明：

控件值脚本，是指可能通过脚本来决定控件的值。

示例 1：表单字段计算

/* 这里我们用单价与数量，计算金额为例进行讲解。 * 表单中有一个字段“单价”，数字类型；有一个字段“数量”，数字类型；有一个字段“金额”，数字类型。 * 单价和数量两个字段都选择了“刷新”属性，金额字段选择了“重计算”属性。 * 其中，金额字段写有值脚本。 */ var price = getItemValueAsDouble("单价"); var count = getItemValueAsDouble("数量"); price * count;

示例 2：汇总子表单字段值

/* 这里我们以汇总子表“金额”字段为例进行讲解。 * 主表有一字段为“总金额”，数字类型；子表有一字段为“金额”，数字类型。 * 子表包含元素控件选择了“刷新”属性，总金额字段选择了“重计算”属性。 * 其中，总金额字段写有值脚本。 */ //此函数有两个参数，第一个参数：子表单名称；第二个参数：汇总字段名称。 sumSubDocument("子表明细","金额");

示例 3：子表单记录计数

/*这里我们以获取子表记录数为例。*/ countSubDocument("子表单名称");

示例 4：自动编号

/*方法 1, 创建数据但未保存时，会出现断号的情况。生成 TLK0001、TLK0002、TLK0003......*/ var number = getItemValue("字段名"); if(number ==null || number.trim().length()<=0){ number = countNext2("TLK",false,false,false,4); } number; /*方法 2, 创建数据但未保存时，会出现断号的情况。生成 TLK201101050001、TLK201101050002、TLK201101050003......*/ var number = getItemValue("字段名"); if(number ==null || number.trim().length()<=0){ number = countNext2("TLK",true,true,true,4); } number;

### 1.2.5 选项脚本

说明：

控件选项脚本，是指通过脚本来创建出控件的可选择项，支持固定选项、动态生成选项。

示例：

/*方法 1:固定选项列表，选项名与选项值相同。*/ ";选项 1;选项 2;选项 3;选项 4"; /*方法 2:固定选项列表，选项名与选项值不相同。*/ var opts=createOptions(); opts.add("",""); opts.add("选项 1","1"); opts.add("选项 2","2"); opts.add("选项 3","3"); opts.add("选项 4","4"); opts; /*方法 3：动态生成选项列表，从基础表中获取数据动态生成选项。*/ //获取所有区域 var opts=createOptions(); opts.add("",""); var dql = "$formname='/片区信息表'"; var datas =queryByDQL(dql); if(datas!=null && datas.size()>0){ for(var iter = datas.iterator();iter.hasNext();){ var doc = iter.next(); opts.add(doc.getItemValueAsString("片区"),doc.getItemValueAsString('片区代码')); } } opts; /*方法 4：动态生成选项列表，从基础表中动态生成选项。 * 从基础表中动态生成选项列表。选项名与选项值取不同字段的值。 */ var dql = "$formname='/片区信息表'"; var key = new Array(); key[0] = "片区代码"; key[1] = "片区"; getOptionsByDQL(dql,key,true); /*方法 5：动态生成选项列表，从基础表中动态生成选项。 * 选项名与选项值取同一个字段的值。 */ var dql = "$formname='/片区信息表'"; getOptionsByDQL(dql,"片区",true);

### 1.2.6 校验脚本

说明：

控件校验脚本，是指通过脚本来对表单控件内容进行校验。

示例 1：判断是否为空

/*这里我们以校验“名称”字段是否填写内容为例。*/ var value = getItemValueAsString("名称"); var rtn = ""; if (value==null || value.trim().length()<=0){ rtn = "名称字段必须填写！"; } rtn;

示例 2：判断是否为数字

/*这里我们以校验“数量”字段填写内容是否为数字为例。*/ var value = getItemValueAsString("数量"); var rtn = ""; if (!isNumberText(value)){ rtn = "字段内容必需为数字！"; } rtn;

示例 3：判断是否为邮件地址

/*这里我们以校验“email”字段填写内容是否为 Email 地址为例。*/ var value = getItemValueAsString("email"); var rtn = ""; if (!isMailAddressText(value)){ rtn = "非法的 Email 地址！"; } rtn;

示例 4：判断是否为正数

/*这里我们以校验“数量”字段填写内容是否为正数为例。*/ var value = getItemValueAsDouble("数量"); var rtn = ""; if (!isPositive(value)){ rtn = "数量必需大于 0！"; } rtn;

示例 5：表单中的 2 个字段进行比较

/*这里我们以校验“总额”字段与“实际金额”字段比较为例。*/ var total_amount = getItemValueAsDouble("总额"); var amount = getItemValueAsDouble("实际金额"); var rtn = ""; if (total_amount< amount){ rtn = "实际金额不能大于总额！"; } rtn;

示例 6：获取参数表中的值，并进行比较

/*这里我们以校验“总额”参数与“实际金额”参数比较为例。*/ var total_amount = getParameterAsDouble("总额"); var amount = getParameterAsDouble("实际金额"); var rtn = ""; if (total_amount< amount){ rtn = "实际金额不能大于总额！"; } rtn;

示例 7：判断记录是否已存在

/*这里我们以校验“任务编号”是否已存在为例。*/ var code = getItemValueAsString("任务编号"); var dql = "$formname='fm_task' and code='"+code + "' and $id<>'" + getId() + "'"; var datas = queryByDQL(dql); var rtn = ""; if (datas!=null && datas.size()>0){ rtn = "任务编号"+code+"已经存在！"; } rtn;

### 1.2.7 隐藏脚本

说明：

控件隐藏脚本，是指当脚本返回值为 true 时，控件隐藏，当返回值为 false 时，控件显示。

示例 1：判断当前用户部门，并决定是否隐藏

/*这里我们以非“总经办”的用户不可查看“合同总额”字段信息为例。我们在“合同总额”字段隐藏脚本。1 表示一级部门*/ var deptid = getDeptIdByNameAndLevel("总经办",1); var dpts = getWebUser().getDepartments(); var isHidden = true; if (dpts!=null && dpts.size()>0){ for (var it = dpts.iterator();it.hasNext();){ var dptVO = it.next(); if (deptid.equals(dptVO.getId())){ isHidden = false; break; } } } isHidden;

示例 2：判断当前用户角色，并决定是否隐藏

/*这里我们以非“总经理”的角色用户不可查看“合同总额”字段信息为例。我们在“合同总额”字段隐藏脚本填写脚本。*/ var roleid = getRoleIdByName("总经理"); var roles = getWebUser().getRoles(); var isHidden = true; if (roles!=null && roles.size()>0){ for (var it = roles.iterator();it.hasNext();){ var roleVO = it.next(); if (roleid.equals(roleVO.getId())){ isHidden = false; break; } } } isHidden;

示例 3：判断当前表单另一字段值，并决定是否隐藏

/*这里我们以表单“任务类型”字段值状态为例。我们在“开发者”字段隐藏脚本填写脚本。当为非开发任务时，隐藏“开发者”*/ var type = getItemValueAsString("任务类型"); var isHidden = true; if ("开发任务".equals(type)){ isHidden = false; } isHidden;

示例 4：判断当前表单处于新建还是编辑状态，并决定是否隐藏

/*这里我们以表单为编辑状态时显示字段值信息为例。“建单人”字段隐藏脚本。*/ var doc = getCurrentDocument(); doc.getIstmp();

### 1.2.8 打印时隐藏脚本

说明：

控件打印时隐藏脚本，当脚本返回值为 true，文档打印时隐藏控件内容；脚本返回值为 false，文档打印时显示控件内容。

### 1.2.9 只读脚本

说明：

控件只读脚本，当脚本返回值为 true，表单字段只读；当脚本返回值为 false，表单字段可编辑。

示例：只作者可编辑

var doc = getCurrentDocument(); var user = getWebUser(); var isReadOlny = true; if (user.getId().equals(doc.getAutho().getId())){ isReadOlny = false; } isReadOlny;

## 1.3 操作

### 1.3.1 基本操作类型

前台效果图 | 名称 | 简述
| 查询 | 用于对应视图的查询表单功能。
| 保存 | 执行保存操作。
| 保存 | 执行保存并触发流程
| 保存并返回 | 执行保存操作后返回上一级页面。
| 保存并新建 | 执行保存操作后并创建新的操作，而且会带有原来的数据。
| 保存并新建 | 执行保存操作后并创建新的操作，但不会带有原来的数据。
| 保存 | 执行保存操作后不进行数据校验。
| 保存并复制 | 执行保存操作后并进行复制。
| 保存并关闭窗口 | 执行保存操作后关闭窗口。
| 返回 | 返回上一级页面。
| 打印 | 数据表进行打印。
| 带流程历史打印 | 打印数据表时并带有流程历史。
| 流程处理 | 提交给下一个流程进行进一步处理。
| 脚本处理 | 对脚本进行处理。
| 关闭窗口 | 把当前的窗口关闭。
| 编辑审批人 | 对数据信息进行操作、修改的人。
| PDF 导出 | 数据信息以PDF格式进行导出。
| 文件下载 | 下载相关文件，文件路径如：”/uploads/aaa.txt”,那么在发布项目的uploads目录下的aaa.txt文件会被下载。
| 电子签章 | 对电子文档进行电子签章。
| 动态打印 | 数据进行嵌套打印。用户可根据自己的需求设计打印界面及其字段实现自定义打印。
| 跳转 | 跳转到对应的表单
| 流程启动 | 触发流程

### 1.3.2 动作执行前脚本

说明：

表单按钮动作执行前脚本，是指按钮的动作执行前触发此脚本。通常也可以用来进行校验操作。当此脚本返回值为：字符串对象、Alert 对象（通过 createAlert 函数创建）、Confirm 对象（可通过 createConfirm 函数创建）。

示例 1：执行前显示 Alert 窗口

/*这里我们以“提交”按钮动作执行前脚本为例。*/ var code = getItemValueAsString("任务编号"); if (code ==null || code.trim().length()<=0){ createAlert("任务编号不能为空！"); }

示例 2：执行前显示 Confirm 窗口

/*这里我们以“提交”按钮动作执行前脚本为例。*/ createConfirm("确定要提交文档？");

### 1.3.3 动作执行后脚本

说明：

表单按钮动作执行后脚本，是指按钮的动作执行后触发此脚本。

示例 1：保存当前文档，并根据当前表单的内容创建另一文档

/*这里我们以“保存”按钮动作执行后脚本为例。*/ var formService = getFormProcess(); //查询出表单 var form = formService.doViewByFormName("fm_purchase_materials",getApplication()); var process = getDocumentProcess(); var doc = process.doNew(form,getWebUser(),createParamsTable()); doc.findItem("title").setValue(getItemValueAsString("标题")); doc.findItem("type").setValue(getItemValueAsString("类型")); doc.findItem("count").setValue(getItemValueAsString("数量")); doc.setIstmp(false); process.doCreate(doc);

示例 2：保存当前文档，并发送邮件通知

/*这里我们以“保存”按钮动作执行后脚本为例。*/ var from=getItemValueAsString("from"); var to=getItemValueAsString("to"); var host=getItemValueAsString("host"); var user=getItemValueAsString("user"); var password=getItemValueAsString("password"); var bbc=getItemValueAsString("bbc"); var subject=getItemValueAsString("subject"); var body=getItemValueAsString("body"); sendMail(from, to, subject, body, host, user, password, bbc, fasle);

示例 3：保存当前文档，自动生成编号

/*自动编号，此种生成方式编号会连号，让编号利用率到最高。当点击保存按钮，数据保存成功后，生成 TLK201101001、TLK201101002、TLK201101003......*/ //保存动作执行后脚本 var doc = getCurrentDocument(); var value = doc.getItemValueAsString("code1"); if(value == null || value.trim().length() <= 0){ value = countNext2("TLK",true,true,false,4); doc.findItem("code1").setValue(value); var process = getDocProcess(getApplication()); //Document 操作类 process.doUpdate(doc); }

# 2 视图

## 2.1 视图的类型

说明：

视图的类型分为：普通，日历视图，树形视图，地图视图，甘特视图

(1) 普通，常规的视图配置

(2) 日历视图，类似挂历或备忘录的形式陈列表单数据，通过映射字段映射表单的日期字段，根据该日期字段以每日，每周，每月显示数据（具体实例请参考 myApps 功能示例/视图示例/日历视图）。

月视图：

周视图：

日视图：

(3) 树形视图，以树形结构显示表单内容，字段中的层级关系通过视图列中映射字段设置来定义的，节点点击打开类型为对应视图和链接（具体实例请参考 myApps 功能示例/视图示例/树形视图（列表）或者树形视图（表单））。

前台显示效果：

(4) 地图视图，为用户提供地图位置，显示位置信息。使用百度地图的插件，需在可连互联网环境才能正常使用，通过映射字段映射地图控件（具体实例请参考 myApps 功能示例/视图示例/地图视图）。

前台显示效果：

(5) 甘特视图，能更直观的统计每日，周，月，季的工作进展情况和完成进度，它通过映射字段对应表单需要的字段（具体实例请参考 myApps 功能示例/视图示例/甘特视图示例）。

前台显示效果：

## 2.2 筛选条件定义

示例 1：在视图中列出单个数据表内容

通过视图基本信息中设置相关表单，即可对应单个表单的数据表，分别可以通过设计，代码（DQL），代码（SQL）来进行对列的条件过滤。(具体实例请参考 myApps 功能示例/视图示例/视图示例 design、DQL 和 SQL)

以上实例中通过查询条件对应绑定查询表单里字段，根据查询表单字段内容对视图数据表进行筛选。

// DQL 语句 /*获取查询表单“字段 1”和“字段 2”的值，当查询表单中“字段 1”和“字段 2”的值等于“视图示例_dql”中“字段 1”和“字段 2”的值时，显示数据表为查询表单中中“字段 1”和“字段 2”对应内容的数据。*/ var value1 = getItemValue("字段 1"); var value2 = getItemValue("字段 2"); var where = (value1 != null && value1.trim().length() > 0)?" and 字段 1='"+ value1+"'":""; where += (value2 != null && value2.trim().length() > 0)?" and 字段 2='"+ value2 + "'":""; "$formname='视图示例_dql' " + where; //SQL 语句 /*获取查询表单“字段 1”的值，当查询表单中“字段 1”的值等于“视图示例_sql”中“字段 1”的值时，显示数据表为查询表单中“字段 1”对应内容的数据。 字段名不是以 item_起头的，就要定义一个别名，别名是以 item_起头，因为如果不是以 item_起头，数据项的值将无法获取到值。*/ var sql = ""; var value = getItemValue("字段 1"); if (value!=null && value.trim().length() > 0){ sql = " and ITEM_字段 1='"+value+"'"; } "SELECT * FROM TLK_视图示例_sql where istmp=0 "+sql; //SQL 语句实现，映射表单数据查询 /*查询映射表单 tb_mapping 中 name 与 title 字段内容。*/ var sql = "select d.*,m.name,m.title from t_document d"; sql +=" inner join tb_mapping m on d.mappingid=m.keyname"; sql;

示例 2：在视图中显示多个数据表（多表查询）的内容

/*通过过滤中代码(SQL)来实现多个数据表（多表查询）的内容，主要通过主子表来实现多表单的数据查询，查询子表单中 finished 与 date 字段的数据。*/ var sql="SELECT d.*,sub.item_finished,sub.item_date from tlk_fm_task d"; sql +=" inner join tlk_fm_task_sub sub on d.id=sub.parent"; sql;

示例 3：SQL 语句实现已办数据查询

var sql="select * from tlk_会议记录 doc where '"+getWebUser().getId(); sql +="' in (select u.ACTORID from T_ACTORHIS a,t_relationhis r"; sql +=" where r.docid=doc.id and a.nodehis_id=r.id"; sql +=" AND r.flowid in "; sql +="(SELECT flowid FROM t_flowstatert WHERE docid=doc.id )) "; sql;

示例 4：实现待办数据查询

//DQL 实现 var dql ="$formname='formname' and $state.actors.actorid='" +getWebUser().getId() + "'"; dql; //SQL 实现 var sql="select d.* from tlk_tablename d"; sql +=" where (d.id in (select states.docid"; sql +=" from t_flowstatert states, t_actorrt actors"; sql +=" where states.id = actors.flowstatert_id"; sql +=" and actors.actorid ='" +getWebUser().getId() + "'))"; sql;

## 2.3 定义列

说明：

定义列的显示，我们可以通过两种方式来实现：FIELD 和 SCRIPT；

（1）FIELD，视图中默认的方式，根据视图查询的表单，选择对应表单的列；

（2）SCRIPT，通过代码来显示数据。

### 2.3.1 获取表单字段值

说明：

获取相关表单字段的值，实现的方式有两种类型：FIELD 和 SCRIPT

（1） FIELD，视图中默认的方式，根据视图查询的表单，选择对应表单的列

（2） SCRIPT，通过代码来显示数据，具体示例如下：

var value = getItemValue("地区"); value;

### 2.3.2 值转换

说明：

通过视图列配置来实现值转换，需要使用 iScript 代码的方式来实现 (具体实例请参考 myApps 功能示例/视图示例/数据转换)

/*获取当前文档“地区”字段的值，当值等于 010 时显示北京，当值等于 020 是显示广州，当值等于 021 时显示上海。“地区”列的值脚本。*/ var doc = getCurrentDocument(); var value = doc.getItemValueAsString("地区"); var rtn=''; if(value=="010") rtn="北京 "; else if(value=='020') rtn="广州 "; else if(value=='021') rtn="上海 "; else rtn= value; rnt;

前台显示的效果是：

### 2.3.3 获取其它表单字段值

说明：

/*列上通过 SCRIPT 代码实现。这里我们以根据物料编辑获取物料名称为例。*/ var code = getItemValueAsString("code"); var dql = "$formname='物料表单' and code='" + code + "'"; var doc = findByDQL(dql); doc.getItemValueAsString("name");

### 2.3.4 在列上显示操作

说明：

列上通过 SCRIPT 代码实现，当点击列名为中“编辑”按钮时，进入当前行中的表单。

示例：编辑操作按钮

var doc = getCurrentDocument(); var rtn = "<input type='Button' value='编辑' "; rtn+="onclick='javascript:document.forms[0].action=\""; rtn+=getContextPath(); rtn+="/portal/dynaform/document/view.action"; rtn+= "?_docid=" + doc.getId() + "&_formid=" + doc.getFormid(); rtn+= "&r_edit=true\";"; rtn+= "document.forms[0].submit();'/>"; rtn;

效果如下：

### 2.3.5 在列上显示图片

示例：

/*列通过 SCRIPT 代码实现，获取相关表单“地区”字段的值，当值等于 010 和 020 和 021 时，显示服务器上相关图片，否者显示另一种相关图片。“结果”列的值脚本。*/ var value = getItemValue("地区"); var rtn=""; if(value=="010" || value=="020" || value=="021"){ rtn="<img src='/obpm/resource/image/backstatelabel.gif'/>"; }else{ rtn="<img src='/obpm/resource/image/terminate1.gif'/>"; } rtn;

### 2.3.6 在列上显示超链接

说明：

列通过 SCRIPT 代码实现(具体实例请参考 myApps 功能示例/视图示例/多样式列视图示例)

示例：列链接到其它网址

/*以字符串的形式获取相关表单“task_type”字段的值，当字段值为 0 时，显示打开百度并在当前页面中链接到百度地址，当字段值为 1 时，显示打开百度并在当前页面中链接到谷歌地址。*/ var value = getItemValueAsString("task_type"); var rtn = ""; if ("0".equals(value)){ rtn = "<a href='http://www.baidu.com'>打开百度</a>"; }else if ("1".equals(value)){ rtn = "<a href='http://www.google.com'>打开谷歌</a>"; } rtn;

## 2.4 操作

### 2.4.1 基本操作类型

说明：操作的类型有：查询，创建，删除，批量提交，导出 EXCEL，导入 EXCEL，清空所有数据，文件下载，批量签章

(1) 查询：对应视图的查询表单功能

(2) 创建：创建关联表单新数据

(3) 删除：删除所选记录

(4) 批量审批：对数据信息进行大量的审批提交（表单有绑定流程）

(5) 导出 EXCEL：导出视图数据的 EXCEL 文件

(6) 导入 EXCEL：根据导入映射的配置，导入相关数据

(7) 清空所有数据：清空所选表单的数据

(8) 文件下载：下载相关文件，通过文件名称脚本，指向下载文件的相对路径，例如”/uploads/aaa.txt”，下载服务器上 uploads 文件夹下名为 aaa.txt 的文件

(9) 批量签章：对所有表单进行签章

### 2.4.2 动作前执行脚本

说明：

视图按钮动作执行前脚本，是指按钮的动作执行前触发此脚本。通常做动作执行前校验操作。

示例：校验是否勾选了记录

/*这里我们以删除按钮动作执行前脚本为例。删除之前需要选择被删除记录。*/ var selects = getParameterAsArray("_selects"); var rtn = ""; if (selects ==null || selects.length<0){ rtn = "请选择记录！"; } rtn;

### 2.4.3 动作后执行脚本

说明：

视图按钮动作执行后脚本，是指按钮的动作执行后触发此脚本。

示例：更新选中记录相关的信息

/*这里在按钮执行后脚本里来更新选中的记录信息。*/ var docids = getParameterAsArray("_selects"); var process = getDocProcess(getApplication()); //Document 操作类 for(var i=0;i<docids.length;i++){ var doc = process.doView(docids[i]); doc.findItem("am_apply_status").setValue('己成生'); process.doUpdate(doc ); }

# 3 流程

## 3.1 状态标签

说明：

状态标签是节点状态的显示，前台中一般显示在进入流程中表单的右上角。表单中可以根据获取到流程的状态来决定表单中某字段的值。表单通过脚本获取状态标签：

//方法 1： var doc = getCurrentDocument(); var statelabel = doc.getStateLabel(); statelabel; //方法 2： var statelabel = getStateLabel(); statelabel;

示例：“软件缺陷管理系统模板”，表单中“指派给”字段是通过流程状态获取的值，其值根据状态变化而变化，这里的用户变量“users”的值是获得某个指定角色下的所有用户，是一个集合，需要通过遍历（如：var iter = users.getDatas().iterator(); iter.hasNext();）后返回选项值，因此控件使用了下拉框，脚本写在该控件属性中的选项脚本。

/*当流程状态标签值为空和“FindBug”和“TestBug”时获取 Developer 角色下的所有用户，否者当值为“FixBug”状态时获取 Tester 角色下的所有用户，当值为“AssignBug”时获取 Developer 角色下的所有用户。*/ var opts = createOptions(); opts.add("",""); var doc = getCurrentDocument(); var process = createProcess("cn.myapps.core.user.ejb.UserProcess"); var users = null; var statelabel = doc.getStateLabel(); if(statelabel == null || statelabel == 'FindBug' || statelabel == 'TestBug'){ users = process.doQueryByRoleId("11de-a80d-a8e5d7bb-b638-55c259677fcd"); }else if(statelabel == 'FixBug'){ users = process.doQueryByRoleId("11de-a80d-b3e7fd0d-b638-55c259677fcd"); }else if(statelabel == "AssignBug"){ users = process.doQueryByRoleId("11de-a80d-a8e5d7bb-b638-55c259677fcd"); } if (users!=null && users.rowCount > 0){ for(var iter = users.getDatas().iterator(); iter.hasNext();) { var user = iter.next(); var userno = user.getLoginno(); var username = user.getName(); opts.add(username,userno); } } opts;

## 3.2 会签

说明：

传统意义上的会签是指撰拟公文的过程中，主办单位主动与有关单位协商并核签的一种办文程序，一般当公文的内容涉及本单位的多个部门或与其他单位有关时，需要进行会签。

流程会签，是指当前环节需要会签，则需要等待当前环节的所有审批人都审批完成，当前环节才算完结，流程才流转到下一环节。

## 3.3 分支

说明：

分支是 A->{B,C,D}->E（这里的 A,B,C,D,E 指的是流程中左侧图标的手动节点，箭头指的是流程中左侧图标的关联节点）， A 执行完以后，可以根据需求对 B,C,D 多个流程节点进行选择和判断。对 B,C,D 分支节点进行多和单的节点选择（这个是在流程节点属性的审批设置中对审批送出设置和审批到达设置中进行选择设定）；判断流程的走向，一般是在流程的关联节点上进行设置。具体示例请参考 myApps 功能示例/流程示例/流程分支示例， 这里建单人创建文档后提交流程，流程根据表单中“字段 2”的值进行路径的判断。大于 100 走“分支 1.1”，小于 100 走“分支 2.1”，根据逻辑这是个单选。因此分别在关联节点和建单人以及分支节点上进行审批设置。

## 3.4 并发流程

说明：

并发流程是 A->{B,C,D}->E（这里的 A,B,C,D,E 指的是流程中左侧图标的手动节点，箭头指的是流程中左侧图标的关联节点）， A 执行完以后，让 B，C，D 来执行，他们的执行没有先后顺序，只有他们三个全部执行完以后，才能到下个流程节点 E。这里的流程节点属性设置中，其中，在 A 节点审批设置审批送出节点设置“当下步有多个审批节点时,可以选择多个节点”，E 节点中审批到达节点设置“当上步有多个审批节点时,所有节点完成,流程可以到达”

## 3.5 调用子流程

说明：

子流程作为父流程中的一种特殊的活动出现。子流程在父流程中，所处的只是一个节点，因此在进行流程处理时，子流程只当做一个流程的节点来看待。调用子流程只是当前流程处理到子流程节点这个环节时，主流程会通知流程引擎，流程引擎开始启动并调用子流程。

## 3.6 绑定表单并设置权限

说明：

流程中在手动节点的基本信息中进行对应的表单绑定，可表单字段进行“只读”，“修改”，“隐藏”的权限控制。默认情况下所有表单字段都是“修改”的权限，因此：

（1）绑定表单，需要修改其中某些字段的“修改”的权限，按照流程的实际需求；

（2）解除绑定，将表单所有字段权限改为“修改”即可。

# 4 报表

## 4.1 交叉报表

说明：

交叉表单是报表中常见的类型，可以配置行和列数据汇总。它的视图查询模式可分为 sql.dql,form

(1) sql:通过 ISCRIPT 语句实现，具体可以参考视图中的代码（SQL）

(2) dql：通过 ISCRIPT 语句实现，具体可以参考视图中的代码（DQL）

(3) form：通过 ISCRIPT 语句实现，具体可以参考视图中的设计

## 4.2 列表

说明：

列表通过视图设计实现，其实现方式有：

（1） 将视图基本信息只读设置为“是”

（2） 将试图列信息中的是否汇总标记上，对列进行汇总

## 4.3 自定义报表（Beta）

说明：

用户根据自己的需求定义表单的统计方式，目前实现了图表视图和仪表版；表格视图，矩阵视图和汇总视图正在开发中。

用户在后台通过菜单的自定义链接（内部）来调用自定义报表界面，链接地址如下：

/portal/share/report/oReport/oReport.jsp

# 5 脚本语言 iScript

## 5.1 常见效果实现

### 5.1.1 执行 SQL

说明：

执行 SQL 分别可通过queryByDSName(dsName,sql)、insertByDSName(dsName,sql)、updateByDSName(dsName,sql)和deleteByDSName(dsName,sql)四个方法实现。

示例1：执行SQL查询

//省份。用户首先需要创建"kd"数据源。 var opts = createOptions(); opts.add("",""); try{ var appid = getApplication(); var sql = "select cityid,cityname from cityinfo where rootid=0"; var datas = queryByDSName("kd",sql); if(datas!=null){ for(var iterator = datas.iterator();iterator.hasNext();){ var map1 = iterator.next(); //取值 var name= map1.get("cityname"); var value = map1.get("cityid"); opts.add(name,value); } } }catch(e){} opts;

示例2：执行SQL插入

var doc = getCurrentDocument(); var sql = "insert into cityinfo(cityid,cityname,rootid) "; sql +=" values('"+doc.getItemValue("citycode")+"','"; sql +=doc.getItemValue("cityname")+"', "; sql +=doc.getItemValue("cityroot")+")"; insertByDSName("kd",sql);

示例3：执行SQL更新

var doc = getCurrentDocument(); var sql = "update cityinfo set cityid = "; sql +="'"+doc.getItemValue("citycode")+"', cityname='"; sql +=doc.getItemValue("cityname")+"', rootid ="; sql +=doc.getItemValue("cityroot"); updateByDSName("kd",sql);

示例4：执行SQL删除

var docid = getId(); var sql = "delete from tlk_施工物料明细 where PARENT='"+docid+"'"; deleteByDSName("kd",sql);

### 5.1.2 通过 SQL 查询

示例：根据当前文档 ID 查询子表记录。

var value = ""; var docid = getId(); var sql = "select * from tlk_fm_ticket_sub parent = '" + docid + "'"; var subdocs1 = queryBySQL(sql); if(subdocs1 != null && subdocs1.size() > 0) { va r temp = null; var temp1 = ""; var temp2 = ""; for (var iter1 = subdocs1.iterator(); iter1.hasNext();) { var subdoc1 = iter1.next(); temp1 = subdoc1.getLastmodified(); if(temp1 > temp2) { temp2 = temp1; temp = subdoc1; } } value = temp.getItemValueAsString("finishdegree"); }else{ value = "0%"; } value;

### 5.1.3 通过 DQL 查询

示例：根据当前文档 ID 查询子表记录。

var value = ""; var docid = getId(); var w1 = " and $parent.$id = '" + docid + "'"; var dql1 = "$formname = 'fm_ticket_sub'" + w1; var subdocs1 = queryByDQL(dql1); if(subdocs1 != null && subdocs1.size() > 0) { va r temp = null; var temp1 = ""; var temp2 = ""; for (var iter1 = subdocs1.iterator(); iter1.hasNext();) { var subdoc1 = iter1.next(); temp1 = subdoc1.getLastmodified(); if(temp1 > temp2) { temp2 = temp1; temp = subdoc1; } } value = temp.getItemValueAsString("finishdegree"); }else{ value = "0%"; } value;

### 5.1.4 通过 SQL 统计

示例：根据 SQL 语句统计对应数据表的数据记录数

var sql = "select count(*) from tlk_queryByDQLDomain 子表"; var t_count = countBySQL(sql); var rtn = ""; if(t_count > 0) { rtn = "表中存在记录。"; } rtn;

### 5.1.5 通过 DQL 统计

示例：根据 DQL 语句统计对应表单的数据记录数。

var dql = "$formname='queryByDQLDomain 子表'"; var t_count = countByDQL(dql); var rtn = ""; if(t_count > 0) { rtn = "表中存在记录。"; } rtn;

### 5.1.6 流程节点审批人脚本

示例：流程节点审批人脚本，获取执行人的上级。这里主要是用于部门主管审批时的脚本代码。

//执行人的上级。这里主要是用于部门主管审批时的脚本代码。 var userid = getItemValue("toperson");//用户选择框 var userProcess = createProcess("cn.myapps.core.user.ejb.UserProcess"); var user = userProcess.doView(userid); var reportto = user.getSuperior(); if(reportto != null){ reportto; }else{ user; }

### 5.1.7 视图选择框确定脚本

示例：视图框确定脚本，自动生成子表记录，刷新子表

//视图框确定脚本，刷新子表 var did = getId(); var parentId = getParameterAsText("_selects"); //获取视图选择框对应的 doc var parentDoc = findDocument(parentId); if(parentDoc!=null && parentId !=null && parentId.trim().length()>0){ //获取视图选择框对应的 doc 的子 doc var subDocs1 = parentDoc.getChilds("施工物料明细"); var process = getDocumentProcess(); var domain = getDomainid(); var application = getApplication(); //如果子 doc 不为空，遍历子 doc，取出每个子 doc 的值并保存到另一张子 doc 中。 if(subDocs1!=null){ var sql = "delete from tlk_施工物料明细 where PARENT='"+did+"'"; deleteByDSName("rims",sql); var formService = getFormProcess(); var form = formService.doViewByFormName("施工物料明细",application); for(var iter = subDocs1.iterator(); iter.hasNext();){ var subdoc = iter.next(); var uuid = new Packages.cn.myapps.util.sequence.Sequence(); var doc = process.doNew(subform,getWebUser(),createParamsTable()); doc.setIstmp(false); doc.setParent(did); doc.addStringItem("施工物料名称",subdoc.getItemValueAsString("施工物料名称")); doc.addStringItem("数量",subdoc.getItemValueAsString("数量")); doc.addStringItem("单位",subdoc.getItemValueAsString("单位")); doc.addStringItem("申请人",subdoc.getItemValueAsString("申请人")); process.doCreate(doc); } } } var rtn = "<script>"; rtn += "var subfrms = getFormFieldByName('包含元素名称');"; rtn += "if (subfrms){ "; rtn += "subfrms.document.location.reload();"; rtn +="}"; rtn +="</script>"; rtn;

### 5.1.8 是否允许编辑文档

示例：只作者可编辑脚本

//表单打开前执行脚本 var user= getWebUser(); var doc = getCurrentDocument(); var author = doc.getAuthor(); var flag = true; if (author!=null && !user.getId().equals(author.getId())){ flag=false; } flag;

### 5.1.9 计算脚本

示例：通过计算脚本控件生成查看按钮

var doc = getCurrentDocument(); var contractnumber=doc.getItemValueAsString("合同编号"); var value=""; var dql = "$formname='fm_twgb_contract'"; dql += " and 合同编号 ='" + contractnumber + "'"; var contractdocs = queryByDQL(dql); if(contractdocs!=null && contractdocs.size()>0){ var contractdoc = contractdocs.get(0); var docUrl = getContextPath()+ "/portal/dynaform/document/view.action?_docid="; docUrl += contractdoc.getId()+"&formid="+contractdoc.getFormid() ; value = "<input type=button value='查看' onclick='window.showModalDialog(\""; value += docUrl+"\",\"\",\"resizable=yes;help=no;status=no;"; value += "font-size:9pt;dialogWidth:850px;dialogHeight:600px;status:no;scroll:no;\")'>"; } value;

### 5.1.10 创建 Document

示例：通过 iScript 脚本以从当前文档获取数据直接创建文档。

var formService = getFormProcess(); var form = formService.doViewByFormName("fm_purchase_materials",getApplication()); //查询出表单 var process = getDocumentProcess(); var doc = process.doNew(form,getWebUser(),createParamsTable()); doc.findItem("title").setValue(getItemValueAsString("标题")); doc.findItem("type").setValue(getItemValueAsString("类型")); doc.findItem("count").setValue(getItemValueAsString("数量")); doc.setIstmp(false); process.doCreate(doc);

# 6 DQL 语法

语法格式：

$formname=’表单名’ [AND filter1……]

说明：

$formname：标识查询的表单；

AND filter1……：表示带过滤条件查询。

示例 1：在表单控件校验脚本中的应用：

var doc = getCurrentDocument(); var dql = "$formname='表单 1' AND code='TASK0001' AND $id <>'"+doc.getId()+"'"; var datas = queryByDQL(dql); var rtn = ""; if (datas.size()>0){ rtn = "系统中已经存在编号为：TASK0001 的记录！"; } rtn;

示例 2：在视图中 DQL 模式内容的应用：

var value1 = getItemValue("_申请单号"); var value2 = getItemValue("_请假类别"); var where = ( value1 != null && value1.trim().length() > 0) ? " and _申请单号 like '"+ value1+"%'" : ""; where += ( value2 != null && value2.trim().length() > 0) ? " and _请假类别='" + value2 + "'" : ""; "$formname=' TLK_请假申请 4' " + where;

总结：

DQL 语句中使用到了 code 字段与$id 字段进行过滤。其中，$id 表示使用系统字段 ID；code 字段为用户通过表单配置的字段名。在 DQL 语句中凡使用系统字段都需要在字段名前加”$”符号，用户配置字段可直接使用。

上面的例子是通过脚本的形式返回一个 DQL 语句的字符串，系统会根据这个 DQL 语句进行查询。

# 7 其它问题

7.1 表单系统保留字段

系统字段 | 描述
ID | 记录唯一标识字段
PARENT | 存储父记录 ID 标识字段
LASTMODIFIED | 最后修改时间
FORMNAME | 对应模板表单名称
STATE | 流程当前状态记录 ID
AUDITDATE | 最近一次的审批时间
AUTHOR | 最近一次的审批人 ID
CREATED | 创建记录时间
FORMID | 表单 ID
ISTMP | 是否为临时记录
FLOWID | 流程 ID
VERSIONS | 版本
SORTID | 排序 ID
APPLICATIONID | 软件 ID
STATEINT | 流程当前状态值
STATELABEL | 流程当前状态标签
LASTFLOWOPERATION | 最近一次的审批操作类别
AUDITORNAMES | 当前审批人名称列表
AUDITORLIST | 当前审批人 ID 列表
MAPPINGID | 表单映射 ID

## 7.2 定时任务

说明：

任务可以让系统在特定日期特定时间执行指定的任务内容，并通过终止条件结束任务。

示例 1：定时清理数据

/*方法 1：定时清理符合 SQL 清除条件的数据*/ var sql = "delete from tlk_qs_user_enterprice_info where istmp=0 and "; sql += " item_username in(select tlk_qs_user_enterprice_info.item_username "; sql += " from tlk_qs_user_enterprice_info where istmp=0 and not exists("; sql += "select t_qe.item_username from t_user,"; sql += " tlk_qs_user_enterprice_info t_qe where t_user.id=t_qe.item_username))"; var process = new Packages.cn.myapps.runtime.dynaform.dts.datasource.ejb.DataSourceProcessBean(); process.remove("del",sql,getApplication()); /*方法 2：定时清理符合 SQL 清除条件的数据*/ //2.4 版本及之后可使用 var sql = "delete from tlk_qs_user_enterprice_info where istmp=0 and "; sql += " item_username in(select tlk_qs_user_enterprice_info.item_username "; sql += " from tlk_qs_user_enterprice_info where istmp=0 and not exists("; sql += "select t_qe.item_username from t_user,"; sql += " tlk_qs_user_enterprice_info t_qe where t_user.id=t_qe.item_username))"; deleteByDSName("del",sql);

示例 2：定时发送提醒邮件

var sql = "select id from tlk_task where item_sendable='待发'"; //kd 数据源名称，需要通过"软件->高级工具->数据源"创建一个 kd 数据源。 var datas = queryByDSName("kd",sql); if (datas!=null && datas.size()>0){ for (var i=0;i<datas.size();){ var docid = datas.get("id"); var doc = findDocument(docid); var from=doc.getItemValueAsString("from"); var to=doc.getItemValueAsString("to"); var host=doc.getItemValueAsString("host"); var user=doc.getItemValueAsString("user"); var password=doc.getItemValueAsString("password"); var bbc=doc.getItemValueAsString("bbc"); var subject=doc.getItemValueAsString("subject"); var body=doc.getItemValueAsString("body"); sendMail(from, to, subject, body, host, user, password, bbc, fasle); } }

## 7.3 WebService

通过 MYAPPS 提供的 WebService 客户端接口来调用 MYAPPS 平台文档创建接口。

代码片断：

String formName = "fm_task";//表单名称 HashMap parameters = new HashMap(); parameters.put("title", "任务 1"); parameters.put("startDate", "2011-01-18"); parameters.put("endDate", "2011-01-31"); parameters.put("type", "开发任务"); parameters.put("desc", "[任务]-[开发任务]-[2011-01-18]-[2011-01-31]"); // Default 应用 String applicationId = "11de-9d0c-38898b6e-ac35-a50fa32293ae"; // 企业域下指定用户的 ID,这里使用 testuser 用户的 ID String domainUserId = "11de-c13a-0cf76f8b-a3db-1bc87eaaad4c"; try { service.createDocumentByDomainUser(formName, parameters, domainUserId, applicationId); } catch (DocumentServiceFault e) { e.printStackTrace(); } catch (RemoteException e) { e.printStackTrace(); }

## 7.4 LDAP 同步

## 7.5 性能调优