CRUD全栈式编程架构之界面层的设计

Layout的设计

模板模式

mvc的模板特别类似设计模式中模板方法模式,结合Layout中RenderSection和RenderBody方法可以将部分html展现逻辑延迟到具体的视图页面去实现里面实现。结合我们增删改查的逻辑,我们的用户界面,我们将页面分为这几个区域,实现部分逻辑以后,部分留给具体的页面去实现。例如图片中新增,编辑,删除,导入,导出,查询都是架构自带的操作,至于复制就给页面扩展,查询条件也留给具体的页面中扩展,模板中给出RenderSection即可。

执行顺序

这个执行顺序的问题可以通过调试去查找答案,大致是返回页面后,先执行具体页面的逻辑,当第一个@{} 代码段执行完毕之后,会检查有没有Layout页面,如果有则进入Layout顺序执行,如果Layout页面有RenderSection则会去字页面检查有没有定义,如果定义则执行,页面中除了RenderSection部分都会当作RenderBody中执行,但是实际程序运行中,页面会全部动态编译成dll去执行。具体可以查看Razor引擎模板的相关内容。

元数据

mvc定义了一套元数据机制,在模板中可以通过ViewData.ModelMatedata去访问这样可以搭配attribute做出很多扩展的设计,具体案例会在本文后面做详细说明。

列表布局页

@using Coralcode.Framework.Mvc.Models.MiniUI
@using HCC.Web.Utils
@{
    Layout = null;
    //如果不使用父类默认设定操作按钮,请重新制定参数
    if (ViewBag.DataOperation == null)
    {
        ViewBag.DataOperation =
            MiniUIDataOperation.Add | MiniUIDataOperation.Edit | MiniUIDataOperation.Delete 
            | MiniUIDataOperation.ExportTemplate | MiniUIDataOperation.Import | MiniUIDataOperation.Export;
    }
    MiniUIDataOperation dataOperation = (MiniUIDataOperation)ViewBag.DataOperation;

    if (ViewBag.AddEditDialogHeight == null)
    {
        ViewBag.AddEditDialogHeight = 600;
    }
    if (ViewBag.AddEditDialogWidth == null)
    {
        ViewBag.AddEditDialogWidth = 800;
    }
    string refreshUrl=ViewBag.ListUrl;
    if (ViewBag.ShowPager)
    {
        refreshUrl = ViewBag.PageUrl;
    }

}
<!DOCTYPE html>
<html>
<head>
    <meta name="viewport" content="width=device-width" />
    <title></title>
    <meta http-equiv="content-type" content="text/Html; charset=UTF-8" />
    @Styles.Render("~/content/miniuicss")
    @Scripts.Render("~/script/miniuijs", "~/script/tooljs")
