Asp.Net Core 项目实战之权限管理系统(8) 功能菜单的动态加载

0 Asp.Net Core 项目实战之权限管理系统(0) 无中生有html

1 Asp.Net Core 项目实战之权限管理系统(1) 使用AdminLTE搭建前端前端

2 Asp.Net Core 项目实战之权限管理系统(2) 功能及实体设计git

3 Asp.Net Core 项目实战之权限管理系统(3) 经过EntityFramework Core使用PostgreSQLgithub

4 Asp.Net Core 项目实战之权限管理系统(4) 依赖注入、仓储、服务的多项目分层实现session

5 Asp.Net Core 项目实战之权限管理系统(5) 用户登陆app

6 Asp.Net Core 项目实战之权限管理系统(6) 功能管理异步

7 Asp.Net Core 项目实战之权限管理系统(7) 组织机构、角色、用户权限ide

8 Asp.Net Core 项目实战之权限管理系统(8) 功能菜单的动态加载布局

github源码地址学习

0 服务层实现

系统登陆后,会在session中记录当前登陆用户的信息。

//检查用户信息
 var user = _userAppService.CheckUser(model.UserName, model.Password);
 if (user != null)
 {
     //记录Session
     HttpContext.Session.SetString("CurrentUserId", user.Id.ToString());
     HttpContext.Session.Set("CurrentUser", ByteConvertHelper.Object2Bytes(user));
     //跳转到系统首页
     return RedirectToAction("Index", "Home");
 }

因为一个用户能够拥有多个角色,咱们须要作的是,根据当前登陆用户的Id,获得其拥有的全部角色信息,而后取当前用户全部角色拥有的功能权限求合集,获得的就是当前登陆用户所拥有的功能权限信息。

定义应用服务接口

在IMenuAppService中定义接口

/// <summary>
/// 根据用户获取功能菜单
/// </summary>
/// <param name="userId">用户ID</param>
/// <returns></returns>
List<MenuDto> GetMenusByUser(Guid userId);

服务接口实现

在MenuAppService中实现接口

/// <summary>
 /// 根据用户获取功能菜单
 /// </summary>
 /// <param name="userId">用户ID</param>
 /// <returns></returns>
 public List<MenuDto> GetMenusByUser(Guid userId)
 {
     List<MenuDto> result = new List<MenuDto>();
     var allMenus = _menuRepository.GetAllList(it=>it.Type == 0).OrderBy(it => it.SerialNumber);
     if (userId == Guid.Empty) //超级管理员
         return Mapper.Map<List<MenuDto>>(allMenus);
     var user = _userRepository.GetWithRoles(userId);
     if (user == null)
         return result;
     var userRoles = user.UserRoles;
     List<Guid> menuIds = new List<Guid>();
     foreach (var role in userRoles)
     {
         menuIds = menuIds.Union(_roleRepository.GetAllMenuListByRole(role.RoleId)).ToList();
     }
     allMenus = allMenus.Where(it => menuIds.Contains(it.Id)).OrderBy(it => it.SerialNumber);
     return Mapper.Map<List<MenuDto>>(allMenus);
 }

1 使用ViewComponent动态加载菜单

在之前的Asp.net MVC中,咱们会常用@Html.Action来发起一个ChildAction请求,渲染并获得一个分部视图填充的主功能视图中,以此来实现一些公用的或者是独立的界面区域。在Asp.Net Core中,不在存在将以ViewComponent替代。

你能够将View Component看作是一个mini的Controller——它只负责渲染一小部份内容,而非所有响应,全部分部视图能解决的问题,你均可以使用View Component来解决,好比:动态导航菜单、Tag标签、登陆窗口、购物车、最近阅读文章等等。

1.0 ViewComponent建立

ViewComponent建立很是简单,只须要将咱们的类继承自ViewComponent类就好了。每一个ViewComponent须要包括一个名称为Invoke的约定方法,你能够在此方法中传入你须要的任何参数,系统也支持InvokeAsync方法实现异步功能,此约定方法为该ViewComponent的最终输出出口。

在Fonour.MVC项目中新建一个名称为“Components”的文件夹,用来存放咱们全部的视图组件类,在该文件夹下新建一个名称为NavigationViewComponent的组件。

