欢迎进入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;
  • 当为已办、或代理人打开待办、或者非任一人处理时,签名的标题处显示“代理”二字;
  • 超时自动提交的任务签名处显示“系统提交”;
  • 否决的任务签名处显示“否决”;
  • 决定性意见为“不同意”的任务签名处显示“不同意”;
  • 其他办理完毕的任务签名不存在,签名处则显示“已处理”。

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

签名栏.png

由于这是一个共性的需求,可以自定义一个显示手写签名栏的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上被李小翔创建

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