</head>
<body>
    <style type="text/css">
        html, body {
            margin: 0;
            padding: 0;
            border: 0;
            width: 100%;
            height: 100%;
            overflow: hidden;
        }
    </style>
    <div id="datagrid_toolbar">
        <div class="mini-toolbar" style="border-bottom: 0; padding: 0px;">
            <table style="width: 100%;">
                <tr>
                    <td style="width: 100%;">
                        <div id="operation">
                            @if ((dataOperation & MiniUIDataOperation.Add) != 0)
                            {
                                <a class="mini-button" iconcls="icon-add" οnclick="add()">增加</a>
                            }
                            @if ((dataOperation & MiniUIDataOperation.Edit) != 0)
                            {
                                <a class="mini-button" iconcls="icon-edit" οnclick="edit()">编辑</a>
                            }
                            @if ((dataOperation & MiniUIDataOperation.Delete) != 0)
                            {
                                <a class="mini-button" iconcls="icon-remove" οnclick="remove()">删除</a>
                            }
                            @if ((dataOperation & MiniUIDataOperation.ExportTemplate) != 0)
                            {
                                <a class="mini-button" iconcls="icon-download" href='@ViewBag.ExportTemplateUrl'>导出模板</a>
                            }
                            @if ((dataOperation & MiniUIDataOperation.Import) != 0)
                            {
                                <a class="mini-button" iconcls="icon-upload" οnclick="importAction()">导入</a>
                            }
                            @if ((dataOperation & MiniUIDataOperation.Export) != 0)
                            {
                                <a class="mini-button" iconcls="icon-download" href='@ViewBag.ExportUrl'>导出</a>
                            }
                            @if ((dataOperation & MiniUIDataOperation.Register) != 0)
                            {
                                <a class="mini-button" iconcls="icon-user" οnclick="RegisterLesson()">预约</a>
                            }
                            @if ((dataOperation & MiniUIDataOperation.Sign) != 0)
                            {
                                <a class="mini-button" iconcls="icon-tip" οnclick='SignLesson()'>签到</a>
                            }
                            @if ((dataOperation & MiniUIDataOperation.InputUser) != 0)
                            {
                                string currentMember = string.Empty;
                                var userIdName = UserHelper.GetCookieIdName();
                                if (userIdName != null)
                                {
                                    currentMember = string.Format("(当前'{0}')",userIdName.Item2);
                                }

                                <a class="mini-button" iconcls="icon-user" id="a_userName" οnclick='InputUserMemberView()'>获取会员@{@currentMember}</a>
                            }
                            @RenderSection("ExtendedOperation", false)
                        </div>
                    </td>
                    <td style="white-space: nowrap;">
                        <div id="query">
                            @RenderSection("Query", false)
                            <a class="mini-button" iconcls="icon-search" οnclick="LoadData()">查询</a>

                        </div>
                    </td>
                </tr>
            </table>
        </div>
    </div>
    <!--撑满页面-->
    <div class="mini-fit">
        <div id="datagrid1" class="mini-datagrid" style="width: 100%; height: 100%;"  showpager="@ViewBag.ShowPager" idfield="id"  multiselect="true">
            <div property="columns">
                @Html.Partial("_HeaderPartial", (List<DataGridColumn>)ViewBag.Header)
            </div>
        </div>
    </div>
    @RenderBody()

    @* ReSharper disable once SyntaxIsNotAllowed *@
    <script type="text/javascript">
    mini.parse();
    var grid = mini.get("datagrid1");
    var url = '@Html.Raw(ViewBag.EditUrl)';
    var importUrl = '@Html.Raw(ViewBag.ImportUrl)';
    var title = '@ViewBag.Title';


    grid.set({
        url: "@Html.Raw(refreshUrl)",
        pageSize:20,
        pageIndex:10,
        sizeList:[10,20,50,100]
    });

    function LoadData() {
        var data="{" +  $("#query input[name]").map(function() {
            return $(this).attr("name") + ":" + "'"+$(this).val()+"'";
        }).get().join(", ")+"}";
        grid.load(mini.decode(data,false));
    }

    //todo 这里如果需要查询怎么办
    LoadData();

    var dynamicUrlParams = "";

    @if ((dataOperation & MiniUIDataOperation.Add) != 0)
    {
        <text>
    function add() {
        mini.open({
            url: url,

            title: "新增" + title,
            width: @ViewBag.AddEditDialogWidth,
            height: @ViewBag.AddEditDialogHeight,
            onload: function() {
                var iframe = this.getIFrameEl();
                var data = { action: "new" };
                //iframe.contentWindow.SetData(data);
            },
            ondestroy: function(action) {

                grid.reload();
            }
        });
    }
    </text>
    }
    @if ((dataOperation & MiniUIDataOperation.Edit) != 0)
    {
        <text>
    function edit() {

        var row = grid.getSelected();
        if (row) {
            var editUrl = url + (url.indexOf('?') > 0 ? '&' : '?') + "id=" + row.Id;

            mini.open({
                url: editUrl,
                title: "编辑" + title,
                width: @ViewBag.AddEditDialogWidth,
                height: @ViewBag.AddEditDialogHeight,

                ondestroy: function(action) {
                    grid.reload();

                }
            });

        } else {
            alert("请选中一条记录");
        }

    }
    </text>
    }
    @if ((dataOperation & MiniUIDataOperation.Delete) != 0)
    {
        <text>
    function remove() {

        var rows = grid.getSelecteds();

        if (rows.length > 0) {
            if (confirm("确定删除选中记录?")) {
                var ids = [];
                for (var i = 0, l = rows.length; i < l; i++) {
                    var r = rows[i];
                    ids.push(r.Id);
                }
                var id = ids.join(',');

                grid.loading("操作中,请稍后......");
                var deleteUrl = '@Html.Raw(ViewBag.DeleteUrl)';
                deleteUrl = deleteUrl + (deleteUrl.indexOf('?') > 0 ? '&' : '?') + "ids=" + id;
                $.ajax({
                    type: 'post',
                    url: deleteUrl,
                    success: function(data) {
                        if (data.State == 0) {
                            grid.reload();
                            return;
                        }
                        grid.unmask();
                        alert(data.Data);

                    },
                    error: function() {
                    }
                });
            }
        } else {
            alert("请选中一条记录");
        }
    }
    </text>
    }
    @if ((dataOperation & MiniUIDataOperation.Import) != 0)
    {
        <text>
    function importAction() {
        mini.open({

            url: importUrl,
            title: "导入" + title,
            width: 600,
            height: 300,

            ondestroy: function(action) {
                grid.reload();

            }
        });
    }
    </text>
    }

    @if ((dataOperation & MiniUIDataOperation.Register) != 0)
        {
            <text>
    function RegisterLesson() {
        var rows = grid.getSelecteds();
        if (rows.length < 1) {
            alert("请选中一条记录");
            return;
        }
        if (rows.length > 1) {
            alert("每次只能预订一节课程");
            return;
        }
        var ids = [];
        for (var i = 0, l = rows.length; i < l; i++) {
            var r = rows[i];
            ids.push(r.Id);
        }
        var id = ids.join(',');

        grid.loading("操作中,请稍后......");
        var registerLessonUrl = '@Html.Raw(ViewBag.RegisterLessonUrl)';
        registerLessonUrl = registerLessonUrl
                + (registerLessonUrl.indexOf('?') > 0 ? '&' : '?') + "ids=" + id
                + "&"+dynamicUrlParams;
        $.ajax({
            type: 'post',
            url: registerLessonUrl,
            success: function (data) {
                if (data.State == 0) {
                    grid.reload();
                    return;
                }
                grid.unmask();
                alert(data.Data);

            },
            error: function () {
            }
        });

    }

    </text>
        }
    @if ((dataOperation & MiniUIDataOperation.Sign) != 0)
        {
            <text>
        </text>
        }

        @if ((dataOperation & MiniUIDataOperation.InputUser) != 0)
        {
            <text>
    
        function InputUserMemberView(){
            mini.prompt("请输入会员号码:", "请输入",
            function (action, value) {
                if (action != "ok") {
                    return;
                } 
                var userLoginUrl = '/Portal/User/Login?number='+value+ "&"+dynamicUrlParams;
                $.ajax({
                    type: 'post',
                    url: userLoginUrl,
                    success: function (data) {
                        if (data.State == 0) {
                            $("#a_userName").children("span").html("获取会员(当前'"+data.Data+"')")
                            return;
                        }
                        alert(data.Data);

                    },
                    error: function () {
                    }
                });



            }
        );
        }


            
            </text>
        }



        function onDateRenderer(e) {
            var value = e.value;
            if (value) return mini.formatDate(value, 'yyyy-MM-dd');
            return "";
        }
        function onDateTimeRenderer(e) {
            var value = e.value;
            if (value) return mini.formatDate(value, 'yyyy-MM-dd hh:mm:dd');
            return "";
        }
        function onTimeRenderer(e) {
            var value = e.value;
            if (value) return mini.formatDate(value, 'hh:mm:dd');
            return "";
        }
    </script>
    @RenderSection("Script", false)
