Asp.Net Core 項目實戰之權限管理系統(8) 功能菜單的動態加載


0 Asp.Net Core 項目實戰之權限管理系統(0) 無中生有

1 Asp.Net Core 項目實戰之權限管理系統(1) 使用AdminLTE搭建前端

2 Asp.Net Core 項目實戰之權限管理系統(2) 功能及實體設計

3 Asp.Net Core 項目實戰之權限管理系統(3) 通過EntityFramework Core使用PostgreSQL

4 Asp.Net Core 項目實戰之權限管理系統(4) 依賴注入、倉儲、服務的多項目分層實現

5 Asp.Net Core 項目實戰之權限管理系統(5) 用戶登錄

6 Asp.Net Core 項目實戰之權限管理系統(6) 功能管理

7 Asp.Net Core 項目實戰之權限管理系統(7) 組織機構、角色、用戶權限

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個月了,每天熬夜熬的精疲力盡,工作上也是忙的不可開交,有些朋友給我的留言中的,我有空看到就回復了,有些朋友的問題,有可能我沒時間看到,也有可能實在抽不出時間去幫你找問題所在,還得麻煩您自己多動手實踐一下了,請見諒。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM