很遺憾,過去將近一年的時間裡一直在學習,工作之中涉及到Java和Flex,業餘時間在和朋友搞Android,實際開發中遇到很多問題都需要學習,偶而擠出來那麼一點時間自己也在瞭解一些新的東西,感覺太多的東西想學,隻是時間少的可憐。
從今天開始,計劃每周不低於一篇文章,把《ExtJS4.1+MVC3+Spring.NET1.3+EF5 整合》寫完,下一系列準備寫個SSH2.0+EasyUI的框架,我還是依舊那麼的喜歡開源技術。
Tree結構應該說是Web開發中必會的對象之一,在關系型數據庫中體現為一種“一對多”的父子表,最為典型的就是系統“管理菜單”和角色“權限”,在本篇之中,“菜單”和“權限”實際上是一張數據表,“菜單”僅涉及到顯示數據,“權限”涉及到更多的ExtJS API操作,正面分別講解這兩種情況下的使用。
1 ExtJS 代碼
理論上說“管理菜單”應該根據當前登錄用戶(所屬角色)的權限,顯示相應的菜單結構,這個可以通過繼承System.Web.Mvc.AuthorizeAttribute來實現,在需要驗證權限的Controller或方法上添加“特性(Attribute)”即可。權限的實現過程準備單獨寫一篇說明。
本文章的“管理菜單”涉及到以下幾個js文件:
app/controller/menu.js
app/store/menu.js
app/view/menu/tree.js
1.1 先看一下控制器類(app/controller/menu.js)代碼
Ext.define('Manage.controller.Menu', { extend: 'Ext.app.Controller', stores: ['Menu'], views: ['menu.Tree'], init: function() { this.control({ 'menuTree': { itemclick: this.selectItem } }); }, selectItem: function (view, record, item, index) { Ext.Ajax.request({ url: '/Manage/Category/Query.aspx?id=' + record.data.id, success: function (response) { var menu = Ext.JSON.decode(response.responseText); var url = menu.children.MenuUrl; if (url.length > 0) { var body = Ext.getCmp('bodyID'); body.removeAll(); body.add({ xtype: url }); } } }); } });
該控制器類很簡單,僅僅是涉及到瞭store和view,這裡selectItem方法的作用是選擇節點的事件,即向id為bodyID的元素中添加一個新的子元素,其實就是點擊左側菜單時,在右側打開相應的菜單模塊。
1.2 看下store(app/store/menu.js)中的定義
Ext.define('Manage.store.Menu', { extend: 'Ext.data.TreeStore', autoLoad: true, sorters: [{ property: 'leaf', direction: 'ASC' }], root: { nodeType: 'async', expanded: true }, proxy: { type: 'ajax', api: { read: '/Manage/Authorize/Menu.aspx' }, reader: { type: 'json', root: 'children' } } });
“管理菜單”僅涉及到“讀”操作,所以這裡的proxy隻設置瞭一個read URL:/Manage/Authorize/Menu.aspx,先把view代碼看完再回過頭看這個URL返回的值。
1.3 view(app/view/menu/tree.js)代碼
Ext.define('Manage.view.menu.Tree', { extend: 'Ext.tree.Panel', alias: 'widget.menuTree', header : false, border : 0, store: 'Menu', rootVisible: false });
視圖代碼很簡單,定義瞭不顯示header,邊框為0,以及不顯示根節點。
2 C# 代碼
ExtJS 中相應的controll、store、view代碼都很簡單,而C#返回的結果也很簡單,“管理菜單”所請求的URL是:/Manage/Authorize/Menu.aspx,打開Areas/Manage/Controllers/AuthorizeController.cs文件:
public class AuthorizeController : Controller { public IRoleService RoleService { get; set; } public IAuthorizeService AuthorizeService { get; set; } public ActionResult Menu() { Role role = this.RoleService.Read("12"); var childs = this.MenuChildrens(ref role, null); var root = new { id = 0, text = "根", expanded = true, leaf = !childs.Any(), children = childs }; return Json(root, JsonRequestBehavior.AllowGet); } [NonAction] private IList MenuChildrens(ref Role role, string parent) { var list = String.IsNullOrEmpty(parent) ? role.Categories.Where(x => String.IsNullOrEmpty(x.ParentCode)) : role.Categories.Where(x => x.ParentCode == parent); IList childs = new List(); if (!list.Any()) { return childs; } foreach (var row in list) { var children = this.MenuChildrens(ref role, row.Code); childs.Add(new { id = row.Code, text = row.Name, expanded = true, leaf = !children.Any(), children = children }); } return childs; } }
在關系型數據庫中為瞭表示一個tree結構,一般都會設計兩個主要的字段:ID和父ID,在遍歷tree時就可以使用父ID來向下索引子節點。這裡的代碼我就不詳述瞭,如果有不清晰或疑問請留言。最後,這裡實際的返回串類似這樣:
{"id":0,"text":"根","expanded":true,"leaf":false,"children":[{"id":"11","text":"數據管理","expanded":true,"leaf":false,"children":[{"id":"1111","text":"新聞管理","expanded":true,"leaf":true,"children":[]}]},{"id":"12","text":"系統管理","expanded":true,"leaf":false,"children":[{"id":"1211","text":"文章模版","expanded":true,"leaf":true,"children":[]}]},{"id":"13","text":"系統配置","expanded":true,"leaf":false,"children":[{"id":"1311","text":"角色管理","expanded":true,"leaf":true,"children":[]},{"id":"1312","text":"管理員管理","expanded":true,"leaf":true,"children":[]},{"id":"1313","text":"權限分配","expanded":true,"leaf":true,"children":[]},{"id":"1314","text":"分類管理","expanded":true,"leaf":true,"children":[]},{"id":"1315","text":"文章管理","expanded":true,"leaf":true,"children":[]}]}]}
使用工具格式化一下再看就比較清晰瞭:
在線格式化工具:https://tool.oschina.net/codeformat/json
或者使用Sublime Text編輯器,64位:https://download.csdn.net/detail/xz2001/6490993,32位:https://download.csdn.net/detail/xz2001/6490973
{ "id": 0, "text": "根", "expanded": true, "leaf": false, "children": [{ "id": "11", "text": "數據管理", "expanded": true, "leaf": false, "children": [{ "id": "1111", "text": "新聞管理", "expanded": true, "leaf": true, "children": [] }] }, { "id": "12", "text": "系統管理", "expanded": true, "leaf": false, "children": [{ "id": "1211", "text": "文章模版", "expanded": true, "leaf": true, "children": [] }] }, { "id": "13", "text": "系統配置", "expanded": true, "leaf": false, "children": [{ "id": "1311", "text": "角色管理", "expanded": true, "leaf": true, "children": [] }, { "id": "1312", "text": "管理員管理", "expanded": true, "leaf": true, "children": [] }, { "id": "1313", "text": "權限分配", "expanded": true, "leaf": true, "children": [] }, { "id": "1314", "text": "分類管理", "expanded": true, "leaf": true, "children": [] }, { "id": "1315", "text": "文章管理", "expanded": true, "leaf": true, "children": [] }] }] }
返回的節點屬性中,id必須唯一,text是顯示出的文本,expanded表示默認該節點是否打開,leaf表示是否是葉節點,children是子節點集合,詳細的屬性請查閱ExtJS API文檔,最終顯示的結果如下:
下一篇說下TreePanel一些復雜的操作,如節點遍歷和復選框的讀、寫操作等。