</body>
</html>

 

操作配置化,并且可扩展

MVC的模板加载顺序,基于mvc的模板顺序,我们可以在自模板中定义出页面操作,如果子页面没有定义,则在模板中给出默认定义。

Flags的枚举 由于页面操作是可以交叉的,所以我们采用flags的枚举来作为判断依据,flags枚举其实就是二进制的位操作,建议没用过的好好去研究下。在很多地方都很有用,特别是条件组合的情况下。

流式布局

结合RennderSection的设计,模板并不知道操作和查询条件有多少,所以我们做成流式布局,操作左对齐,查询右对齐,在少数操作和少数查询的时候效果还不错,

新增编辑布局页

@{
    Layout = null;
}
<!DOCTYPE html>
<html>
<head>
    <meta name="viewport" />
    <title></title>
    <meta http-equiv="content-type" content="text/Html; charset=UTF-8" />
    <script type="text/javascript" src="@Url.Content("~/Content/js/jquery-1.6.2.min.js")"></script>
    <script type="text/javascript" src="@Url.Content("~/Content/js/boot.js")"></script>
    @Styles.Render("~/content/miniuicss")
    @Scripts.Render("~/script/miniuijs")
    @Scripts.Render("~/script/tooljs")
    <link href="@Url.Content("~/Content/css/edit.css")" type="text/css" rel="stylesheet"/>

    <script type="text/javascript" src="@Url.Content("~/Content/js/jquery.form.js")"></script>
