不知不覺本系統寫了快三個月了,最近寫頁面的具體功能時感覺到有點吃力,很多地方如果張嘴來講的話可以說得很細,很全面,可寫成文字的話,就不太會寫了,有些地方想講得清晰的話,得用多幾倍的文字+實例+變化中的圖片才能表達得清楚,而寫這些又太費時間了,近段時間又特忙,所以只能是盡力而為,希望大家自行研究,如果有什么地方不明白的,發發評論或郵件給我,我再重新詳細講解。
說回正題,對於頁面訪問權限以及每個按鍵的權限控制,很久以前用過好幾種不同的方法,比如為每個控件分配名稱或編碼,然后在寫代碼時綁定這些值,又比如用XML來配置制權限等方法。這些方法都比較麻煩,而且由於都是使用編碼方法,開發時需要一個個進行綁定,容易出錯。經過后來不斷的完善,最后完成了本系統所采用的頁面控件注冊管理來綁定控件權限(如有雷同,純屬巧合,哈哈...),首先創建菜單,並綁定好對應的文件(頁面),然后將系統要用到的名稱添加到公用標識庫中,跟着在頁面控件權限管理頁面對各個頁面控件進行綁定(只需要點擊鼠標即可),通過職位(角色)來賦於不同的操作權限,只需要設置好管理員的職位,那么該管理員就擁有他所綁定的角色的全部權限了。
如下面的說明
首先在后端注冊菜單(菜單綁定頁面)
創建公用頁面權限標識,待用
為需要綁定頁面控件權限的菜單頁面綁定操作控件(左列為上圖錄入的公共控件名稱,右列為已綁定的頁面控件,只需要點擊鼠標就可以輕松綁定)
創建好部門
在不同部門創建相應的職位(角色)
為不同角色設置菜單與頁面控件操作權限
開發說明:(主要講講與上一章中不同的內容)
1、PagePowerSignPublicList.aspx.cs 公用頁面控件權限標識列表管理文件
這個頁面是比較經典的普通列表頁面,比較代碼,有以下一些地方和菜單頁面不一樣的
1 #region 加載數據 2 /// <summary>讀取數據</summary> 3 public override void LoadData() 4 { 5 //設置排序 6 if (sortList == null) 7 { 8 Sort(null); 9 } 10 11 //綁定Grid表格 12 bll.BindGrid(Grid1, Grid1.PageIndex + 1, Grid1.PageSize, null, sortList); 13 } 14 15 #endregion
對於排序函數的調用,菜單頁面是有層次感列表,所以要自定義排序函數,而對於正常的列表,我們直接調用父類的Sort(null)函數即可
綁定Grid表格也與菜單頁面調用的不一樣,bll.BindGrid()函數使用的是參數中需要添加當前在第幾頁,每個頁面顯示多少行,null是條件參數,后面會詳細解釋它怎么使用,最后一個參數是排序規則參數
另外,Delete()刪除函數也使用批量刪除功能,不過會在刪除前進行檢查,發現指定記錄不能刪除時,則彈出提示是那個Id的記錄無法刪除,大家自行比較一下兩個列表文件就明白了。
2、PagePowerSignPublicEdit.aspx.cs公用頁面控件權限標識編輯文件
這個文件要注意的地方是Save()函數

