- A+
入门实战-权限管理之整理菜单导航控制显示
这系列的教程最开始就是从一个菜单功能讲起的,最后也在此功能上结束。导航菜单现在都是静态的,在默认的_Layout.cshtml文件中,它的静态代码结构是这样的:
<li class="nav-item"> <a class="nav-link text-dark" asp-area="" asp-controller="Manager" asp-action="Index">用户管理</a> </li> <li class="nav-item"> <a class="nav-link text-dark" asp-area="" asp-controller="ManagerRole" asp-action="Index">角色管理</a> </li> <li class="nav-item"> <a class="nav-link text-dark" asp-area="" asp-controller="Menu" asp-action="Index">菜单管理</a> </li> <li class="nav-item"> <a class="nav-link text-dark" asp-area="" asp-controller="Article" asp-action="Index">文章管理</a> </li>
现在,我通过程序的方式控制代码输出显示,变成动态的结构形式,我采用2种方式实现动态菜单,一个是采用Html.Partial的形式来实现局部视图代码,一个是采用ViewCommpent的形式来实现。
(1).现在导航_Layout.cshtml文件中,导航代码部分做个修改:
Html.PartialAsync()此处调用需要2个参数,一个是局部视图的名称,一个是数据对象;
(2).我们还在Shared文件夹下新建一个视图
_Nav.cshtml代码改为:
@using RjWebCms.Models.Roles; @model RoleMenuView[] @if(Model !=null) { foreach (var item in Model) { <li class="nav-item"> <a class="nav-link text-dark" asp-action="@item.MenuUrl">@item.MenuName</a> </li> } } @*<li class="nav-item"> <a class="nav-link text-dark" asp-area="" asp-controller="ManagerRole" asp-action="Index">角色管理1</a> </li> <li class="nav-item"> <a class="nav-link text-dark" asp-area="" asp-controller="Menu" asp-action="Index">菜单管理1</a> </li> <li class="nav-item"> <a class="nav-link text-dark" asp-area="" asp-controller="Article" asp-action="Index">文章管理1</a> </li>*@
其实这段代码就是从原来的_Layout.cshtml中拷贝过来的,然后在_Nav.cshtml中需要改成动态输出。
(3). Html.PartialAsync()的第二个参数数据模型是这样生成的
(3.1)在HomeController.cs中的Index的Action中,定义了一个ViewBag数据,当然你改成ViewData也行的通。
(3.2)补充好_rolePermissionService.GetRoleMenus(int.Parse(RoleId));对应的函数,注意,在HomeController中注入RolePermissionService
(3.3).在RolePermissionService.cs中添加一个GetRoleMenus(int roleid)函数,目的就是找出该角色的菜单列表来。
注意,这里为菜单的需要,建了一个RoleMenuView的ModelView对象,物理表RolePermission只是存储2个关键字段,RoleId和MenuId,无法满足我们同时还需要名称,Url等信息。
public class RoleMenuView { public int RoleId { get; set; } public string RoleName { get; set; } public int MenuId { get; set; } public string MenuName { get; set; } public string MenuUrl { get; set; } }
可以把第三步的过程反过来看,就也能进一步了解导航动态实现的效果。
(4). Component.InvokeAsync()方式,实现动态菜单。
(4.1).第三步中的RoleMenuView类,还有RolePermissionService中的GetRoleMenus()保持不变。
(4.2)在项目根目录中新加一个文件夹:Components,里面新建一个类文件:NavViewCompoent.cs.
代码为(注意:ViewComponent(Name = "Navigation")的声明,Name值就是_Layout中引用的名称):
[ViewComponent(Name = "Navigation")] public class NavViewCompoent : ViewComponent { private readonly IRolePermissionService _rolePermissionService; public NavViewCompoent(IRolePermissionService rolePermissionService) { _rolePermissionService = rolePermissionService; } public IViewComponentResult Invoke() { if (User.Identity.IsAuthenticated) { string RoleId = HttpContext.User.FindFirst(ClaimTypes.Role).Value;//读取用户的Claims信息 var menus = _rolePermissionService.GetRoleMenus(int.Parse(RoleId)); //手动指定了路径的方式,如果是默认:return View(menus);则回自动寻找路径 return View($"~/Views/Shared/Components/Navigation.cshtml", menus); } else return View($"~/Views/Shared/Components/Navigation.cshtml"); } }
(4.3).在Views/Shared/文件夹下(注意,如果采用默认路径,需要这样)再建文件夹Navigation,然后在其内建个default.cshtml的视图文件(代码与上面第三步的_Nav.cshtml的代码一样),用来编写像上面第3步中的_Nav.cshtml的导航文件。我是采用手动指定路径的方式,见上面NavViewCompoent的代码中的return view(),我手动指定了返回路径。
否则,就是报错:默认路径不对的错误!
(4.4).在_Layout.cshtml中的代码为:
InvokeAsync()的参数就是[ViewComponent(Name = "Navigation")]的name值。
效果如下:
实现过程的注意事项:
- 新建了一个专门的ViewModel用来展示导航菜单:RoleMenuView
- Controller与_Layout.cshtml之间的传值,用了ViewBag,要注意类型的一致性;
- 如果是列出二级菜单,注意将RoleMenuView中添加一个子菜单集合,参考将下拉框的递归功能代码。
- 列出的菜单,将系统管理这样的没有Url的都列出了,可以通过linq条件再做次筛选。
关于Partial View(局部视图)和Compoent View(视图组件)的区别,网上有很多信息,百度,bing一下就有很多网友的讲解。