</head>
<body style="margin: 0px;height: 100%">

    <form method="post" id="formmain" style="width: 100%; height: 100%">
        <div class="mini-fit" style="padding: 5px">

            <table style="table-layout: fixed; width: 100%;">
                @RenderBody()
            </table>
        </div>


        <div class="mini-toolbar" style="text-align:center;padding-top:8px;padding-bottom:8px; " borderStyle="border:1;">

            <a id="submit" class="mini-button" iconcls="icon-ok" οnclick="submit()">确定</a>
            <span style="display: inline-block; width: 25px;"></span>
            <a id="cancel" class="mini-button" iconcls="icon-cancel" οnclick="onCancel(this)">取消</a>
        </div>

    </form>
    @Scripts.Render("~/script/jquery.validate")
<script type="text/javascript">
    
    function submit() {
        $.ajax({
            type: "Post",
            data:$("form").serialize(),
            success: function (jsonResult) {

                if (jsonResult.State === 0) {
                    CloseWindow("save");
                    return;
                }
                alert(jsonResult.Data);
            }
        });
    };
    
    function onCancel(e) {
        CloseWindow("cancel");
    }
    function CloseWindow(action) {
        if (window.CloseOwnerWindow) window.CloseOwnerWindow(action);
        else window.close();
    }
</script>
    @RenderSection("Script", false)
</body>
</html>

 

界面尽量精简

编辑模板操作ajax提交的方式,将提交逻辑封装在模板页中,子页面只需要填充输入字段即可这里要特别提醒,尽量做到编辑界面的简洁,将那些复杂的输入作为编辑模板扩展,这部分将在下一节中说明。

自适应的布局

布局中,将操作放在最下方提供提交和取消两个操作即可满足要求。剩下与输入相关的部分可以在具体页面中去做扩展。最终我们是要实现Html.EditForModel的扩展,暂时精力有限没有做到这一步,最后在更精简的设计中看是否有时间去实现这个逻辑。当然由于部分页面输入逻辑比较复杂,所以即使做出来了,也不能替代全部的编辑页面。

MVC编辑模板

挖掘MVC源代码地图

我们查看mvc的源代码可以发现,mvc结合DataType提供了编辑和显示两种模板类型。放在Views/Shared的EditTemplate和DisplayTemplate中,同时在模板中也可以获取到对应模型属性的元数据,这里要特别注意,不要给html.textbox这类输入方法中提供name属性,mvc自己会在name中默认拼接上元数据中的属性名。掌握这种方式之后可以做在编辑页面做出更简洁的设计,如果你扩展的类型够多,那么将会给程序员编程提供很大的便利.

富文本编辑

