最早接触到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
![](https://images2017.cnblogs.com/blog/683765/201801/683765-20180123171845834-1462448625.png)
代码如下,其中涉及到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
基本结构如下
![](https://images2017.cnblogs.com/blog/683765/201801/683765-20180123171850725-669855273.png)
代码如下
(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
* 流程节点ViewModel
13
* @param linkItemKo 连接信息
14
* @param flowItemKo 流程信息
15
* @param t 类型
16
* @constructor
17
*/
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
* 节点信息修改ViewModel
111
* @param nodeItemKo
112
* @param nodeTableViewModel
113
* @constructor
114
*/
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
* @constructor
148
*/
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
// nodeEditDiv
208
var nodeLayer,
209
nodeEditDiv = document.getElementById('nodeEditDiv');
210
211
/**
212
* 关闭nodeLayer
213
*/
214
function closeNodeOpenCommon() {
215
if(nodeLayer){
216
layer.close(nodeLayer);
217
}
218
}
219
220
/**
221
* 新增和修改节点信息的公用layer
222
* @param title
223
* @param nodeItemKo
224
*/
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
* @constructor
244
*/
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 t
325
* @param linkItemKo
326
* @param flowItemKo
327
* @param title
328
*/
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信息的ViewModel
352
* @param flowItem
353
* @constructor
354
*/
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 updLinkVm
407
*/
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
* 列表页的ViewModel
427
* @constructor
428
*/
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
* 声明一个TableViewModel
3
*/
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出来的TableViewModel
2
//$('#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.后端接口
结构
![](https://images2017.cnblogs.com/blog/683765/201801/683765-20180123171858834-1401563822.png)
比较简单,查出需要的数据返回前端,前端需要保存删除的数据后端删除等
7.注意点
ko监控对象的结构不再是简单的json结构,这也是引入ko.mapping的原因
注意重复绑定的问题,如果ko.applyBindings没有指定绑定作用域的话,默认是当前的document