数据的自动流转之二
一般用户可以忽略本节内容。
本节的内容可以参考CaseStudy目录下的文件:自动流转二.Table
打开此文件的时候,请以张三、李四、王五或赵六登录。
上一节介绍的自动流转,是批量处理、批量保存、批量流转的。
本节介绍的自动流转是逐行处理、逐行保存,逐行流转的。
本节对于流程定义与上节基本相同,只是增加了一个流程:
1、流程一由张三负责,输入第一列、第二列的数据。
2、流程二由李四负责,输入第三列、第四列的数据。
3、流程三由王五负责,输入第五列、第六列的数据。
4、流程四由赵六负责,输入第七列、第八列的数据。
4、上级流程处理完毕,才能处理本级流程,例如流程一处理完毕,才能处理流程二。
5、禁止李四、王五、赵六增加行,因为他们不是流程的初始发起者,只是后续流程的处理者。
6、只加载登录用户负责处理的行,例如李四负责流程二,所以李四登录后,只加载流程一处理完毕,但流程二未处理的行。
但要求:
1、禁止直接在表中编辑数据,改用录入窗口形式编辑。
2、双击表中的任何一行,即可打开录入窗口编辑此行(即处理本流程)。
3、每次只能处理一条记录(行)
4、在保存或取消此记录的修改前,不能移到其他记录,也不能关闭窗口。
5、单击窗口中的"保存"按钮,保存对当前行的修改,如果此行已经处理完毕,则移除此行(注意不是删除)。
6、单击窗口中的"撤销"按钮,撤销对当前行的修改,所有列返回修改前的值。
7、单击窗口中的"追载"按钮,从后台追载新的待处理记录。
8、单击窗口中的"打回"按钮,将当前行返回上一流程重新处理。
9、如果所有行均处理完毕,那么定期从后台追载新的待处理行。
设计步骤:
1、给表增加一个名为“进度”的字符型列,用于记录流程的处理进度,此列的值和流程进度的对应关系为:
值 | 进度 |
1A | 流程一开始 |
1B | 流程一完毕 |
2A | 流程二开始 |
2B | 流程二完毕 |
3A | 流程三开始 |
3B | 流程三完毕 |
4A | 流程四开始 |
4B | 流程四完毕 |
每个流程都有开始和结束标记,是行自动流转的需要。
例如新增行的初始流程状态为"1A",张三处理完毕后,状态变为"1B"(表示流程一处理完毕),李四检测到后台有状态为"1B"的行,将其追载到表中处理,并将其状态设置为"2A"(表示流程二开始)并保存。
假定没有开始标记,只有结束标记,当李四再次追载后台数据时,由于此行的状态还是"1B",将无法判断此行是否已经追载,影响行的自动流转效率。
2、为了只加载登录用户负责处理的行,首先必须确保默认不加载任何数据:
如果是内部表,设置项目事件BeforeLoadInnerTable的代码为:
If
e.DataTableName = "表A"
Then
e.Filter = "[_Identify] Is Null"
End
If
如果是外部表,可以在定义外部表的时候,直接定义加载条件:
3、然后在项目事件LoadUserSetting中设置代码,加载由登录用户负责处理的行,并设置本流程开始标记:
'加载登录用户负责处理的行
4、禁止直接在表中输入数据,为此将表的PrepareEdit事件代码设置为:
e.Cancel = True
5、为了禁止除张三之外的人增加行,将表的BeforeAddDataRow事件代码设置为:
If
User.Name <> "张三" Then6、将表的DataRowAdding事件代码设置为:
e.
DataRow("进度") = "1A"这样新增行的时候,进度列的值默认为"1A"。
7、在计划管理中新增一个计划,计划的执行间隔为10秒(即10000毫秒),代码为:
Dim
Filter As String
上面的代码就会每隔10秒执行一次,如果当前表已经没有数据,就从后台追载新的待处理行,并为新追载行设置本流程开始标记。
具体间隔时间可以根据需要调整,但不宜过短,以免服务器的负载过重。
利用消息推送实现即时刷新
前述代码利用计划,每隔10秒追载一下待处理的数据,用户量大的时候,这种定时轮询的方式会导致服务器负载过重。
Foxtable 2016内置了消息推送工具OpenQQ,可以通过代码进行信息的自动收发。
这样负责上一流程的用户处理完成之后,可以给负责下一流程的用户给发一个约定格式的信号,该用户收到信号后,立即自动追载新的数据。
这种主动追载数据的方式,不仅即时,而且高效,具体实现可以参考帮助文件《消息推送》这一章。
8、新建一个下图所示的窗口:
为了编码方便,八个文本框的名称和其绑定的列名保持一致,例如第一个文本框绑定到第一列,其名称也应该设置为第一列:
将窗口的AfterLoad事件代码设置为:
e.Form.Controls(
"第一列").Enabled = (User.Name = "张三")上述代码确保每个用户只能编辑自己负责的列。
窗口的BeforeClose事件设置为:
If
Tables("表A").Current.DataRow.RowState <> DataRowState.Unchanged Then '如果当前行已经修改过上面的代码在关闭窗口前执行,如果当前记录(行)是已经修改但没有保存,将禁止关闭窗口,直到保存或撤销修改。
窗口中各按钮的代码:
按钮 | 代码 |
上一条 | With
Tables("表A") If .Current IsNot Nothing Then If .Current.DataRow.RowState = DataRowState.Unchanged Then '如果当前行未曾修改 .Position = .Position - 1 End If End If End With |
下一条 | With
Tables("表A") If .Current IsNot Nothing Then If .Current.DataRow.RowState = DataRowState.Unchanged Then '如果当前行未曾修改 .Position = .Position + 1 End If End If End With |
第一条 | With
Tables("表A") If .Current IsNot Nothing Then If .Current.DataRow.RowState = DataRowState.Unchanged Then '如果当前行未曾修改 .Position = 0 End If End If End With |
最末条 | With
Tables("表A") If .Current IsNot Nothing Then If .Current.DataRow.RowState = DataRowState.Unchanged Then '如果当前行未曾修改 .Position = .Rows.Count - 1 End If End If End With |
新增 | With
Tables("表A") If .Current Is Nothing OrElse .Current.DataRow.RowState = DataRowState.Unchanged Then '如果当前行未曾修改 .AddNew() End If End With |
删除 | With
Tables("表A") If .Current IsNot Nothing Then .Current.Delete() End If End With |
追载 |
Dim Filter
As String Dim bj As String Dim drs As List(Of DataRow) Select Case User.Name Case "张三" Filter = "进度 Is Null" '进度为空 bj = "1A" Case "李四" Filter = "进度 = '1B'" '进度一结束 bj = "2A" Case "王五" Filter = "进度 = '2B'" '进度二结束 bj = "3A" Case "赵六" Filter = "进度 = '3B'" '进度三结束 bj = "4A" Case Else Return '其他用户不追载 End Select drs = DataTables("表A").AppendLoad(Filter,False) '追载待处理行 If drs.Count > 0 Then '设置新流程的开始标记 For Each dr As DataRow In drs dr("进度") = bj dr.Save() Next End If |
打回 | Dim
r As
Row =
Tables("表A").Current If r IsNot Nothing Select Case User.Name Case "李四" If r("进度") = "2A" Then '进度3打回的时候,将进度标记设置为Nothing,意思是进度1处理无效,需要重新处理. r("进度") = Nothing r("第三列") = Nothing r("第四列") = Nothing r.Save() r.Remove() End If Case "王五" If r("进度") = "3A" Then '进度3打回的时候,将进度标记设置为1B,意思是进度1已经完成,进度2重新处理. r("进度") = "1B" r("第五列") = Nothing r("第六列") = Nothing r.Save() r.Remove() End If Case "赵六" If r("进度") = "4A" Then '进度4打回的时候,将进度标记设置为2B,意思是进度2已经完成,进度3重新处理. r("进度") = "2B" r("第七列") = Nothing r("第八列") = Nothing r.Save() r.Remove() End If End Select End If 说明: 以进度3打回给进度2为例,需要将进度调整为“1B”,也许你会感到奇怪,为什么不直接将进度调整为“2A”? 这是为了方便负责进度2的用户及时追载打回的行,因为负责进度2的用户会定期追载进度为“1B”的行,也就是说那些进度1已经完成但是进度2还未开始的行;如果进度3打回给进度2的时候,直接将进度调整为“2A”,那么负责负责进度2的用户只有重新打开项目,才能看到这些被打回的行。 |
保存 | Dim
r As
Row
= Tables("表A").Current If r IsNot Nothing Then Dim jd As String = "1A" If r.IsNull("第一列") = False AndAlso r.IsNull("第二列") = False Then jd = "1B" If r.IsNull("第三列") = False AndAlso r.IsNull("第四列") = False Then jd = "2B" If r.IsNull("第五列") = False AndAlso r.IsNull("第六列") = False Then jd = "3B" If r.IsNull("第七列") = False AndAlso r.IsNull("第八列") = False Then jd = "4B" End If End If End If End If If jd = "1B" AndAlso r("进度") = "2A" Then ElseIf jd = "2B" AndAlso r("进度") = "3A" Then ElseIf jd = "3B" AndAlso r("进度") = "4A" Then Else r("进度") = jd End If r.Save() Select Case User.Name Case "张三" If r("进度") = "1B" Then r.Remove End If Case "李四" If r("进度") = "2B" Then r.Remove End If Case "王五" If r("进度") = "3B" Then r.Remove End If Case "赵六" If r("进度") = "4B" Then r.Remove End If Case Else Return '其他用户正常返回 End Select End If |
取消 | With
Tables("表A") If.Current IsNot Nothing Then .Current.Reject() End If End With |
从上面的代码可以看出,如果当前记录(行)已经修改但没有保存,窗口中的上一条、下一条、第一条、最末条、新增按钮将拒绝执行,直到保存或撤销修改。
10、将表的DoubleClick事件代码设置为:
Forms(
"窗口1").Open()