[ViewComponent(Name = "Navigation")]
public class NavigationViewComponent : ViewComponent
{
    private readonly IMenuAppService _menuAppService;
    private readonly IUserAppService _userAppService;
    public NavigationViewComponent(IMenuAppService menuAppService, IUserAppService userAppService)
    {
        _menuAppService = menuAppService;
        _userAppService = userAppService;
    }

    public IViewComponentResult Invoke()
    {
        var userId = HttpContext.Session.GetString("CurrentUserId");
        var menus = _menuAppService.GetMenusByUser(Guid.Parse(userId));
        return View(menus);
    }
}

1.1 组件对应的视图文件建立

上面实现的约定方法Invoke中,最后Return View(Menus),与咱们控制器中Action返回视图的方法很类似。ViewComponent寻找视图也是遵守约定的。他会自动从如下路径去寻找对应的视图文件。

/Views/[CurrentController]/Components/[NameOfComponent]/Default.cshtml
/Views/Shared/Components/[NameOfComponent]/Default.cshtml

对于某个具体功能对应的组件,咱们最好按照上面第一种路径规则,把对应的视图文件放在功能对应的Controller下面

对于系统级别的视图组件,好比咱们如今要实现的左侧功能导航菜单,建议按照第二种路径规则,放在Views/Shared下面。

在Views/Shared文件夹下新建一个名称为Components的文件夹,而后在该文件夹下建立一个名称为“Navigation”的文件夹,注意这个文件夹名与咱们上面定义的组件名称是要保持一直的。

image

在“Navigation”文件夹下建立一个名称为Default.cshtml的视图页。内容以下:

@model List<Fonour.Application.MenuApp.Dtos.MenuDto>
<li class="header">权限管理</li>
@foreach (var menu in Model)
{
    var isActive = ViewBag.CurrentMenu == menu.Code; //判断当前功能是否处于激活
    <li class="@(isActive ? "active" : "")"><a href="@menu.Url"><i class="fa fa-link"></i> <span>@menu.Name</span></a></li>
}

该视图接受一个在咱们组件类中返回的菜单集合对象,并根据此菜单集合渲染咱们须要的菜单。咱们根据ViewBag.CurrentMenu是否等于当前菜单的编码,来肯定该菜单是否属于激活状态,这样能够实现咱们单击某个菜单界面刷新后,该功能菜单处于激活状态。

基于此,咱们须要在每一个功能页面指定ViewBag.CurrentMenu的值。如用户管理功能,咱们在/Views/User/Index.cshtml顶部添加以下代码便可。

@{
    ViewBag.CurrentMenu = "User";
}

固然,若是你以为这种硬编码的方式你不喜欢,你能够在菜单单击的时候,把当前菜单对应的code值传至服务端,由服务端在返回对应的视图以前,指定ViewBag.CurrentMenu的值便可。

1.2 ViewComponent的使用

咱们修改布局页_Layout.cshtml中菜单加载部分的代码,将写死的功能菜单信息,修改成经过使用ViewComponent根据用户Id动态加载功能菜单。

<ul class="sidebar-menu">
    @await Component.InvokeAsync("Navigation");
    @*<li class="treeview">
            <a href="#">
                <i class="fa fa-link"></i> <span>Multilevel</span>
                <span class="pull-right-container">
                    <i class="fa fa-angle-left pull-right"></i>
                </span>
            </a>
            <ul class="treeview-menu">
                <li><a href="#">Link in level 2</a></li>
                <li><a href="#">Link in level 2</a></li>
            </ul>
        </li>*@
</ul>

此时咱们使用一个只分配了其中三个菜单权限的帐户登陆系统,一切按照咱们预想的,能够根据当前登陆用户动态加载该用户所拥有权限的功能菜单了,并且咱们进入某个功能后,页面刷新后已经能够自动把咱们进入的功能菜单设置为激活状态了。

image

image

2 总结

本次主要学习了ViewComponent的使用,实现了功能菜单的动态加载。

最开始是本着本身学习的态度写这个系列的,内容整体来讲属于基础入门级的,到如今也算是基本功能都已经实现了,里面有不少不足或不完善的地方,能够根据须要进行调整吧。

从孩子出生,转眼间到如今3个月了,天天熬夜熬的精疲力尽,工做上也是忙的不可开交,有些朋友给个人留言中的,我有空看到就回复了,有些朋友的问题,有可能我没时间看到,也有可能实在抽不出时间去帮你找问题所在,还得麻烦您本身多动手实践一下了,请见谅。

相关文章
相关标签/搜索