zoukankan      html  css  js  c++  java
  • 在ASP.NET MVC下实现树形导航菜单


    在需要处理很多分类以及导航的时候,树形导航菜单就比较适合。例如在汽车之家上:

    1

    页面主要分两部分,左边是导航菜单,右边显示对应的内容。现在,我们就在ASP.NET MVC 4 下临摹一个,如下:

    2

    实现的效果包括:
    1、点击导航菜单上的品牌,左侧显示该品牌下的所有车型。
    2、点击导航菜单上的车系,左侧显示该车系下的所有车型。
    3、点击左侧上方的字母导航,锚点跳到导航菜单的对应部分。
    4、页面加载完毕,显示所有品牌和车系,即树形导航完全展开。
    5、点击导航菜单上的品牌,收缩或展开对应的车系,收缩时,品牌前面图标为+号,展开时,品牌前面的图片为-号。
    ......

    源码部分,在这里

    思路呢?

    页面分成左右2部分,使用Bootstrap轻松实现:

    <div class="row">
    
        <div class="col-md-2 col-lg-2 col-sm-2">
    
        </div>
    
        <div class="col-md-10 col-lg-10 col-sm-10">
    
        </div>
    
    </div>

    左侧最上方的字母导航,被放在一个div中,页面加载的时候向控制器动态请求。

    品牌上方的字母归类,比如奥迪上方的字母A,实际上是一个div。

    品牌和车系放在了ul中,比如奥迪品牌以及奥迪下的奥迪A4和奥迪A6车系。车系被放在了dl中。

    树形菜单采用模版比较合适,先把数据填充到模版,再把模版追加到页面元素。

    当点击左侧树形导航上的品牌或车系,右侧通过iframe来呈现对应的内容。

    领域先行。有关品牌和车系就抽象成如下的类:

        public class CarCategory
    
        {
    
            public int Id { get; set; }
    
            public int ParentId { get; set; }
    
            public string Name { get; set; }
    
            public string FirstLetter { get; set; }
    
            public string AnchorName { get; set; }
    
            public int Level { get; set; }
    
            public short DelFlag { get; set; }
    
        }

    有关车型就抽象成如下的类:

        public class Car
    
        {
    
            public int Id { get; set; }
    
            public int PinPaiId { get; set; }
    
            public int CheXiId { get; set; } 
    
            public string Name { get; set; }
    
        }

    页面左侧呈现树形导航需要向控制器请求json数据,大致格式是:

    首字母
    锚点名称
    所有品牌
        品牌编号
        品牌名称
        所有车系
            车系编号
            车系名称
            车系下车型的总数量

    貌似有3层,那就从最里面这层开始建模。有关车系在树形导航中的显示:

        public class CheXiForDisplay
    
        {
    
            public int CheXiId { get; set; }
    
            public int TotalCount { get; set; }
    
            public string CheXiName { get; set; }
    
        }

    有关品牌在树形导航中的显示:

        public class PinPaiForDisplay
    
        {
    
            public int PinPaiId { get; set; }
    
            public string PinPaiName { get; set; }
    
            public List<CheXiForDisplay> CheXis { get; set; }
    
        }

    有关品牌车系分组的:

        public class PinPaiCheXiForDisplay
    
        {
    
            public string FirstLetter { get; set; }
    
            public string Anchor { get; set; }
    
            public List<PinPaiForDisplay> PinPais { get; set; }
    
        }

    数据源从哪里来?模拟了一个:

        public class Database
    
        {
    
            public static IEnumerable<CarCategory> GetAllCarCategories()
    
            {
    
                return new List<CarCategory>
    
                {
    
                    new CarCategory(){Id = 1, ParentId = 0, Name = "奥迪",FirstLetter = "A", AnchorName = "AA", Level = 1, DelFlag = 0},
    
                    new CarCategory(){Id = 2, ParentId = 0, Name = "宝马",FirstLetter = "B", AnchorName = "BB", Level = 1, DelFlag = 0},
    
                    new CarCategory(){Id = 3, ParentId = 0, Name = "保时捷",FirstLetter = "B", AnchorName = "BB", Level = 1, DelFlag = 0},
    
                    new CarCategory(){Id = 4, ParentId = 0, Name = "长安",FirstLetter = "C", AnchorName = "CC", Level = 1, DelFlag = 0},
    
                    new CarCategory(){Id = 5, ParentId = 0, Name = "大众",FirstLetter = "D", AnchorName = "DD", Level = 1, DelFlag = 0},
    
                    new CarCategory(){Id = 6, ParentId = 0, Name = "东风",FirstLetter = "D", AnchorName = "DD", Level = 1, DelFlag = 0},
    
                    new CarCategory(){Id = 7, ParentId = 0, Name = "丰田",FirstLetter = "F", AnchorName = "FF", Level = 1, DelFlag = 0},
    
                    new CarCategory(){Id = 8, ParentId = 0, Name = "福特",FirstLetter = "F", AnchorName = "FF", Level = 1, DelFlag = 0},
    
                    new CarCategory(){Id = 9, ParentId = 1, Name = "奥迪A4",FirstLetter = "A", AnchorName = "AA", Level = 2, DelFlag = 0},
    
                    new CarCategory(){Id = 10, ParentId = 1, Name = "奥迪A6",FirstLetter = "A", AnchorName = "AA", Level = 2, DelFlag = 0},
    
                    new CarCategory(){Id = 11, ParentId = 2, Name = "宝马1",FirstLetter = "B", AnchorName = "BB", Level = 2, DelFlag = 0},
    
                    new CarCategory(){Id = 12, ParentId = 2, Name = "宝马2",FirstLetter = "B", AnchorName = "BB", Level = 2, DelFlag = 0},
    
                    new CarCategory(){Id = 13, ParentId = 3, Name = "保时捷1",FirstLetter = "B", AnchorName = "BB", Level = 2, DelFlag = 0},
    
                    new CarCategory(){Id = 14, ParentId = 3, Name = "保时捷2",FirstLetter = "B", AnchorName = "BB", Level = 2, DelFlag = 0},
    
                    new CarCategory(){Id = 15, ParentId = 4, Name = "长安1",FirstLetter = "C", AnchorName = "CC", Level = 2, DelFlag = 0},
    
                    new CarCategory(){Id = 16, ParentId = 4, Name = "长安2",FirstLetter = "C", AnchorName = "CC", Level = 2, DelFlag = 0},
    
                    new CarCategory(){Id = 17, ParentId = 5, Name = "大众1",FirstLetter = "D", AnchorName = "DD", Level = 2, DelFlag = 0},
    
                    new CarCategory(){Id = 18, ParentId = 5, Name = "大众2",FirstLetter = "D", AnchorName = "DD", Level = 2, DelFlag = 1},
    
                    new CarCategory(){Id = 19, ParentId = 6, Name = "东风1",FirstLetter = "D", AnchorName = "DD", Level = 2, DelFlag = 0},
    
                    new CarCategory(){Id = 20, ParentId = 6, Name = "东风2",FirstLetter = "D", AnchorName = "DD", Level = 2, DelFlag = 0},
    
                    new CarCategory(){Id = 21, ParentId = 7, Name = "丰田1",FirstLetter = "F", AnchorName = "FF", Level = 2, DelFlag = 0},
    
                    new CarCategory(){Id = 22, ParentId = 7, Name = "丰田2",FirstLetter = "F", AnchorName = "FF", Level = 2, DelFlag = 0},
    
                    new CarCategory(){Id = 23, ParentId = 8, Name = "福特1",FirstLetter = "F", AnchorName = "AFF", Level = 2, DelFlag = 0},
    
                    new CarCategory(){Id = 24, ParentId = 8, Name = "福特2",FirstLetter = "F", AnchorName = "AFF", Level = 2, DelFlag = 0}
    
                };
    
            }
    
            public static IEnumerable<Car> GetAllCars()
    
            {
    
                return new List<Car>
    
                {
    
                    new Car(){Id = 1, PinPaiId = 1, CheXiId = 9, Name = "奥迪A401"},
    
                    new Car(){Id = 2, PinPaiId = 1, CheXiId = 9, Name = "奥迪A402"},
    
                    new Car(){Id = 3, PinPaiId = 1, CheXiId = 10, Name = "奥迪A601"},
    
                    new Car(){Id = 4, PinPaiId = 1, CheXiId = 10, Name = "奥迪A602"},
    
                    new Car(){Id = 5, PinPaiId = 2, CheXiId = 11, Name = "宝马101"},
    
                    new Car(){Id = 6, PinPaiId = 2, CheXiId = 11, Name = "宝马102"},
    
                    new Car(){Id = 7, PinPaiId = 2, CheXiId = 12, Name = "宝马201"},
    
                    new Car(){Id = 8, PinPaiId = 2, CheXiId = 12, Name = "宝马202"},
    
                    new Car(){Id = 9, PinPaiId = 3, CheXiId = 13, Name = "保时捷101"},
    
                    new Car(){Id = 10, PinPaiId = 3, CheXiId = 13, Name = "保时捷102"},
    
                    new Car(){Id = 11, PinPaiId = 3, CheXiId = 14, Name = "保时捷201"},
    
                    new Car(){Id = 12, PinPaiId = 3, CheXiId = 14, Name = "保时捷202"},
    
                    new Car(){Id = 13, PinPaiId = 4, CheXiId = 15, Name = "长安101"},
    
                    new Car(){Id = 14, PinPaiId = 4, CheXiId = 15, Name = "长安102"},
    
                    new Car(){Id = 15, PinPaiId = 4, CheXiId = 16, Name = "长安201"},
    
                    new Car(){Id = 16, PinPaiId = 4, CheXiId = 16, Name = "长安202"},
    
                    new Car(){Id = 17, PinPaiId = 5, CheXiId = 17, Name = "大众101"},
    
                    new Car(){Id = 18, PinPaiId = 5, CheXiId = 17, Name = "大众102"},
    
                    new Car(){Id = 19, PinPaiId = 5, CheXiId = 18, Name = "大众201"},
    
                    new Car(){Id = 20, PinPaiId = 5, CheXiId = 18, Name = "大众202"},
    
                    new Car(){Id = 21, PinPaiId = 6, CheXiId = 19, Name = "东风101"},
    
                    new Car(){Id = 22, PinPaiId = 6, CheXiId = 19, Name = "东风102"},
    
                    new Car(){Id = 23, PinPaiId = 6, CheXiId = 20, Name = "东风201"},
    
                    new Car(){Id = 24, PinPaiId = 6, CheXiId = 20, Name = "东风202"},
    
                    new Car(){Id = 25, PinPaiId = 7, CheXiId = 21, Name = "丰田101"},
    
                    new Car(){Id = 26, PinPaiId = 7, CheXiId = 21, Name = "丰田102"},
    
                    new Car(){Id = 27, PinPaiId = 7, CheXiId = 22, Name = "丰田201"},
    
                    new Car(){Id = 28, PinPaiId = 7, CheXiId = 22, Name = "丰田202"},
    
                    new Car(){Id = 29, PinPaiId = 8, CheXiId = 23, Name = "福特101"},
    
                    new Car(){Id = 30, PinPaiId = 8, CheXiId = 23, Name = "福特102"},
    
                    new Car(){Id = 31, PinPaiId = 8, CheXiId = 24, Name = "福特201"},
    
                    new Car(){Id = 32, PinPaiId = 8, CheXiId = 24, Name = "福特202"}
    
                };
    
            }
    
        }       
    

    好,现在可以向控制器要数据了。

       public class HomeController : Controller
    
        {
    
            public ActionResult Index()
    
            {
    
                return View();
    
            }
    
            //获取所有首字母以及锚点的json
    
            public ActionResult GetFirstLettersJson()
    
            {
    
                var allCarCategories = Database.GetAllCarCategories();
    
                var result = from l in allCarCategories
    
                    group l by l.FirstLetter
    
                    into g
    
                    select new {firstletter = g.Key, anchor=g.ToList()[0].AnchorName};
    
                return Json(result, JsonRequestBehavior.AllowGet);
    
            }
    
            //获取按首字母分组后的品牌车系json
    
            public ActionResult GetPinPaiCheXiJson() 
    
            {
    
                var allPinPais = Database.GetAllCarCategories().Where(c => c.Level == 1).OrderBy(c => c.FirstLetter);
    
                var allPinPaisGroup = from p in allPinPais
    
                    group p by new
    
                    {
    
                        p.FirstLetter,
    
                        p.AnchorName
    
                    };
    
                List<PinPaiCheXiForDisplay> result1 = new List<PinPaiCheXiForDisplay>();
    
                foreach (var item in allPinPaisGroup)
    
                {
    
                    //品牌车系
    
                    PinPaiCheXiForDisplay pinPaiCheXiForDisplay = new PinPaiCheXiForDisplay();
    
                    pinPaiCheXiForDisplay.FirstLetter = item.Key.FirstLetter;
    
                    pinPaiCheXiForDisplay.Anchor = item.Key.AnchorName;
    
                    
    
                    //品牌
    
                    List<PinPaiForDisplay> pinPaiForDisplays = new List<PinPaiForDisplay>();
    
                    foreach (var pinpai in item.ToList())
    
                    {
    
                        PinPaiForDisplay pinPaiForDisplay = new PinPaiForDisplay();
    
                        pinPaiForDisplay.PinPaiId = pinpai.Id;
    
                        pinPaiForDisplay.PinPaiName = pinpai.Name;
    
                        
    
                        //车系
    
                        List<CheXiForDisplay> cheXiForDisplays = new List<CheXiForDisplay>();
    
                        var cheXis = Database.GetAllCarCategories().Where(c => c.ParentId == pinpai.Id).OrderBy(c => c.Id);
    
                        foreach (var chexi in cheXis)
    
                        {
    
                            CheXiForDisplay cheXiForDisplay = new CheXiForDisplay();
    
                            cheXiForDisplay.CheXiId = chexi.Id;
    
                            cheXiForDisplay.CheXiName = chexi.Name;
    
                            cheXiForDisplay.TotalCount = cheXis.Count();
    
                            cheXiForDisplays.Add(cheXiForDisplay);
    
                        }
    
                        pinPaiForDisplay.CheXis = cheXiForDisplays;
    
                        pinPaiForDisplays.Add(pinPaiForDisplay);
    
                    }
    
                    pinPaiCheXiForDisplay.PinPais = pinPaiForDisplays;
    
                    result1.Add(pinPaiCheXiForDisplay);
    
                }
    
                return Json(result1, JsonRequestBehavior.AllowGet);
    
            }
    
            //根据品牌Id显示车型
    
            public ActionResult GetCheXingsByPId(int pid)
    
            {
    
                var cars = Database.GetAllCars().Where(c => c.PinPaiId == pid);
    
                return View(cars);
    
            }
    
            //根据车系Id显示车型
    
            public ActionResult GetCheXingsByChexiId(int cxid)
    
            {
    
                var cars = Database.GetAllCars().Where(c => c.CheXiId == cxid);
    
                return View(cars);
    
            }
    
        }
    

    在Shared/_Layout.cshtml中,该引用的css,js都要引用上。

    <head>
    
        <meta charset="utf-8" />
    
        <meta name="viewport" content="width=device-width" />
    
        <title>@ViewBag.Title</title>
    
        <link href="~/bootstrap/css/bootstrap.min.css" rel="stylesheet" />
    
        @Styles.Render("~/Content/css")
    
        @RenderSection("styles", required: false)
    
        @Scripts.Render("~/bundles/jquery")
    
        <script src="~/bootstrap/js/bootstrap.min.js"></script>
    
    </head>
    
    <body>
    
        @RenderBody()
    
        
    
        @RenderSection("scripts", required: false)
    
    </body>  

    Home/Index.cshtml就负责显示就行了。

    @{
    
        ViewBag.Title = "Index";
    
        Layout = "~/Views/Shared/_Layout.cshtml";
    
    }
    
    @section styles
    
    {
    
        <link href="~/Content/sidemenu.css" rel="stylesheet" />
    
    }
    
    <div class="row">
    
        <div class="col-md-2 col-lg-2 col-sm-2">
    
            <!--字母导航开始-->
    
            <div id="lDaoHang">
    
            </div>
    
            <!--字母导航结束-->
    
            <!--树开始-->
    
            <div id="cTreeDiv" style="overflow-x: hidden; overflow-y: scroll; height: 550px;  99%;">
    
            </div>
    
            <!--树结束-->
    
            <div>
    
                <dl id="test"></dl>
    
            </div>
    
        </div>
    
        <div class="col-md-10 col-lg-10 col-sm-10">
    
            <div class="carContent" id="carContent">
    
                <iframe id="frameCar" src="" scrolling="no" frameborder="0" height="100%" width="100%" onload="this.height=this.contentWindow.document.documentElement.scrollHeight"></iframe>
    
            </div>
    
        </div>
    
    </div>
    
    @section scripts
    
    {
    
        <script src="~/Scripts/jquery.tmpl.min.js"></script>
    
        <script type="text/javascript">
    
            $(function () {
    
                //加载首字母
    
                $.getJSON('@Url.Action("GetFirstLettersJson", "Home")', function (data) {
    
                    $('#firstLetterTemplate').tmpl(data).appendTo('#lDaoHang');
    
                });
    
                //加载所有品牌车系
    
                $.getJSON('@Url.Action("GetPinPaiCheXiJson", "Home")', function (data) {
    
                    $('#pinpaiTemplate').tmpl(data).appendTo('#cTreeDiv');
    
                    $('.pLink').each(function () {
    
                        pinPaiInitialState($(this));
    
                    });
    
                });
    
                //隐藏ifame所在div
    
                $("#carContent").css("display", "none");
    
                //点击品牌
    
                $('#cTreeDiv').on("click", ".pLink", function () {
    
                    //切换
    
                    togglePinPaiState($(this));
    
                    //显示右边区域
    
                    var url = "/Home/GetCheXingsByPId?pid=" + $(this).attr('id');
    
                    $("#frameCar").attr("src", url);
    
                    $("#carContent").css("display", "block");
    
                });
    
                //点击车系
    
                $('#cTreeDiv').on("click", ".cxLink", function () {
    
                    //显示右边区域
    
                    var url = "/Home/GetCheXingsByChexiId?cxid=" + $(this).attr('id');
    
                    $("#frameCar").attr("src", url);
    
                    $("#carContent").css("display", "block");
    
                });
    
            });
    
            //品牌的初始状态,即把车系隐藏和品牌前面的图标为+号
    
            var pstate = 0;
    
            //品牌只有2种状态:所有车系隐藏,品牌前面的图标变为为+号;要么显示品牌下的所有车系,品牌前面的图标变为-号
    
            //把车系隐藏和品牌前面的图标为+号,记为状态0
    
            //把车系隐藏和品牌前面的图标为-号,记为状态1
    
            function togglePinPaiState($pinpai) {
    
                if (pstate == 0) {
    
                    var $i = $pinpai.parent().find("i");
    
                    var attr = $i.attr('class');
    
                    if (typeof attr !== typeof undefined && attr !== false) {
    
                        $i.removeClass("iconHide");
    
                    }
    
                    $i.addClass("iconShow");
    
                    $pinpai.parent().parent().find("dl").show();
    
                    pstate = 1;
    
                } else {
    
                    var $j = $pinpai.parent().find("i");
    
                    var attr1 = $j.attr('class');
    
                    if (typeof attr1 !== typeof undefined && attr1 !== false) {
    
                        $j.removeClass("iconShow");
    
                    }
    
                    $j.addClass("iconHide");
    
                    $pinpai.parent().parent().find("dl").hide();
    
                    pstate = 0;
    
                }
    
            }
    
            function pinPaiInitialState($pinpai) {
    
                pstate = 0;
    
                togglePinPaiState($pinpai);
    
            }
    
        </script>
    
        <!--首字母模版-->
    
        <script id="firstLetterTemplate" type="text/x-jQuery-tmpl">
    
            <div class="lWrapper">
    
                <a href="#${anchor}" class="lLink">${firstletter}</a>
    
            </div>
    
        </script>
    
        <!--品牌模版-->
    
        <script id="pinpaiTemplate" type="text/x-jQuery-tmpl">
    
            <div class="lHeader" id="${Anchor}">${FirstLetter}</div>
    
            <ul class="uTree">
    
                {{if PinPais}}
    
                {{each PinPais}}
    
                <li>
    
                    <h5 class="font-bold">
    
                        <a href="javascript:void(0)" class="pLink" id="${$value.PinPaiId}"><i></i>${$value.PinPaiName}</a>
    
                    </h5>
    
                    <dl>
    
                        {{tmpl(CheXis) "#chexiTemplate"}}
    
                    </dl>
    
                </li>
    
                {{/each}}
    
                {{else}}
    
                <li>没有对应品牌</li>
    
                {{/if}}
    
                    
    
            </ul>
    
        </script>
    
        <!--车系模版-->
    
        <script id="chexiTemplate" type="text/x-jQuery-tmpl">
    
            <dd><a id="${CheXiId}" href="javascript:void(0)" class="cxLink">${CheXiName}<em>(${TotalCount})</em></a></dd>
    
        </script>
    
    }

    以上,
    ○ 从控制器返回的有关树形菜单的json数据,先填充到jquery.tmpl.min.js模版中,然后追加到页面上。
    ○ 树形菜单的展开或收缩,通过全局变量pstate在0和1之间的切换来实现,页面初次加载给变量pstate一个初始值。


    另外,点击树形导航上的品牌,iframe加载的视图是Home/GetCheXingsByPId.cshtml

    @model IEnumerable<MvcApplication1.Models.Car>
    
    @{
    
        ViewBag.Title = "GetCheXingsByPId";
    
        Layout = "~/Views/Shared/_Layout.cshtml";
    
    }
    
    <h2>GetCheXingsByPId</h2>
    
    <div>
    
        @foreach (var item in Model)
    
        {
    
            <p>@item.Name </p>
    
        
    
        }
    
    </div>
    

    点击树形导航上的车系,iframe加载的视图是Home/GetCheXingsByChexiId.cshtml

    @model IEnumerable<MvcApplication1.Models.Car>
    
    @{
    
        ViewBag.Title = "GetCheXingsByChexiId";
    
        Layout = "~/Views/Shared/_Layout.cshtml";
    
    }
    
    <h2>GetCheXingsByChexiId</h2>
    
    <div>
    
        @foreach (var item in Model)
    
        {
    
            <p>@item.Name </p>
    
        
    
        }
    
    </div
    

    就这样。

  • 相关阅读:
    min-height IE6的解决方案
    javascript数据类型检测方法
    typeof、instanceof与isPrototypeOf()的差异与联系
    获取真实IP地址
    表格中的IE BUG
    【译】你对position的了解有多少?
    vim快捷键
    syntaxerror : unexpected token &
    .Net 高效开发之不可错过的实用工具
    @media screen 针对不同移动设备——响应式设计
  • 原文地址:https://www.cnblogs.com/darrenji/p/4364783.html
Copyright © 2011-2022 走看看