zoukankan      html  css  js  c++  java
  • 【开源】OSharp框架解说系列(2.1):EasyUI的后台界面搭建及极致重构

    OSharp是什么?

      OSharp是个快速开发框架,但不是一个大而全的包罗万象的框架,严格的说,OSharp中什么都没有实现。与其他大而全的框架最大的不同点,就是OSharp只做抽象封装,不做实现。依赖注入、ORM、对象映射、日志、缓存等等功能,都只定义了一套最基础最通用的抽象封装,提供了一套统一的API、约定与规则,并定义了部分执行流程,主要是让项目在一定的规范下进行开发。所有的功能实现端,都是通过现有的成熟的第三方组件来实现的,除了EntityFramework之外,所有的第三方实现都可以轻松的替换成另一种第三方实现,OSharp框架正是要起隔离作用,保证这种变更不会对业务代码造成影响,使用统一的API来进行业务实现,解除与第三方实现的耦合,保持业务代码的规范与稳定。

    本文已同步到系列目录:OSharp快速开发框架解说系列

    前言

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

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

    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, 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=" 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="' + url + '" style="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 ContentResult
    16             {
    17                 Content = Resources.Mvc_ActionAttribute_AjaxOnlyMessage
    18             };
    19         }
    20     }
    21 }

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

      

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

      

    EasyUI-datagrid布局

    提取父视图(模板)_DataGridLayout.cshtml

       在实践中,我们会发现,大部分 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 的目的
    5. 父视图中需要的 C# 变量,通过在子视图中定义 ViewBag 传递过去,比如dom元素的id,数据操作的URL等等

      根据 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 footer
    13 {
    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             //构造 datagrid
    37             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="/Scripts/plugins/datagrid-filter.js" type="text/javascript"></script>
     18     <script src="/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 = [], 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 = [
    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                 onLoadError: loadError,
    195                 onDblClickRow: beginEditRow,
    196                 onHeaderContextMenu: headerContextMenu,
    197                 showFooter: true
    198             });
    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 =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,用户列表(ViewsUsersIndex.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: "编号",  40, halign: "center", align: "right", sortable: true },
    13         { field: "Name", title: "用户名",  150, sortable: true },
    14         { field: "NickName", title: "用户昵称",  150, sortable: true },
    15         { field: "IsDeleted", title: "已删除",  80, sortable: true, align: "center", formatter: formatBoolean },
    16         { field: "CreatedTime", title: "创建时间",  150, halign: "center", align: "center", sortable: true, formatter: function (value) { return $.osharp.tools.formatDate(value); } }
    17     ]];
    18 </script>
    19 }

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

      

      比如添加一个角色信息列表,视图(ViewsRolesIndex.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: "编号",  40, halign: "center", align: "right", sortable: true },
    13             { field: "Name", title: "角色名",  150, sortable: true },
    14             { field: "Remark", title: "角色描述",  150, sortable: true },
    15             { field: "CreatedTime", title: "创建时间",  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” 关键字即可找到
      

    系列导航

    本文已同步到系列目录:OSharp快速开发框架解说系列

  • 相关阅读:
    Windows下Git多账号配置,同一电脑多个ssh-key的管理
    Android ScrollView监听滑动到顶部和底部的两种方式(你可能不知道的细节)
    处理Linux下subversion尝试连接自建的VisualSVN server报“Key usage violation in certificate has been detected”错误的问题
    #Linux学习笔记# Linux文件的属性以及权限说明
    #Linux学习笔记# Linux文件的所有者、群组和其他人
    #Linux学习笔记# Linux在线帮助文档man page
    #Linux学习笔记# Linux系统查看文件内容的命令
    #Linux学习笔记# 自定义shell终端提示符
    Docker私有仓库Registry的搭建验证
    LinuxMint下Docker的安装部署和验证
  • 原文地址:https://www.cnblogs.com/guomingfeng/p/osharp-easyui-view.html
Copyright © 2011-2022 走看看