最早接触到knockoutJs应该是2017年初了。彼时只是了解了一下大概信息,写了一些简单的例子,没有怎么实际使用过。现在有机会再项目中实战一下,记录一下。
1.项目架构
前端:bootstrap和layer负责页面展示,knockoutJs负责mvvm,引入jquery.validate做表单校验,还有一些其他不怎么重要的,这里没有涉及到,不介绍
后端:ssm框架
2.功能介绍
做一个工作流管理的菜单,没有采用图像化的做法,直接做数据库的增删改查。包括:工作流信息、工作流节点信息、节点关联关系
3.引入knockoutJs
因为是后期功能,因此没有在项目全局引用,只在涉及到的功能中引用了
<%--knockoutJS,mvvm框架的前端部分,可以实现页面数据和js对象的相互映射--%>
<script type="text/javascript" src="${pageContext.request.contextPath}/home/js/knockout-3.4.2.js"></script>
<%--knockoutJS的一个帮助工具:将普通的json对象转为knocoutJS所需的对象--%>
<script type="text/javascript" src="${pageContext.request.contextPath}/home/js/knockout.mapping.js"></script>4
1
<%--knockoutJS,mvvm框架的前端部分,可以实现页面数据和js对象的相互映射--%>2
<script type="text/javascript" src="${pageContext.request.contextPath}/home/js/knockout-3.4.2.js"></script>3
<%--knockoutJS的一个帮助工具:将普通的json对象转为knocoutJS所需的对象--%>4
<script type="text/javascript" src="${pageContext.request.contextPath}/home/js/knockout.mapping.js"></script>knockout.mapping.js是一个knockout的帮助工具。后边会说明,使用时,必须放在knockoutJS之后
4.前端JSP
基本结构如下
每一个div对应js中的一个ViewModel

