將近大半年都沒有更新博客了,趁這段時間不忙,后續會繼續分享一些技術和實際應用。對於Sharepoint的定制開發有很多種方式,對於一般的應用系統,可以使用Sharepoint本身自帶的功能,如列表作為數據源和web服務等,再通過Sharepoint Designer工具可以快速的定制開發,從效率和可維護性角度來說,這種方式是最高的,且可移植性好。去年本人去上海參加了微軟技術大會,根據微軟的介紹,Sharepoint 2013的前端功能支持越來越強大,后端的開發會慢慢弱化,能讓更多的前端開發工程師參與到Sharepoint的生態鏈中。對於這方面本人有一些實踐,如本人去年利用Sharepoint Designer的定制開發過公司的移動OA系統(根據訪問日志統計,運行了一年多,日活躍度還是不錯的,自認為這個系統還是比較成功的),Sharepoint Designer主要是用於前端(基於HTML5)的開發,后端通過VS自定義開發移動OA的Web服務發布到Sharepoint中進行交互。
本文介紹一種本人上半年為公司定制開發的sharepoint專家庫系統實例,本來還想開發移動APP端。但由於各種原因,這個系統的沒怎么用起來,畢竟企業內部信息不是完全開放的,對於高級人才的信息需要嚴格保密,一旦使用人數少又不是必備的,這樣系統發揮的價值就不大了,但設計和開發的方式相信對各位同學會有啟發,是一個很好的學習實例,僅供學習參考。通過這種方式開發專家系統投入的時間也不多,大概用了2到3周的時間,成本是很低的。
首先進行需求分析,既然我們專家庫系統是基於Sharepoint平台的,能不能利用sharepoint自帶的一些功能呢?答案是肯定的,因為專家本身也是sharepoint的用戶,熟悉sharepoint的架構都知道,sharepoint每個用戶都會有自己的個人配置信息,且專家的信息與sharepoint的個人用戶信息很多都是一致的,只要擴展一下sharepoint的個人用戶信息即可,且sharepoint支持對用戶信息進行擴展,所以第一步在sharepoint的后台管理中心中擴展sharepoint用戶的配置信息(如增加教育經歷、工作經歷、論文、專利等),這個只需要在sharepoint后台配置用戶信息一下即可,擴展后可以維護專家個人信息,如下圖:

其次,建立專家庫管理數據源,自定義一些列表(如專家個人專長及簡介、專家庫專業模塊、專家庫專業模塊領域、專業模塊技術帶頭人等),讓人力資源管理人員可以自行管理專家庫系統(相當於專家庫的管理后台,數據可以動態管理),如下圖:


最后進行前端系統的開發(主要面向最終用戶),通過Sharepoint Designer工具進行定制開發,界面的UI主要使用Jquery LigerUI(LigerUI是基於jQuery開發的一系列控件組,包括表單、布局、表格等等常用UI控件 使用LigerUI可以快速創建風格統一的界面效果 LigerUI視圖簡潔明了,操作較為簡便,采用json格式傳遞數據。)。無論是Sharepoint的個人用戶信息還是列表數據,都是可以通過Sharepoint的web服務進行調用交互,sharepoint從2007開始,已有很多自帶的sharepoint web服務,考慮到sharepoint的升級和維護,本文使用了,老外封裝好了Sharepoint web服務的基於Jquery的SPServices庫,支持sharepoint 2007、sharepoint 2010、sharepoint 2013,如下圖:


專家庫系統部分界面,如下圖:



使用SQL Server Report Builder進行報表開發並發布到Sharepoint后,監控系統使用情況,如下:



