基于MVC+EasyUI的Web开发框架经验总结(3)- 使用Json实体类构建菜单数据

最近花了不少时间在重构和进一步提炼我的Web开发框架上,力求在用户体验和界面设计方面,和Winform开发框架保持一致,而在Web上,我主要采用EasyUI的前端界面处理技术,走MVC的技术路线,在重构完善过程中,很多细节花费不少时间进行研究和提炼,一步步走过来,也积累了不少经验,本系列将主要介绍我在进一步完善我的Web框架基础上积累的经验进行分享,本随笔主要介绍使用如何使用Json实体类构建菜单数据,然后在主界面中进行使用。
菜单的界面效果如下所示,菜单分为一级菜单、二级菜单、三级菜单,他们各自在位置上是不同的定义,这个界面布局规定三级菜单就是最小的菜单节点了,也就是叶子节点。



要实现以上的菜单,需要把菜单定义成相关的Json数据,然后通过脚本把它们添加到界面里面去,如下数据和脚本就是定义相关的菜单数据的。

<script type="text/javascript">
var _menus = {
    "default": [
        {
            "menuid": "1", "icon": "icon-computer", "menuname": "权限管理",
            "menus": [
                      { "menuid": "13", "menuname": "用户管理", "icon": "icon-user", "url": "/User/Index" },
                      { "menuid": "14", "menuname": "组织机构管理", "icon": "icon-organ", "url": "/OU/Index" },
                      { "menuid": "15", "menuname": "角色管理", "icon": "icon-group-key", "url": "/Role/Index" },
                      { "menuid": "16", "menuname": "功能管理", "icon": "icon-key", "url": "/Function/Index" },
                      { "menuid": "17", "menuname": "登陆日志", "icon": "icon-view", "url": "/LoginLog/Index" }
            ]
        },
       {
           "menuid": "2", "icon": "icon-user", "menuname": "其他管理",
           "menus": [{ "menuid": "21", "menuname": "修改密码", "icon": "icon-lock", "url": "javascript:ShowPasswordDialog()" }
           ]
       }
    ],
    "point": [
        {
            "menuid": "3", "icon": "icon-computer", "menuname": "事务中心",
            "menus": [
                      { "menuid": "33", "menuname": "测试菜单1", "icon": "icon-user", "url": "../Commonpage/building.htm" },
                      { "menuid": "34", "menuname": "测试菜单2", "icon": "icon-organ", "url": "../Commonpage/building.htm" },
                      { "menuid": "35", "menuname": "测试菜单3", "icon": "icon-group-key", "url": "../Commonpage/building.htm" },
                      { "menuid": "36", "menuname": "测试菜单4", "icon": "icon-key", "url": "../Commonpage/building.htm" }
            ]
        },
        {
            "menuid": "4", "icon": "icon-user", "menuname": "其他菜单",
            "menus": [{ "menuid": "41", "menuname": "测试菜单5", "icon": "icon-lock", "url": "../Commonpage/building.htm" }]
        }
    ]
};

function showSubMenu(url, title, menuCategory, defaultIcon) {
    if (defaultIcon == null || defaultIcon == "") {
        defaultIcon = "icon-table";
    }
    addTab(title, url, "icon " + defaultIcon);
    Clearnav();
    if (menuCategory != "") {
        addNav(_menus[menuCategory]);
    }
}
</script>

从上面的菜单Json数据来看,它是一个字典的Json数据列表,在Web界面上,通过下面的代码可以展开上面Json定义的二级菜单。

<li><a href="#" onclick="showSubMenu('/User/Index', '用户管理', 'default')">权限管理</a></li>

虽然上面的定义的数据能够解决菜单的显示问题,但是对于我们需要动态控制的菜单,显然做不到,因此需要把上面的json数据,通过菜单控制器进行动态生成才可以,然后在脚本里面通过Jquery的方式获取Json数据,如下所示。

var _menus = {};
//同步获取
$.ajax({
    type: 'GET',
    url: '/Menu/GetMenuData?r=' + Math.random(),
    async: false,//同步
    dataType: 'json',
    success: function (json) {
        _menus = json;
    },
    error: function (xhr, status, error) {
        alert("操作失败"); //xhr.responseText
    }
});

上面的GetMenuData方法,通过后台的控制器进行动态生成的,它的代码如下所示

/// <summary>
/// 获取树形展示数据
/// </summary>
/// <returns></returns>
public ActionResult GetMenuData()
{
    string json = GetTreeJson("-1", "", "");
    json = json.Trim(',');
    return Content(string.Format("[{0}]", json));
}