我们选用DataType.MultilineText作为富文本的扩展,将ViewModel属性标记[DataType(DataType.MultilineText)],然后再编辑的时候只需Html.EditFor()即可实现自动调用,我们选用UEditer作为复文本框,只需简单几行代码就可以实现富文本框的扩展。

 

@model string
@Scripts.Render("~/script/ueditor")
@Styles.Render("~/content/ueditor")
@Html.TextArea("", Model, new { id = ViewData.ModelMetadata.PropertyName })
<script type="text/javascript">
    var editor = new baidu.editor.ui.Editor();
    editor.render('@ViewData.ModelMetadata.PropertyName');
</script>

 

时间相关扩展

在mvc的DataType的枚举中有DateTime,Date,Time三种和时间相关的类型,这里用mini ui自带的时间控件做的扩展,使用的时候只需要结合属性的DateTypeAttribute和
Html.EditFor即可。

DateTime扩展

@model DateTime
@{
    DateTime value = DateTime.Now;
    if (Model != DateTime.MinValue)
    {
        value=Model ;
    }
}
@Html.TextBox("", value, new { @class = "mini-datepicker", format = "yyyy-MM-dd H:mm:ss", timeFormat = "H:mm:ss", showTime = "true" ,width=165})

 Date扩展

@model DateTime
@{
    DateTime value = DateTime.Now;
    if (Model != DateTime.MinValue)
    {
        value = Model;
    }
}
@Html.TextBox("", value, new { @class = "mini-datepicker", format = "yyyy-MM-dd", showTime = "false" })

 Time扩展

@model DateTime
@{
    DateTime value = DateTime.Now;
    if (Model != DateTime.MinValue)
    {
        value = Model;
    }
}
@Html.TextBox(ViewData.ModelMetadata.PropertyName, value, new { @class = "mini-timespinner", format = "H:mm:ss" })

 

菜单的设计

菜单的数据结构

首先看看整体的压面布局,分为顶栏,左边菜单,右边内容,底部关于这种布局。上边部分就很简单,给出用户信息和登出操作就好了。这里我使用miniui自带的树形控件做的菜单,结合样式miniui自带样式就可以简洁的实现如图的效果

菜单加载就用miniui的tree就很方便了,用Identify和ParentId作为父子关系即可,其中parent属性忽略掉。

 

var tree = mini.get("leftTree");
tree.loadList(data.Data, "Identify", "ParentId");
两行js语句即可

public class MenuModel : IViewModel
  {
      /// <summary>
      ///     标识
      /// </summary>
      public string Identify { get; set; }

      /// <summary>
      ///     标题
      /// </summary>
      public string Text { get; set; }

      /// <summary>
      ///     父节点
      /// </summary>
      [JsonIgnore]
      public MenuModel Parent { get; set; }

      /// <summary>
      ///     父节点标识
      /// </summary>
      public string ParentId { get; set; }

      /// <summary>
      ///     链接
      /// </summary>
      public string Url { get; set; }

      /// <summary>
      ///     等级
      /// </summary>
      public int Level
      {
          get
          {
              if (Parent == null)
                  return 0;
              return Parent.Level + 1;
          }
      }

      /// <summary>
      ///     子节点
      /// </summary>
      public List<MenuModel> Children { get; set; }

      public long Id { get; set; }
  }

 

菜单的服务

using System.Collections.Generic;
using HCC.Core.ViewModel;

namespace HCC.Core.Services
{
    public interface IMenuService
    {
        /// <summary>
        ///     根节点
        /// </summary>
        MenuModel Root { get; }

        /// <summary>
        ///     菜单注册
        /// </summary>
        /// <param name="menuName">菜单名称</param>
        /// <param name="url">页面地址</param>
        /// <param name="parentId">上级菜单</param>
        /// <param name="menuId">菜单Id</param>
        MenuModel Regist(string menuName, string url, string parentId, string menuId);

