供稿人:许庆洋
表单中,可以使用标签 p:opinions 来显示流转意见,如:
<tr class="prop">
<td valign="top" class="name">流转意见:</td>
<td valign="top" class="value" colspan="3"><p:opinions bean="${xxxInstance}"/></td>
</tr>
默认的显示效果如下图所示:

可以通过css样式表来设置显示效果。
默认的样式表为:
/** 流转意见 */
.wfopinions {
table: 100%;
table.wfopinionstable {
width: 100%;
border-width: 0px;
padding: 0px 0px;
th, td {
border: none;
}
}
/* 意见签名图片 */
img.signature {
border-width: 0px;
width: 80px;
}
/* 意见签名 */
span.signature {
}
/* 意见签名后面的“代”字 */
span.substitute {
}
/* 意见签名中的机构名称 */
span.organization {
}
/* 意见时间 */
span.time {
}
/* 意见中的员工头衔 */
span.title {
}
}
注:上面是 less 语法的样式,转换成css,就是 ".wfopinions table {...}"、“.wfopinions span.signature” 等规则。
比如,设置签名字体:
/* 意见签名 */
.wfopinions span.signature {
font-size: 16px;
font-family: 华文行楷, 华文楷体, 楷体, 黑体, 宋体;
font-weight: bold;
}
当然,opinions 标签中提供其他选项来控制意见显示的内容,比如以前标签过滤、环节过滤、排序方式、显示机构名称、显示手写签名等十多个选项,如果这些预制选项还无法满足显示需求的话,可以通过 htmlFormater、formater 定制选项,甚至调用API来编写完全自定义的 opinions 标签。
如下图所示:

taglib的代码如下,通过 htmlFormater 选项来自定义每行意见的显示内容和效果:
class FoobarLib {
/** 命名空间为 foo */
static namespace = "foo"
/** 自定义的意见tag */
def opinions = { attrs, body ->
if ( !attrs.bean?.id ) return;
// 设置属性
attrs.override = 1; // 只显示最近一条
attrs.datetime = 1; // 显示日期和时间
attrs.order = 11; // 按时间顺序+同一机构的放在一起
attrs.title = 1; // 显示头衔
attrs.organization = 1; // 显示最低一级机构名称
attrs.blank = 1; // 空意见仅显示前面+时间
// 格式化意见
attrs.htmlFormater = {Map map, StringBuilder sb->
// 机构、头衔
if ( map.title!="董事长" && map.title!="总裁" ) {
sb << "<tr title='"<< map.status << "'><td style='text-align:left'>"
sb << "<span class='organization'>" << map.organization << "</span>";
if ( map.title ) sb << "<span class='title'>(" << map.title << ")</span>";
sb << ":</td></tr>"
}
// 意见正文
if ( map.content ) {
sb << "<tr title='"<< map.status << "'><td style='text-align:left'>"
sb << " " << map.content << "</td></tr>"
}
// 签名
sb << "<tr title='" << map.status << "'><td style='text-align:right'><nobr>";
sb << "<span class='signature'>" << map.signature << "</span>";
if ( map.substitute ) sb << "<span class='substitute'>(" << map.substitute << ")</span>";
sb << " <span class='time'>" << map.time << "</span></nobr></td></tr>";
}
// 输出
out << p.opinions(attrs, body);
}
}

taglib的代码如下,通过 formater 选项来完全自定义意见的显示内容和效果:
/**
* 意见标签
* @see ProcessTagLib.opinions
* @attr bean REQUIRED
*/
def opinions = { attrs, body ->
if ( !attrs.bean?.id ) return;
// 设置属性
attrs.datetime = 1; // 显示日期和时间
attrs.order = 1; // 按时间顺序
attrs.title = 1; // 显示头衔
attrs.organization = 1; // 显示最低一级机构名称
attrs.blank = 1; // 空意见仅显示前面+时间
attrs.formater = {List maps, StringBuilder sb->
if ( maps ) {
sb << "<div class='wfopinions'><table class='wfopinionstable'>"
sb << """<tr class='prop'><td valign='top' class='name' colspan='5' style='text-align:center'>
${message(code:"bropen.bpm.instance.ProcessInstance.opinions")}</td></tr>"""
sb << """<tr class='prop'>
<td class='name' style='text-align:center; width:10%'>办理人</td>
<td class='name' style='text-align:center; width:15%'>组织</td>
<td class='name' style='text-align:center; width:15%'>岗位</td>
<td class='name' style='text-align:center; width:15%'>提交时间</td>
<td class='name' style='text-align:center; width:45%'>意见</td>
</tr>"""
for ( Integer i = 0; i < maps.size(); i++ ) {
Map map = maps[i];
sb << "<tr class='prop'>"
sb << "<td class='value' style='width:10%'>" << map.signature << "</td>"
sb << "<td class='value' style='width:15%'>" << map.organization << "</td>"
sb << "<td class='value' style='width:15%'>" << (map.title ?: "员工") << "</td>"
sb << "<td class='value' style='width:15%'>" << DateUtils.formatDatetime(map.opinion.task.completeTime) << "</td>"
// 提交路径 + 意见
String displayTransition = map.opinion.task.displayTransition; // 提交路径
sb << "<td class='value' style='width:45%'>" << (displayTransition ? (displayTransition + "。") : "")
sb << (map.content ?: "") << "</td>"
sb << "</tr>"
}
sb << "</table></div>"
} else sb << ""
}
// 输出
out << p.opinions(attrs, body);
}
表单页面(gsp)代码如下:
<div>
<table>…</table>
<foo:opinions bean="${xxxInstance}"/>
</div>