一般动作
登录
Wiki: BROPEN ▼: Document Index
»
Space: FAQ ▼: Document Index
»
Page: 如何在表单上显示手写签名（项目示例）
搜索
zh
页面动作
导出 ▼: 以PDF格式导出 以RTF格式导出 以HTML格式导出
更多动作 ▼: 打印预览 察看源码
欢迎进入Wiki » FAQ » 如何在表单上显示手写签名（项目示例）?

# 如何在表单上显示手写签名（项目示例）?

在2014-01-03 20:12上被李小翔修改
评论 (0) · 附件 (1) · 记录 · 信息

供稿人：许庆洋

维护员工信息时，可以上传由配置 bropen.framework.osm.employee.signature.img.size 制定大小的手写签名图片，签名图片可以通过配置 p:opinion 标签的 signature 属性，显示在意见栏中，或者调用相应的API实现Word或WPS套打。

在下面的项目需求中，需要在表单的上方显示所有审批人的手写签名，并按照一定规则排序：

- 流程处于起草状态时，不显示签名；
- 如果流程被退回，则前面的签名都擦除；
- 按照分组显示意见；
- 如果任务列表中存在相同任务环节名，并且他们的任务实际办理人相同，且存在已完成的任务，则擦除已完成任务的环节签名；
- 签名显示分3类：审批环节的签名、审议环节的签名、总裁/董事长的签名：
  集团领导的审批任务，放到总裁、董事长审批行；
  关键任务列表，按时间顺序排列、根据审批人查重，放到审批行；
  其他任务列表，按照级别（员工title）排序、根据审批人查重，放到审议行；
- 每行最多显示8条签名，签名图片大小为100*40；
- 当为已办、或代理人打开待办、或者非任一人处理时，签名的标题处显示“代理”二字；
- 超时自动提交的任务签名处显示“系统提交”；
- 否决的任务签名处显示“否决”；
- 决定性意见为“不同意”的任务签名处显示“不同意”；
- 其他办理完毕的任务签名不存在，签名处则显示“已处理”。

签名栏显示效果如下图所示：

由于这是一个共性的需求，可以自定义一个显示手写签名栏的taglib标签，以下实现代码：