        /// <summary>
        ///     菜单注册
        /// </summary>
        /// <param name="menuName">菜单名称</param>
        /// <param name="url">页面地址</param>
        /// <param name="parentId">上级菜单</param>
        /// <param name="menuId">菜单Id</param>
        MenuModel Regist(string menuName, string url, string parentId);

        /// <summary>
        ///     注销菜单
        /// </summary>
        /// <param name="menuId"></param>
        /// <returns></returns>
        MenuModel Unregist(string menuId);

        /// <summary>
        ///     根据菜单ID获取菜单
        /// </summary>
        /// <param name="menuId"></param>
        /// <returns></returns>
        MenuModel FindById(string menuId);

        /// <summary>
        ///     获取子菜单
        /// </summary>
        /// <returns></returns>
        List<MenuModel> GetChildrenMenus(string parentId);

        /// <summary>
        ///     根据级别获取菜单
        /// </summary>
        /// <returns></returns>
        List<MenuModel> GetByLevel(List<int> levels);

        /// <summary>
        ///     获取全部菜单
        /// </summary>
        /// <returns></returns>
        List<MenuModel> GetAll();

        /// <summary>
        ///     重新注册菜单
        /// </summary>
        void ResetMenu();
    }
}

 

注意其中菜单服务这里,可以订阅eventbus事件来动态更新菜单

using System.Collections.Generic;
using System.Linq;
using Coralcode.Framework.Aspect;
using Coralcode.Framework.Data.Repository.Core;
using Coralcode.Framework.Data.Specification;
using Coralcode.Framework.Mapper;
using Coralcode.Framework.MessageBus.Event;
using Coralcode.Framework.Services;
using Coralcode.Framework.Utils;
using HCC.Core.Model;
using HCC.Core.ViewModel;

namespace HCC.Core.Services.Imp
{
    [Inject(RegisterType = typeof (IMenuService), LifetimeManagerType = LifetimeManagerType.ContainerControlled)]
    public class MenuService : CrudCoralService<Menu, MenuModel>, IMenuService
    {
        internal static Dictionary<string, MenuModel> MenuCache = new Dictionary<string, MenuModel>();

        public MenuService(IRepository<Menu> repository, IEventBus eventBus)
            : base(repository, eventBus)
        {
            Root = new MenuModel
            {
                Identify = "root",
                Text = "后台管理",
                Url = "",
                Children = new List<MenuModel>(),
                Parent = null
            };
            MenuCache.Add(Root.Identify, Root);
            var allMenus = Repository.GetAll().ToList();
            allMenus.ForEach(item => { Regist(item.Text, item.Url, item.ParentId, item.Identify); });
        }

        /// <summary>
        ///     根节点
        /// </summary>
        public MenuModel Root { get; private set; }

        public MenuModel Regist(string menuName, string url, string parentId, string menuId)
        {
            if (MenuCache.ContainsKey(menuId))
                return MenuCache[menuId];
            if (string.IsNullOrEmpty(parentId) || !MenuCache.ContainsKey(parentId))
                return null;

            var menu = new MenuModel
            {
                Identify = menuId,
                Text = menuName,
                Url = url,
                Children = new List<MenuModel>(),
                ParentId = parentId,
                Parent = MenuCache[parentId]
            };

            AddMenu(menu);
            MenuCache.Add(menu.Identify, menu);
            MenuCache[parentId].Children.Add(menu);
            return menu;
        }

        public MenuModel Regist(string menuName, string url, string parentId)
        {
            return Regist(menuName, url, parentId, GeneralMenuId(parentId, menuName));
        }

        public MenuModel Unregist(string menuId)
        {
            MenuModel model;
            if (!MenuCache.TryGetValue(menuId, out model))
                return null;
            model.Parent.Children.Remove(model);
            UnregistChildren(model);
            Repository.UnitOfWork.Commit();
            return model;
        }

        public MenuModel FindById(string menuId)
        {
            return !MenuCache.ContainsKey(menuId) ? null : MenuCache[menuId];
        }