代码如下,其中涉及到knockout的标签是data-bind=“”
data-bind="click: $root.init" 表示给元素绑定点击事件$root.init,$root代表的是绑定到该div的viewmodel,后边讲解js代码的时候会提到
data-bind="textinput:queryFlowName" 表示input中的value对应ko监控的值queryFlowName,textinpu是在form中可以输入的元素中使用的,如果只是显示的元素,如td,则用text(data-bind="text: flowName")
foreach循环如下:
<tbody id="linkTbody" data-bind="foreach: linkItems">
<tr>
<td data-bind="text: $index() + 1"></td>
<td>
<a href="#" data-bind="click : $root.delLink">删除</a>
<a href="#" data-bind="click : $root.updLink">修改</a>
</td>
<td data-bind="text: linkName"></td>
<td data-bind="text: nodeName"></td>
<td data-bind="text: nextNodeName"></td>
<td data-bind="text: conditionStatus"></td>
</tr>
</tbody>13
1
<tbody id="linkTbody" data-bind="foreach: linkItems">2
<tr>3
<td data-bind="text: $index() + 1"></td>4
<td>5
<a href="#" data-bind="click : $root.delLink">删除</a>6
<a href="#" data-bind="click : $root.updLink">修改</a>7
</td>8
<td data-bind="text: linkName"></td>9
<td data-bind="text: nodeName"></td>10
<td data-bind="text: nextNodeName"></td>11
<td data-bind="text: conditionStatus"></td>12
</tr>13
</tbody>其中data-bind="foreach: linkItems"表示要迭代的数组名称是viewmodel中的linkItems
data-bind="text: $index() + 1" 循环时会有一个默认的索引$index(),从0开始
后边的linkName、nodeName等都是linkItems中的对象中拥有的key
绑定多个事件
data-bind="event : {click : $root.trClick, mouseover : $root.trMouseOver, mouseout : $root.trMouseout}"1
1
data-bind="event : {click : $root.trClick, mouseover : $root.trMouseOver, mouseout : $root.trMouseout}"<%--knockoutJS,mvvm框架的前端部分,可以实现页面数据和js对象的相互映射--%>
<script type="text/javascript" src="${pageContext.request.contextPath}/home/js/knockout-3.4.2.js"></script>
<%--knockoutJS的一个帮助工具:将普通的json对象转为knocoutJS所需的对象--%>
<script type="text/javascript" src="${pageContext.request.contextPath}/home/js/knockout.mapping.js"></script>
<style>
#linkEditDiv td:nth-child(odd){
text-align: right;
}
#linkEditDiv td:nth-child(even){
text-align: left;
}
#linkEditDiv tr{
height:50px;
}
</style>
<%--流程列表--%>
<div class="row">
<div class="col-md-12" style="text-align:left" id='sysFlowManagePage'>
<!-- 按钮工具栏 -->
<ol class="breadcrumb" style="height:25px;padding-top:0;">
<li>
<a href="javascript:void(0);" data-bind="click: $root.init">
<span class="glyphicon glyphicon-search" style=" font-weight:bold;"></span> 搜索
</a>
</li>
<li>
<a href="javascript:void(0);" data-bind="click: $root.addFlow">
<span class="glyphicon glyphicon-plus" style=" font-weight:bold;"></span> 新增
</a>
</li>
</ol>
<form id="queryPage">
<table style="width:100%;" align="center">
<caption class="table-caption"></caption>
<tbody>
<tr>
<td class="query_td_right">流程名称:</td>
<td class="query_td_left">
<input type="text" class="txt query-text" name="flowName" data-bind="textinput:queryFlowName">
</td>
</tr>
</tbody>
</table>
</form>
<br/>
<!-- 查询条件table end-->
<!-- 列表 -->
<div class="table-responsive">
<table id="cpaAcctAppList" align="center" class="table table-striped table-bordered table-hover table-fix">
<thead>
<tr>
<th width="50">序号 </th>
<th width="70">操作 </th>
<th width="150">流程名称</th>
<th width="150">流程描述</th>
</tr>
</thead>
<tbody data-bind="foreach: items">
<tr>
<td data-bind="text: $index() + 1"></td>
<td>
<a href="#" data-bind="click: $root.updFlow">修改</a>
<a href="#" data-bind="click: $root.delFlow">删除</a>
<a href="#" data-bind="click: $root.opeFlowLink">流程连接</a>
<a href="#" data-bind="click: $root.opeFlowNode">流程节点</a>
</td>
<td data-bind="text: flowName"></td>
<td data-bind="text: flowDesc"></td>
</tr>
</tbody>
</table>
</div>
</div>
</div>
<%--修改流程信息--%>
<div id="flowEditDiv" style="display: none;height:140px;" class="layer_notice">
<ol class="breadcrumb" style="height:25px;padding-top:0;text-align: left;">
<li>
<a data-bind="click : $root.ok" href="javascript:void(0);">
<span class="glyphicon glyphicon-search" style=" font-weight:bold;"></span> 保存
</a>
</li>
<li>
<a data-bind="click : $root.no" href="javascript:void(0);">
<span class="glyphicon glyphicon-search" style=" font-weight:bold;"></span> 取消
</a>
</li>
</ol>
<form role="form" id="updFlowForm">
<div style="margin-top: 20px">
<label>流程名称:</label><input data-bind="textinput:flowItem.flowName" type="text" name="roleName" style="width: 180px;"/>
</div>
<div style="padding:10px 0 10px 0">
<label>流程描述:</label><input data-bind="textinput:flowItem.flowDesc" type="text" name="roleDesc" style="width: 180px;"/>
</div>
</form>
</div>
<%--显示连接列表--%>
<div id="flowLinkDiv" style="display: none;height:300px;margin:0 auto;" class="layer_notice">
<ol class="breadcrumb" style="height:25px;padding-top:0;text-align: left;">
<li>
<a data-bind="click : $root.addLink" href="javascript:void(0);">
<span class="glyphicon glyphicon-search" style=" font-weight:bold;"></span> 新增
</a>
</li>
</ol>
<div class="table-responsive" style="width:800px;margin:10px auto;">
<table id="linkTable" align="center" class="table table-striped table-bordered table-hover table-fix">
<thead>
<tr>
<th width="50">序号</th>
<th width="70">操作</th>
<th width="150">连接名称</th>
<th width="150">节点名称</th>
<th width="150">下一个节点名称</th>
<th width="100">条件状态</th>
</tr>
</thead>
<tbody id="linkTbody" data-bind="foreach: linkItems">
<tr>
<td data-bind="text: $index() + 1"></td>
<td>
<a href="#" data-bind="click : $root.delLink">删除</a>
<a href="#" data-bind="click : $root.updLink">修改</a>
</td>
<td data-bind="text: linkName"></td>
<td data-bind="text: nodeName"></td>
<td data-bind="text: nextNodeName"></td>
<td data-bind="text: conditionStatus"></td>
</tr>
</tbody>
</table>
</div>
</div>
<%--修改节点信息--%>
<div id="nodeEditDiv" style="display: none;height:190px;" class="layer_notice">
<ol class="breadcrumb" style="height:25px;padding-top:0;text-align: left;">
<li>
<a data-bind="click : $root.ok" href="javascript:void(0);">
<span class="glyphicon glyphicon-search" style=" font-weight:bold;"></span> 保存
</a>
</li>
<li>
<a data-bind="click : $root.no" href="javascript:void(0);">
<span class="glyphicon glyphicon-search" style=" font-weight:bold;"></span> 取消
</a>
</li>
</ol>
<form role="form" id="updNodeForm">
<table style="margin: 15px 40px;">
<tr style="height: 50px;">
<td>节点名称:</td>
<td>
<input data-bind="textinput:nodeItem.nodeName" type="text" name="" style="width: 180px;" required/>
</td>
<td>所属流程:</td>
<td>
<input data-bind="textinput:nodeItem.flowName" type="text" name="flowName" style="width: 180px;" readonly required/>
<span class="glyphicon glyphicon-search" data-bind="click:$root.selFlow"/>
</td>
</tr>
<tr style="height: 50px;">
<td>节点描述:</td>
<td><input data-bind="textinput:nodeItem.nodeDesc" type="text" name="nodeDesc" style="width: 180px;"/></td>
<td>节点类型:</td>
<td>
<input data-bind="textinput:nodeItem.nodeType" type="text" name="_nodeType" style="width: 180px;" required/>
</td>
</tr>
</table>
</form>
</div>
<%--修改连接信息--%>
<div id="linkEditDiv" style="display: none;height:190px;;" class="layer_notice">
<ol class="breadcrumb" style="height:25px;padding-top:0;text-align: left;">
<li>
<a data-bind="click : $root.ok" href="javascript:void(0);">
<span class="glyphicon glyphicon-search" style=" font-weight:bold;"></span> 保存
</a>
</li>
<li>
<a data-bind="click : $root.no" href="javascript:void(0);">
<span class="glyphicon glyphicon-search" style=" font-weight:bold;"></span> 取消
</a>
</li>
</ol>
<form role="form" id="updLinkForm">
<table style="margin: 15px 40px;">
<tr>
<td>流转名称:</td>
<td><input data-bind="textinput:item.linkName" type="text" name="linkName" style="width: 180px;" required/></td>
<td>本节点名称:</td>
<td>
<input data-bind="textinput:item.nodeName" type="text" name="_nodeName" style="width: 180px;" readonly required/>
<span class="glyphicon glyphicon-search" data-bind="click:$root.selNode" desc="node"/>
</td>
</tr>
<tr>
<td>流转描述:</td>
<td><input data-bind="textinput:item.linkDesc" type="text" name="linkDesc" style="width: 180px;"/></td>
<td>下个节点名称:</td>
<td>
<input data-bind="textinput:item.nextNodeName" type="text" name="nextNodeName" style="width: 180px;" readonly required/>
<span class="glyphicon glyphicon-search" data-bind="click:$root.selNextNode" desc="nextNode"/>
</td>
</tr>
<tr>
<td>条件状态:</td>
<td>
<input data-bind="textinput:item.conditionStatus" type="text" name="conditionStatus" style="width: 180px;" required/>
</td>
<td></td>
<td></td>
</tr>
</table>
</form>
</div>
<%--选择节点--%>
<div id="selNode" style="display: none;height:50px;margin:0 auto;" class="layer_notice">
<ol class="breadcrumb" style="height:25px;padding-top:0;text-align: left;">
<li>
<a data-bind="click : $root.addNode" href="javascript:void(0);">
<span class="glyphicon glyphicon-search" style=" font-weight:bold;"></span> 新增
</a>
</li>
</ol>
<div class="table-responsive" style="width: 800px;margin: 10px auto;">
<table id="nodeTable" align="center" class="table table-striped table-bordered table-hover table-fix">
<thead>
<tr>
<th width="50">序号</th>
<th width="60">操作</th>
<th width="150">节点名称</th>
<th width="50">节点类型</th>
<th width="150">节点描述</th>
</tr>
</thead>
<tbody id="nodeTbody" data-bind="foreach: nodeItems">
<tr data-bind="event : {click : $root.trClick, mouseover : $root.trMouseOver, mouseout : $root.trMouseout}">
<td data-bind="text: $index() + 1"></td>
<td>
<a href="javascript:void(0)" data-bind="click : $root.updNode">修改</a>
<a href="javascript:void(0)" data-bind="click : $root.delNode">删除</a>
</td>
<td data-bind="text: nodeName"></td>
<td data-bind="text: nodeType"></td>
<td data-bind="text: nodeDesc"></td>
</tr>
</tbody>
</table>
</div>
</div>
<%--选择流程--%>
<div id="selFlow" style="display: none;height:50px;margin:0 auto;" class="layer_notice">
<div class="table-responsive" style="width: 800px;margin: 10px auto;">
<table id="flowTable" align="center" class="table table-striped table-bordered table-hover table-fix">
<thead>
<tr>
<th width="50">序号</th>
<%--<th width="60">操作</th>--%>
<th width="150">流程名称</th>
<th width="150">流程描述</th>
</tr>
</thead>
<tbody id="flowTbody" data-bind="foreach: flowItems">
<tr data-bind="event : {click : $root.trClick, mouseover : $root.trMouseOver, mouseout : $root.trMouseout}">
<td data-bind="text: $index() + 1"></td>
<%--<td>--%>
<%--<a href="javascript:void(0)" data-bind="click : $root.updNode">修改</a>--%>
<%--<a href="javascript:void(0)" data-bind="click : $root.delNode">删除</a>--%>
<%--</td>--%>
<td data-bind="text: flowName"></td>
<td data-bind="text: flowDesc"></td>
</tr>
</tbody>
</table>
</div>
</div>
<script type="text/javascript" src="${pageContext.request.contextPath}/js/sys_flow/sys_flow.js"></script>285
1
<%--knockoutJS,mvvm框架的前端部分,可以实现页面数据和js对象的相互映射--%>2
<script type="text/javascript" src="${pageContext.request.contextPath}/home/js/knockout-3.4.2.js"></script>3
<%--knockoutJS的一个帮助工具:将普通的json对象转为knocoutJS所需的对象--%>4
<script type="text/javascript" src="${pageContext.request.contextPath}/home/js/knockout.mapping.js"></script>5
<style>6
#linkEditDiv td:nth-child(odd){7
text-align: right;8
}9
#linkEditDiv td:nth-child(even){10
text-align: left;11
}12
#linkEditDiv tr{13
height:50px;14
}15
</style>16
<%--流程列表--%>17
<div class="row">18
<div class="col-md-12" style="text-align:left" id='sysFlowManagePage'>19
<!-- 按钮工具栏 -->20
<ol class="breadcrumb" style="height:25px;padding-top:0;">21
<li>22
<a href="javascript:void(0);" data-bind="click: $root.init">23
<span class="glyphicon glyphicon-search" style=" font-weight:bold;"></span> 搜索24
</a>25
</li>26
<li>27
<a href="javascript:void(0);" data-bind="click: $root.addFlow">28
<span class="glyphicon glyphicon-plus" style=" font-weight:bold;"></span> 新增29
</a>30
</li>31
</ol>32
<form id="queryPage">33
<table style="width:100%;" align="center">34
<caption class="table-caption"></caption>35
<tbody>36
<tr>37
<td class="query_td_right">流程名称:</td>38
<td class="query_td_left">39
<input type="text" class="txt query-text" name="flowName" data-bind="textinput:queryFlowName">40
</td>41
</tr>42
</tbody>43
</table>44
</form>45
<br/>46
<!-- 查询条件table end-->47
<!-- 列表 -->48
<div class="table-responsive">49
<table id="cpaAcctAppList" align="center" class="table table-striped table-bordered table-hover table-fix">50
<thead>51
<tr>52
<th width="50">序号 </th>53
<th width="70">操作 </th>54
<th width="150">流程名称</th>55
<th width="150">流程描述</th>56
</tr>57
</thead>58
<tbody data-bind="foreach: items">59
<tr>60
<td data-bind="text: $index() + 1"></td>61
<td>62
<a href="#" data-bind="click: $root.updFlow">修改</a>63
<a href="#" data-bind="click: $root.delFlow">删除</a>64
<a href="#" data-bind="click: $root.opeFlowLink">流程连接</a>65
<a href="#" data-bind="click: $root.opeFlowNode">流程节点</a>66
</td>67
<td data-bind="text: flowName"></td>68
<td data-bind="text: flowDesc"></td>69
</tr>70
</tbody>71
</table>72
</div>73
</div>74
</div>75
76
<%--修改流程信息--%>77
<div id="flowEditDiv" style="display: none;height:140px;" class="layer_notice">78
<ol class="breadcrumb" style="height:25px;padding-top:0;text-align: left;">79
<li>80
<a data-bind="click : $root.ok" href="javascript:void(0);">81
<span class="glyphicon glyphicon-search" style=" font-weight:bold;"></span> 保存82
</a>83
</li>84
<li>85
<a data-bind="click : $root.no" href="javascript:void(0);">86
<span class="glyphicon glyphicon-search" style=" font-weight:bold;"></span> 取消87
</a>88
</li>89
</ol>90
<form role="form" id="updFlowForm">91
<div style="margin-top: 20px">92
<label>流程名称:</label><input data-bind="textinput:flowItem.flowName" type="text" name="roleName" style="width: 180px;"/>93
</div>94
<div style="padding:10px 0 10px 0">95
<label>流程描述:</label><input data-bind="textinput:flowItem.flowDesc" type="text" name="roleDesc" style="width: 180px;"/>96
</div>97
</form>98
</div>99
<%--显示连接列表--%>100
<div id="flowLinkDiv" style="display: none;height:300px;margin:0 auto;" class="layer_notice">101
<ol class="breadcrumb" style="height:25px;padding-top:0;text-align: left;">102
<li>103
<a data-bind="click : $root.addLink" href="javascript:void(0);">104
<span class="glyphicon glyphicon-search" style=" font-weight:bold;"></span> 新增105
</a>106
</li>107
</ol>108
<div class="table-responsive" style="width:800px;margin:10px auto;">109
<table id="linkTable" align="center" class="table table-striped table-bordered table-hover table-fix">110
<thead>111
<tr>112
<th width="50">序号</th>113
<th width="70">操作</th>114
<th width="150">连接名称</th>115
<th width="150">节点名称</th>116
<th width="150">下一个节点名称</th>117
<th width="100">条件状态</th>118
</tr>119
</thead>120
<tbody id="linkTbody" data-bind="foreach: linkItems">121
<tr>122
<td data-bind="text: $index() + 1"></td>123
<td>124
<a href="#" data-bind="click : $root.delLink">删除</a>125
<a href="#" data-bind="click : $root.updLink">修改</a>126
</td>127
<td data-bind="text: linkName"></td>128
<td data-bind="text: nodeName"></td>129
<td data-bind="text: nextNodeName"></td>130
<td data-bind="text: conditionStatus"></td>131
</tr>132
</tbody>133
</table>134
</div>135
</div>136
137
<%--修改节点信息--%>138
<div id="nodeEditDiv" style="display: none;height:190px;" class="layer_notice">139
<ol class="breadcrumb" style="height:25px;padding-top:0;text-align: left;">140
<li>141
<a data-bind="click : $root.ok" href="javascript:void(0);">142
<span class="glyphicon glyphicon-search" style=" font-weight:bold;"></span> 保存143
</a>144
</li>145
<li>146
<a data-bind="click : $root.no" href="javascript:void(0);">147
<span class="glyphicon glyphicon-search" style=" font-weight:bold;"></span> 取消148
</a>149
</li>150
</ol>151
<form role="form" id="updNodeForm">152
<table style="margin: 15px 40px;">153
<tr style="height: 50px;">154
<td>节点名称:</td>155
<td>156
<input data-bind="textinput:nodeItem.nodeName" type="text" name="" style="width: 180px;" required/>157
</td>158
<td>所属流程:</td>159
<td>160
<input data-bind="textinput:nodeItem.flowName" type="text" name="flowName" style="width: 180px;" readonly required/>161
<span class="glyphicon glyphicon-search" data-bind="click:$root.selFlow"/>162
</td>163
</tr>164
<tr style="height: 50px;">165
<td>节点描述:</td>166
<td><input data-bind="textinput:nodeItem.nodeDesc" type="text" name="nodeDesc" style="width: 180px;"/></td>167
<td>节点类型:</td>168
<td>169
<input data-bind="textinput:nodeItem.nodeType" type="text" name="_nodeType" style="width: 180px;" required/>170
</td>171
</tr>172
</table>173
</form>174
</div>175
176
<%--修改连接信息--%>177
<div id="linkEditDiv" style="display: none;height:190px;;" class="layer_notice">178
<ol class="breadcrumb" style="height:25px;padding-top:0;text-align: left;">179
<li>180
<a data-bind="click : $root.ok" href="javascript:void(0);">181
<span class="glyphicon glyphicon-search" style=" font-weight:bold;"></span> 保存182
</a>183
</li>184
<li>185
<a data-bind="click : $root.no" href="javascript:void(0);">186
<span class="glyphicon glyphicon-search" style=" font-weight:bold;"></span> 取消187
</a>188
</li>189
</ol>190
<form role="form" id="updLinkForm">191
<table style="margin: 15px 40px;">192
<tr>193
<td>流转名称:</td>194
<td><input data-bind="textinput:item.linkName" type="text" name="linkName" style="width: 180px;" required/></td>195
<td>本节点名称:</td>196
<td>197
<input data-bind="textinput:item.nodeName" type="text" name="_nodeName" style="width: 180px;" readonly required/>198
<span class="glyphicon glyphicon-search" data-bind="click:$root.selNode" desc="node"/>199
</td>200
</tr>201
<tr>202
<td>流转描述:</td>203
<td><input data-bind="textinput:item.linkDesc" type="text" name="linkDesc" style="width: 180px;"/></td>204
<td>下个节点名称:</td>205
<td>206
<input data-bind="textinput:item.nextNodeName" type="text" name="nextNodeName" style="width: 180px;" readonly required/>207
<span class="glyphicon glyphicon-search" data-bind="click:$root.selNextNode" desc="nextNode"/>208
</td>209
</tr>210
<tr>211
<td>条件状态:</td>212
<td>213
<input data-bind="textinput:item.conditionStatus" type="text" name="conditionStatus" style="width: 180px;" required/>214
</td>215
<td></td>216
<td></td>217
</tr>218
</table>219
</form>220
</div>221
222
<%--选择节点--%>223
<div id="selNode" style="display: none;height:50px;margin:0 auto;" class="layer_notice">224
<ol class="breadcrumb" style="height:25px;padding-top:0;text-align: left;">225
<li>226
<a data-bind="click : $root.addNode" href="javascript:void(0);">227
<span class="glyphicon glyphicon-search" style=" font-weight:bold;"></span> 新增228
</a>229
</li>230
</ol>231
<div class="table-responsive" style="width: 800px;margin: 10px auto;">232
<table id="nodeTable" align="center" class="table table-striped table-bordered table-hover table-fix">233
<thead>234
<tr>235
<th width="50">序号</th>236
<th width="60">操作</th>237
<th width="150">节点名称</th>238
<th width="50">节点类型</th>239
<th width="150">节点描述</th>240
</tr>241
</thead>242
<tbody id="nodeTbody" data-bind="foreach: nodeItems">243
<tr data-bind="event : {click : $root.trClick, mouseover : $root.trMouseOver, mouseout : $root.trMouseout}">244
<td data-bind="text: $index() + 1"></td>245
<td>246
<a href="javascript:void(0)" data-bind="click : $root.updNode">修改</a>247
<a href="javascript:void(0)" data-bind="click : $root.delNode">删除</a>248
</td>249
<td data-bind="text: nodeName"></td>250
<td data-bind="text: nodeType"></td>251
<td data-bind="text: nodeDesc"></td>252
</tr>253
</tbody>254
</table>255
</div>256
</div>257
258
<%--选择流程--%>259
<div id="selFlow" style="display: none;height:50px;margin:0 auto;" class="layer_notice">260
<div class="table-responsive" style="width: 800px;margin: 10px auto;">261
<table id="flowTable" align="center" class="table table-striped table-bordered table-hover table-fix">262
<thead>263
<tr>264
<th width="50">序号</th>265
<%--<th width="60">操作</th>--%>266
<th width="150">流程名称</th>267
<th width="150">流程描述</th>268
</tr>269
</thead>270
<tbody id="flowTbody" data-bind="foreach: flowItems">271
<tr data-bind="event : {click : $root.trClick, mouseover : $root.trMouseOver, mouseout : $root.trMouseout}">272
<td data-bind="text: $index() + 1"></td>273
<%--<td>--%>274
<%--<a href="javascript:void(0)" data-bind="click : $root.updNode">修改</a>--%>275
<%--<a href="javascript:void(0)" data-bind="click : $root.delNode">删除</a>--%>276
<%--</td>--%>277
<td data-bind="text: flowName"></td>278
<td data-bind="text: flowDesc"></td>279
</tr>280
</tbody>281
</table>282
</div>283
</div>284
285
<script type="text/javascript" src="${pageContext.request.contextPath}/js/sys_flow/sys_flow.js"></script>5.前端js
基本结构如下