/**
* 手写签名栏，放在表单的<h1>标签前
* 例如：<boe:signature bean="${xxxxInstance}" />
* @attr bean REQUIRED
*/
def signature = {attrs->
if ( !attrs.bean?.id ) return;
ProcessInstance flowInst = attrs.bean.workflowInstance;
def proc = flowInst.processDefinition();
// 流程处于起草状态时，不显示签名
if ( flowInst.startNode() == flowInst.nodesName ) return;
//
List tasks = [], criticalNodes = [], otherNodes = [], leaderTasks, criticalTasks, otherTasks;
// 需要显示签名的任务列表：非起草环节的待办
Task lastDraftTask = flowInst.tasks[0]; // 计算最后一次起草环节的任务：签名从这里开始，一旦退回，签名擦除
for ( t in flowInst.tasks ) if (t!=lastDraftTask && t.type==Task.TYPE_TODO && t.node==lastDraftTask.node && t.status!='terminated') lastDraftTask = t;
for ( t in flowInst.tasks ) if (t.type==Task.TYPE_TODO && t.createTime>lastDraftTask.createTime && t.status!='terminated') tasks << t;
// 如果退回到环节A，则不显示原来在环节A的任务-被退回来的环节A的任务（不含）
// 例如路径 A-B-C-A，其中C-A是退回，则不显示前面三个任务（A-B-C）的签名
tasks = CollectionUtils.sort(tasks, 'asc', 'id');
for ( int i=0; i<tasks.size(); i++ ) {
if ( !tasks[i] ) continue;
String node = tasks[i].node;
for ( int j=i+1; j<tasks.size(); j++ ) {
if ( tasks[j].node==node && tasks[j].sendType==Task.SEND_TYPE_BACK ) {
List prevs = tasks[j].prevAllTasks();
for ( int k=i; k<j; k++ ) if ( prevs.contains(tasks[k]) ) tasks[k] = null;
break;
}
}
}
// 如果任务列表中存在相同任务环节名，并且他们的任务实际办理人相同，且存在已完成的任务，则擦除已完成任务的环节签名
for ( int i=0; i<tasks.size(); i++ ) {
if ( tasks[i] == null || tasks[i].completeTime ) continue;
for ( int j=0; j<i; j++ ) {
if ( tasks[j] && tasks[j].completeTime && tasks[j].node==tasks[i].node && tasks[j].actorId==tasks[i].actorId ) tasks[j] = null;
}
}
tasks -= null;
// 集团领导的审批任务，放到总裁、董事长审批行
Closure uniqueClosure = {it.node + "@" + it.actorId};
Closure sortSwapper = { List valList, Date completeTime, Task task->
return completeTime ?: task.createTime;
}
leaderTasks = CollectionUtils.findAll(tasks, "actorTitle", ["总裁","董事长"]); // 后面再查重，下面两段代码还用这个list变量
// 关键任务列表，按时间顺序排列、根据审批人查重，放到审批行
for ( n in proc.values() ) if (n instanceof Map && n.node && n.critical ) criticalNodes << n.node;
criticalTasks = CollectionUtils.sort(
CollectionUtils.sort(CollectionUtils.findAll(tasks, "node", criticalNodes)-leaderTasks,
"desc", "completeTime").unique(uniqueClosure), "asc", "completeTime", sortSwapper);
// 其他任务列表，按照级别（员工title）排序、根据审批人查重，放到审议行
for ( n in proc.values() ) if (n instanceof Map && n.node && !n.critical ) otherNodes << n.node;
otherTasks = CollectionUtils.sort(
CollectionUtils.findAll(tasks, "node", otherNodes)-leaderTasks, "desc",
"completeTime", sortSwapper).unique(uniqueClosure);
otherTasks = otherTasks.sort{-app.AppUtils.getLevelByTitle(it.actorTitle)};
leaderTasks = leaderTasks.unique(uniqueClosure); // 集团领导的审批任务查重
// 渲染签名表格
StringBuilder sb = new StringBuilder();
sb << '<table style="padding: 5px 0; border-collapse: collapse;">'
renderSignature("审批", criticalTasks, sb);
renderSignature("审议", otherTasks, sb);
renderSignature("总裁、董事长审批", leaderTasks, sb);
sb << '</table>';
out << sb
}
private renderSignature(String caption, List tasks, StringBuilder sb) {
if ( !tasks ) return;
sb << '<tr class="prop"><td class="nameX" style="white-space:normal; text-align:center; vertical-align:middle; width:100px" rowspan="'
sb << (Math.ceil(tasks.size()/8) * 2) << '">' << caption << '</td>'
// 每行最多显示8条签名，签名图片大小为100*40
int max = 8;
for ( int i=0; i<tasks.size(); i+=max ) {
if ( i!=0 ) sb << '<tr class="prop">';
renderSignature( tasks.subList(i,[i+max,tasks.size()].min()), sb );
sb << '</tr>'
}
}
private renderSignature(List tasks, StringBuilder sb) {
for ( t in tasks ) {
sb << '<td class="nameX" style="white-space:normal; text-align:center; vertical-align:middle; width:100px; padding:4px">'
if ( t.actorSubstituteId ) {
// 仅当为已办、或代理人打开待办、或者非任一人处理时，才显示“代理”二字
if ( t.completeTime || t.substituteType==Substitute.TYPE_MOVE || t.actorSubstituteId==session.employeeId ) {
sb << '(代理)<br/>';
}
}
//sb << DateUtils.formatDatetime(t.createTime) << "<br/>"
sb << bropen.framework.core.osm.Organization.splitName(t.organizationFullName,1) << '/' << (t.actorTitle?:"员工") << '<br/>' << t.actor << '</td>'
}
sb << '</tr><tr class="prop" style="height:40px">'
for ( t in tasks ) {
// 计算员工签名：超时自动提交的显示“系统提交”，签名不存在则显示“已处理”
String sigid = null;
if ( t.prevStatus == "timeout" ) {
sigid = "wf_timeout"
} else if ( t.decisiveOpinion == "不同意" ) {
sigid = "wf_disagree";
} else if ( t.transition == "否决" ) {
sigid = "wf_reject";
} else if ( t.actorSubstituteId ) {
sigid = t.actorSubstitute?.isSignatureExists() ? t.actorSubstitute.id : "wf_done";
} else {
sigid = t.actor?.isSignatureExists() ? t.actor.id : "wf_done";
}
// 渲染单元格
sb << '<td class="valueX" style="text-align:center; vertical-align:middle; padding:0px; width:100px">'
if ( t.status=="completed" ) {
sb << '<img width="100" height="40" src="' << createLink(controller:'app', action:'signatureImage', id:sigid) << '"/>'
} else {
sb << "&nbsp;"
}
sb << '</td>'
}
}

上述代码中，显示签名图片的是一个app控制器的 signatureImage 操作，代码如下：

/**
* 显示签名图片（不加水印），用于签名栏
*/
def signatureImage() {
String filename = Employee.signatureFilename(params.id);
File file = new File(filename);
if ( file.exists() && file.canRead() ) {
// 渲染图片，并设置1天的浏览器缓存
osmEmployeeService.renderSignature( response, filename, false,
[validFor:86400, shared:true, store:true, lastModified:file.lastModified()] );
} else render "";
}

此外，也可以直接使用产品中显示签名图片的控制器操作 osm/signatureImage，该操作将返回一个添加了干扰水印的签名图片。

定义完手写签名的taglib后，在form、edit页面上面添加foo:signature标签即可。代码如下：

<div class="body">
<foo:signature bean="${xxxInstance}" />
<h1>${entityName}</h1>
....
</div>

总之，根据需求的不同，我们可以很灵活的定义不同的规则，展示出不同显示效果的签名栏。

标签： BroBPM BroFramework
在2014-01-03 20:10上被李小翔创建

- 评论 （0）
- 附件 （1）
- 记录
- 信息

# 快速链接

- 首页
- 新闻动态
- FAQ

Copyright © 2013 北京博瑞开源软件有限公司
京ICP备12048974号