自定義專家庫首頁完整參考C#代碼如下:
<%@ Page Language="C#" masterpagefile="../_catalogs/masterpage/minimal.master" title="無標題 1" inherits="Microsoft.SharePoint.WebPartPages.WebPartPage, Microsoft.SharePoint, Version=14.0.0.0, Culture=neutral, PublicKeyToken=71e9bce111e9429c" meta:progid="SharePoint.WebPartPage.Document" %>
<asp:Content ID="PageHead" ContentPlaceHolderID="PlaceHolderPageTitleInTitleArea" runat="server">
<link href="../lib/ligerUI/skins/Aqua/css/ligerui-all.css" rel="stylesheet" type="text/css" />
<link href="../lib/ligerUI/skins/Gray2014/css/all.css" rel="stylesheet" type="text/css" />
<link rel="stylesheet" type="text/css" id="mylink" />
<script type="text/javascript" src="../js/jquery.1.10.2.min.js"></script>
<script type="text/javascript" src="../js/jquery.SPServices-2013.01.min.js"></script>
<script src="../lib/ligerUI/js/ligerui.all.js" type="text/javascript"></script>
<script src="../lib/ligerUI/js/plugins/ligerTab.js" type="text/javascript"></script>
<script src="../lib/json2.js" type="text/javascript"></script>
<script type="text/javascript" src="http://pv.sohu.com/cityjson?ie=utf-8" ></script>
<script type="text/javascript">
function getcurrentusername()
{
$.ajax({
type: "POST",
url: "http://ZJK/_layouts/SPOAFlowServices/SPOAFlowServices.asmx/GetCurrentUserName",
cache: false,
data: "{}",
contentType: "application/json; charset=utf-8",
dataType: "json",
success: function (response) {
$('#currentusername').empty();
users = jQuery.parseJSON(response.d);
CreateNewItem(users);
},
failure: function (msg) {
}
});
}
Date.prototype.Format = function (fmt) { //author: meizz
var o = {
"M+": this.getMonth() + 1, //月份
"d+": this.getDate(), //日
"h+": this.getHours(), //小時
"m+": this.getMinutes(), //分
"s+": this.getSeconds(), //秒
"q+": Math.floor((this.getMonth() + 3) / 3), //季度
"S": this.getMilliseconds() //毫秒
};
if (/(y+)/.test(fmt)) fmt = fmt.replace(RegExp.$1, (this.getFullYear() + "").substr(4 - RegExp.$1.length));
for (var k in o)
if (new RegExp("(" + k + ")").test(fmt)) fmt = fmt.replace(RegExp.$1, (RegExp.$1.length == 1) ? (o[k]) : (("00" + o[k]).substr(("" + o[k]).length)));
return fmt;
}
function CreateNewItem(user) {
try
{
var vpage = window.location.href;
var cip;
var city;
try
{
cip= returnCitySN['cip'];
}
catch(e){cip= '';}
try
{
city= returnCitySN['cname'];
}
catch(e){city='內網';}
var useragent = navigator.userAgent;
var Sys = {};
var ua = navigator.userAgent.toLowerCase();
var brw;
(brw = ua.match(/msie ([\d.]+)/)) ? Sys.ie = brw[1] :
(brw = ua.match(/firefox\/([\d.]+)/)) ? Sys.firefox = brw[1] :
(brw = ua.match(/chrome\/([\d.]+)/)) ? Sys.chrome = brw[1] :
(brw = ua.match(/opera.([\d.]+)/)) ? Sys.opera = brw[1] :
(brw = ua.match(/version\/([\d.]+).*safari/)) ? Sys.safari = brw[1] : 0;
var time2 = new Date().Format("yyyy-MM-dd hh:mm:ss");
var title = "專家庫主頁";
var batch =
"<Batch OnError=\"Continue\"> \
<Method ID=\"1\" Cmd=\"New\"> \
<Field Name=\"Title\">" + user + "</Field> \
<Field Name=\"UserName\">" + user + "</Field> \
<Field Name=\"VisitPage\">" + vpage + "</Field> \
<Field Name=\"VisitTime\">" + time2 + "</Field> \
<Field Name=\"VisitIPAddress\">" + cip + "</Field> \
<Field Name=\"VisitIPCity\">" + city + "</Field> \
<Field Name=\"UserAgent\">" + useragent + "</Field> \
<Field Name=\"Browser\">" + brw + "</Field> \
<Field Name=\"VisitPageName\">"+title+"</Field> \
</Method> \
</Batch>";
var soapEnv =
"<?xml version=\"1.0\" encoding=\"utf-8\"?> \
<soap:Envelope xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" \
xmlns:xsd=\"http://www.w3.org/2001/XMLSchema\" \
xmlns:soap=\"http://schemas.xmlsoap.org/soap/envelope/\"> \
<soap:Body> \
<UpdateListItems xmlns=\"http://schemas.microsoft.com/sharepoint/soap/\"> \
<listName>用戶訪問日志</listName> \
<updates> \
" + batch + "</updates> \
</UpdateListItems> \
</soap:Body> \
</soap:Envelope>";
$.ajax({
url: "http://ZJK/_vti_bin/lists.asmx",
beforeSend: function(xhr) {
xhr.setRequestHeader("SOAPAction",
"http://schemas.microsoft.com/sharepoint/soap/UpdateListItems");
},
type: "POST",
dataType: "xml",
data: soapEnv,
complete: processResult,
contentType: "text/xml; charset=utf-8"
});
}
catch(e){}
}
function processResult(xData, status) {
}
$(document).ready(function () {
getcurrentusername();
});
indexdata = [];
var cj;
var myQuery = "<Query><OrderBy><FieldRef Name='ISort' /></OrderBy><Where><Eq><FieldRef Name='IsDisplay' /><Value Type='Boolean'>1</Value></Eq></Where></Query>";
var arraymdl = new Array();
var mn = new Object();
mn.Rows = [];
$().SPServices({
operation: "GetListItems",
async: false,
listName: "專家庫專業模塊",
CAMLViewFields: "<ViewFields><FieldRef Name='SpecialtyModule' /><FieldRef Name='ID' /></ViewFields>",
CAMLQuery: myQuery,
webURL: "http://ZJK",
completefunc: function (xData, Status) {
var i=-1;
$(xData.responseXML).SPFilterNode("z:row").each(function() {
i++;
indexdata[i] = new Object();
indexdata[i].text= $(this).attr("ows_SpecialtyModule");
indexdata[i].isparent=1;
indexdata[i].isexpand = false;
indexdata[i].children = [];
arraymdl[i]=$(this).attr("ows_ID");
mn.Rows[i] = new Object();
mn.Rows[i].CstRowID = i;
mn.Rows[i].SpmID =$(this).attr("ows_ID");
});
}
});
GetExpertMainCategoryLead();
GetExpertChildCategory();
function GetExpertMainCategoryLead()
{
var mquery="";
var myQuery ="<Query><OrderBy><FieldRef Name='ISort' /></OrderBy><Where><And><In><FieldRef Name='SpecialtyModule' LookupId='True' /><Values>";
for(md in arraymdl)
{
mquery= mquery+"<Value Type='Lookup'>"+arraymdl[md]+"</Value>";
}
myQuery=myQuery+mquery+"</Values></In><Eq><FieldRef Name='IsDisplay' /><Value Type='Boolean'>1</Value></Eq></And></Where></Query>";
var myQueryOptions="<QueryOptions><ExpandUserField>True</ExpandUserField></QueryOptions>";
$().SPServices({
operation: "GetListItems",
async: false,
listName: "專業模塊技術帶頭人",
CAMLViewFields: "<ViewFields><FieldRef Name='SpecialtyModule' /> <FieldRef Name='ModuleLead' /></ViewFields>",
CAMLQuery: myQuery,
CAMLQueryOptions: myQueryOptions,
webURL: "http://ZJK",
completefunc: function (xData, Status) {
var j = -1;
cj = -1;
$(xData.responseXML).SPFilterNode("z:row").each(function() {
var arr = $(this).attr("ows_ModuleLead").split("#");
if(arr.length > 2)
{
j++;
cj++;
var acn =arr[2].replace(',','');
acn = "AboutExpertW.aspx?accountname="+acn;
var ld = arr[1].replace(',','');
ld= ld+"(技術帶頭人)";
for(dd in mn.Rows)
{
if(mn.Rows[dd].SpmID ==$(this).attr("ows_SpecialtyModule").split(";#")[0])
{
indexdata[mn.Rows[dd].CstRowID].children[j] = new Object();
indexdata[mn.Rows[dd].CstRowID].children[j].text = ld;
indexdata[mn.Rows[dd].CstRowID].children[j].url = acn;
indexdata[mn.Rows[dd].CstRowID].children[j].isparent=3;
j=-1;
break;
}
}
}
});
}
});
}
function GetExpertChildCategory()
{
var mquery="";
var myQuery ="<Query><OrderBy><FieldRef Name='ISort' /></OrderBy><Where><And><In><FieldRef Name='SpecialtyModule' LookupId='True' /><Values>";
for(md in arraymdl)
{
mquery= mquery+"<Value Type='Lookup'>"+arraymdl[md]+"</Value>";
}
myQuery=myQuery+mquery+"</Values></In><Eq><FieldRef Name='IsDisplay' /><Value Type='Boolean'>1</Value></Eq></And></Where></Query>";
var myQueryOptions="<QueryOptions><ExpandUserField>True</ExpandUserField></QueryOptions>";
$().SPServices({
operation: "GetListItems",
async: false,
listName: "專家庫專業模塊領域",
CAMLViewFields: "<ViewFields> <FieldRef Name='SpecialtyModule' /><FieldRef Name='SpecialtyModuleArea' /><FieldRef Name='AreaExpert' /></ViewFields>",
CAMLQuery: myQuery,
CAMLQueryOptions: myQueryOptions,
webURL: "http://ZJK",
completefunc: function (xData, Status) {
var j ;
$(xData.responseXML).SPFilterNode("z:row").each(function() {
var r=-1;
for(dd in mn.Rows)
{
if(mn.Rows[dd].SpmID ==$(this).attr("ows_SpecialtyModule").split(";#")[0])
{
j = indexdata[mn.Rows[dd].CstRowID].children.length;
indexdata[mn.Rows[dd].CstRowID].children[j] = new Object();
indexdata[mn.Rows[dd].CstRowID].children[j].text = $(this).attr("ows_SpecialtyModuleArea");
indexdata[mn.Rows[dd].CstRowID].children[j].isparent=2;
indexdata[mn.Rows[dd].CstRowID].children[j].isexpand = false;
indexdata[mn.Rows[dd].CstRowID].children[j].children = [];
r = mn.Rows[dd].CstRowID;
break;
}
}
var k =-1;
var arr = $(this).attr("ows_AreaExpert").split(";");
for(var key in arr){
if(arr[key].indexOf(',')>0)
{
var arrc = arr[key].split("#");
if(arrc.length > 2)
{
k++;
var acn =arrc[2].replace(',','');
acn = "AboutExpertW.aspx?accountname="+acn;
k = indexdata[r].children[j].children.length;
indexdata[r].children[j].children[k] = new Object();
indexdata[r].children[j].children[k].url = acn;
indexdata[r].children[j].children[k].text= arrc[1].replace(',','');
indexdata[r].children[j].children[k].isparent=4;
}
}
}
});
}
});
}
var tab = null;
var accordion = null;
var tree = null;
var tabItems = [];
$(function ()
{
//布局
$("#layout1").ligerLayout({ leftWidth: 190, height: '100%',heightDiff:-34,space:4, onHeightChanged: f_heightChanged });
var height = $(".l-layout-center").height();
//Tab
$("#framecenter").ligerTab({
height: height,
showSwitchInTab : true,
showSwitch: true,
onAfterAddTabItem: function (tabdata)
{
tabItems.push(tabdata);
saveTabStatus();
},
onAfterRemoveTabItem: function (tabid)
{
for (var i = 0; i < tabItems.length; i++)
{
var o = tabItems[i];
if (o.tabid == tabid)
{
tabItems.splice(i, 1);
saveTabStatus();
break;
}
}
},
onReload: function (tabdata)
{
var tabid = tabdata.tabid;
addFrameSkinLink(tabid);
}
});
//面板
$("#accordion1").ligerAccordion({ height: height - 32, speed: null });
$(".l-link").hover(function ()
{
$(this).addClass("l-link-over");
}, function ()
{
$(this).removeClass("l-link-over");
});
//樹
$("#tree1").ligerTree({
data : indexdata,
checkbox: false,
slide: false,
nodeWidth: 120,
attribute: ['nodename', 'url'],
onSelect: function (node)
{
if(node.data.isparent ==1)
{
$('#home').attr("src","ListExpert.aspx?specialtymodule="+node.data.text);
}
if(node.data.isparent ==2)
{
$('#home').attr("src","ListExpert.aspx?specialtymodulearea="+node.data.text);
}
if (!node.data.url) return;
var tabid = $(node.target).attr("tabid");
if (!tabid)
{
tabid = new Date().getTime();
$(node.target).attr("tabid", tabid)
}
f_addTab(tabid, node.data.text, node.data.url);
}
});
tab = liger.get("framecenter");
accordion = liger.get("accordion1");
tree = liger.get("tree1");
$("#pageloading").hide();
css_init();
pages_init();
}
);
function f_heightChanged(options)
{
if (tab)
tab.addHeight(options.diff);
if (accordion && options.middleHeight - 32 > 0)
accordion.setHeight(options.middleHeight - 32);
}
function f_addTab(tabid, text, url)
{
tab.addTabItem({
tabid: tabid,
text: text,
url: url,
callback: function ()
{
addFrameSkinLink(tabid);
}
});
}
function addFrameSkinLink(tabid)
{
var prevHref = getLinkPrevHref(tabid) || "";
var skin = getQueryString("skin");
if (!skin) return;
skin = skin.toLowerCase();
attachLinkToFrame(tabid, prevHref + skin_links[skin]);
}
var skin_links = {
"aqua": "../lib/ligerUI/skins/Aqua/css/ligerui-all.css",
"gray": "../lib/ligerUI/skins/Gray/css/all.css",
"silvery": "../lib/ligerUI/skins/Silvery/css/style.css",
"gray2014": "../lib/ligerUI/skins/gray2014/css/all.css"
};
function pages_init()
{
var tabJson = $.cookie('liger-home-tab');
if (tabJson)
{
var tabitems = JSON2.parse(tabJson);
for (var i = 0; tabitems && tabitems[i];i++)
{
f_addTab(tabitems[i].tabid, tabitems[i].text, tabitems[i].url);
}
}
}
function saveTabStatus()
{
$.cookie('liger-home-tab', JSON2.stringify(tabItems));
}
function css_init()
{
var css = $("#mylink").get(0), skin = getQueryString("skin");
$("#skinSelect").val(skin);
$("#skinSelect").change(function ()
{
if (this.value)
{
location.href = "index.htm?skin=" + this.value;
} else
{
location.href = "index.htm";
}
});
if (!css || !skin) return;
skin = skin.toLowerCase();
$('body').addClass("body-" + skin);
$(css).attr("href", skin_links[skin]);
}
function getQueryString(name)
{
var now_url = document.location.search.slice(1), q_array = now_url.split('&');
for (var i = 0; i < q_array.length; i++)
{
var v_array = q_array[i].split('=');
if (v_array[0] == name)
{
return v_array[1];
}
}
return false;
}
function attachLinkToFrame(iframeId, filename)
{
if(!window.frames[iframeId]) return;
var head = window.frames[iframeId].document.getElementsByTagName('head').item(0);
var fileref = window.frames[iframeId].document.createElement("link");
if (!fileref) return;
fileref.setAttribute("rel", "stylesheet");
fileref.setAttribute("type", "text/css");
fileref.setAttribute("href", filename);
head.appendChild(fileref);
}
function getLinkPrevHref(iframeId)
{
if (!window.frames[iframeId]) return;
var head = window.frames[iframeId].document.getElementsByTagName('head').item(0);
var links = $("link:first", head);
for (var i = 0; links[i]; i++)
{
var href = $(links[i]).attr("href");
if (href && href.toLowerCase().indexOf("ligerui") > 0)
{
return href.substring(0, href.toLowerCase().indexOf("lib") );
}
}
}
</script>
<style type="text/css">
body,html{height:100%;}
body{ padding:0px; margin:0; overflow:hidden;
text-align: left;
}
.l-link{ display:block; height:26px; line-height:26px; padding-left:10px; text-decoration:underline; color:#333;}
.l-link2{text-decoration:underline; color:white; margin-left:2px;margin-right:2px;}
.l-layout-top{background:#102A49; color:White;}
.l-layout-bottom{ background:#E5EDEF; text-align:center;}
#pageloading{position:absolute; left:0px; top:0px; background:white url('loading.gif') no-repeat center; width:100%; height:100%;z-index:99999;}
.l-link{ display:block; line-height:22px; height:22px; padding-left:16px;border:1px solid white; margin:4px;}
.l-link-over{ background:#FFEEAC; border:1px solid #DB9F00;}
.l-winbar{ background:#2B5A76; height:30px; position:absolute; left:0px; bottom:0px; width:100%; z-index:99999;}
.space{ color:#E7E7E7;}
/* 頂部 */
.l-topmenu{ margin:0; padding:0; height:31px; line-height:31px; background:url('lib/images/top.jpg') repeat-x bottom; position:relative; border-top:1px solid #1D438B; }
.l-topmenu-logo{ color:#E7E7E7; padding-left:35px; line-height:26px;background:url('lib/images/topicon.gif') no-repeat 10px 5px;}
.l-topmenu-welcome{ position:absolute; height:24px; line-height:24px; right:30px; top:2px;color:#070A0C;}
.l-topmenu-welcome a{ color:#E7E7E7; text-decoration:underline}
.body-gray2014 #framecenter{
margin-top:3px;
}
.viewsourcelink {
background:#B3D9F7; display:block; position:absolute; right:10px; top:3px; padding:6px 4px; color:#333; text-decoration:underline;
}
.viewsourcelink-over {
background:#81C0F2;
}
.l-topmenu-welcome label {color:white;
}
#skinSelect {
margin-right: 6px;
}
</style>
</asp:Content>
<asp:Content ContentPlaceHolderId="PlaceHolderMain" runat="server">
<div id="layout1" style="width:99.2%; margin:0 auto; margin-top:0px; ">
<div position="left" title="專家數據庫" id="accordion1" >
<div title="專業領域" class="l-scroll" >
<ul id="tree1" style="margin-top:3px;" />
</div>
</div>
<div position="center" id="framecenter">
<div tabid="home" title="專家列表" style="height:300px" >
<iframe frameborder="0" name="home" id="home" src="ListExpert.aspx"></iframe>
</div>
</div>
</div>
<div style="height:32px; line-height:32px; text-align:center;">
XX公司 版權所有 2014-2015 技術支持:軟件人生(http://www.cnblogs.com/nbpowerboy)
</div>
<div style="display:none"></div>
</asp:Content>
同時歡迎關注本人的微信號QYXXHQY,不定期更新企業信息化前沿相關技術和應用,歡迎掃描關注,二維碼如下:

| 本博客為軟件人生原創,歡迎轉載,轉載請標明出處:http://www.cnblogs.com/nbpowerboy/p/4166836.html 。演繹或用於商業目的,但是必須保留本文的署名軟件人生(包含鏈接)。如您有任何疑問或者授權方面的協商,請給我留言。 |