代码如下
(function () {
var linkUpdLayer,selNodeLayer,selFlowLayer,flowUpdLayer,
flowList ,
baseUrl = contentPath + '/sysFlow/';
$(function () {
var vm = new TableViewModel();
ko.applyBindings(vm, $('#sysFlowManagePage')[0]);
vm.init();
});
/**
* 流程节点ViewModel
* @param linkItemKo 连接信息
* @param flowItemKo 流程信息
* @param t 类型
* @constructor
*/
function NodeListViewModel(linkItemKo,flowItemKo,t) {
var self = this;
self.linkItem = linkItemKo;
self.flowItem = flowItemKo;
self.nodeItems = ko.observableArray([]);
//是否是流程操作
self.isLink = (function () {
return !(linkItemKo === null || !linkItemKo);
})();
//初始化节点列表
self.initNodeTable = function () {
$.postAjax(baseUrl+'queryFlowNode',{flowId:self.isLink?linkItemKo.flowId():flowItemKo.flowId()},function (data) {
self.nodeItems(data);
});
};
//鼠标移入事件
self.trMouseOver = function (item,event) {
if(!self.isLink){
return;
}
trMouseOver($(event.target));
};
//鼠标移出事件
self.trMouseout = function(){
if(!self.isLink){
return;
}
trMouseOut($(event.target));
};
//鼠标点击事件
self.trClick = function (item) {
if(!self.isLink){
return;
}
switch (t){
case 1:
self.linkItem.nodeName(item.nodeName);
self.linkItem.nodeId(item.nodeId);
break;
case 2:
self.linkItem.nextNodeName(item.nodeName);
self.linkItem.nextNodeId(item.nodeId);
break;
}
if(selNodeLayer){
layer.close(selNodeLayer);
}
};
self.addNode = function () {
var newNodeItem = {
flowId: '',flowName : '',
nodeName: '', nodeDesc: '',nodeType:''
};
var nodeItemKo = new NodeEditViewModel(ko.mapping.fromJS(newNodeItem),self);
nodeOpenCommon('新增节点信息',nodeItemKo);
};
//删除节点
self.delNode = function(item){
if($.isEmpty(item.nodeId)){
layer.alert('发生错误!');
return;
}
var con = layer.confirm('确定删除该记录吗?', {
btn: ['确定','取消'] //按钮
}, function(){
layer.close(con);
$.postAjax(baseUrl+'delNode',{nodeId : item.nodeId},function (state) {
if(state.success){
layer.msg('删除成功!');
self.initNodeTable();
}else{
layer.alert('删除失败!失败原因:' + state.message);
}
});
}, function(){});
};
//修改节点
self.updNode = function (item) {
if(flowList){
for(var i=0;i<flowList.length;i++){
if(item.flowId === flowList[i].flowId){
item.flowName = flowList[i].flowName;
break;
}
}
}
var updNodeVm = new NodeEditViewModel(ko.mapping.fromJS(item),self);
nodeOpenCommon('修改节点信息',updNodeVm);
};
/**
* 节点信息修改ViewModel
* @param nodeItemKo
* @param nodeTableViewModel
* @constructor
*/
function NodeEditViewModel(nodeItemKo,nodeTableViewModel) {
var nodeSelf = this;
nodeSelf.nodeItem = nodeItemKo;
//取消
nodeSelf.no = function () {
closeNodeOpenCommon();
};
//选择流程
nodeSelf.selFlow = function (item) {
var selFlow = document.getElementById('selFlow'),
$flowTbody = $('#flowTbody'),
flowTbodyHtml = $flowTbody.html(),
flowListViewModel = new FlowListViewModel(ko.mapping.fromJS(item));
selFlowLayer = layer.open({
title: '选择流程',
type: 1,
content: $(selFlow),
area: ['880px', '460px'],
success : function () {
ko.cleanNode(selFlow);
ko.applyBindings(flowListViewModel, selFlow);
flowListViewModel.initFlowTable();
},
end : function () {
$flowTbody.html(flowTbodyHtml);
$(selFlow).hide();
}
});
};
/**
* 选择流程
* @constructor
*/
function FlowListViewModel(nodeItemKo) {
var self = this;
self.nodeItem = nodeItemKo;
self.flowItems = ko.observableArray([]);
//初始化列表
self.initFlowTable = function () {
$.postAjax(baseUrl + 'queryFlow', null, function (data) {
self.flowItems(data);
});
};
//鼠标移入事件
self.trMouseOver = function () {
trMouseOver($(event.target));
};
//鼠标移出事件
self.trMouseout = function(){
trMouseOut($(event.target));
};
//鼠标点击事件
self.trClick = function (item) {
self.nodeItem.nodeItem.flowName(item.flowName);
self.nodeItem.nodeItem.flowId(item.flowId);
if(selFlowLayer){
layer.close(selFlowLayer);
}
};
}
//校验并保存
nodeSelf.ok = function (item) {
var validateOptions = Validation;
validateOptions.validateOptions = true;
validateOptions.messages = {
_nodeName: '请输入流转名称',
_nodeType : '请输入节点类型',
flowName : '请选择所属流程'
};
validateOptions.errorPlacement = function (label, element) {};
validateOptions.submitHandler=function (form) {};
var $updNodeForm = $('#updNodeForm');
$updNodeForm.validate(validateOptions);
var is = $updNodeForm.valid();
if(!is){
layer.alert('信息不完整');
return;
}
$.postAjax(baseUrl+'saveOrUpdFlowNode',ko.mapping.toJS(item.nodeItem),function (state) {
if(state.success){
layer.alert('保存成功!');
closeNodeOpenCommon();
nodeTableViewModel.initNodeTable();
}else{
layer.alert('保存失败!');
}
});
};
}
// nodeEditDiv
var nodeLayer,
nodeEditDiv = document.getElementById('nodeEditDiv');
/**
* 关闭nodeLayer
*/
function closeNodeOpenCommon() {
if(nodeLayer){
layer.close(nodeLayer);
}
}
/**
* 新增和修改节点信息的公用layer
* @param title
* @param nodeItemKo
*/
function nodeOpenCommon(title,nodeItemKo) {
nodeLayer = layer.open({
title: title,
type: 1,
content: $(nodeEditDiv),
area: ['600px', '250px'],
success : function () {
ko.cleanNode(nodeEditDiv);
ko.applyBindings(nodeItemKo, nodeEditDiv);
},
end : function () {
$(nodeEditDiv).hide();
}
});
}
}
/**
* 修改Link信息
* @constructor
*/
function LinkEditViewModel(linkItem,linkTableViewModel) {
var self = this;
self.item = ko.mapping.fromJS(linkItem);
//选择本节点
self.selNode = function () {
selNodeCommon(1,self.item,null,'选择本节点');
};
//选择下一个节点
self.selNextNode = function () {
selNodeCommon(2,self.item,null,'选择下一个节点');
};
//取消
self.no = function () {
closeLinkUpdLayer();
};
//保存
self.ok = function () {
var validateOptions = Validation;
validateOptions.validateOptions = true;
validateOptions.messages = {
linkName: '请输入流转名称',
conditionStatus : '请输入条件状态',
_nodeName : '请选择本节点名称',
nextNodeName : '请选择下一个节点名称'
};
validateOptions.errorPlacement = function (label, element) {};
validateOptions.submitHandler=function (form) {};
var $updLinkForm = $('#updLinkForm');
$updLinkForm.validate(validateOptions);
var is = $updLinkForm.valid();
if(!is){
layer.alert('信息不完整');
return;
}
$.postAjax(baseUrl+'saveOrUpdFlowLink',ko.mapping.toJS(self.item),function (state) {
if(state.success){
layer.alert('保存成功!');
closeLinkUpdLayer();
linkTableViewModel.initLinkTable();
}else{
layer.alert('保存失败!');
}
});
};
//关闭layer弹出框
function closeLinkUpdLayer() {
if(linkUpdLayer){
layer.close(linkUpdLayer);
}
}
}
/**
* 鼠标移入事件
*/
function trMouseOver() {
var $target = $(event.target);
if($target.is('td')){
$target = $target.parent();
}
$target.css('background-color','#CCFF80');
$target.css('cursor','pointer');
}
/**
* 鼠标移除事件
*/
function trMouseOut() {
var $target = $(event.target);
if($target.is('td')){
$target = $target.parent();
}
$target.css('background-color','white');
$target.css('cursor','pointer');
}
/**
* 流程节点
* @param t
* @param linkItemKo
* @param flowItemKo
* @param title
*/
function selNodeCommon(t,linkItemKo,flowItemKo,title) {
var selNode = document.getElementById('selNode'),
$nodeTbody = $('#nodeTbody'),
nodeTbodyHtml = $nodeTbody.html(),
nodeListViewModel = new NodeListViewModel(linkItemKo,flowItemKo,t);
selNodeLayer = layer.open({
title: title,
type: 1,
content: $(selNode),
area: ['880px', '460px'],
success : function () {
ko.cleanNode(selNode);
ko.applyBindings(nodeListViewModel, selNode);
nodeListViewModel.initNodeTable();
},
end : function () {
$nodeTbody.html(nodeTbodyHtml);
$(selNode).hide();
}
});
}
/**
* 查看流程Link信息的ViewModel
* @param flowItem
* @constructor
*/
function LinkTableViewModel(flowItem){
var self = this;
var linkEditDiv = document.getElementById('linkEditDiv'),flowId = $.trim(flowItem.flowId);
self.linkItems = ko.observableArray([]);
//初始化列表
self.initLinkTable = function () {
$.postAjax(baseUrl + 'selectLinkByFlowId', {flowId : flowId}, function (data) {
self.linkItems(data);
});
};
//新增
self.addLink = function(){
var newItem = {
flowId: flowId,
linkName: '', linkDesc: '',
nodeId: '', nodeName: '',
nextNodeId: '', nextNodeName: '',
conditionStatus: ''
};
var updLinkVm = new LinkEditViewModel(ko.mapping.fromJS(newItem),self);
linkOpenCommon('新增连接信息',updLinkVm);
};
//修改
self.updLink = function (item) {
var updLinkVm = new LinkEditViewModel(item,self);
linkOpenCommon('修改连接信息',updLinkVm);
};
//删除
self.delLink = function (item) {
if($.isEmpty(item.linkId)){
layer.alert('发生错误!');
return;
}
var con = layer.confirm('确定删除该记录吗?', {
btn: ['确定','取消'] //按钮
}, function(){
layer.close(con);
$.postAjax(baseUrl+'delLink',{linkId : item.linkId},function (state) {
if(state.success){
layer.msg('删除成功!');
self.initLinkTable();
}else{
layer.alert('删除失败!失败原因:' + state.message);
}
});
}, function(){});
};
/**
* link的layer弹出框公用部分
* @param title layer弹出框的标题
* @param updLinkVm
*/
function linkOpenCommon(title,updLinkVm) {
linkUpdLayer = layer.open({
title: title,
type: 1,
content: $(linkEditDiv),
area: ['600px', '250px'],
success : function () {
ko.cleanNode(linkEditDiv);//取消绑定
ko.applyBindings(updLinkVm, linkEditDiv);//绑定
},
end :function () {
$(linkEditDiv).hide();
}
});
}
}
/**
* 列表页的ViewModel
* @constructor
*/
function TableViewModel() {
var self = this,
flowEditDiv = document.getElementById('flowEditDiv');
self.items = ko.observableArray([]);
self.queryFlowName = ko.observable('');
self.delFlow = function (item) {
if($.isEmpty(item.flowId)){
layer.alert('发生错误!');
return;
}
//TODO:注意,此处尚未做级联删除
var con = layer.confirm('如果删除该记录,该记录下的节点信息将同时删除.<br>确定删除该记录吗?', {
btn: ['确定','取消'] //按钮
}, function(){
layer.close(con);
$.postAjax(baseUrl+'delFlow',{flowId : item.flowId},function (state) {
if(state.success){
layer.msg('删除成功!');
self.init();
}else{
layer.alert('删除失败!失败原因:' + state.message);
}
});
}, function(){});
};
//新增
self.addFlow = function () {
var addVm = ko.mapping.fromJS({flowName:'',flowDesc:''});
flowOpenCommon(flowEditDiv,addVm,'新增工作流信息');
};
//修改
self.updFlow = function (item) {
var updVm = ko.mapping.fromJS(item);
flowOpenCommon(flowEditDiv,updVm,'修改工作流信息');
};
function flowOpenCommon(flowEditDiv,updVm,title) {
flowUpdLayer = layer.open({
title: title,
type: 1,
content: $(flowEditDiv),
area: ['320px', '215px'],
success:function () {
ko.cleanNode(flowEditDiv);//取消绑定
var flowEditViewModel = new FlowEditViewModel(updVm);
ko.applyBindings(flowEditViewModel, flowEditDiv);//绑定
},
end:function () {
$(flowEditDiv).hide();
}
});
}
//初始化列表
self.init = function () {
$.postAjax(baseUrl + 'queryFlow', {flowName: $.trim(self.queryFlowName())}, function (data) {
flowList = data;
self.items(data);
});
};
//流程节点
self.opeFlowNode = function (item) {
selNodeCommon(null,null,ko.mapping.fromJS(item),'流程节点信息');
};
function FlowEditViewModel(flowItemKo) {
var flowSelf = this;
flowSelf.flowItem = flowItemKo;
flowSelf.ok = function () {
var validateOptions = Validation;
validateOptions.validateOptions = true;
validateOptions.messages = {
flowName: '请输入工作流名称'
};
validateOptions.errorPlacement = function (label, element) {};
validateOptions.submitHandler=function (form) {};
var $updFlowForm = $('#updFlowForm');
$updFlowForm.validate(validateOptions);
var is = $updFlowForm.valid();
if(!is){
layer.alert('信息不完整');
return;
}
$.postAjax(baseUrl+'saveOrUpdFlow',ko.mapping.toJS(flowSelf.flowItem),function (state) {
if(state.success){
layer.alert('保存成功!');
closeFlowLayer();
self.init();
}else{
layer.alert('保存失败!');
}
});
};
flowSelf.no = function () {
closeFlowLayer();
};
function closeFlowLayer() {
if(flowUpdLayer){
layer.close(flowUpdLayer);
}
}
}
//流程连接
self.opeFlowLink = function (item) {
//将tbody缓存起来,解决数据重复问题
var $linkTbody = $('#linkTbody'),
tbody = $linkTbody.html(),
flowLinkDiv = document.getElementById('flowLinkDiv');
linkUpdLayer = layer.open({
title: '工作流连接信息',
type: 1,
content: $(flowLinkDiv),
area: ['880px', '450px'],
success : function(){
var linkTableViewModel = new LinkTableViewModel(item);
ko.cleanNode(flowLinkDiv);//取消绑定
ko.applyBindings(linkTableViewModel, flowLinkDiv);
linkTableViewModel.initLinkTable();
},
end:function () {
$(flowLinkDiv).hide();
$linkTbody.html(tbody);
}
});
}
})();556
1
(function () {2
var linkUpdLayer,selNodeLayer,selFlowLayer,flowUpdLayer,3
flowList ,4
baseUrl = contentPath + '/sysFlow/';5
$(function () {6
var vm = new TableViewModel();7
ko.applyBindings(vm, $('#sysFlowManagePage')[0]);8
vm.init();9
});10
11
/**12
* 流程节点ViewModel13
* @param linkItemKo 连接信息14
* @param flowItemKo 流程信息15
* @param t 类型16
* @constructor17
*/18
function NodeListViewModel(linkItemKo,flowItemKo,t) {19
var self = this;20
self.linkItem = linkItemKo;21
self.flowItem = flowItemKo;22
self.nodeItems = ko.observableArray([]);23
//是否是流程操作24
self.isLink = (function () {25
return !(linkItemKo === null || !linkItemKo);26
})();27
//初始化节点列表28
self.initNodeTable = function () {29
$.postAjax(baseUrl+'queryFlowNode',{flowId:self.isLink?linkItemKo.flowId():flowItemKo.flowId()},function (data) {30
self.nodeItems(data);31
});32
};33
//鼠标移入事件34
self.trMouseOver = function (item,event) {35
if(!self.isLink){36
return;37
}38
trMouseOver($(event.target));39
};40
41
//鼠标移出事件42
self.trMouseout = function(){43
if(!self.isLink){44
return;45
}46
trMouseOut($(event.target));47
};48
//鼠标点击事件49
self.trClick = function (item) {50
if(!self.isLink){51
return;52
}53
switch (t){54
case 1:55
self.linkItem.nodeName(item.nodeName);56
self.linkItem.nodeId(item.nodeId);57
break;58
case 2:59
self.linkItem.nextNodeName(item.nodeName);60
self.linkItem.nextNodeId(item.nodeId);61
break;62
}63
if(selNodeLayer){64
layer.close(selNodeLayer);65
}66
};67
self.addNode = function () {68
var newNodeItem = {69
flowId: '',flowName : '',70
nodeName: '', nodeDesc: '',nodeType:''71
};72
var nodeItemKo = new NodeEditViewModel(ko.mapping.fromJS(newNodeItem),self);73
nodeOpenCommon('新增节点信息',nodeItemKo);74
};75
//删除节点76
self.delNode = function(item){77
if($.isEmpty(item.nodeId)){78
layer.alert('发生错误!');79
return;80
}81
var con = layer.confirm('确定删除该记录吗?', {82
btn: ['确定','取消'] //按钮83
}, function(){84
layer.close(con);85
$.postAjax(baseUrl+'delNode',{nodeId : item.nodeId},function (state) {86
if(state.success){87
layer.msg('删除成功!');88
self.initNodeTable();89
}else{90
layer.alert('删除失败!失败原因:' + state.message);91
}92
});93
}, function(){});94
95
};96
//修改节点97
self.updNode = function (item) {98
if(flowList){99
for(var i=0;i<flowList.length;i++){100
if(item.flowId === flowList[i].flowId){101
item.flowName = flowList[i].flowName;102
break;103
}104
}105
}106
var updNodeVm = new NodeEditViewModel(ko.mapping.fromJS(item),self);107
nodeOpenCommon('修改节点信息',updNodeVm);108
};109
/**110
* 节点信息修改ViewModel111
* @param nodeItemKo112
* @param nodeTableViewModel113
* @constructor114
*/115
function NodeEditViewModel(nodeItemKo,nodeTableViewModel) {116
var nodeSelf = this;117
nodeSelf.nodeItem = nodeItemKo;118
//取消119
nodeSelf.no = function () {120
closeNodeOpenCommon();121
};122
123
//选择流程124
nodeSelf.selFlow = function (item) {125
var selFlow = document.getElementById('selFlow'),126
$flowTbody = $('#flowTbody'),127
flowTbodyHtml = $flowTbody.html(),128
flowListViewModel = new FlowListViewModel(ko.mapping.fromJS(item));129
selFlowLayer = layer.open({130
title: '选择流程',131
type: 1,132
content: $(selFlow),133
area: ['880px', '460px'],134
success : function () {135
ko.cleanNode(selFlow);136
ko.applyBindings(flowListViewModel, selFlow);137
flowListViewModel.initFlowTable();138
},139
end : function () {140
$flowTbody.html(flowTbodyHtml);141
$(selFlow).hide();142
}143
});144
};145
/**146
* 选择流程147
* @constructor148
*/149
function FlowListViewModel(nodeItemKo) {150
var self = this;151
self.nodeItem = nodeItemKo;152
self.flowItems = ko.observableArray([]);153
//初始化列表154
self.initFlowTable = function () {155
$.postAjax(baseUrl + 'queryFlow', null, function (data) {156
self.flowItems(data);157
});158
};159
160
//鼠标移入事件161
self.trMouseOver = function () {162
trMouseOver($(event.target));163
};164
165
//鼠标移出事件166
self.trMouseout = function(){167
trMouseOut($(event.target));168
};169
//鼠标点击事件170
self.trClick = function (item) {171
self.nodeItem.nodeItem.flowName(item.flowName);172
self.nodeItem.nodeItem.flowId(item.flowId);173
if(selFlowLayer){174
layer.close(selFlowLayer);175
}176
};177
}178
//校验并保存179
nodeSelf.ok = function (item) {180
var validateOptions = Validation;181
validateOptions.validateOptions = true;182
validateOptions.messages = {183
_nodeName: '请输入流转名称',184
_nodeType : '请输入节点类型',185
flowName : '请选择所属流程'186
};187
validateOptions.errorPlacement = function (label, element) {};188
validateOptions.submitHandler=function (form) {};189
var $updNodeForm = $('#updNodeForm');190
$updNodeForm.validate(validateOptions);191
var is = $updNodeForm.valid();192
if(!is){193
layer.alert('信息不完整');194
return;195
}196
$.postAjax(baseUrl+'saveOrUpdFlowNode',ko.mapping.toJS(item.nodeItem),function (state) {197
if(state.success){198
layer.alert('保存成功!');199
closeNodeOpenCommon();200
nodeTableViewModel.initNodeTable();201
}else{202
layer.alert('保存失败!');203
}204
});205
};206
}207
// nodeEditDiv208
var nodeLayer,209
nodeEditDiv = document.getElementById('nodeEditDiv');210
211
/**212
* 关闭nodeLayer213
*/214
function closeNodeOpenCommon() {215
if(nodeLayer){216
layer.close(nodeLayer);217
}218
}219
220
/**221
* 新增和修改节点信息的公用layer222
* @param title223
* @param nodeItemKo224
*/225
function nodeOpenCommon(title,nodeItemKo) {226
nodeLayer = layer.open({227
title: title,228
type: 1,229
content: $(nodeEditDiv),230
area: ['600px', '250px'],231
success : function () {232
ko.cleanNode(nodeEditDiv);233
ko.applyBindings(nodeItemKo, nodeEditDiv);234
},235
end : function () {236
$(nodeEditDiv).hide();237
}238
});239
}240
}241
/**242
* 修改Link信息243
* @constructor244
*/245
function LinkEditViewModel(linkItem,linkTableViewModel) {246
var self = this;247
self.item = ko.mapping.fromJS(linkItem);248
//选择本节点249
self.selNode = function () {250
selNodeCommon(1,self.item,null,'选择本节点');251
};252
//选择下一个节点253
self.selNextNode = function () {254
selNodeCommon(2,self.item,null,'选择下一个节点');255
};256
257
//取消258
self.no = function () {259
closeLinkUpdLayer();260
};261
//保存262
self.ok = function () {263
var validateOptions = Validation;264
validateOptions.validateOptions = true;265
validateOptions.messages = {266
linkName: '请输入流转名称',267
conditionStatus : '请输入条件状态',268
_nodeName : '请选择本节点名称',269
nextNodeName : '请选择下一个节点名称'270
};271
validateOptions.errorPlacement = function (label, element) {};272
validateOptions.submitHandler=function (form) {};273
var $updLinkForm = $('#updLinkForm');274
$updLinkForm.validate(validateOptions);275
var is = $updLinkForm.valid();276
if(!is){277
layer.alert('信息不完整');278
return;279
}280
$.postAjax(baseUrl+'saveOrUpdFlowLink',ko.mapping.toJS(self.item),function (state) {281
if(state.success){282
layer.alert('保存成功!');283
closeLinkUpdLayer();284
linkTableViewModel.initLinkTable();285
}else{286
layer.alert('保存失败!');287
}288
});289
};290
291
//关闭layer弹出框292
function closeLinkUpdLayer() {293
if(linkUpdLayer){294
layer.close(linkUpdLayer);295
}296
}297
}298
299
/**300
* 鼠标移入事件301
*/302
function trMouseOver() {303
var $target = $(event.target);304
if($target.is('td')){305
$target = $target.parent();306
}307
$target.css('background-color','#CCFF80');308
$target.css('cursor','pointer');309
}310
311
/**312
* 鼠标移除事件313
*/314
function trMouseOut() {315
var $target = $(event.target);316
if($target.is('td')){317
$target = $target.parent();318
}319
$target.css('background-color','white');320
$target.css('cursor','pointer');321
}322
/**323
* 流程节点324
* @param t325
* @param linkItemKo326
* @param flowItemKo327
* @param title328
*/329
function selNodeCommon(t,linkItemKo,flowItemKo,title) {330
var selNode = document.getElementById('selNode'),331
$nodeTbody = $('#nodeTbody'),332
nodeTbodyHtml = $nodeTbody.html(),333
nodeListViewModel = new NodeListViewModel(linkItemKo,flowItemKo,t);334
selNodeLayer = layer.open({335
title: title,336
type: 1,337
content: $(selNode),338
area: ['880px', '460px'],339
success : function () {340
ko.cleanNode(selNode);341
ko.applyBindings(nodeListViewModel, selNode);342
nodeListViewModel.initNodeTable();343
},344
end : function () {345
$nodeTbody.html(nodeTbodyHtml);346
$(selNode).hide();347
}348
});349
}350
/**351
* 查看流程Link信息的ViewModel352
* @param flowItem353
* @constructor354
*/355
function LinkTableViewModel(flowItem){356
var self = this;357
var linkEditDiv = document.getElementById('linkEditDiv'),flowId = $.trim(flowItem.flowId);358
self.linkItems = ko.observableArray([]);359
//初始化列表360
self.initLinkTable = function () {361
$.postAjax(baseUrl + 'selectLinkByFlowId', {flowId : flowId}, function (data) {362
self.linkItems(data);363
});364
};365
//新增366
self.addLink = function(){367
var newItem = {368
flowId: flowId,369
linkName: '', linkDesc: '',370
nodeId: '', nodeName: '',371
nextNodeId: '', nextNodeName: '',372
conditionStatus: ''373
};374
var updLinkVm = new LinkEditViewModel(ko.mapping.fromJS(newItem),self);375
linkOpenCommon('新增连接信息',updLinkVm);376
};377
//修改378
self.updLink = function (item) {379
var updLinkVm = new LinkEditViewModel(item,self);380
linkOpenCommon('修改连接信息',updLinkVm);381
};382
//删除383
self.delLink = function (item) {384
if($.isEmpty(item.linkId)){385
layer.alert('发生错误!');386
return;387
}388
var con = layer.confirm('确定删除该记录吗?', {389
btn: ['确定','取消'] //按钮390
}, function(){391
layer.close(con);392
$.postAjax(baseUrl+'delLink',{linkId : item.linkId},function (state) {393
if(state.success){394
layer.msg('删除成功!');395
self.initLinkTable();396
}else{397
layer.alert('删除失败!失败原因:' + state.message);398
}399
});400
}, function(){});401
402
};403
/**404
* link的layer弹出框公用部分405
* @param title layer弹出框的标题406
* @param updLinkVm407
*/408
function linkOpenCommon(title,updLinkVm) {409
linkUpdLayer = layer.open({410
title: title,411
type: 1,412
content: $(linkEditDiv),413
area: ['600px', '250px'],414
success : function () {415
ko.cleanNode(linkEditDiv);//取消绑定416
ko.applyBindings(updLinkVm, linkEditDiv);//绑定417
},418
end :function () {419
$(linkEditDiv).hide();420
}421
});422
}423
}424
425
/**426
* 列表页的ViewModel427
* @constructor428
*/429
function TableViewModel() {430
var self = this,431
flowEditDiv = document.getElementById('flowEditDiv');432
self.items = ko.observableArray([]);433
self.queryFlowName = ko.observable('');434
self.delFlow = function (item) {435
if($.isEmpty(item.flowId)){436
layer.alert('发生错误!');437
return;438
}439
//TODO:注意,此处尚未做级联删除440
var con = layer.confirm('如果删除该记录,该记录下的节点信息将同时删除.<br>确定删除该记录吗?', {441
btn: ['确定','取消'] //按钮442
}, function(){443
layer.close(con);444
$.postAjax(baseUrl+'delFlow',{flowId : item.flowId},function (state) {445
if(state.success){446
layer.msg('删除成功!');447
self.init();448
}else{449
layer.alert('删除失败!失败原因:' + state.message);450
}451
});452
}, function(){});453
};454
//新增455
self.addFlow = function () {456
var addVm = ko.mapping.fromJS({flowName:'',flowDesc:''});457
flowOpenCommon(flowEditDiv,addVm,'新增工作流信息');458
};459
//修改460
self.updFlow = function (item) {461
var updVm = ko.mapping.fromJS(item);462
flowOpenCommon(flowEditDiv,updVm,'修改工作流信息');463
};464
465
function flowOpenCommon(flowEditDiv,updVm,title) {466
flowUpdLayer = layer.open({467
title: title,468
type: 1,469
content: $(flowEditDiv),470
area: ['320px', '215px'],471
success:function () {472
ko.cleanNode(flowEditDiv);//取消绑定473
var flowEditViewModel = new FlowEditViewModel(updVm);474
ko.applyBindings(flowEditViewModel, flowEditDiv);//绑定475
},476
end:function () {477
$(flowEditDiv).hide();478
}479
});480
}481
482
//初始化列表483
self.init = function () {484
$.postAjax(baseUrl + 'queryFlow', {flowName: $.trim(self.queryFlowName())}, function (data) {485
flowList = data;486
self.items(data);487
});488
};489
490
//流程节点491
self.opeFlowNode = function (item) {492
selNodeCommon(null,null,ko.mapping.fromJS(item),'流程节点信息');493
};494
495
function FlowEditViewModel(flowItemKo) {496
var flowSelf = this;497
flowSelf.flowItem = flowItemKo;498
499
flowSelf.ok = function () {500
var validateOptions = Validation;501
validateOptions.validateOptions = true;502
validateOptions.messages = {503
flowName: '请输入工作流名称'504
};505
validateOptions.errorPlacement = function (label, element) {};506
validateOptions.submitHandler=function (form) {};507
var $updFlowForm = $('#updFlowForm');508
$updFlowForm.validate(validateOptions);509
var is = $updFlowForm.valid();510
if(!is){511
layer.alert('信息不完整');512
return;513
}514
$.postAjax(baseUrl+'saveOrUpdFlow',ko.mapping.toJS(flowSelf.flowItem),function (state) {515
if(state.success){516
layer.alert('保存成功!');517
closeFlowLayer();518
self.init();519
}else{520
layer.alert('保存失败!');521
}522
});523
};524
flowSelf.no = function () {525
closeFlowLayer();526
};527
function closeFlowLayer() {528
if(flowUpdLayer){529
layer.close(flowUpdLayer);530
}531
}532
}533
//流程连接534
self.opeFlowLink = function (item) {535
//将tbody缓存起来,解决数据重复问题536
var $linkTbody = $('#linkTbody'),537
tbody = $linkTbody.html(),538
flowLinkDiv = document.getElementById('flowLinkDiv');539
linkUpdLayer = layer.open({540
title: '工作流连接信息',541
type: 1,542
content: $(flowLinkDiv),543
area: ['880px', '450px'],544
success : function(){545
var linkTableViewModel = new LinkTableViewModel(item);546
ko.cleanNode(flowLinkDiv);//取消绑定547
ko.applyBindings(linkTableViewModel, flowLinkDiv);548
linkTableViewModel.initLinkTable();549
},550
end:function () {551
$(flowLinkDiv).hide();552
$linkTbody.html(tbody);553
}554
});555
}556
})();关键代码说明
/**
* 声明一个TableViewModel
*/
function TableViewModel() {
var self = this,
flowEditDiv = document.getElementById('flowEditDiv');
//监控对象是一个数组
self.items = ko.observableArray([]);
//监控对象是一个字符串
self.queryFlowName = ko.observable('');
self.delFlow = function (item) {
if($.isEmpty(item.flowId)){
layer.alert('发生错误!');
return;
}
var con = layer.confirm('如果删除该记录,该记录下的节点信息将同时删除.<br>确定删除该记录吗?', {
btn: ['确定','取消'] //按钮
}, function(){
layer.close(con);
$.postAjax(baseUrl+'delFlow',{flowId : item.flowId},function (state) {
if(state.success){
layer.msg('删除成功!');
self.init();
}else{
layer.alert('删除失败!失败原因:' + state.message);
}
});
}, function(){});
};
//新增
self.addFlow = function () {
//注意这里的ko.mapping.fromJS,刚开始的时候,我们不是提到了一个ko.mapping.js吗?这里就是其中的一个功能
//ko监控的对象结构如下:
//{key:ko.observable(value)}
//也就是需要ko.observable声明一下,如果需要监控的对象键值对比较多,一个个转就太麻烦了
//使用ko.mapping.fromJS就可以直接转换了
//另外,ko.mapping.toJS则可以反过来将一个ko监控对象转为正常的json对象
var addVm = ko.mapping.fromJS({flowName:'',flowDesc:''});
flowOpenCommon(flowEditDiv,addVm,'新增工作流信息');
};
//修改
self.updFlow = function (item) {
var updVm = ko.mapping.fromJS(item);
flowOpenCommon(flowEditDiv,updVm,'修改工作流信息');
};
function flowOpenCommon(flowEditDiv,updVm,title) {
flowUpdLayer = layer.open({
title: title,
type: 1,
content: $(flowEditDiv),
area: ['320px', '215px'],
success:function () {
ko.cleanNode(flowEditDiv);//取消绑定
var flowEditViewModel = new FlowEditViewModel(updVm);
ko.applyBindings(flowEditViewModel, flowEditDiv);//绑定
},
end:function () {
$(flowEditDiv).hide();
}
});
}
//初始化列表
self.init = function () {
$.postAjax(baseUrl + 'queryFlow', {flowName: $.trim(self.queryFlowName())}, function (data) {
flowList = data;
//给ko监控对象赋值,不能使用=,
//因为ko监控对象的结构并不是简单的json对象格式
//需要使用()赋值,如下
self.items(data);
});
};
//流程节点
self.opeFlowNode = function (item) {
selNodeCommon(null,null,ko.mapping.fromJS(item),'流程节点信息');
};
function FlowEditViewModel(flowItemKo) {
var flowSelf = this;
flowSelf.flowItem = flowItemKo;
flowSelf.ok = function () {
var validateOptions = Validation;
validateOptions.validateOptions = true;
validateOptions.messages = {
flowName: '请输入工作流名称'
};
validateOptions.errorPlacement = function (label, element) {};
validateOptions.submitHandler=function (form) {};
var $updFlowForm = $('#updFlowForm');
$updFlowForm.validate(validateOptions);
var is = $updFlowForm.valid();
if(!is){
layer.alert('信息不完整');
return;
}
$.postAjax(baseUrl+'saveOrUpdFlow',ko.mapping.toJS(flowSelf.flowItem),function (state) {
if(state.success){
layer.alert('保存成功!');
closeFlowLayer();
self.init();
}else{
layer.alert('保存失败!');
}
});
};
flowSelf.no = function () {
closeFlowLayer();
};
function closeFlowLayer() {
if(flowUpdLayer){
layer.close(flowUpdLayer);
}
}
}
//流程连接
self.opeFlowLink = function (item) {
//将tbody缓存起来,解决数据重复问题
var $linkTbody = $('#linkTbody'),
tbody = $linkTbody.html(),
flowLinkDiv = document.getElementById('flowLinkDiv');
linkUpdLayer = layer.open({
title: '工作流连接信息',
type: 1,
content: $(flowLinkDiv),
area: ['880px', '450px'],
success : function(){
var linkTableViewModel = new LinkTableViewModel(item);
ko.cleanNode(flowLinkDiv);//取消绑定
ko.applyBindings(linkTableViewModel, flowLinkDiv);
linkTableViewModel.initLinkTable();
},
end:function () {
$(flowLinkDiv).hide();
$linkTbody.html(tbody);
}
});
}140
1
/**2
* 声明一个TableViewModel3
*/4
function TableViewModel() {5
var self = this,6
flowEditDiv = document.getElementById('flowEditDiv');7
//监控对象是一个数组8
self.items = ko.observableArray([]);9
//监控对象是一个字符串10
self.queryFlowName = ko.observable('');11
self.delFlow = function (item) {12
if($.isEmpty(item.flowId)){13
layer.alert('发生错误!');14
return;15
}16
var con = layer.confirm('如果删除该记录,该记录下的节点信息将同时删除.<br>确定删除该记录吗?', {17
btn: ['确定','取消'] //按钮18
}, function(){19
layer.close(con);20
$.postAjax(baseUrl+'delFlow',{flowId : item.flowId},function (state) {21
if(state.success){22
layer.msg('删除成功!');23
self.init();24
}else{25
layer.alert('删除失败!失败原因:' + state.message);26
}27
});28
}, function(){});29
};30
//新增31
self.addFlow = function () {32
//注意这里的ko.mapping.fromJS,刚开始的时候,我们不是提到了一个ko.mapping.js吗?这里就是其中的一个功能33
//ko监控的对象结构如下:34
//{key:ko.observable(value)}35
//也就是需要ko.observable声明一下,如果需要监控的对象键值对比较多,一个个转就太麻烦了36
//使用ko.mapping.fromJS就可以直接转换了37
//另外,ko.mapping.toJS则可以反过来将一个ko监控对象转为正常的json对象38
var addVm = ko.mapping.fromJS({flowName:'',flowDesc:''});39
flowOpenCommon(flowEditDiv,addVm,'新增工作流信息');40
};41
//修改42
self.updFlow = function (item) {43
var updVm = ko.mapping.fromJS(item);44
flowOpenCommon(flowEditDiv,updVm,'修改工作流信息');45
};46
47
function flowOpenCommon(flowEditDiv,updVm,title) {48
flowUpdLayer = layer.open({49
title: title,50
type: 1,51
content: $(flowEditDiv),52
area: ['320px', '215px'],53
success:function () {54
ko.cleanNode(flowEditDiv);//取消绑定55
var flowEditViewModel = new FlowEditViewModel(updVm);56
ko.applyBindings(flowEditViewModel, flowEditDiv);//绑定57
},58
end:function () {59
$(flowEditDiv).hide();60
}61
});62
}63
64
//初始化列表65
self.init = function () {66
$.postAjax(baseUrl + 'queryFlow', {flowName: $.trim(self.queryFlowName())}, function (data) {67
flowList = data;68
//给ko监控对象赋值,不能使用=,69
//因为ko监控对象的结构并不是简单的json对象格式70
//需要使用()赋值,如下71
self.items(data);72
});73
};74
75
//流程节点76
self.opeFlowNode = function (item) {77
selNodeCommon(null,null,ko.mapping.fromJS(item),'流程节点信息');78
};79
80
function FlowEditViewModel(flowItemKo) {81
var flowSelf = this;82
flowSelf.flowItem = flowItemKo;83
84
flowSelf.ok = function () {85
var validateOptions = Validation;86
validateOptions.validateOptions = true;87
validateOptions.messages = {88
flowName: '请输入工作流名称'89
};90
validateOptions.errorPlacement = function (label, element) {};91
validateOptions.submitHandler=function (form) {};92
var $updFlowForm = $('#updFlowForm');93
$updFlowForm.validate(validateOptions);94
var is = $updFlowForm.valid();95
if(!is){96
layer.alert('信息不完整');97
return;98
}99
$.postAjax(baseUrl+'saveOrUpdFlow',ko.mapping.toJS(flowSelf.flowItem),function (state) {100
if(state.success){101
layer.alert('保存成功!');102
closeFlowLayer();103
self.init();104
}else{105
layer.alert('保存失败!');106
}107
});108
};109
flowSelf.no = function () {110
closeFlowLayer();111
};112
function closeFlowLayer() {113
if(flowUpdLayer){114
layer.close(flowUpdLayer);115
}116
}117
}118
//流程连接119
self.opeFlowLink = function (item) {120
//将tbody缓存起来,解决数据重复问题121
var $linkTbody = $('#linkTbody'),122
tbody = $linkTbody.html(),123
flowLinkDiv = document.getElementById('flowLinkDiv');124
linkUpdLayer = layer.open({125
title: '工作流连接信息',126
type: 1,127
content: $(flowLinkDiv),128
area: ['880px', '450px'],129
success : function(){130
var linkTableViewModel = new LinkTableViewModel(item);131
ko.cleanNode(flowLinkDiv);//取消绑定132
ko.applyBindings(linkTableViewModel, flowLinkDiv);133
linkTableViewModel.initLinkTable();134
},135
end:function () {136
$(flowLinkDiv).hide();137
$linkTbody.html(tbody);138
}139
});140
}//上边提到的$root指的既是这里new出来的TableViewModel
//$('#sysFlowManagePage')[0]要绑定的元素,也是TableViewModel的作用域,也就是说在$('#sysFlowManagePage')[0])中出现的data-bind归TableViewModel管
//注意,不能重复绑定,如果需要清除,使用ko.cleanNode($('#sysFlowManagePage')[0]);清除绑定
$(function () {
var vm = new TableViewModel();
ko.applyBindings(vm, $('#sysFlowManagePage')[0]);
vm.init();
});8
1
//上边提到的$root指的既是这里new出来的TableViewModel2
//$('#sysFlowManagePage')[0]要绑定的元素,也是TableViewModel的作用域,也就是说在$('#sysFlowManagePage')[0])中出现的data-bind归TableViewModel管3
//注意,不能重复绑定,如果需要清除,使用ko.cleanNode($('#sysFlowManagePage')[0]);清除绑定4
$(function () {5
var vm = new TableViewModel();6
ko.applyBindings(vm, $('#sysFlowManagePage')[0]);7
vm.init();8
});6.后端接口
结构

比较简单,查出需要的数据返回前端,前端需要保存删除的数据后端删除等
7.注意点
ko监控对象的结构不再是简单的json结构,这也是引入ko.mapping的原因
注意重复绑定的问题,如果ko.applyBindings没有指定绑定作用域的话,默认是当前的document