SharePoint:扩展DVWP - 第25部分:通过SPServices创建列表项实现审计跟踪
回顾在本系列第14部分中,我们提到“如何将PreSaveAction()与jQuery配合使用”。PreSaveAction()允许我们在保存记录前做一些事情,当时我快速的列了一些在现实场景中可能会用jQuery做的事。我们举了一个审计跟踪的例子。当主列表发生变更时,向另一个列表中写入主列表的变更信息。最终实现的效果是我们有两块体现真实信息的地方:(1)当前视图(2)一个关于变更的历史记录,展示达到当前视图所经历的过程。
在本文中,我打算花一点时间剥开一层洋葱皮,看一下具体如何实现当列表项被创建,修改或删除时在另一个列表中写记录。
在列表B中创建一条来源与列表A的记录
有太多的项目中需要我们在列表中插入,编辑,或删除列表项,没准你现在进行的项目中就有类似的需求吧。或许你以前做过订单处理解决方案,其中订单产生后我们需要更新库存信息。又或许是一个图书借阅系统,当有人借出一本书时你需要更新该书的状态。
要给另一个列表更新信息我们有很多选择。但如果你是要创建一个条全新的列表项,就像审计跟踪例子中需要的那样,要面临哪些挑战呢?显然,你必要决定什么数据需要抓取到审计跟踪记录里。在主列表中的每一个需要记录变更的栏,都必须在审计列表中存在对应的栏。
从本例的设计初衷出发,我们会选取主列表中几乎所有的栏记录其变更,进行审计。
主列表——FTE(全日制雇员,Full-time Employees)
FTE的审计跟踪列表
其中的名称和顺序没必要完全吻合。对于本例来说,栏的对应关系如下:
主列表(显示名称| 内部名称) |
审计列表(显示名称| 内部名称) |
Location | @LocDept |
Location | @Location |
Group | @Group12 |
Group | @Group |
Position | @Positions |
Position | @Position |
Worker | @Title |
Employee | @Title |
Work Shift | @Workshift |
Work Shift | @WorkShift |
FTE | @FTE |
FTE |@FTE |
EffDate | @EffDate |
Effective Date | @EffDate |
Old Location | @Old%5Fx0020%5FLocation |
|
Last Change | @Audit |
|
|
Change Type | @ChangeType |
|
Month | @Month |
|
Year | @Year |
|
From/To | @FromTo |
创建者(Created By) 和 修改者(Modified By) 会由 SharePoint 自动填写,所以在两个列表中我们都不考虑这两个字段。
注意:两个列表的定义有以下几点不同:
- 在主列表中有一些查阅项类型的栏,对应到审计跟踪列表时变成了单行文本。
- 我在主列表中添加了一个Last Change栏。我们将用于保存最新一条审计记录的ID,并显示其修改时间字段。这里与Effective Date不同。
- 在审计列表中,我添加了:
- Change Type:一个选项型的栏(New,Update,Terminate,Resign,Transfer)——当一条列表项被插入,该值为New;对于编辑,如果Location发生了修改,则该值为Transfer;任何其他的修改都设为Update。如果列表项被删除,我们将记录是有公司解雇的(Terminate)还是有雇员提出辞职的(Resign)。【标注:在这里做一个记号,将来当我们讨论创建一个备用编辑模版时还会提及】
- From/To:选项型的栏(From,To)——用于显示记录变更前/后的视图,即一次修改会创建两条记录。
- Month:计算栏,用来取出Effective Date的月份,方便进行排序/分组。[公式: =TEXT([Effective Date],"mm – mmmm")]
- Year:计算栏,用来取出Effective Date的年,方便排序。 [公式:=TEXT([Effective Date],"yyyy")]
FTE 变更审计
回到jQuery部分的工作
这里的jQuery脚本也是来自与第14部分(有关变量的创建,请参考第13部分),其中用到了Marc Anderson的SPServices库:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 | function PreSaveAction() { var txtChangeType = "Update" ; var txtNewName = $( "input[name*=$ff1_]" ).val(); var txtOldName = $( "span[id*=_ff9_]" ).text(); var txtNewPos = $( "input[name*=$ff3_]" ).val(); var txtOldPos = $( "span[id*=_ff11_]" ).text(); var txtNewShift = $( "select[name*=$ff4_]" ).val(); var txtOldShift = $( "span[id*=_ff12_]" ).text(); var txtNewFTE = $( "input[name*=$ff5_]" ).val(); var txtOldFTE = $( "span[id*=_ff13_]" ).text(); var txtNewGrp = $( "input[name*=$ff2_]" ).val(); var txtOldGrp = $( "span[id*=_ff10_]" ).text(); var txtNewLoc = $( "input[name*=$ff6_]" ).val(); var txtOldLoc = $( "span[id*=_ff14_]" ).text(); var txtEffDate = $( "input[name*=$ff7_]" ).val(); if (txtNewLoc != txtOldLoc) txtChangeType = "Transfer" ; $().SPServices({ operation: "UpdateListItems" , async: false , listName: "FTE Change Audit" , updates: "<Batch OnError='Continue' PreCalc='TRUE'>" + "<Method ID='1' Cmd='New'>" + "<Field Name='FromTo'>From</Field>" + "<Field Name='Title'>" + txtOldName + "</Field>" + "<Field Name='Location'>" + txtOldLoc + "</Field>" + "<Field Name='Group'>" + txtOldGrp + "</Field>" + "<Field Name='Position'>" + txtOldPos + "</Field>" + "<Field Name='WorkShift'>" + txtOldShift + "</Field>" + "<Field Name='FTE'>" + txtOldFTE + "</Field>" + "<Field Name='EffDate'>" + txtEffDate + "</Field>" + "<Field Name='ChangeType'>" + txtChangeType + "</Field>" + "</Method>" + "</Batch>" , completefunc: function (xData, Status) { } }); $().SPServices({ operation: "UpdateListItems" , async: false , listName: "FTE Change Audit" , updates: "<Batch OnError='Continue' PreCalc='TRUE'>" + "<Method ID='1' Cmd='New'>" + "<Field Name='FromTo'>To</Field>" + "<Field Name='Title'>" + txtNewName + "</Field>" + "<Field Name='Location'>" + txtNewLoc + "</Field>" + "<Field Name='Group'>" + txtNewGrp + "</Field>" + "<Field Name='Position'>" + txtNewPos + "</Field>" + "<Field Name='WorkShift'>" + txtNewShift + "</Field>" + "<Field Name='FTE'>" + txtNewFTE + "</Field>" + "<Field Name='EffDate'>" + txtEffDate + "</Field>" + "<Field Name='ChangeType'>" + txtChangeType + "</Field>" + "</Method>" + "</Batch>" , completefunc: function (xData, Status) { } }); return true ; }; </script> |
拆开来看
假设表单操作按钮已经被我们改为调用PreSaveAction(),那么这段脚本就会把变更保存至到审计列表。下面是整个过程的慢动作回放:
修改类型
1 | var txtChangeType = "Update" ; |
大多数的变更为Update类型。但是当Location发生修改时,类型就变成了Transfer(不考虑其他栏是否发生了修改...你的场景可能于此不同)。在我们设置所有的变量前,应对该项进行检查。
1 2 3 4 5 6 | var txtNewLoc = $( "input[name*=$ff6_]" ).val(); var txtOldLoc = $( "span[id*=_ff14_]" ).text(); . . . if (txtNewLoc != txtOldLoc) txtChangeType = "Transfer" ; |
Effective Date
由于Effective Date的变更只涉及到本身,所以这种情况下From和To两个变更版本是相同的。
SPServices
现在,我们得到了变更前的“旧”值和变更后的“新”值,接下来直接通过SPServices调用UpdateListItems创建两条记录就可以了。在上面的29行到41行,我们告诉函数创建一条新记录。然后,我们只需加载上CAML把值填入审计列表的相应的栏即可。
或者,我们可以用下面的语法替换updates中使用的CAML:
1 2 3 4 5 6 7 8 9 10 | batchCmd: "New" , valuepairs: [[ "FromTo" , "From" ], [ "Title" , txtOldName], [ "Location" , txtOldLoc], [ "Group" , txtOldGrp], [ "Position" , txtOldPos], [ "WorkShift" , txtOldShift], [ "FTE" , txtOldFTE], [ "EffDate" , txtEffDate], [ "ChangeType" , txtChangeType]], |
注意到其中listName是目标列表的显示名称:
1 | listName: "FTE Change Audit" , |
接下来对应FromTo="To"再重复一遍上面的过程。我们就完成了两条审计跟踪记录的创建。
插入和删除
你可以在新建时使用相同的技术,但如果用工作流来处理“新建项目时”会更容易些。如果你需要我对这两种方式进行详细讲解的话请发表评论。
我们会在将来讲到添加一个备用编辑模版时使用Resign和Terminate变更类型。
下一次:如果你需要在DVWP中从一个关联的列表更新数据该怎么办?在接下来的扩展DVWP系列中,我们会公布谜底。
参考资料
SharePoint:Extending the DVWP - Part 25:Using an Audit Trail by Creating List Items with SPServices
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· AI与.NET技术实操系列:基于图像分类模型对图像进行分类
· go语言实现终端里的倒计时
· 如何编写易于单元测试的代码
· 10年+ .NET Coder 心语,封装的思维:从隐藏、稳定开始理解其本质意义
· .NET Core 中如何实现缓存的预热?
· 25岁的心里话
· 闲置电脑爆改个人服务器(超详细) #公网映射 #Vmware虚拟网络编辑器
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· 零经验选手,Compose 一天开发一款小游戏!
· 一起来玩mcp_server_sqlite,让AI帮你做增删改查!!