首页 > 代码库 > 【开源】OSharp框架解说系列(2.1):EasyUI的后台界面搭建及极致重构

【开源】OSharp框架解说系列(2.1):EasyUI的后台界面搭建及极致重构

〇、前言

  要了解一个东西长什么样,至少得让我们能看到,才能提出针对性的见解。所以,为了言之有物,而不是凭空漫谈,我们先从UI说起,后台管理页面的UI我们将使用应用比较普遍的easyui框架。

  以前在用easyui的时候,每个页面都得从0做起,或者不厌其烦地由以前的页面通过“复制-粘贴”的方式来修改,久页久之,就会造成页面庞大且难以维护。其实,前端的html,javascript代码与后端的代码是一样的,通过一定的组织,把重复的代码抽离出来,同样也通过达到很好的复用率。而MVC的天生的Layout布局与分布视图(Partial View),就是对重复代码抽离的需求有很好的支持。

一、目录

  • 〇、前言
  • 一、目录
  • 二、easyui-layout布局
  • 三、easyui-datagrid布局
  • 四、开源说明
  • 系列导航

二、EasyUI-Layout布局

_Layout.cshtml

  MVC的布局,最先当然是作为根视图存在的_Layout.cshtml了,_Layout.cshtml很简单,只是负责一些样式文件和公共脚本的引用。开发阶段,先使用绝对地址进行引用,发布的时候再进行压缩代码的考虑。

  在_Layout.cshtml中,除了必需的 @RenderBody() ,还定义了两个Section,分别为负责引用子级视图样式的 @RenderSection("header", false) 和负责引用子级视图脚本的 @RenderSection("footer", false)

 1 @{ 2     Layout = null; 3 } 4 <!DOCTYPE html> 5 <html> 6 <head> 7     <meta http-equiv="Content-Type" content="text/html; charset=utf-8" /> 8     <meta name="viewport" content="width=device-width, initial-scale=1.0"> 9     <title>@ViewBag.Title - OSharp管理系统</title>10     <link href="/Content/themes/gray/easyui.css" rel="stylesheet" />11     <link href="/Content/themes/icon.css" rel="stylesheet" />12     <link href="/Content/osharp-icons.css" rel="stylesheet" />13     <link href="/Content/osharp-admin.css" rel="stylesheet"/>14     @RenderSection("header", false)15 </head>16 <body>17     @RenderBody()18     <script src="/Scripts/jquery-1.11.1.js" type="text/javascript"></script>19     <script src="/Scripts/jquery.easyui-1.4.1.js" type="text/javascript"></script>20     <script src="/Scripts/locale/easyui-lang-zh_CN.js" type="text/javascript"></script>21     <script src="/Scripts/json2.js" type="text/javascript"></script>22     <script src="/Scripts/osharp.global.js" type="text/javascript"></script>23     <script src="/Scripts/osharp.easyui.js" type="text/javascript"></script>24     <script src="/Scripts/osharp.data.js" type="text/javascript"></script>25     @RenderSection("footer", false)26 </body>27 </html>

 后台的EasyUI-Layout布局

  一般来说,后台管理页面都是这样一个布局方式:

  1. 上边一个顶栏
  2. 左边一个手风琴或树形的导航栏
  3. 中间是一个由iframe加载具体内容页的多选项卡tab页面

  这样,就要用到easyui的easyui-layout来做整体布局,左边的导航栏使用easyui-accordion,右边加载内容页的多选项卡使用easyui-tabs。easyui的布局在网上也很普遍,具体的就不说了,完整代码如下:

