SharePoint 中时间轴 Timeline的实现
客户需要在OA中实现每日动态功能,能够记录每一位员工的每天的工作动态,我很快想到了时间轴,因为时间轴能很直观的现实员工每一刻的动态。就像Facebook的Timeline效果(点击查看)。
尝试着搜索这个效果,园友的这篇博文正好给我启发,接下来就去实现吧。
成果演示
最终的效果如下所示:
点击每个员工的姓名,即可进入他当天的工作动态(只能看),若点击自己的名字(既能看又能发送/编辑/删除动态),如下所示:
动态的详细页,如下所示:
点击时间轴,即可新增动态,如下所示:
编辑效果,鼠标移至内容区域,现实黄色提醒,如下所示:
单击即可显示编辑界面,如下所示:
移开鼠标,即可自动保存。
当然如果想把一条当太删掉,点击右上角X即可。
实现原理
关于效果的实现原理可以参考这篇文章。
了解了上面提到的这篇文章之后(Masonry.js),接下来就是Sharepoint 客户端对象模型的实现了,比如Ecmascript。
- 根据登陆的用户点击的员工名字获取当天的动态,这儿需要利用CAML拼接出查询条件
function GetCurrentUser(){ //Get the current context var context=new SP.ClientContext.get_current(); //Load the web object var web=context.get_web(); //Get current user this.currentUser=web.get_currentUser(); //load currentUser context.load(currentUser); //Make a query call to execute the above statements context.executeQueryAsync(OnGetCurrentUserSuccess,OnGetCurrentUserFailed); } function OnGetCurrentUserSuccess(){ GetDailyWorks(); } function OnGetCurrentUserFailed(sender,args){ console.log('Request failed. ' + args.get_message() + '\n' + args.get_stackTrace()); } function GetDailyWorks(){ //Get the current context var context=new SP.ClientContext.get_current(); //Load the web object var web=context.get_web(); //Get the list var list=web.get_lists().getByTitle(listNameForDailyWork); //Get items by caml in the specific list var camlQuery=new SP.CamlQuery(); var d=new Date(); //Get specific field name this.userNameWhenClickView=$("#currentUserHidden").val(); camlQuery.set_viewXml('<View><Query>'+ '<Where>'+ '<And>'+ '<And>'+ '<And>'+ '<Eq>'+ '<FieldRef Name=\'Title\'/>'+ '<Value Type=\'Text\'>'+userNameWhenClickView+'</Value>'+ '</Eq>'+ '<Eq>'+ '<FieldRef Name=\'CurrentYear\'/>'+ '<Value Type=\'Text\'>'+d.getFullYear()+'</Value>'+ '</Eq>'+ '</And>'+ '<Eq>'+ '<FieldRef Name=\'CurrentMonth\'/>'+ '<Value Type=\'Text\'>'+(d.getMonth()+1)+'</Value>'+ '</Eq>'+ '</And>'+ '<Eq>'+ '<FieldRef Name=\'CurrentDay\'/>'+ '<Value Type=\'Text\'>'+d.getDate()+'</Value>'+ '</Eq>'+ '</And>'+ '</Where>'+ '<OrderBy>'+ '<FieldRef Name=\'Created\' Ascending=\'True\'/>'+ '</OrderBy></Query></View>'); dailyWorks=list.getItems(camlQuery); //Load the web in the context and retrieve only selected columns to improve perfomance context.load(dailyWorks,'Include(ID,Title,DailyContent,Created)'); //Make a query call to execute the above statements context.executeQueryAsync(OnGetDailyWorksSuccess,OnGetDailyWorkFailed); } function OnGetDailyWorksSuccess(){ //Get the collection var dailyWorksCollection=dailyWorks.getEnumerator(); //Iterate through daily works while(dailyWorksCollection.moveNext()){ //Load the current daily work item in iterate var workItem=dailyWorksCollection.get_current(); //Add work item to container addWorkToContainer(workItem.get_item('ID'),workItem.get_item('Title'),workItem.get_item('DailyContent'),workItem.get_item("Created")); } //Items has added in container and execute AutoMasonry method AutoMasonry(); //Init in line edit if current user has permission to edit if(userNameWhenClickView==currentUser.get_title()){ InitInlineEdit($('.editable, .editable-area')); } } //Error Handler function OnGetDailyWorkFailed(sender,args){ console.log('Request failed. ' + args.get_message() + '\n' + args.get_stackTrace()); }
- 当然Add一个Item也是比较方便的,当Add Item完毕后,很重要的一点是利用Masonry Reload一下所有的item
function AddNewDailyWorkItem(content){ //get the current context var context=new SP.ClientContext.get_current(); //Load the web object var web=context.get_web(); //Get the list var list =web.get_lists().getByTitle(listNameForDailyWork); //create the ListItemInfomational object var listItemInfo=new SP.ListItemCreationInformation(); //add the item to the list this.listItem=list.addItem(listItemInfo); //get current user display name var userDisplayName=currentUser.get_title(); //Assign values for fields listItem.set_item('Title',userDisplayName); listItem.set_item('DailyContent',content); //Get current year ,month,day and assign values for fields var newObj=new Date(); listItem.set_item('CurrentYear',newObj.getFullYear()); listItem.set_item('CurrentMonth',newObj.getMonth()+1); listItem.set_item('CurrentDay',newObj.getDate()); //Apply changes to item listItem.update(listItem); context.load(listItem); //Make a query call to execute the above statements context.executeQueryAsync(AddDailyWorkItemSuccess,AddDailyWorkItemFailed); } function AddDailyWorkItemSuccess(sender,args){ var content = $("#update").val(); $('<div class="item"><a href="#" itemId="'+listItem.get_id()+'" class="deletebox">X</a>' + '<div class="inner"><p itemId="'+listItem.get_id()+'" class="editable-area">' + content + '</p></div><p class="sendStyle">发送于'+(new Date()).getHours()+':'+((new Date()).getMinutes()<10?"0"+(new Date()).getMinutes():(new Date()).getMinutes())+'</p></div>').insertBefore("div#popup"); //reload masnory $("#container").masonry("reload"); //Hiding existing arrows $(".rightCorner").hide(); $(".leftCorner").hide(); //injecting fresh arrows Arrow_Points(); //clear popup text box value $("#update").val(""); //popup hide $("#popup").hide(); //Init in line edit $('p[itemId='+listItem.get_id()+']'); InitInlineEdit($('p[itemId='+listItem.get_id()+']')); } function AddDailyWorkItemFailed(sender,args){ console.log('Request failed. ' + args.get_message() + '\n' + args.get_stackTrace()); }
- Update 和Add是相同的逻辑,记得最后Reload即可
function UpdateDailyWorkItem(itemId,updateContent){ //If no title is setted then show alert and set focus /*if(updateContent==''){ $() }*/ //Get the current context var context = new SP.ClientContext.get_current(); //Load the web object var web=context.get_web(); //Get the list var list=web.get_lists().getByTitle(listNameForDailyWork); //Get item to update by Id from the specific list this.listItem=list.getItemById(itemId); //Set the new property value listItem.set_item('DailyContent',updateContent); //Call the update method to commit the change listItem.update(); context.executeQueryAsync(updateWorkItemSuccess,updateWorkItemFailed); } function updateWorkItemSuccess(){ $editable .removeClass('active-inline') .children() .replaceWith(edited); if ($editable.hasClass('editable-area')) { rapture($editable); } //reload masnory $("#container").masonry("reload"); //Hiding existing arrows $(".rightCorner").hide(); $(".leftCorner").hide(); //injecting fresh arrows Arrow_Points(); } function updateWorkItemFailed(){ }
- Delete Item,根据item id进行删除,同Add和Update逻辑,删除完毕后也是需要Reload
//Delete daily work item by item id function deleteDailyWork(workElement){ //Get the daily work item id var itemId=workElement.attr("itemId"); //Get the current context var context=new SP.ClientContext.get_current(); //Load the web object var web=context.get_web(); //Get the list var list=web.get_lists().getByTitle(listNameForDailyWork); //Get item to delete by if form the list var itemToDelete=list.getItemById(itemId); //Add Delete method to the query itemToDelete.deleteObject(); //Execute the query to perform the deletion context.executeQueryAsync(DeleteWorkItemSuccess,DeleteWorkItemFailed); } function DeleteWorkItemSuccess(){ //Masonry Reload } function DeleteWorkItemFailed(sender,args){ console.log('Request failed. ' + args.get_message() + '\n' + args.get_stackTrace()); }
总结
值得注意的是我将每日的动态存入List中,对于List,他能负担的item的个数和一次从数据库里获取的item都是有限制,对于数据量很大的情况下,是有风险的。