/// <summary>
/// 递归获取树形信息
/// </summary>
/// <returns></returns>
private string GetTreeJson(string PID, string folderIcon, string leafIcon)
{
    string condition = string.Format("PID='{0}' ", PID);
    List<MenuInfo> nodeList = BLLFactory<Menu>.Instance.Find(condition);
    StringBuilder content = new StringBuilder();
    foreach (MenuInfo model in nodeList)
    {
        string ParentID = (model.PID == "-1" ? "0" : model.PID);
        string subMenu = this.GetTreeJson(model.ID, folderIcon, leafIcon);
        string parentMenu = string.Format("{{ \"id\":\"{0}\", \"pId\":\"{1}\", \"name\":\"{2}\" ", model.ID, ParentID, model.Name);
        if (string.IsNullOrEmpty(subMenu))
        {
            if (!string.IsNullOrEmpty(leafIcon))
            {
                parentMenu += string.Format(",\"icon\":\"{0}\" }},", leafIcon);
            }
            else
            {
                parentMenu += "},";
            }
        }
        else
        {
            if (!string.IsNullOrEmpty(folderIcon))
            {
                parentMenu += string.Format(",\"icon\":\"{0}\" }},", folderIcon);
            }
            else
            {
                parentMenu += "},";
            }
        }

        content.AppendLine(parentMenu.Trim());
        content.AppendLine(subMenu.Trim());
    }
    return content.ToString().Trim();
}

不过对于上面的代码,我觉得虽然能解决问题,能够正确生成相关的Json代码,但是感觉不够优雅,我不喜欢使用拼凑方法构建数据。

前面看了Menu的Json脚本,我说过他是一个字典类型的Json数据格式,那么我们是否可以通过字典和实体信息来承载,然后直接通过ToJson方法出来呢?答案是可以的。

/// <summary>
/// 获取菜单的树形展示数据
/// </summary>
/// <returns></returns>
public ActionResult GetMenuData()
{
    Dictionary<string, List<MenuData>> dict = new Dictionary<string, List<MenuData>>();
                                             
    List<MenuInfo> list = BLLFactory<Menu>.Instance.GetTopMenu(MyConstants.SystemType);
    int i = 0;
    foreach (MenuInfo info in list)
    {
        if (!HasFunction(info.FunctionId))
        {
            continue;
        }              
        List<MenuData> treeList = new List<MenuData>();
        List<MenuNodeInfo> nodeList = BLLFactory<Menu>.Instance.GetTreeByID(info.ID);
        foreach (MenuNodeInfo nodeInfo in nodeList)
        {
            if (!HasFunction(nodeInfo.FunctionId))
            {
                continue;
            }                                                                                                                                                                       
            MenuData menuData = new MenuData(nodeInfo.ID, nodeInfo.Name, string.IsNullOrEmpty(nodeInfo.WebIcon) ? "icon-computer" : nodeInfo.WebIcon);
            foreach (MenuNodeInfo subNodeInfo in nodeInfo.Children)
            {
                if (!HasFunction(subNodeInfo.FunctionId))
                {
                    continue;
                }
                string icon = string.IsNullOrEmpty(subNodeInfo.WebIcon) ? "icon-organ" : subNodeInfo.WebIcon;
                menuData.menus.Add(new MenuData(subNodeInfo.ID, subNodeInfo.Name, icon, subNodeInfo.Url));
            }
            treeList.Add(menuData);
        }

        //添加到字典里面,如果是第一个,默认用default名称
        string dictName = (i++ == 0) ? "default" : info.ID;
        dict.Add(dictName, treeList);
    }

    string content = ToJson(dict);
    return Content(content.Trim(','));
}

上面的代码,通过MenuData的对象数据,来承载相关的菜单信息,然后把它添加到字典Dictionary<string, List<MenuData>> dict 里面就可以了,这样的代码,没有那么多拼凑出来的感觉,是不是很好看呢?把对象转换为Json数据,直接通过ToJson就可以解决了,很简单吧。

而菜单的权限控制,就是通过集合权限管理进行判断,父菜单如果没有权限,就直接跳过,不在继续生成下面的子菜单,权限判断的如下所示。

if (!HasFunction(info.FunctionId))
{
       continue;
 } 

当然,在界面上展开二级菜单的操作界面,也应该通过脚本动态进行生成的,这样才能做到所有的内容动态构建。

<ul class="navigation" style="display:block">
    @Html.Raw(@ViewBag.HeaderScript)</ul>

上面使用ViewBag对象进行传递脚本内容到界面上,其实后台生成的操作,是一行HTML代码就是了,代码类似下面的内容。

<li>
    <a href="#" onclick="showSubMenu('/User/Index', '用户管理', 'default')">权限管理</a>
</li>

最后出来的效果,就是博客开始介绍的界面截图,没有任何变化,但是代码我们已经经过了几步的优化整理,看起来很清爽,更能实现动态变化了。


有空可以回顾下其他两篇的经验总结内容:

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,362评论 5 477
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,330评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,247评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,560评论 1 273
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,580评论 5 365
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,569评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,929评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,587评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,840评论 1 297
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,596评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,678评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,366评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,945评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,929评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,165评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 43,271评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,403评论 2 342

推荐阅读更多精彩内容