1 #region 保存 2 /// <summary> 3 /// 數據保存 4 /// </summary> 5 /// <returns></returns> 6 public override string Save() 7 { 8 string result = string.Empty; 9 int id = ConvertHelper.Cint0(hidId.Text); 10 11 try 12 { 13 #region 數據驗證 14 15 if (string.IsNullOrEmpty(txtCName.Text.Trim())) 16 { 17 return txtCName.Label + "不能為空!"; 18 } 19 var sName = StringHelper.Left(txtCName.Text, 20); 20 if (PagePowerSignPublicBll.GetInstence().Exist(x => x.CName == sName && x.Id != id)) 21 { 22 return txtCName.Label + "已存在!請重新輸入!"; 23 } 24 if (string.IsNullOrEmpty(txtEName.Text.Trim())) 25 { 26 return txtEName.Label + "不能為空!"; 27 } 28 var sEname = StringHelper.Left(txtEName.Text, 50); 29 if (PagePowerSignPublicBll.GetInstence().Exist(x => x.EName == sEname && x.Id != id)) 30 { 31 return txtEName.Label + "已存在!請重新輸入!"; 32 } 33 34 #endregion 35 36 #region 賦值 37 //定義是否更新標識——即當前記錄的名稱是否改變了 38 bool isUpdate = false; 39 40 //獲取實體 41 var model = new PagePowerSignPublic(x => x.Id == id); 42 43 //判斷是否有改變名稱 44 if (id > 0 && (sName != model.CName || sEname != model.EName)) 45 { 46 isUpdate = true; 47 } 48 49 //設置名稱 50 model.CName = sName; 51 //設置英文名稱 52 model.EName = sEname; 53 #endregion 54 55 //---------------------------------------------------------- 56 //存儲到數據庫 57 PagePowerSignPublicBll.GetInstence().Save(this, model); 58 59 //判斷是否需要同步更新關聯表字段 60 if (isUpdate) 61 { 62 //調用更新函數,同步更新對應的所有記錄 63 PagePowerSignBll.GetInstence().UpdateValue_For_PagePowerSignPublic_Id(this, model.Id, PagePowerSignTable.CName, model.CName, PagePowerSignTable.EName, model.EName); 64 } 65 } 66 catch (Exception e) 67 { 68 result = "保存失敗!"; 69 70 //出現異常,保存出錯日志信息 71 CommonBll.WriteLog(result, e); 72 } 73 74 return result; 75 } 76 #endregion
在函數中,大家可以找到下面這些代碼
1 //定義是否更新標識——即當前記錄的名稱是否改變了 2 bool isUpdate = false; 3 4 //判斷是否有改變名稱 5 if (id > 0 && (sName != model.CName || sEname != model.EName)) 6 { 7 isUpdate = true; 8 } 9 10 //判斷是否需要同步更新關聯表字段 11 if (isUpdate) 12 { 13 //調用更新函數,同步更新對應的所有記錄 14 PagePowerSignBll.GetInstence().UpdateValue_For_PagePowerSignPublic_Id(this, model.Id, PagePowerSignTable.CName, model.CName, PagePowerSignTable.EName, model.EName); 15 }
這是用於我們修改A表記錄的名稱時,同步修改其他關聯表引用了這個記錄名稱字段的所有記錄
為什么要這么處理呢?大家在看數據字典的數據庫結構時,會發現很多表與其他表關聯時,不單將其他表的Id引用了過來,還將這個Id對應的名稱也引用了,這種操作方式使我們在編寫查詢語句時,幾乎可以做到不用多表關聯,因為我們想要顯示的內容已經在查詢的表中存在了,這樣處理后,我們在修改相關表的名稱時,就必須同步修改關聯表中的名稱,對於這些關聯表名稱的修改,我們的T4模板也生成了相應的函數,如:UpdateValue_For_表名_Id()函數,然后直接按上面代碼編寫方法實現就可以了。
3、UseLogList.aspx.cs用戶操作日志管理文件
這個文件要注意的是InquiryCondition()函數
1 /// <summary> 2 /// 查詢條件 3 /// </summary> 4 /// <returns></returns> 5 private List<ConditionHelper.SqlqueryCondition> InquiryCondition() 6 { 7 var wheres = new List<ConditionHelper.SqlqueryCondition>(); 8 9 //如果Id有值時,即表示查詢的是指定管理員的操作日志 10 if (_id != 0) 11 { 12 wheres.Add(new ConditionHelper.SqlqueryCondition(ConstraintType.Where, LoginLogTable.Manager_Id, Comparison.Equals, _id)); 13 } 14 15 //起始時間 16 if (!string.IsNullOrEmpty(dpStart.Text.Trim())) 17 { 18 wheres.Add(new ConditionHelper.SqlqueryCondition(ConstraintType.And, LoginLogTable.AddDate, Comparison.GreaterOrEquals, StringHelper.FilterSql(dpStart.Text))); 19 //終止時間 20 if (!string.IsNullOrEmpty(dpEnd.Text.Trim())) 21 { 22 wheres.Add(new ConditionHelper.SqlqueryCondition(ConstraintType.And, LoginLogTable.AddDate, Comparison.LessOrEquals, StringHelper.FilterSql(dpEnd.Text))); 23 } 24 } 25 26 //ip地址 27 if (!string.IsNullOrEmpty(txtIp.Text.Trim())) 28 { 29 wheres.Add(new ConditionHelper.SqlqueryCondition(ConstraintType.And, LoginLogTable.Ip, Comparison.Equals, StringHelper.FilterSql(txtIp.Text))); 30 } 31 //登錄備注信息 32 if (!string.IsNullOrEmpty(txtloginfo.Text.Trim())) 33 { 34 wheres.Add(new ConditionHelper.SqlqueryCondition(ConstraintType.And, LoginLogTable.Notes, Comparison.Like, "%" + StringHelper.FilterSql(txtloginfo.Text) + "%")); 35 } 36 37 return wheres; 38 }
這是查詢條件函數,ConditionHelper.SqlqueryCondition這個類是自定義封裝條件類
它的構造函數(public SqlqueryCondition(ConstraintType ctype, string columnname, Comparison cparsion, object value, bool isParentheses = false))一共有5個參數,其中4個為必填參數
第一個參數ConstraintType ctype為查詢的類型,主要是ConstraintType.Where、ConstraintType.And、ConstraintType.Or三種。
如果有多個條件時,ConstraintType.Where只能放在最前面,且只能有一個條件使用這個參數,一般都很少用它。
ConstraintType.And指的是當前參數與前面參數的關系是And關系
ConstraintType.Or通常情況下指的是當前參數與前面參數的關系是Or關系
第二個參數string columnname是條件列字段名稱
第三個參數Comparison cparsion是表達式,使用Comparison.X來設置,根據需求設置==、>、>=、<、<=、like、in、not in......
第四個參數object value是條件值,如果是in與not in表達式時,條件值必須為數據類型,比如:string[]、int[]、object[]等
第五個參數bool isParentheses是加左括號,而右括號使用new ConditionHelper.SqlqueryCondition()或new ConditionHelper.SqlqueryCondition(Comparison.CloseParentheses)來添加,然后in或not in表達式時,必須緊跟着右括號
例子1:
(A<2 Or A>= 10) And B=100
1 var wheres = new List<ConditionHelper.SqlqueryCondition>(); 2 3 wheres.Add(new ConditionHelper.SqlqueryCondition(ConstraintType.Where, "A", Comparison.LessThan, 2, true)); 4 5 wheres.Add(new ConditionHelper.SqlqueryCondition(ConstraintType.Or, "A", Comparison.GreaterOrEquals, 10)); 6 7 wheres.Add(new ConditionHelper.SqlqueryCondition()); 8 9 wheres.Add(new ConditionHelper.SqlqueryCondition(ConstraintType.And, "B", Comparison.Equals, 100));
例子2:
A == 2 And B in arr And C like 'abc%' (in查詢必須加左右括號)
1 var wheres = new List<ConditionHelper.SqlqueryCondition>(); 2 3 wheres.Add(new ConditionHelper.SqlqueryCondition(ConstraintType.Where, "A", Comparison.Equals, 2)); 4 //加左括號 5 wheres.Add(new ConditionHelper.SqlqueryCondition(ConstraintType.And, "B", Comparison.In, arr, true)); 6 //加右括號 7 wheres.Add(new ConditionHelper.SqlqueryCondition()); 8 9 wheres.Add(new ConditionHelper.SqlqueryCondition(ConstraintType.And, "C", Comparison.Like, "abc%"));
這個封裝類最終會生成SubSonic3.0底層調用的條件參數,經過測試發現,SubSonic3.0底層調用的條件參數只支持單括號,不支持括號的嵌套,多重嵌套后最終生成的也只是單括號且會出錯,所以多重嵌套括號時,最好使用其它方法來實現,比如Linq、存儲過程、SQL語句拼接等。
4、小結
本次更新的代碼功能比如多,已將部門、職位、公用頁面控件權限標識、頁面控件權限管理、在線用戶列表、用戶登陸日志、用戶操作日志、錯誤日志等功能都已完成了,同時也對在線用戶相關類和函數進行了優化處理。
內容看起來好像挺多了,其實都是對之前所寫的模板函數以及其他公共函數的調用,這么多頁面查看cs代碼時可以發現,里面的內容都差不多,都是很格式化的東西,如果你嘗試過用這些已完成的模板頁面添加新功能,就可以發現很簡單,開發也很迅速。
至於每個頁面如何去實現,接下來就不必再像之前那樣講得很細致了,會針對一些特殊功能或函數的調用進行解說,往細講也只能從邏輯層的各個函數應用來說起,所以就不再羅嗦一一細說,代碼中有大量的注釋,大家慢慢研究吧。
最好附上幾張頁面的效果圖
由於框架不是非常成熟,很多朋友不是用來學習而是直接用到項目中,但不熟悉框架引起不少小問題,所以停止提供下載,有需要學習的可以到群共享里下,不便之處敬請諒解。
版權聲明:
本文由AllEmpty原創並發布於博客園,歡迎轉載,未經本人同意必須保留此段聲明,且在文章頁面明顯位置給出原文鏈接,否則保留追究法律責任的權利。如有問題,可以通過1654937@qq.com 聯系我,非常感謝。
發表本編內容,只要主為了和大家共同學習共同進步,有興趣的朋友可以加加Q群:327360708 ,大家一起探討。
更多內容,敬請觀注博客:http://www.cnblogs.com/EmptyFS/