在线时间:8:00-16:00
迪恩网络APP
随时随地掌握行业动态
扫描二维码
关注迪恩网络微信公众号
上一章,我们实现了用户的注册和登录,登录之后展示的是我们的主页,页面的左侧是多级的导航菜单,定位并展示用户需要访问的不同页面。目前导航菜单是写死的,考虑以后菜单管理的便捷性,我们这节实现下可视化配置菜单的功能,这样以后我们可以动态的配置导航菜单,不用再编译发布网站程序了。 增加后台管理模块第1步,左侧导航菜单中,添加后台管理模块,用作管理员登录后,可以进行一些后台管理的操作,当然,目前还没有权限控制(后期加入),所以对所有用户可见。大概菜单结构如下: 有了菜单项,我们还需要控制视图的跳转,所以,接下来需要写对应的控制器和视图。 为了将相关功能组织成一组单独命名空间(路由)和文件夹结构(视图),解决方案中右键添加区域(Area),取名后台管理(Configuration),代表后台管理模块,.Net Core脚手架(scaffold)自动帮我们实现了目录划分:控制器(Controllers)、模型(Models)、视图(Views) 菜单模型定义菜单的基本属性有:菜单名称、菜单类型、菜单的图标样式、菜单url路径。另外,菜单在逻辑上是树状结构,但是要在物理数据库中存储,需要进行扁平化处理,每个菜单项有个父菜单属性(根节点的父菜单为空),还有同一父节点底下,在组类的排序属性,定义如下: 1 /// <summary> 2 /// 菜单 3 /// </summary> 4 public class Menu 5 { 6 /// <summary> 7 /// 主键ID 8 /// </summary> 9 [DatabaseGenerated(DatabaseGeneratedOption.None)] 10 [Required(ErrorMessage = "请输入菜单编号")] 11 public string Id { get; set; } 12 13 /// <summary> 14 /// 菜单名称 15 /// </summary> 16 [Required(ErrorMessage = "请输入菜单名称")] 17 [StringLength(256)] 18 public string Name { get; set; } 19 20 /// <summary> 21 /// 父级ID 22 /// </summary> 23 [DisplayFormat(NullDisplayText = "无")] 24 public string ParentId { get; set; } 25 26 /// <summary> 27 /// 菜单组内排序 28 /// </summary> 29 [Range(0, 99, ErrorMessage = "请选择1-99范围内的整数")] 30 public int IndexCode { get; set; } 31 32 /// <summary> 33 /// 菜单路径 34 /// </summary> 35 [StringLength(256)] 36 [DisplayFormat(NullDisplayText = "无")] 37 public string Url { get; set; } 38 39 /// <summary> 40 /// 类型:0导航菜单;1操作按钮。 41 /// </summary> 42 [Required(ErrorMessage = "请选择菜单类型")] 43 public MenuTypes? MenuType { get; set; } 44 45 /// <summary> 46 /// 菜单图标名称 47 /// </summary> 48 [Required(ErrorMessage = "请输入菜单图标")] 49 [StringLength(50)] 50 public string Icon { get; set; } 51 52 /// <summary> 53 /// 菜单备注 54 /// </summary> 55 public string Remarks { get; set; } 56 } 57 /// <summary> 58 /// 菜单类型 59 /// </summary> 60 public enum MenuTypes 61 { 62 /// <summary> 63 /// 导航菜单 64 /// </summary> 65 导航菜单, 66 /// <summary> 67 /// 操作菜单 68 /// </summary> 69 操作菜单 70 }
有了我们的菜单模型,在控制器目录中,我们右键建立第1个自己的控制器,取名MenuController,用来菜单管理,上下文选取定义好的Menu模型,还是利用脚手架,自动帮我们生成增删改查对应的后来逻辑和视图。此时,我们把菜单导向该控制器,其实是可以正常访问的,不过还远远达不到我们的要求,所以我们还得完善下自动生成的代码。 菜单控制器改写为了方便今后的拓展,新增一个AppController控制器,继承Controller,以后所有的控制器,都继承于AppController,方便一些公共的方法调用。 .Net Core有个比较方便的一点,就是实现了构造器的依赖注入,这样我们不用像以前那样手工New一个DBContext对象,直接在控制器将需要的DBContext注入,调用的时候,直接访问注入的对象即可,有关依赖注入的知识,这里就不在多说了,有兴趣大家可以了解一下:.Net Core依赖注入 首先,在ApplicationDbContext添加Menu数据集 1 public class ApplicationDbContext : IdentityDbContext<ApplicationUser> 2 { 3 public ApplicationDbContext(DbContextOptions<ApplicationDbContext> options) 4 : base(options) 5 { 6 } 7 8 protected override void OnModelCreating(ModelBuilder builder) 9 { 10 base.OnModelCreating(builder); 11 } 12 13 public DbSet<ApplicationUser> ApplicationUsers { get; set; } 14 15 public DbSet<Menu> Menus { get; set; } 16 } 这里我们修改下MenuController构造器: 1 private readonly ApplicationDbContext _context; 2 3 public MenuController(ApplicationDbContext context, INavMenuService navMenuService) 4 { 5 _context = context; 6 _NavMenuService = navMenuService; 7 } 为了后面方便统一提供下拉框选择,这里实现一个下拉框初始化方法: 1 /// <summary> 2 /// 初始化下拉选择框 3 /// </summary> 4 /// <param name="menu"></param> 5 private void UpdateDropDownList(Menu menu = null) 6 { 7 var menusParent = _context.Menus.AsNoTracking().Where(s => s.MenuType == MenuTypes.导航菜单); 8 List<SelectListItem> listMenusParent = new List<SelectListItem>(); 9 foreach (var menuParent in menusParent) 10 { 11 listMenusParent.Add(new SelectListItem 12 { 13 Value = menuParent.Id, 14 Text = menuParent.Id + $"({menuParent.Name})", 15 Selected = (menu != null && menuParent.Id == menu.ParentId) 16 }); 17 } 18 ViewBag.ParentIds = listMenusParent; 19 20 if (menu == null) 21 { 22 ViewBag.MenuTypes = MenuTypes.导航菜单.GetSelectListByEnum(); 23 } 24 else 25 { 26 ViewBag.MenuTypes = MenuTypes.导航菜单.GetSelectListByEnum(Convert.ToInt32(menu.MenuType)); 27 } 28 } 列表页改写控制器调整:增加查询传入参数,根据参数筛选查询结果; 1 /// <summary> 2 /// 列表页 3 /// </summary> 4 /// <param name="query"></param> 5 /// <returns></returns> 6 public async Task<IActionResult> Index(MenuIndexQuery query) 7 { 8 var menus = _context.Menus.AsNoTracking(); 9 if (!string.IsNullOrEmpty(query.QName)) 10 { 11 menus = menus.Where(s => s.Name.Contains(query.QName.Trim())); 12 } 13 if (!string.IsNullOrEmpty(query.QId)) 14 { 15 menus = menus.Where(s => s.Id.Contains(query.QId.Trim())); 16 } 17 if (!string.IsNullOrEmpty(query.QParentId)) 18 { 19 menus = menus.Where(s => s.ParentId == query.QParentId.Trim()); 20 } 21 if (query.QMenuType != null) 22 { 23 menus = menus.Where(s => s.MenuType == query.QMenuType); 24 } 25 26 UpdateDropDownList(); 27 return View(new MenuIndexVM { Menus = await menus.ToListAsync(), Query = query }); 28 } 视图调整:用户点击删除时,弹出确认框,调用Ajax方式删除数据,不再通过页面跳转; 1 @using MyWebSite.ViewModels 2 @addTagHelper *, Microsoft.AspNetCore.Mvc.TagHelpers 3 4 @model MyWebSite.Areas.Configuration.ViewModels.MenuIndexVM 5 @{ 6 ViewData["Title"] = "菜单列表"; 7 8 var breadcrumb = new BreadCrumb("菜单列表", "Version 2.0", new List<NavCrumb> 9 { 10 new NavCrumb(name:"菜单管理",url: "/Configuration/Menu"), 11 new NavCrumb(name:"菜单列表"), 12 }); 13 ViewBag.BreadCrumb = breadcrumb; 14 15 Layout = "~/Views/Shared/_Layout.cshtml"; 16 } 17 18 <div class="row"> 19 <div class="col-xs-12"> 20 <div class="box with-border"> 21 <form class="form" asp-action="Index"> 22 <div class="box-header"> 23 <h3 class="box-title"><i class="fa fa-search margin-r-5">查询条件</i></h3> 24 <div class="box-tools pull-right"> 25 <button type="submit" class="btn btn-success margin-r-5"><i class="fa fa-search margin-r-5"></i>查询</button> 26 <a class="btn btn-primary" href="/Configuration/Menu/Create"><i class="fa fa-plus margin-r-5"></i>新建</a> 27 </div> 28 <div asp-validation-summary="All" class="text-danger"></div> 29 <div class="row"> 30 <div class="col-md-3"> 31 <div class="form-group"> 32 <label asp-for="Query.QName">菜单名称:</label> 33 <input asp-for="Query.QName" class="form-control input-sm"> 34 </div> 35 </div> 36 <div class="col-md-3"> 37 <div class="form-group"> 38 <label asp-for="Query.QId">菜单编码:</label> 39 <input asp-for="Query.QId" class="form-control input-sm"> 40 </div> 41 </div> 42 <div class="col-md-3"> 43 <div class="form-group"> 44 <label asp-for="Query.QParentId">父级菜单:</label> 45 <select asp-for="Query.QParentId" class="form-control input-sm select2" asp-items="ViewBag.ParentIds"> 46 <option value="">-- 请选择 --</option> 47 </select> 48 </div> 49 </div> 50 <div class="col-md-3"> 51 <div class="form-group"> 52 <label asp-for="Query.QMenuType">菜单类型:</label> 53 <select asp-for="Query.QMenuType" class="form-control input-sm select2" asp-items="ViewBag.MenuTypes"> 54 <option value="">-- 请选择 --</option> 55 </select> 56 </div> 57 </div> 58 </div> 59 </div> 60 </form> 61 <div class="box-body"> 62 <table class="table table-bordered table-hover" style="width: 100%"> 63 <thead> 64 <tr> 65 <th>#</th> 66 <th>菜单名称</th> 67 <th>菜单编号</th> 68 <th>父级编号</th> 69 <th>组内排序</th> 70 <th>菜单类型</th> 71 <th>菜单图标</th> 72 <th>菜单路径</th> 73 <th>操作</th> 74 </tr> 75 </thead> 76 <tbody> 77 @{ 78 var index = 0; 79 } 80 @foreach (var item in Model.Menus) 81 { 82 index++; 83 <tr> 84 <td> 85 @index.ToString("D3") 86 </td> 87 <td> 88 @Html.ActionLink(@item.Name, "Details", new { id = @item.Id }) 89 </td> 90 <td> 91 <span>@item.Id</span> 92 </td> 93 <td> 94 <span>@Html.DisplayFor(modelItem => item.ParentId)</span> 95 </td> 96 <td> 97 <span>@item.IndexCode</span> 98 </td> 99 <td> 100 <span>@item.MenuType</span> 101 </td> 102 <td> 103 <i class="fa @item.Icon" data-toggle="tooltip" data-placement="right" title="@item.Icon"></i> 104 </td> 105 <td> 106 <i class="fa fa-ellipsis-h" data-toggle="tooltip" data-placement="top" title="@Html.DisplayFor(modelItem => item.Url)"></i> 107 </td> 108 <td> 109 @Html.ActionLink("编辑", "Edit", new { id = @item.Id })| 110 @Html.ActionLink("详情", "Details", new { id = @item.Id })| 111 <a href="#" onclick="onDelete('@item.Id', '@item.Name');">删除</a> 112 </td> 113 </tr> 114 } 115 </tbody> 116 </table> 117 </div> 118 </div> 119 </div> 120 </div> 121 122 @section Scripts{ 12 |
请发表评论