        public List<MenuModel> GetChildrenMenus(string parentId)
        {
            if (string.IsNullOrEmpty(parentId))
                return DataMapperProvider.Mapper.Convert<List<MenuModel>, List<MenuModel>>(Root.Children);
            if (MenuCache.ContainsKey(parentId))
                return DataMapperProvider.Mapper.Convert<List<MenuModel>, List<MenuModel>>(MenuCache[parentId].Children);
            return null;
        }

        public List<MenuModel> GetByLevel(List<int> levels)
        {
            return MenuCache.Values.Where(item => levels.Contains(item.Level)).ToList();
        }

        /// <summary>
        ///     重新注册菜单
        /// </summary>
        public void ResetMenu()
        {
            //删掉之前的菜单
            var menus = Repository.GetAll().ToList();
            if (menus.Count != 0)
            {
                menus.ForEach(item => { Repository.Remove(item); });
                Repository.UnitOfWork.CommitAndRefreshChanges();
                MenuCache.Clear();
                MenuCache.Add(Root.Identify, Root);
            }


            //课程管理
            var lessonMenu = Regist("课程管理", "", "root", "lessonmanager");
            Regist("私教课", "/portal/privatelesson/index", lessonMenu.Identify, "privatelesson");
            Regist("公开课", "/portal/publiclesson/index", lessonMenu.Identify, "publiclesson");
            Regist("体验课", "/portal/triallesson/index", lessonMenu.Identify, "triallesson");
            Regist("会员活动", "/portal/useractivity/index", lessonMenu.Identify, "useractivity");


            //人员管理
            var userManagerMenu = Regist("人员管理", "", "root", "personmanager");

            Regist("教练", "/portal/coach/index", userManagerMenu.Identify, "coachmanager");

            Regist("人员", "/portal/user/index", userManagerMenu.Identify, "usermanager");
            //系统管理
            var systemManagerMenu = Regist("系统管理", "", "root", "systemmanager");

            Regist("管理员", "/portal/manager/index", systemManagerMenu.Identify, "manager");
            Regist("新闻管理", "/portal/news/index", systemManagerMenu.Identify, "news");
            Regist("留言管理", "/portal/message/index", systemManagerMenu.Identify, "message");
            Regist("课程模板", "/portal/lessontemplate/index", systemManagerMenu.Identify, "lessontemplate");
            Regist("系统设置", "/portal/systemsetting/index", systemManagerMenu.Identify, "systemsetting");
            //统计查询
            var stasticsMenu = Regist("统计", "", "root", "systemstastistics");
            Regist("统计", "/portal/statistic/index", stasticsMenu.Identify, "stastistics");
        }

        private void UnregistChildren(MenuModel menu)
        {
            menu.Children.ForEach(item =>
            {
                if (MenuCache.ContainsKey(item.Identify))
                    RemoveMenu(item);
                UnregistChildren(item);
            });
            RemoveMenu(menu);
        }

        private void AddMenu(MenuModel menu)
        {
            if (MenuCache.ContainsKey(menu.Identify))
                return;
            var model = Repository.GetFirst(new DirectSpecification<Menu>(item => item.Identify == menu.Identify));
            if (model != null)
                return;
            Repository.Add(Convert(menu));
            Repository.UnitOfWork.Commit();
        }

        private void RemoveMenu(MenuModel menu)
        {
            if (MenuCache.ContainsKey(menu.Identify))
                MenuCache.Remove(menu.Identify);
            var model = Repository.GetFirst(new DirectSpecification<Menu>(item => item.Identify == menu.Identify));
            if (model == null)
                return;
            Repository.Remove(model);
        }

        private string GeneralMenuId(params string[] keys)
        {
            return StringUtil.FormatKey(keys.ToArray());
        }
    }
}

 

菜单的缓存

菜单缓存采用和两种模式一种是根节点的树形模式,一种是字段缓存,这样既可以从树
去访问,也可以从字典根据key去访问。

 

Ps:界面代码比较乱,后面放出demo时候再整理,快了,由于最近确实比较忙,更新比较慢,见谅

 

转载于:https://www.cnblogs.com/Skyven/p/5674865.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值