技术分享
  1 @{  2     ViewBag.Title = "OSharp后台管理";  3     Layout = "~/Areas/Admin/Views/Shared/_Layout.cshtml";  4     string navDataUrl = Url.Action("GetNavData");  5 }  6 <div class="easyui-layout" data-options="fit:true">  7     <div data-options="region:‘north‘, height:50" style="padding: 10px;">  8         <span style="font-size: 18px;">OSharp后台管理系统</span>  9         <a href="/" target="_blank">返回首页</a> 10     </div> 11     <div data-options="region:‘west‘, split:true, minWidth:100, width:150, title:‘导航菜单‘"> 12         <div id="main-nav" class="easyui-accordion" data-options="fit:true, border:false, selected:true"> 13  14         </div> 15     </div> 16     <div data-options="region:‘center‘"> 17         <div id="main-tab" class="easyui-tabs" data-options="fit:true, border:false"> 18             <div title="我的主页" iconcls="pic_209" style="padding: 5px;"> 19                 <iframe width="100%" height="100%" frameborder="0" src="@Url.Action("Welcome")" marginheight="0" marginwidth="0"></iframe> 20             </div> 21         </div> 22     </div> 23     <div data-options="region:‘south‘, height:50"> 24         <p style="text-align:center; line-height:20px;">Copyright &copy; OSharp @DateTime.Now.Year</p> 25     </div> 26 </div> 27 <div id="tab-menu" class="easyui-menu" style="width: 150px;"> 28     <div id="tab-menu-refresh" data-options="iconCls:‘icon-reload‘">刷新</div> 29     <div id="tab-menu-openFrame" data-options="iconCls:‘pic_138‘">新窗口打开</div> 30     <div class="menu-sep"></div> 31     <div id="tab-menu-close" data-options="iconCls:‘icon-remove‘">关闭</div> 32     <div id="tab-menu-closeleft" data-options="iconCls:‘icon-undo‘">关闭左边</div> 33     <div id="tab-menu-closeright" data-options="iconCls:‘icon-redo‘">关闭右边</div> 34     <div class="menu-sep"></div> 35     <div id="tab-menu-closeother" data-options="iconCls:‘pic_101‘">关闭其他</div> 36     <div id="tab-menu-closeall" data-options="iconCls:‘pic_283‘">关闭所有</div> 37 </div> 38  39 @section footer{ 40     <script type="text/javascript"> 41         $(function() { 42             $.getJSON("@navDataUrl", function(data) { 43                 if (data.length == 0) { 44                     return; 45                 } 46                 //第一层生成手风琴的项 47                 $.each(data, function(i, item) { 48                     var id = item.Id; 49                     $("#main-nav").accordion("add", { 50                         title: item.Text, 51                         content: "<ul id=‘tree-" + id + "‘></ul>", 52                         selected: true, 53                         iconCls: item.IconCls 54                     }); 55                     $.parser.parse(); 56                     //第二层生成树节点 57                     if (!item.Children || item.Children.length == 0) { 58                         return true; 59                     } 60                     var treeData = transToTreeData(item.Children); 61                     $("#tree-" + id).tree({ 62                         data: treeData, 63                         onClick: function(node) { 64                             if (node.attributes) { 65                                 var tabTitle = node.text; 66                                 var url = node.attributes.url; 67                                 var icon = node.iconCls; 68                                 addTab(tabTitle, url, icon); 69                             } 70                         } 71                     }); 72                 }); 73             }); 74  75             $("#main-tab").tabs({ 76                 onContextMenu: function(e, title) { 77                     e.preventDefault(); 78                     $("#tab-menu").menu("show", { left: e.pageX, top: e.pageY }) 79                         .data("tabTitle", title); //将点击的Tab标题加到菜单数据中 80                 } 81             }); 82  83             $("#tab-menu").menu({ 84                 onClick: function(item) { 85                     tabHandle(this, item.id); 86                 } 87             }); 88         }); 89  90         function addTab(title, url, icon) { 91             var $mainTabs = $("#main-tab"); 92             if ($mainTabs.tabs("exists", title)) { 93                 $mainTabs.tabs("select", title); 94             } else { 95                 $mainTabs.tabs("add", { 96                     title: title, 97                     closable: true, 98                     icon: icon, 99                     content: createFrame(url)100                 });101             }102         }103 104         function createFrame(url) {105             var html = <iframe scrolling="auto" frameborder="0"  src="http://www.mamicode.com/ + url + " style="width:100%;height:99%;"></iframe>;106             return html;107         }108 109         function tabHandle(menu, type) {110             var title = $(menu).data("tabTitle");111             var $tab = $("#main-tab");112             var tabs = $tab.tabs("tabs");113             var index = $tab.tabs("getTabIndex", $tab.tabs("getTab", title));114             var closeTitles = [];115             switch (type) {116                 case "tab-menu-refresh":117                     var iframe = $(".tabs-panels .panel").eq(index).find("iframe");118                     if (iframe) {119                         var url = iframe.attr("src");120                         iframe.attr("src", url);121                     }122                     break;123                 case "tab-menu-openFrame":124                     var iframe = $(".tabs-panels .panel").eq(index).find("iframe");125                     if (iframe) {126                         window.open(iframe.attr("src"));127                     }128                     break;129                 case "tab-menu-close":130                     closeTitles.push(title);131                     break;132                 case "tab-menu-closeleft":133                     if (index == 0) {134                         $.osharp.easyui.msg.tip("左边没有可关闭标签。");135                         return;136                     }137                     for (var i = 0; i < index; i++) {138                         var opt = $(tabs[i]).panel("options");139                         if (opt.closable) {140                             closeTitles.push(opt.title);141                         }142                     }143                     break;144                 case "tab-menu-closeright":145                     if (index == tabs.length - 1) {146                         $.osharp.easyui.msg.tip("右边没有可关闭标签。");147                         return;148                     }149                     for (var i = index + 1; i < tabs.length; i++) {150                         var opt = $(tabs[i]).panel("options");151                         if (opt.closable) {152                             closeTitles.push(opt.title);153                         }154                     }155                     break;156                 case "tab-menu-closeother":157                     for (var i = 0; i < tabs.length; i++) {158                         if (i == index) {159                             continue;160                         }161                         var opt = $(tabs[i]).panel("options");162                         if (opt.closable) {163                             closeTitles.push(opt.title);164                         }165                     }166                     break;167                 case "tab-menu-closeall":168                     for (var i = 0; i < tabs.length; i++) {169                         var opt = $(tabs[i]).panel("options");170                         if (opt.closable) {171                             closeTitles.push(opt.title);172                         }173                     }174                     break;175             }176             for (var i = 0; i < closeTitles.length; i++) {177                 $tab.tabs("close", closeTitles[i]);178             }179         }180 181         function transToTreeData(data) {182             return $.Enumerable.From(data).Select(function(m) {183                 var obj = {};184                 obj.id = m.Id;185                 obj.text = m.Text;186                 obj.iconCls = m.IconCls;187                 obj.checked = m.Checked;188                 if (m.Url) {189                     obj.attributes = { url: m.Url };190                 }191                 if (m.Children && m.Children.length > 0) {192                     obj.children = transToTreeData(m.Children);193                 }194                 return obj;195             }).ToArray();196         }197     </script>198 }
View Code

  效果如下:

  技术分享

 左导航数据加载

  由上面的代码可知,左边导航菜单,完全是由JS解析后端返回的JSON数据来构建,使用后端来返回数据,而不是在前端构建菜单数据,主要是便于将来进行权限控制,后端可以根据当前用户的权限返回特定的菜单数据。后端代码如下:

技术分享
 1 [AjaxOnly] 2 public ActionResult GetNavData() 3 { 4     List<TreeNode> nodes = new List<TreeNode>() 5     { 6         new TreeNode() 7         { 8             Text = "权限", 9             IconCls = "pic_26",10             Children = new List<TreeNode>()11             {12                 new TreeNode() { Text = "用户管理", IconCls = "pic_5", Url = Url.Action("Index", "Users") },13                 new TreeNode() { Text = "角色管理", IconCls = "pic_198", Url = Url.Action("Index", "Roles") },14                 new TreeNode() { Text = "组织机构管理", IconCls = "pic_93", Url = Url.Action("Index", "Organizations") },15             }16         },17         new TreeNode()18         {19             Text = "系统",20             IconCls = "pic_100",21             Children = new List<TreeNode>()22             {23                 new TreeNode() { Text = "操作日志", IconCls = "pic_125", Url = Url.Action("Index", "OperateLogs") },24                 new TreeNode() { Text = "系统日志", IconCls = "pic_101", Url = Url.Action("Index", "SystemLogs") },25                 new TreeNode() { Text = "系统设置", IconCls = "pic_89", Url = Url.Action("Index", "SystemSettings") }26             }27         }28     };29 30     Action<ICollection<TreeNode>> action = list =>31     {32         foreach (var node in list)33         {34             node.Id = "node" + node.Text;35         }36     };37 38     foreach (var node in nodes)39     {40         node.Id = "node" + node.Text;41         if (node.Children != null && node.Children.Count > 0)42         {43             action(node.Children);44         }45     }46 47     return Json(nodes, JsonRequestBehavior.AllowGet);48 }
View Code

  上面的代码中,添加了一个 [AjaxOnly],作用标记此方法只允许AJAX的调用方式,拦截非Ajax调用,在数据安全上能起到一定的作用。 

 1 /// <summary> 2 /// 限制当前功能只允许以Ajax的方式来访问 3 /// </summary> 4 [AttributeUsage(AttributeTargets.Class | AttributeTargets.Method, AllowMultiple = false, Inherited = true)] 5 public class AjaxOnlyAttribute : ActionFilterAttribute 6 { 7     /// <summary> 8     /// Called before an action method executes. 9     /// </summary>10     /// <param name="filterContext">The filter context.</param>11     public override void OnActionExecuting(ActionExecutingContext filterContext)12     {13         if (!filterContext.HttpContext.Request.IsAjaxRequest())14         {15             filterContext.Result = new ContentResult16             {17                 Content = Resources.Mvc_ActionAttribute_AjaxOnlyMessage18             };19         }20     }21 }

  打上此自定义属性后,如果使用非AJAX的方式来调用上面的GetNavData代码,无法得到返回的JSON数据

  技术分享

  正确解析返回数据后,构建导航菜单,点击菜单后打开相应的选项卡

  技术分享

三、EasyUI-datagrid布局

   在实践中,我们会发现,大部分 datagrid 的代码组织方式都相似的,不同的只是数据源不同,操作之后提交的URL不同。为了减少重复代码,提高代码的利用率,我们可以把共同的代码提取出来,而MVC的 Layout 又刚好是支持嵌套的,那么,类似于前面的 _Layout.cshtml,我们可以提取一个datagrid的共同父视图 _DataGridLayout.cshtml。

  _DataGridLayout.cshtml 的提取原理如下:

  1. javascript 的变量均是全局变量,并且是有前后顺序的,就可以按需要进行重新赋值
  2. 在 父视图(_Layout)中初始化 javascript变量,并在适当的位置(变量真正使用之前)向 子视图(Partial View)开放 RenderSection
  3. 子视图(Partial View)按需要对 父视图(_Layout)中定义的 javascript变量 进行重新赋值
  4. 正在的运算逻辑运算的时候,使用的就是重新赋值的新值了,以达到利用 Layout 的目的。

  根据 easyui-datagrid 的常用变量及上面的原理,定义的 _DataGridLayout.cshtml 大致结构如下,请结合注释进行理解:

 1 @{ 2     Layout = "~/Areas/Admin/Views/Shared/_Layout.cshtml"; 3     string toolbarItem = ViewBag.ToolbarItem ?? "add,edit,save,cancel,delete"; 4 } 5 @section header 6 { 7 <style type="text/css"> 8     html { font-family: sans-serif; } 9     .datagrid-header-inner { font-weight: bold; }10 </style>11 }12 @section footer13 {14     @*这里进行变量初始化*@15     <script type="text/javascript">16         //定义及初始化变量17         var rownumbers = true, singleSelect = false, ctrlSelect = true, multiSort = false, pageSize = 25;18         var grid, frozenColumns = [[]], columns = [[]], ...19 20         //前置逻辑,将在构造datagrid之前执行21         var startfunction = function() { };22         //后置逻辑,将在构造datagrid之后执行23         var endfunction = function() { }; 24 25     </script>26 27     @*开放一个Section,让子视图(Partial View)可以插入代码,对上面定义的变量进行重新赋值。*@28     @RenderSection("customScript", true)29 30     @*这里才正在执行业务逻辑*@31     <script type="text/javascript">32         $(function () {33             //执行前置逻辑34             startfunction();35 36             //构造 datagrid37             grid = $("#grid-@ViewBag.GridId").datagrid({38                 title: "@ViewBag.Title",39                 fit: true,40                 frozenColumns: frozenColumns,41                 columns: columns,42                 fitColumns: false,43                 url: "@ViewBag.GridDataUrl",44                 ...45             });46             47             //执行后置逻辑48             endfunction();49         });50     </script>51 }52 @* 后台还有可能有需要执行的逻辑,开放一个Section *@53 @RenderSection("endScript", false)54 }55 @* datagrid 前面有可能需要插入html,开放一个Section *@56 @RenderSection("headHtml", false)57 <div id="grid-@ViewBag.GridId"></div>58 @* datagrid 后面有可能需要插入html,开放一个Section *@59 @RenderSection("footHtml", false)

  结合实际需求,OSharp中定义的一个可用的 _DataGridLayout.cshtml 如下,可以根据需求进行更改:

技术分享
  1 @{  2     Layout = "~/Areas/Admin/Views/Shared/_Layout.cshtml";  3     string toolbarItem = ViewBag.ToolbarItem ?? "add,edit,save,cancel,delete";  4 }  5 @section header{  6     <style type="text/css">  7         html {  8             font-family: sans-serif;  9         } 10  11         .datagrid-header-inner { 12             font-weight: bold; 13         } 14     </style> 15 } 16 @section footer{ 17     <script src="http://www.mamicode.com/Scripts/plugins/datagrid-filter.js" type="text/javascript"></script> 18     <script src="http://www.mamicode.com/Scripts/plugins/datagrid-detailview.js" type="text/javascript"></script> 19     <script type="text/javascript"> 20         var rownumbers = true, singleSelect = false, ctrlSelect = true, multiSort = false, pageSize = 25; 21         var grid, frozenColumns = [[]], columns = [[]], filterData = http://www.mamicode.com/[], enableFilterData = false, editIndex = undefined, columnMenu = undefined; 22  23         var startfunction = function () { 24         }; 25         var endfunction = function () { 26         }; 27         var addObject = function () { 28             return {}; 29         }; 30         var replaceSearchField = function (field) { 31             return field; 32         }; 33     </script> 34     @RenderSection("paramInit", false) 35     <script type="text/javascript"> 36         function formatBoolean(value) { 37             var icon = value ? ‘checkmark‘ : ‘checknomark‘; 38             return ‘<span class="tree-file icon-‘ + icon + ‘"></span>‘; 39         } 40  41         var addNewRow = function () { 42             if (!endEditing()) { 43                 $.osharp.easyui.msg.tip("请先提交或取消正在编辑的行。"); 44                 return; 45             } 46             grid.datagrid("appendRow", addObject() || {}); 47             editIndex = grid.datagrid("getRows").length - 1; 48             grid.datagrid("selectRow", editIndex) 49                 .datagrid("beginEdit", editIndex); 50         }; 51  52         var beginEdit = function () { 53             var row = grid.datagrid("getSelected"); 54             if (!row) { 55                 $.osharp.easyui.msg.tip("请选择要编辑的行。"); 56                 return; 57             } 58             var index = grid.datagrid("getRowIndex", row); 59             beginEditRow(index); 60         }; 61  62         var beginEditRow = function (index) { 63             @if (toolbarItem == null || !toolbarItem.Contains(",save")) 64             { 65                 @Html.Raw("return;") 66             } 67  68             if (endEditing()) { 69                 grid.datagrid("selectRow", index) 70                     .datagrid("beginEdit", index); 71                 editIndex = index; 72             } else { 73                 grid.datagrid("unselectRow", index) 74                     .datagrid("selectRow", editIndex); 75             } 76         }; 77  78         var cancelEdit = function () { 79             grid.datagrid("rejectChanges"); 80             editIndex = undefined; 81         }; 82  83         var saveChanges = function () { 84             if (!endEditing()) { 85                 return; 86             } 87             var adds = grid.datagrid("getChanges", "inserted"); 88             if (adds && adds.length > 0) { 89                 submitAdds(adds); 90             } 91             var edits = grid.datagrid("getChanges", "updated"); 92             if (edits && edits.length > 0) { 93                 submitEdits(edits); 94             } 95         }; 96  97         var deleteRows = function () { 98             var selectRows = grid.datagrid("getSelections"); 99             if (selectRows.length == 0) {100                 $.osharp.easyui.msg.tip("请先选中要删除的行。");101                 return;102             }103             var ids = $.Enumerable.From(selectRows).Select(function (m) { return m.Id; }).ToArray();104             $.osharp.easyui.msg.confirm("是否要删除所有选中的行?此操作是不可恢复的。", null, function () {105                 $.post("@ViewBag.DeleteUrl", { ids: JSON.stringify(ids) }, ajaxResultHandler);106             });107         };108 109         function endEditing() {110             if (editIndex == undefined) {111                 return true;112             }113             if (grid.datagrid("validateRow", editIndex)) {114                 grid.datagrid("endEdit", editIndex);115                 editIndex = undefined;116                 return true;117             } else {118                 return false;119             }120         }121 122         function submitAdds(objs) {123             $.post("@ViewBag.AddUrl", { dtos: JSON.stringify(objs) }, ajaxResultHandler);124         }125 126         function submitEdits(objs) {127             $.post("@ViewBag.EditUrl", { dtos: JSON.stringify(objs) }, ajaxResultHandler);128         }129 130         function ajaxResultHandler(data) {131             if (data.Type == "Success") {132                 grid.datagrid("reload");133             }134             if (data.Type == "Error") {135                 $.osharp.easyui.msg.error(data.Content);136             } else {137                 $.osharp.easyui.msg.tip(data.Content);138             }139         }140 141         var toolbarData =http://www.mamicode.com/ [142             @if (toolbarItem.Contains("add"))143             {144                 @:{ text: "增加", iconCls: "icon-add", handler: addNewRow },145             }146             @if (toolbarItem.Contains("edit"))147             {148                 <text>149             { text: "编辑", iconCls: "icon-edit", handler: beginEdit },150             "-",151             </text>152             }153             @if (toolbarItem.Contains("save"))154             {155                 @:{ text: "保存", iconCls: "icon-save", handler: saveChanges },156             }157             @if (toolbarItem.Contains("cancel"))158             {159                 <text>160             { text: "取消", iconCls: "icon-undo", handler: cancelEdit },161             "-",162             </text>163             }164             @if (toolbarItem.Contains("delete"))165             {166                 @:{ text: "删除", iconCls: "icon-remove", handler: deleteRows },167             }168         ];169     </script>170     @RenderSection("customScript", true)171     <script type="text/javascript">172         $(function () {173             startfunction();174 175             grid = $("#grid-@ViewBag.GridId").datagrid({176                 title: "@ViewBag.Title",177                 fit: true,178                 frozenColumns: frozenColumns,179                 columns: columns,180                 fitColumns: false,181                 url: "@ViewBag.GridDataUrl",182                 loadMsg: "正在加载数据,请稍候",183                 toolbar: toolbarData,184                 rownumbers: rownumbers,185                 singleSelect: singleSelect,186                 ctrlSelect: ctrlSelect,187                 multiSort: multiSort,188                 pagination: true,189                 pageSize: pageSize,190                 pageList: [10, 25, 50, 100, 200],191                 remoteFilter: true,192                 onBeforeLoad: beforeLoad,193                 loadFilter: loadFilter,194                 onl oadError: loadError,195                 onDblClickRow: beginEditRow,196                 onHeaderContextMenu: headerContextMenu,197                 showFooter: true198             });199             if (enableFilterData) {200                 grid.datagrid("enableFilter", filterData);201             }202             203             endfunction();204         });205 206         //Header右键207         function headerContextMenu(e) {208             e.preventDefault();209             if (!columnMenu) {210                 createColumnMenu();211             }212             columnMenu.menu("show", { left: e.pageX, top: e.pageY });213         }214 215         function createColumnMenu() {216             columnMenu = $("<div/>").appendTo("body");217             columnMenu.menu({218                 onClick: function (item) {219                     if (item.iconCls == "icon-checkmark") {220                         grid.datagrid("hideColumn", item.name);221                         columnMenu.menu("setIcon", { target: item.target, iconCls: "icon-checknomark" });222                     } else {223                         grid.datagrid("showColumn", item.name);224                         columnMenu.menu("setIcon", { target: item.target, iconCls: "icon-checkmark" });225                     }226                 }227             });228             var fields = grid.datagrid("getColumnFields");229             for (var i = 0; i < fields.length; i++) {230                 var field = fields[i];231                 var col = grid.datagrid("getColumnOption", field);232                 columnMenu.menu("appendItem", { text: col.title, name: field, iconCls: col.hidden ? "icon-checknomark" : "icon-checkmark" });233             }234         }235 236         //param的部分属性与后台要求不符,重置属性并删除原有属性237         function beforeLoad(param) {238             if (param.page) {239                 param.pageIndex = param.page;240                 delete param.page;241             }242             if (param.rows) {243                 param.pageSize = param.rows;244                 delete param.rows;245             }246             if (param.sort) {247                 var array = param.sort.split(‘,‘);248                 for (var i = 0; i < array.length; i++) {249                     var field = array[i];250                     array[i] = replaceSearchField(field);251                 }252                 param.sort = $.osharp.tools.array.expandAndToString(array, ",");253                 param.sortField = param.sort;254                 delete param.sort;255             }256             if (param.order) {257                 param.sortOrder = param.order;258                 delete param.order;259             }260             if (param.filterRules) {261                 if (param.filterRules != "[]") {262                     param.where = getFilterGroup(param.filterRules);263                 }264                 delete param.filterRules;265             }266         }267 268         function getFilterGroup(filterRules) {269             var group = new $.osharp.filter.group();270             var rules = eval(filterRules);271             for (var i = 0; i < rules.length; i++) {272                 var rule = rules[i];273                 rule.field = replaceSearchField(rule.field);274                 rule.op = rule.op == "beginwith" ? "startswith" : rule.op == "endwith" ? "endswith" : rule.op;275 276                 group.Rules.push(new $.osharp.filter.rule(rule.field, rule.value, rule.op));277             }278             return JSON.stringify(group);279         }280 281         function loadFilter(data) {282             if (data.Type != undefined && data.Type == "Error") {283                 $.osharp.easyui.msg.error(data.Content);284                 data.rows = [];285                 data.total = 0;286                 return data;287             }288             if (data.Rows != undefined && data.Total != undefined) {289                 data.rows = data.Rows;290                 data.total = data.Total;291                 delete data.Rows;292                 delete data.Total;293             }294             return data;295         }296 297         function loadError() {298             $.osharp.easyui.msg.error("远程数据载入失败,请重试或检查参数。");299         }300 301     </script>302     @RenderSection("endScript", false)303 }304 @RenderBody()305 @RenderSection("headHtml", false)306 <div id="grid-@ViewBag.GridId"></div>307 @RenderSection("footHtml", false)
View Code

   OSharp.Web组件中,定义了一个专用于返回表格数据的类,表格只需要行数据与总行数

 1 /// <summary> 2 /// 列表数据,封装列表的行数据与总记录数 3 /// </summary> 4 /// <typeparam name="T"></typeparam> 5 public class GridData<T> 6 { 7     public GridData() 8         : this(new List<T>(), 0) 9     { }10 11     public GridData(IEnumerable<T> rows, int total)12     {13         Rows = rows;14         Total = total;15     }16 17     /// <summary>18     /// 获取或设置 行数据19     /// </summary>20     public IEnumerable<T> Rows { get; set; }21 22     /// <summary>23     /// 获取或设置 数据行数24     /// </summary>25     public int Total { get; set; }26 }

  通过这个类,就可以向easyui返回数据了,如下:

 1 [AjaxOnly] 2 public ActionResult GridData() 3 { 4     List<object>data =http://www.mamicode.com/new List<object>(); 5     for (int i = 1; i <= 20; i++) 6     { 7         var item = new { Id = i, Name = "UserName" + i, NickName = "用户" + i, IsDeleted = false, CreatedTime = DateTime.Now.AddMinutes(i) }; 8         data.Add(item); 9     }10     return Json(new GridData<object>(data, data.Count), JsonRequestBehavior.AllowGet);11 }

  有了前面定义的 datagrid 父视图 _DataGridLayout.cshtml,用户列表(Views\Users\Index.cshtml)的代码就是如此的简单,仅仅需要把columns重新赋值而已

 1 @{ 2     ViewBag.Title = "用户信息列表"; 3     Layout = "~/Areas/Admin/Views/Shared/_DataGridLayout.cshtml"; 4  5     ViewBag.GridId = "users"; 6     ViewBag.GridDataUrl = Url.Action("GridData"); 7 } 8 @section customScript 9 {10 <script type="text/javascript">11     columns = [[12         { field: "Id", title: "编号", width: 40, halign: "center", align: "right", sortable: true },13         { field: "Name", title: "用户名", width: 150, sortable: true },14         { field: "NickName", title: "用户昵称", width: 150, sortable: true },15         { field: "IsDeleted", title: "已删除", width: 80, sortable: true, align: "center", formatter: formatBoolean },16         { field: "CreatedTime", title: "创建时间", width: 150, halign: "center", align: "center", sortable: true, formatter: function (value) { return $.osharp.tools.formatDate(value); } }17     ]];18 </script>19 }

   这样,便可以运行出用户列表的结果,如下

  技术分享

  比如添加一个角色信息列表,视图(Views\Roles\Index.cshtml)也同用户列表一样,简单到极致:

 1 @{ 2     ViewBag.Title = "角色信息列表"; 3     Layout = "~/Areas/Admin/Views/Shared/_DataGridLayout.cshtml"; 4  5     ViewBag.GridId = "roles"; 6     ViewBag.GridDataUrl = Url.Action("GridData"); 7 } 8 @section customScript 9 {10     <script type="text/javascript">11         columns = [[12             { field: "Id", title: "编号", width: 40, halign: "center", align: "right", sortable: true },13             { field: "Name", title: "角色名", width: 150, sortable: true },14             { field: "Remark", title: "角色描述", width: 150, sortable: true },15             { field: "CreatedTime", title: "创建时间", width: 150, halign: "center", align: "center", sortable: true, formatter: function (value) { return $.osharp.tools.formatDate(value); } }16         ]];17     </script>18 }

 

  运行效果:

  技术分享

  就是这样,多动脑,多总结,前端的代码也同样能像后台C#代码一样重构,重构到极致。

  未完待续。。。

四、开源说明

 (一)github.com

   OSharp项目已在github.com上开源,地址为:https://github.com/i66soft/osharp,欢迎阅读代码,欢迎 Fork,如果您认同 OSharp 项目的思想,欢迎参与 OSharp 项目的开发。

  在Visual Studio 2013中,可直接获取 OSharp 的最新源代码,获取方式如下,地址为:https://github.com/i66soft/osharp.git

  技术分享

 (二)nuget

  OSharp的相关类库已经发布到nuget上,欢迎试用,直接在nuget上搜索 “osharp” 关键字即可找到
  技术分享

系列导航

  1. 【开源】OSharp框架解说系列(1):总体设计
  2. 【开源】OSharp框架解说系列(2):从后台UI说起-EasyUI的后台界面搭建

【开源】OSharp框架解说系列(2.1):EasyUI的后台界面搭建及极致重构