.net core3.1 abp动态菜单和动态权限(动态菜单实现和动态权限添加) (三)

我们来创建动态菜单吧

首先,先对动态菜单的概念、操作、流程进行约束:1.Host和各个Tenant有自己的自定义菜单2.Host和各个Tenant的权限与自定义菜单相关联2.Tenant有一套默认的菜单,规定对应的TenantId=-1,在添加租户时自动将标准菜单和标准菜单的权限初始化到添加的租户

一、先实现菜单在数据库中的增删改查

第一步:创建表、实体,添加DbContext

我们需要创建一个菜单表,延续Abp的命名方法,表名叫AbpMenus吧(菜单和权限、验证我们要关联,所以文件尽量放在Authorization文件夹下)

把创建的实体放在AbpLearn.Core/Authorization下面,新建一个Menus文件夹,再创建Menus实体

public class AbpMenus : Entity
    {
        public string MenuName { set; get; }
        public string PageName { set; get; }
        public string Name { set; get; }
        public string Url { set; get; }
        public string Icon { set; get; }
        public int ParentId { set; get; }
        public bool IsActive { set; get; }
        public int Orders { set; get; }
        public int? TenantId { set; get; }
    }
如果翻过源码中实体的定义,可以发现很多实体的继承,例如:1.继承接口 IMayHaveTenant,继承后生成的sql语句将自动增加TenantId的查询条件,表中必须包含TenantId列2.继承接口 IPassivable,继承后表中必须包含IsActive列3.继承接口 FullAuditedEntity TPrimaryKey可以是long、int等值类型,必须包含IsDeleted、DeleterUserId、DeletionTime,其中这个接口        还继承了AuditedEntity, IFullAudited, IAudited, ICreationAudited, IHasCreationTime, IModificationAudited, IHasModificationTime, IDeletionAudited, IHasDeletionTime, ISoftDelete,这些父类型、接口的定义自己F12就可以看到

AbpLearn.EntityFrameworkCore/EntityFrameworkCore/AbpLearnDbContext.cs增加DbSet

public class AbpLearnDbContext : AbpZeroDbContext
    {
        /* Define a DbSet for each entity of the application */

        public AbpLearnDbContext(DbContextOptions options)
            : base(options)
        {

        }

        public DbSet AbpMenus { set; get; }

    }

再去数据库中添加AbpMenus表 字段长度请自行调整

DROP TABLE IF EXISTS AbpMenus;
CREATE TABLE AbpMenus (
Id int NOT NULL AUTO_INCREMENT,
MenuName varchar(50) DEFAULT NULL,
PageName varchar(50) DEFAULT NULL,
LName varchar(50) DEFAULT NULL,
Url varchar(50) DEFAULT NULL,
Icon varchar(20) DEFAULT NULL,
ParentId int DEFAULT NULL,
IsActive bit(1) NOT NULL DEFAULT b’0′,
Orders int DEFAULT NULL,
TenantId int DEFAULT NULL,
PRIMARY KEY (Id)
) ENGINE=InnoDB AUTO_INCREMENT=2 DEFAULT CHARSET=utf8;

第二步:添加Service和Dto

AbpLearn.Application/Authorization下添加Menus文件夹,然后添加IMenusAppService、MenusAppService,然后添加Dto文件夹

.net core3.1 abp动态菜单和动态权限(动态菜单实现和动态权限添加)  (三)

第三步:添加控制器和前台页面、js

Controller文件,MenusController.cs

.net core3.1 abp动态菜单和动态权限(动态菜单实现和动态权限添加)  (三)

前台添加Menus及对应的js文件,可以简单省事的把其他文件夹复制粘贴一份,然后关键词修改下

.net core3.1 abp动态菜单和动态权限(动态菜单实现和动态权限添加)  (三)

这些文件太多了,我会把这套代码上传到github中,文章最低部会把链接挂出来

.net core3.1 abp动态菜单和动态权限(动态菜单实现和动态权限添加)  (三)

此时, 我们的菜单这一块的crud已经做好了,我们可以看到有一个Host管理员这个部分是什么意思哪?

我们为了在当前Host中可以控制所有租户的菜单和权限,将当前Host、标准菜单、租户做一个select,代码如下

public class ChangeModalViewModel
    {
        public int? TenantId { get; set; }

        public string TenancyName { get; set; }

        public int? TenantMenuType { get; set; }

        public List TeneacyItems { get; set; }
    }
public async Task IndexAsync(int? id = 0)
        {
            var loginTenant = id 0 ? null : _tenantManager.GetById((int)id);

            var viewModel = new ChangeModalViewModel
            {
                TenancyName = loginTenant?.TenancyName,
                TenantId = id
            };

            viewModel.TeneacyItems = _tenantManager.Tenants
                .Select(p => new ComboboxItemDto(p.Id.ToString(), p.Name) { IsSelected = viewModel.TenancyName == p.TenancyName })
                .ToList();

            viewModel.TeneacyItems.Add(new ComboboxItemDto("0","Host管理员") { IsSelected = id == 0 });

            viewModel.TeneacyItems.Add(new ComboboxItemDto("-1", "默认菜单") { IsSelected = id == -1 });

            ViewBag.LoginInfo = await _sessionAppService.GetCurrentLoginInformations();

            return View(viewModel);
        }

然后在Index.cshtml中添加或修改

@model ChangeModalViewModel  // 添加

@await Html.PartialAsync(“~/Views/Menus/Index.AdvancedSearch.cshtml”, Model) //修改

@await Html.PartialAsync(“~/Views/Menus/_CreateModal.cshtml”,Model.TenantId) //修改

//添加

$(“#ChangeTenancyName”).change(function (e) {
location.href = “/Menus/Index/” + this.options[this.selectedIndex].value;
});

修改_CreateModal.cshtml

.net core3.1 abp动态菜单和动态权限(动态菜单实现和动态权限添加)  (三).net core3.1 abp动态菜单和动态权限(动态菜单实现和动态权限添加)  (三)
@using Abp.Authorization.Users
@using Abp.MultiTenancy
@using AbpLearn.MultiTenancy
@using AbpLearn.Web.Models.Common.Modals
@model int
@{
    Layout = null;
}
class="modal fade" id="MenuCreateModal" tabindex="-1" role="dialog" aria-labelledby="MenuCreateModalLabel" data-backdrop="static">
    class="modal-dialog modal-lg" role="document">
        class="modal-content">
            @await Html.PartialAsync("~/Views/Shared/Modals/_ModalHeader.cshtml", new ModalHeaderViewModel(L("CreateNewMenu")))
            "systemMenuCreateForm" role="form" class="form-horizontal">
                class="modal-body">
                    class="form-group row required">
                        class="col-md-3 col-form-label">@L("MenuName")
                        class="col-md-9">
                            "text" name="MenuName" class="form-control" required minlength="2">

                    class="form-group row required">
                        class="col-md-3 col-form-label">@L("LName")
                        class="col-md-9">
                            "text" name="LName" class="form-control" required>

                    class="form-group row required">
                        class="col-md-3 col-form-label">@L("Url")
                        class="col-md-9">
                            "text" name="Url" class="form-control">

                    class="form-group row">
                        class="col-md-3 col-form-label">@L("PageName")
                        class="col-md-9">
                            "text" name="PageName" class="form-control">

                    class="form-group row">
                        class="col-md-3 col-form-label">@L("ParentId")
                        class="col-md-9">
                            "text" name="ParentId" class="form-control">

                    class="form-group row">
                        class="col-md-3 col-form-label">@L("Orders")
                        class="col-md-9">
                            "text" name="Orders" class="form-control">

                    class="form-group row">
                        class="col-md-3 col-form-label" for="CreateMenuIsActive">@L("IsActive")
                        class="col-md-9">
                            "CreateMenuIsActive" type="checkbox" name="IsActive" value="true" checked />

                "hidden" name="TenantId" value="@(Model)" />
                @await Html.PartialAsync("~/Views/Shared/Modals/_ModalFooterWithSaveAndCancel.cshtml")

View Code

修改_EditModal.cshtml

.net core3.1 abp动态菜单和动态权限(动态菜单实现和动态权限添加)  (三).net core3.1 abp动态菜单和动态权限(动态菜单实现和动态权限添加)  (三)
@using AbpLearn.Authorization.Menus.Dto
@using AbpLearn.Web.Models.Common.Modals
@model MenuDto
@{
    Layout = null;
}
@await Html.PartialAsync("~/Views/Shared/Modals/_ModalHeader.cshtml", new ModalHeaderViewModel(L("EditMenu")))
"MenuEditForm" role="form" class="form-horizontal"> "hidden" name="Id" value="@Model.Id" />
class="modal-body">
class="form-group row required">
class="col-md-9"> "tenancy-name" type="text" class="form-control" name="MenuName" value="@Model.MenuName" required maxlength="64" minlength="2">
class="form-group row required">
class="col-md-9"> "name" type="text" class="form-control" name="LName" value="@Model.LName" required maxlength="128">
class="form-group row required">
class="col-md-9"> "name" type="text" class="form-control" name="Url" value="@Model.Url" required maxlength="128">
    <div>class=<span>&quot;</span><span>form-group row required</span><span>&quot;</span>&gt;
        <label>class=<span>&quot;</span><span>col-md-3 col-form-label</span><span>&quot;</span> <span>for</span>=<span>&quot;</span><span>name</span><span>&quot;</span>&gt;@L(<span>&quot;</span><span>PageName</span><span>&quot;</span>)</label>
        <div>class=<span>&quot;</span><span>col-md-9</span><span>&quot;</span>&gt;
            <input>&quot;<span>name</span><span>&quot;</span> type=<span>&quot;</span><span>text</span><span>&quot;</span> <span>class</span>=<span>&quot;</span><span>form-control</span><span>&quot;</span> name=<span>&quot;</span><span>PageName</span><span>&quot;</span> value=<span>&quot;</span><span>@Model.PageName</span><span>&quot;</span> required maxlength=<span>&quot;</span><span>128</span><span>&quot;</span>&gt;
        </div>
    </div>
    <div>class=<span>&quot;</span><span>form-group row required</span><span>&quot;</span>&gt;
        <label>class=<span>&quot;</span><span>col-md-3 col-form-label</span><span>&quot;</span> <span>for</span>=<span>&quot;</span><span>name</span><span>&quot;</span>&gt;@L(<span>&quot;</span><span>ParentId</span><span>&quot;</span>)</label>
        <div>class=<span>&quot;</span><span>col-md-9</span><span>&quot;</span>&gt;
            <input>&quot;<span>name</span><span>&quot;</span> type=<span>&quot;</span><span>text</span><span>&quot;</span> <span>class</span>=<span>&quot;</span><span>form-control</span><span>&quot;</span> name=<span>&quot;</span><span>ParentId</span><span>&quot;</span> value=<span>&quot;</span><span>@Model.ParentId</span><span>&quot;</span> required maxlength=<span>&quot;</span><span>128</span><span>&quot;</span>&gt;
        </div>
    </div>
    <div>class=<span>&quot;</span><span>form-group row required</span><span>&quot;</span>&gt;
        <label>class=<span>&quot;</span><span>col-md-3 col-form-label</span><span>&quot;</span> <span>for</span>=<span>&quot;</span><span>name</span><span>&quot;</span>&gt;@L(<span>&quot;</span><span>Orders</span><span>&quot;</span>)</label>
        <div>class=<span>&quot;</span><span>col-md-9</span><span>&quot;</span>&gt;
            <input>&quot;<span>name</span><span>&quot;</span> type=<span>&quot;</span><span>text</span><span>&quot;</span> <span>class</span>=<span>&quot;</span><span>form-control</span><span>&quot;</span> name=<span>&quot;</span><span>Orders</span><span>&quot;</span> value=<span>&quot;</span><span>@Model.Orders</span><span>&quot;</span> required maxlength=<span>&quot;</span><span>128</span><span>&quot;</span>&gt;
        </div>
    </div>
    <div>class=<span>&quot;</span><span>form-group row</span><span>&quot;</span>&gt;
        <label>class=<span>&quot;</span><span>col-md-3 col-form-label</span><span>&quot;</span> <span>for</span>=<span>&quot;</span><span>isactive</span><span>&quot;</span>&gt;@L(<span>&quot;</span><span>IsActive</span><span>&quot;</span>)</label>
        <div>class=<span>&quot;</span><span>col-md-9</span><span>&quot;</span>&gt;
            <input>&quot;<span>isactive</span><span>&quot;</span> type=<span>&quot;</span><span>checkbox</span><span>&quot;</span> name=<span>&quot;</span><span>IsActive</span><span>&quot;</span> value=<span>&quot;</span><span>true</span><span>&quot;</span> @(Model.IsActive ? <span>&quot;</span><span>checked</span><span>&quot;</span> : <span>&quot;&quot;</span>) /&gt;
        </div>
    </div>
</div><span>
@await Html.PartialAsync(</span><span>&quot;</span><span>~/Views/Shared/Modals/_ModalFooterWithSaveAndCancel.cshtml</span><span>&quot;</span><span>)

View Code

修改Index.AdvancedSearch.cshtml

@using AbpLearn.Web.Views.Shared.Components.TenantChange
@using Abp.Application.Services.Dto
@model ChangeModalViewModel

    class="abp-advanced-search">
        "MenusSearchForm" class="form-horizontal">
            "hidden" name="TenantId" value="@Model.TenantId" />

            class="form-horizontal">
                class="form-group">
                    @Html.DropDownList(
                       "ChangeTenancyNames",
                       Model.TeneacyItems.Select(i => i.ToSelectListItem()),
                       new { @class = "form-control edited", id = "ChangeTenancyName" })

因为在abp里面加载当前列表调用的是abp.services.app.menus.getAll方法,我们还需要对MenusAppService中的GetAllAsync做一下修改

[Serializable]
    public class MenusPagedResultRequestDto: PagedResultRequestDto, IPagedAndSortedResultRequest
    {
        public virtual int? TenantId { get; set; }

        public virtual string Sorting { get; set; }

        public virtual bool ShowAll { get; set; }

    }
#region 查询全部菜单
        ///
        /// 查询全部菜单
        ///
        ///
        ///
        public override async Task> GetAllAsync(MenusPagedResultRequestDto input)
        {
            IQueryable query;

            query = CreateFilteredQuery(input).Where(o => o.TenantId == (input.TenantId == 0 ? null : input.TenantId));

            var totalCount = await AsyncQueryableExecuter.CountAsync(query);

            query = ApplySorting(query, input);
            if (!input.ShowAll) query = ApplyPaging(query, input);

            var entities = await AsyncQueryableExecuter.ToListAsync(query);

            return new PagedResultDto(
                totalCount,
                entities.Select(MapToEntityDto).ToList()
            );
        }

        #endregion

这样,我们在选中下面中的任意一个Tenant时,将会跳到对应的菜单里面了

.net core3.1 abp动态菜单和动态权限(动态菜单实现和动态权限添加)  (三)

我们先把Host管理员菜单和默认菜单配置一下

.net core3.1 abp动态菜单和动态权限(动态菜单实现和动态权限添加)  (三)

.net core3.1 abp动态菜单和动态权限(动态菜单实现和动态权限添加)  (三)

二、实现添加租户时,初始化标准菜单和权限

首先我们找到添加租户的地方,去TenantAppService里面去找,可以看到有CreateAsync的重写

public override async Task CreateAsync(CreateTenantDto input)
        {
            CheckCreatePermission();

            // Create tenant
            var tenant = ObjectMapper.Map(input);
            tenant.ConnectionString = input.ConnectionString.IsNullOrEmpty()
                ? null
                : SimpleStringCipher.Instance.Encrypt(input.ConnectionString);

            var defaultEdition = await _editionManager.FindByNameAsync(EditionManager.DefaultEditionName);
            if (defaultEdition != null)
            {
                tenant.EditionId = defaultEdition.Id;
            }

            await _tenantManager.CreateAsync(tenant);
            await CurrentUnitOfWork.SaveChangesAsync(); // To get new tenant's id.

            // Create tenant database
            _abpZeroDbMigrator.CreateOrMigrateForTenant(tenant);

            // We are working entities of new tenant, so changing tenant filter
            using (CurrentUnitOfWork.SetTenantId(tenant.Id))
            {
                // Create static roles for new tenant
                CheckErrors(await _roleManager.CreateStaticRoles(tenant.Id));

                await CurrentUnitOfWork.SaveChangesAsync(); // To get static role ids

                // Grant all permissions to admin role
                var adminRole = _roleManager.Roles.Single(r => r.Name == StaticRoleNames.Tenants.Admin);
                await _roleManager.GrantAllPermissionsAsync(adminRole);

                // Create admin user for the tenant
                var adminUser = User.CreateTenantAdminUser(tenant.Id, input.AdminEmailAddress);
                await _userManager.InitializeOptionsAsync(tenant.Id);
                CheckErrors(await _userManager.CreateAsync(adminUser, User.DefaultPassword));
                await CurrentUnitOfWork.SaveChangesAsync(); // To get admin user's id

                // Assign admin user to role!

                CheckErrors(await _userManager.AddToRoleAsync(adminUser, adminRole.Name));
                await CurrentUnitOfWork.SaveChangesAsync();
            }

            return MapToEntityDto(tenant);
        }

我们需要做的是,在 using (CurrentUnitOfWork.SetTenantId(tenant.Id)) 的内部尾部添加赋予菜单和权限的方法即可

赋予菜单和权限的方法我们分开写,都放在MenusAppService中,

    public interface IMenusAppService : IAsyncCrudAppServiceint, MenusPagedResultRequestDto, CreateMenuDto, MenuDto>
    {
        ///
        /// 赋予默认菜单
        ///
        ///
        ///
        Task GiveMenusAsync(EntityDto<int> input);

        ///
        /// 赋予当前租户Admin角色菜单权限
        ///
        ///
        ///
        Task GivePermissionsAsync(EntityDto<int> input);
    },>
        #region 赋予默认菜单
        public async Task GiveMenusAsync(EntityDto input)
        {
            if (input.Id > 0)
            {
                var tenant = await _tenantManager.GetByIdAsync(input.Id);

                using (_unitOfWorkManager.Current.SetTenantId(tenant.Id))
                {
                    var query = CreateFilteredQuery(new MenusPagedResultRequestDto()).Where(o => o.TenantId == tenant.Id);

                    var systemMenus = await AsyncQueryableExecuter.ToListAsync(query);

                    if (!systemMenus.Any())
                    {
                        query = CreateFilteredQuery(new MenusPagedResultRequestDto()).Where(o => o.TenantId == -1);

                        var defaultMenus = await AsyncQueryableExecuter.ToListAsync(query);
                        if (defaultMenus.Any())
                        {
                            List GetMenusInserts(List abpMenus,int parentId = 0)
                            {
                                List menusInserts = new List();
                                foreach (var entity in abpMenus.Where(o => o.ParentId == parentId))
                                {
                                    var insert = new MenusInsert()
                                    {
                                        LName = entity.LName,
                                        MenuName = entity.MenuName,
                                        PageName = entity.PageName,
                                        Icon = entity.Icon,
                                        Url = entity.Url,
                                        IsActive = entity.IsActive,
                                        Orders = entity.Orders,
                                        ParentId = entity.ParentId,
                                        TenantId = tenant.Id
                                    };
                                    insert.menusInserts = GetMenusInserts(abpMenus, entity.Id);
                                    menusInserts.Add(insert);
                                }
                                return menusInserts;
                            }

                            async Task InsertMenusAsync(List inserts,int parentId = 0)
                            {
                                foreach (var insert in inserts)
                                {
                                    var entity = await CreateAsync(new AbpMenus()
                                    {
                                        LName = insert.LName,
                                        MenuName = insert.MenuName,
                                        PageName = insert.PageName,
                                        Icon = insert.Icon,
                                        Url = insert.Url,
                                        IsActive = insert.IsActive,
                                        Orders = insert.Orders,
                                        ParentId = parentId,
                                        TenantId = tenant.Id
                                    });
                                    if (insert.menusInserts.Any())
                                    {
                                        await InsertMenusAsync(insert.menusInserts, entity.Id);
                                    }
                                }
                            }
                            await InsertMenusAsync(GetMenusInserts(defaultMenus));

                        }
                    }
                }
            }

        }
        #endregion

        #region 赋予当前租户Admin角色菜单权限
        ///
        /// 赋予当前租户Admin角色菜单权限
        ///
        ///
        ///
        public async Task GivePermissionsAsync(EntityDto<int> input)
        {
            if (input.Id > 0)
            {
                var tenant = await _tenantManager.GetByIdAsync(input.Id);

                using (_unitOfWorkManager.Current.SetTenantId(tenant.Id))
                {
                    var adminRoles = await _roleRepository.GetAllListAsync(o => o.Name == StaticRoleNames.Tenants.Admin && o.TenantId == tenant.Id);
                    if (adminRoles.FirstOrDefault() != null)
                    {
                        var adminRole = adminRoles.FirstOrDefault();

                        var query = CreateFilteredQuery(new MenusPagedResultRequestDto()).Where(o => o.TenantId == tenant.Id);

                        var systemMenus = await AsyncQueryableExecuter.ToListAsync(query);

                        var permissions = ConvertTenantPermissions(systemMenus);

                        //await _roleManager.ResetAllPermissionsAsync(adminRole.FirstOrDefault()); //重置授权

                        var active_BatchCount = 10;
                        var active_permissions = ConvertTenantPermissions(systemMenus.Where(o => o.IsActive).ToList());
                        for (int i = 0; i < active_permissions.Count(); i += 10)//每次后移5位
                        {
                            //await _roleManager.SetGrantedPermissionsAsync(adminRole.FirstOrDefault().Id, active_permissions.Take(active_BatchCount).Skip(i));
                            foreach (var notActive_permission in active_permissions.Take(active_BatchCount).Skip(i))
                            {
                                await _roleManager.GrantPermissionAsync(adminRole, notActive_permission);
                            }
                            active_BatchCount += 10;//每次从数组中选出N+10位,skip前N位
                        }

                        var notActive_BatchCount = 10;
                        var notActive_permissions = ConvertTenantPermissions(systemMenus.Where(o => !o.IsActive).ToList());
                        for (int i = 0; i < notActive_permissions.Count(); i += 10)//每次后移5位
                        {
                            foreach (var notActive_permission in notActive_permissions.Take(notActive_BatchCount).Skip(i))
                            {
                                await _roleManager.ProhibitPermissionAsync(adminRole, notActive_permission);
                            }
                            notActive_BatchCount += 10;//每次从数组中选出N+10位,skip前N位
                        }
                    }
                    else
                    {
                        throw new AbpDbConcurrencyException("未获取到当前租户的Admin角色!");
                    }
                }
            }
            else
            {
                var adminRoles = await _roleRepository.GetAllListAsync(o => o.Name == StaticRoleNames.Tenants.Admin && o.TenantId == null);
                if (adminRoles.FirstOrDefault() != null)
                {
                    var adminRole = adminRoles.FirstOrDefault();

                    var query = CreateFilteredQuery(new MenusPagedResultRequestDto()).Where(o => o.TenantId == null || o.TenantId == 0);

                    var systemMenus = await AsyncQueryableExecuter.ToListAsync(query);

                    //await _roleManager.ResetAllPermissionsAsync(adminRole.FirstOrDefault()); //重置授权

                    var active_BatchCount = 10;
                    var active_permissions = ConvertHostPermissions(systemMenus.Where(o => o.IsActive).ToList());
                    for (int i = 0; i < active_permissions.Count(); i += 10)//每次后移5位
                    {
                        //await _roleManager.SetGrantedPermissionsAsync(adminRole.FirstOrDefault().Id, active_permissions.Take(active_BatchCount).Skip(i));
                        foreach (var notActive_permission in active_permissions.Take(active_BatchCount).Skip(i))
                        {
                            await _roleManager.GrantPermissionAsync(adminRole, notActive_permission);
                        }
                        active_BatchCount += 10;//每次从数组中选出N+10位,skip前N位
                    }

                    var notActive_BatchCount = 10;
                    var notActive_permissions = ConvertHostPermissions(systemMenus.Where(o => !o.IsActive).ToList());
                    for (int i = 0; i < notActive_permissions.Count(); i += 10)//每次后移5位
                    {
                        foreach (var notActive_permission in notActive_permissions.Take(notActive_BatchCount).Skip(i))
                        {
                            await _roleManager.ProhibitPermissionAsync(adminRole, notActive_permission);
                        }
                        notActive_BatchCount += 10;//每次从数组中选出N+10位,skip前N位
                    }
                }
            }
        }

        public IEnumerable ConvertTenantPermissions(IReadOnlyList systemMenus)
        {
            return systemMenus.Select(o => new Permission(o.PageName, L(o.MenuName), L(o.LName), MultiTenancySides.Tenant));
        }

        public IEnumerable ConvertHostPermissions(IReadOnlyList systemMenus)
        {
            return systemMenus.Select(o => new Permission(o.PageName, L(o.MenuName), L(o.LName), MultiTenancySides.Host));
        }
        #endregion

TenantAppService.cs中做一下修改

public override async Task CreateAsync(CreateTenantDto input)
        {
            CheckCreatePermission();

            // Create tenant
            var tenant = ObjectMapper.Map(input);
            tenant.ConnectionString = input.ConnectionString.IsNullOrEmpty()
                ? null
                : SimpleStringCipher.Instance.Encrypt(input.ConnectionString);

            var defaultEdition = await _editionManager.FindByNameAsync(EditionManager.DefaultEditionName);
            if (defaultEdition != null)
            {
                tenant.EditionId = defaultEdition.Id;
            }

            await _tenantManager.CreateAsync(tenant);
            await CurrentUnitOfWork.SaveChangesAsync(); // To get new tenant's id.

            // Create tenant database
            _abpZeroDbMigrator.CreateOrMigrateForTenant(tenant);

            // We are working entities of new tenant, so changing tenant filter
            using (CurrentUnitOfWork.SetTenantId(tenant.Id))
            {
                // Create static roles for new tenant
                CheckErrors(await _roleManager.CreateStaticRoles(tenant.Id));

                await CurrentUnitOfWork.SaveChangesAsync(); // To get static role ids

                // Grant all permissions to admin role
                var adminRole = _roleManager.Roles.Single(r => r.Name == StaticRoleNames.Tenants.Admin);
                await _roleManager.GrantAllPermissionsAsync(adminRole);

                // Create admin user for the tenant
                var adminUser = User.CreateTenantAdminUser(tenant.Id, input.AdminEmailAddress);
                await _userManager.InitializeOptionsAsync(tenant.Id);
                CheckErrors(await _userManager.CreateAsync(adminUser, User.DefaultPassword));
                await CurrentUnitOfWork.SaveChangesAsync(); // To get admin user's id

                // Assign admin user to role!

                CheckErrors(await _userManager.AddToRoleAsync(adminUser, adminRole.Name));
                await CurrentUnitOfWork.SaveChangesAsync();

                await _menusAppService.GiveMenusAsync(new EntityDto() { Id = tenant.Id });
                await CurrentUnitOfWork.SaveChangesAsync();

                await _menusAppService.GivePermissionsAsync(new EntityDto() { Id = tenant.Id });
                await CurrentUnitOfWork.SaveChangesAsync();
            }

            return MapToEntityDto(tenant);
        }

现在我们添加租户企业1、企业2

.net core3.1 abp动态菜单和动态权限(动态菜单实现和动态权限添加)  (三)

.net core3.1 abp动态菜单和动态权限(动态菜单实现和动态权限添加)  (三)

现在菜单已经同步好了,我们去数据库看下权限的同步

.net core3.1 abp动态菜单和动态权限(动态菜单实现和动态权限添加)  (三)

TenantId:

null是Host

1是abp页面第一次加载时初始化的Default租户

2是我之前添加的旧的企业1,那个时候方法没写好,就把2的删掉了

3是企业2

4是企业1

由此可以看出,我们添加的菜单对应的PageName已经作为权限添加到权限表了

三、实现菜单修改后,权限赋予对应租户

这一个其实在二里面已经写好了,前台做一个按钮,赋予权限,调用一下就好了

例如:

Index.cshtml //为什么要加getCurrentLoginInformationsOutput.Tenant == null的判断?是因为租户在进入菜单管理的地方,我们不给他们添加、赋予权限的权限

.net core3.1 abp动态菜单和动态权限(动态菜单实现和动态权限添加)  (三)

在/wwwroot/view-resources/Views/Menus/Index.js中添加

$(document).on('click', '#GivePermissions', function (e) {
        var tenantId = $(this).attr('data-tenant-id');

        abp.message.confirm(
            abp.utils.formatString(
                "是否赋予当前租户管理员账号所有权限?",
                "系统"
            ),
            null,
            (isConfirmed) => {
                if (isConfirmed) {
                    _menuService
                        .givePermissions({
                            id: tenantId
                        })
                        .done(() => {
                            abp.notify.info("操作成功!");
                            _$menusTable.ajax.reload();
                        });
                }
            }
        );
    });

四、实现菜单的动态加载

https://www.cnblogs.com/wangpengzong/p/13089690.html中我们找到了菜单生成的地方,在最底部,通过NavigationManager来获取到Menus,这里其实有一个初始化方法(Initialize ),调用的是AbpLearnNavigationProvider的SetNavigation方法来进行本地化,然后在

NavigationManager的非静态构造函数中去获取已经本地化的Menus,但是本地化Menus因为是在初始化时,程序的初始化我们无法获取到当前的Tenant信息,所以只能将获取Menus的地方推迟,放在倒数第二个类UserNavigationManager里面的GetMenuAsync方法中,我们来看下GetMenuAsync
public async Task GetMenuAsync(string menuName, UserIdentifier user)
        {
            var menuDefinition = _navigationManager.Menus.GetOrDefault(menuName);
            if (menuDefinition == null)
            {
                throw new AbpException("There is no menu with given name: " + menuName);
            }

            var userMenu = new UserMenu(menuDefinition, _localizationContext);
            await FillUserMenuItems(user, menuDefinition.Items, userMenu.Items);
            return userMenu;
        }

第一句话获取menuDefinition是关键点,我们将menuDefinition修改为从数据库中获取,在AbpLearn.Application/Authorization/Menus下添加UserNavigationManager.cs

using Abp;
using Abp.Application.Features;
using Abp.Application.Navigation;
using Abp.Authorization;
using Abp.Dependency;
using Abp.Localization;
using Abp.MultiTenancy;
using Abp.Runtime.Session;
using AbpLearn.Authorization.Menus.Dto;
using AbpLearn.Sessions;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace AbpLearn.Authorization.Menus
{
    public class UserNavigationManager : IUserNavigationManager, ITransientDependency
    {
        public IAbpSession AbpSession { get; set; }

        private readonly INavigationManager _navigationManager;
        private readonly ILocalizationContext _localizationContext;
        private readonly IIocResolver _iocResolver;
        private readonly IMenusAppService _menuAppService;
        private readonly ISessionAppService _sessionAppService;

        public IDictionary Menus { get; private set; }
        public MenuDefinition MainMenu
        {
            get { return Menus["MainMenu"]; }
        }
        public UserNavigationManager(
            INavigationManager navigationManager,
            ILocalizationContext localizationContext,
            IMenusAppService menuAppService,
            ISessionAppService sessionAppService,
            IIocResolver iocResolver)
        {
            _navigationManager = navigationManager;
            _localizationContext = localizationContext;
            _iocResolver = iocResolver;
            AbpSession = NullAbpSession.Instance;
            _menuAppService = menuAppService;
            _sessionAppService = sessionAppService;
        }

        public async Task GetMenuAsync(string menuName, UserIdentifier user)
        {
            var loginInfo = await _sessionAppService.GetCurrentLoginInformations();

            Menus = new Dictionary
                    {
                        {"MainMenu", new MenuDefinition("MainMenu", new LocalizableString("MainMenu", AbpConsts.LocalizationSourceName))}
                    };

            var lists = await _menuAppService.GetAllAsync(new MenusPagedResultRequestDto() { ShowAll = true, TenantId = (loginInfo.Tenant == null ? 0 : loginInfo.Tenant.Id) });
            var ParentMenu = lists.Items.Where(k => k.IsActive).ToList().Where(x => x.ParentId == 0).ToList();
            if (ParentMenu.Any())
            {
                ParentMenu.ForEach(g =>
                {
                    var menu = new MenuItemDefinition(
                          g.LName,
                          MenuL(g.MenuName),
                          g.Icon,
                          g.Url,
                          false,
                          g.Orders
                          );
                    BuildSubMenu(menu, g.Id, lists.Items.Where(k => k.IsActive).ToList());
                    MainMenu.AddItem(menu);
                });
            }

            var menuDefinition = MainMenu;
            if (menuDefinition == null)
            {
                throw new AbpException("There is no menu with given name: " + menuName);
            }
            var userMenu = new UserMenu();
            userMenu.Name = menuDefinition.Name;
            userMenu.DisplayName = menuDefinition.DisplayName.Localize(_localizationContext);
            userMenu.CustomData = menuDefinition.CustomData;
            userMenu.Items = new List();
            await FillUserMenuItems(user, menuDefinition.Items, userMenu.Items);
            return userMenu;
        }

        public async Task> GetMenusAsync(UserIdentifier user)
        {
            var userMenus = new List();

            foreach (var menu in _navigationManager.Menus.Values)
            {
                userMenus.Add(await GetMenuAsync(menu.Name, user));
            }

            return userMenus;
        }
        public void BuildSubMenu(MenuItemDefinition menu, int parentId, List list)
        {
            var nList = list.Where(x => x.ParentId == parentId).ToList();
            if (nList != null && nList.Count > 0)
            {
                nList.ForEach(g =>
                {
                    var subMenu = new MenuItemDefinition(
                         g.PageName,
                        MenuL(g.MenuName),
                        g.Icon,
                        g.Url,
                        false,
                      g.Orders
                        );
                    menu.AddItem(subMenu);
                    BuildSubMenu(subMenu, g.Id, list);
                });
            }
        }

        private static ILocalizableString MenuL(string name)
        {
            return new LocalizableString(name, AbpLearnConsts.LocalizationSourceName);
        }
        private async Task FillUserMenuItems(UserIdentifier user, IList menuItemDefinitions, IList userMenuItems)
        {
            //TODO: Can be optimized by re-using FeatureDependencyContext.

            var addedMenuItemCount = 0;

            using (var scope = _iocResolver.CreateScope())
            {
                var permissionDependencyContext = scope.Resolve();
                permissionDependencyContext.User = user;

                var featureDependencyContext = scope.Resolve();
                featureDependencyContext.TenantId = user == null ? null : user.TenantId;

                foreach (var menuItemDefinition in menuItemDefinitions)
                {
                    if (menuItemDefinition.RequiresAuthentication && user == null)
                    {
                        continue;
                    }

                    if (menuItemDefinition.PermissionDependency != null &&
                        (user == null || !(await menuItemDefinition.PermissionDependency.IsSatisfiedAsync(permissionDependencyContext))))
                    {
                        continue;
                    }

                    if (menuItemDefinition.FeatureDependency != null &&
                        (AbpSession.MultiTenancySide == MultiTenancySides.Tenant || (user != null && user.TenantId != null)) &&
                        !(await menuItemDefinition.FeatureDependency.IsSatisfiedAsync(featureDependencyContext)))
                    {
                        continue;
                    }

                    var userMenuItem = new UserMenuItem(menuItemDefinition, _localizationContext);
                    if (menuItemDefinition.IsLeaf || (await FillUserMenuItems(user, menuItemDefinition.Items, userMenuItem.Items)) > 0)
                    {
                        userMenuItems.Add(userMenuItem);
                        ++addedMenuItemCount;
                    }
                }
            }

            return addedMenuItemCount;
        }
    }
}

然后在Mvc项目的Startup.cs/ConfigureServices下增加

services.AddScoped();

因为在abp中菜单被做做成了模块,在程序初始化时模块添加进去,但是我们将菜单修改成了每次读取数据库加载,那么我们就不需要加载这个模块了

在mvc项目的AbpLearnWebMvcModule.cs注释下面这句话

//Configuration.Navigation.Providers.Add();

将AbpLearnNavigationProvider.cs/SetNavigation方法的内容全部注释掉

预览一下mvc,用Host登录一下

.net core3.1 abp动态菜单和动态权限(动态菜单实现和动态权限添加)  (三)

用企业1登陆下,登录切换Host和Tenant,是在登录界面 Current tenant:未选() 点击Change,在弹框中输入 E1(因为上面设置的企业1标识是E1),点击save,页面刷新后就变为了Current tenant:E1 () ,输入账号密码登录

.net core3.1 abp动态菜单和动态权限(动态菜单实现和动态权限添加)  (三)

OK,我们的动态菜单已经完成了

添加jstree

当然,我的菜单使用的是table来显示,你也可以使用tree来,我找到了一个jstree,下面修改一下

MenusAppService.cs

        #region 获取当前账户的菜单树
        ///
        /// 获取当前账户的菜单树
        ///
        ///
        ///
        public async Task<string> GetTreeAsync(MenusPagedResultRequestDto input)
        {
            var query = CreateFilteredQuery(new MenusPagedResultRequestDto()).Where(o => o.TenantId == input.TenantId);

            var systemMenus = await AsyncQueryableExecuter.ToListAsync(query);

            var childJObject = new JObject();
            var openJObject = new JObject();
            openJObject.Add("opened", true);
            childJObject.Add("id", 0);
            childJObject.Add("text", "根目录");
            childJObject.Add("icon", "");
            childJObject.Add("state", openJObject);
            childJObject.Add("children", GetJArray(systemMenus, 0));
            return childJObject.ToString();
        }

        #region 获取目录Array
        ///
        /// 获取目录Array
        ///
        ///
        ///
        ///
        private JArray GetJArray(List systemMenus, int parentdId)
        {
            JArray jArray = new JArray();
            foreach (var menu in systemMenus.Where(o => o.ParentId == parentdId))
            {
                var jObject = new JObject();
                jObject.Add("id", menu.Id);
                jObject.Add("text", menu.MenuName);
                jObject.Add("icon", menu.Icon);
                //jObject.Add("state", menu.Icon);
                if (systemMenus.Any(o => o.ParentId == menu.Id))
                {
                    jObject.Add("children", GetJArray(systemMenus, menu.Id));
                }
                jArray.Add(jObject);
            }
            return jArray;
        }

        #endregion

        #endregion

前端Index.cshtml jstree去https://github.com/vakata/jstree/zipball/3.3.8下载,下载后在mvc项目的wwwroot文件夹下添加jstree文件夹,下载文件的src里面内容全部赋值到jstree文件夹

注释掉table标签

添加jstree1

例如:

@section styles
{

}                            "jstree1" style="width:100%;">

@section scripts
{

        $(function () {
            var _menuService = abp.services.app.menus;

            l = abp.localization.getSource('A_b_p');

            $('#jstree1').jstree({
                "core": {
                    "data": function (node, callback) {
                        var filter = $('#MenusSearchForm').serializeFormToObject(true);
                        this, _menuService.getTree(filter).done(function (result) {
                            callback.call(this, JSON.parse(result));
                        });
                    },
                    "themes": {
                        "variant": "large",//加大
                        "ellipsis": true //文字多时省略
                    },
                    "check_callback": true,
                },
                "plugins": ["contextmenu", "wholerow", "themes"],//"checkbox"
                "contextmenu": {
                    select_node: false,
                    show_at_node: true,
                    "items": {
                        "create": {
                            "label": "新增子菜单",
                            "action": function (obj) {
                                var inst = jQuery.jstree.reference(obj.reference);
                                var clickedNode = inst.get_node(obj.reference);
                                if (parseInt(clickedNode.original.id) >= 0) {
                                    $("#ParentId").val(clickedNode.original.id);
                                    $("#MenuCreateModal").modal();
                                } else {
                                    abp.notify.info("父节点获取出错");
                                }
                            },
                        },
                        "rename": {
                            "label": "修改",
                            "action": function (obj) {
                                var inst = jQuery.jstree.reference(obj.reference);
                                var clickedNode = inst.get_node(obj.reference);
                                if (parseInt(clickedNode.original.id) >= 0) {
                                    abp.ajax({
                                        url: abp.appPath + 'Menus/EditModal?menuId=' + clickedNode.original.id,
                                        type: 'POST',
                                        dataType: 'html',
                                        success: function (content) {
                                            $("#MenuEditModal").modal();
                                            $('#MenuEditModal div.modal-content').html(content);
                                        },
                                        error: function (e) { }
                                    });
                                } else {
                                    abp.notify.info("菜单获取出错");
                                }
                            }
                        },
                        "delete": {
                            "label": "更改菜单状态",
                            "action": function (obj) {
                                var inst = jQuery.jstree.reference(obj.reference);
                                var clickedNode = inst.get_node(obj.reference);
                                abp.message.confirm(
                                    abp.utils.formatString("是否" + (clickedNode.original.state.disabled?"启用":"禁用") + "当前菜单:" + clickedNode.original.text + "?"),
                                    null,
                                    (isConfirmed) => {
                                        if (isConfirmed) {
                                            _menuService
                                                .delete({
                                                    id: clickedNode.original.id
                                                })
                                                .done(() => {
                                                    abp.notify.info(l('SuccessfullyDeleted'));
                                                    location.reload();
                                                });
                                        }
                                    }
                                );
                            },

                        }
                    }
                }
            }).on('select_node.jstree', function (event, data) {
                console.log(data.node);
            }).on('changed.jstree', function (event, data) {
                console.log("-----------changed.jstree");
                console.log("action:" + data.action);
                console.log(data.node);
            });

        });

}

预览一下吧

.net core3.1 abp动态菜单和动态权限(动态菜单实现和动态权限添加)  (三)

github地址

本文github:https://github.com/wangpengzong/AbpLearn

下一篇开始动态权限

吐槽区域(写的不好、不对,欢迎吐槽)

Original: https://www.cnblogs.com/spatxos/p/13096427.html
Author: spatxos
Title: .net core3.1 abp动态菜单和动态权限(动态菜单实现和动态权限添加) (三)

原创文章受到原创版权保护。转载请注明出处:https://www.johngo689.com/582926/

转载文章受原作者版权保护。转载请注明原作者出处!

(0)

大家都在看

  • IDEA 无法显示 Run Dashboard 的解决方法

    前言 最近开始接触微服务的项目,项目很多有时候本地调测需要启动多个项目,看到同事都是使用dashboard管理项目,服务和端口排列的整整齐齐。但是我dashboard里面啥都没有,…

    Linux 2023年6月14日
    0122
  • JuiceFS V1.0 RC1 发布,大幅优化 dump/load 命令性能, 深度用户不容错过

    各位社区的伙伴, JuiceFS v1.0 RC1 今天正式发布了! 这个版本中,最值得关注的是对元数据迁移备份工具 dump/load 的优化。 这个优化需求来自于某个社区重度用…

    Linux 2023年6月14日
    084
  • SpringCloud gateway自定义请求的 httpClient

    本文为博主原创,转载请注明出处: 引用 的 spring cloud gateway 的版本为 2.2.5 ; SpringCloud gateway 在实现服务路由并请求的具体过…

    Linux 2023年6月14日
    0101
  • Pytorch 中 tensor的维度拼接

    torch.stack() 和 torch.cat() 都可以按照指定的维度进行拼接,但是两者也有区别,torch.satck() 是 增加新的维度进行堆叠,即其维度拼接后会增加一…

    Linux 2023年6月7日
    0106
  • 手把手教你设置MongoDB密码

    mongodb密码和传统数据如mysql等有些区别: mongodb的用户名和密码是基于特定数据库的,而不是基于整个系统的。所有所有数据库db都需要设置密码。 1. 查看所有数据库…

    Linux 2023年6月14日
    0107
  • python装饰器(新年第一写)

    祭奠碌碌无为的2018,想想其实也不算碌碌无为,至少我还搞懂了装饰器,写了一堆有用没用的玩意 原来觉得装饰器挺难的,直到2018年的最后几天,突然就明白了,难道这就是传说中的开天聪…

    Linux 2023年6月6日
    0105
  • Spring5 学习笔记

    学习地址: B站-动力节点 个人代码: GitHub Spring 概述 1.1 Spring 简介 Spring Framework 是一个使用Java开发的、轻量级的、开源框架…

    Linux 2023年6月14日
    095
  • haproxy服务部署

    haproxy haproxy 一、haproyx是什么 二、负载均衡类型 三、部署haproxy 1.源码部署haproxy 2.Haproxy搭建http负载均衡 一、hapr…

    Linux 2023年6月6日
    0116
  • 18-网络七层架构

    七层架构主要包括 ①、 物理层 主要定义物理设备标准,如网线的接口类型、光纤的接口类型、各种传输介质的传输速率等。它的主要作用是传输比特流(就是由 1、0 转化为电流强弱来进行传输…

    Linux 2023年6月7日
    0131
  • 抑制stable_secret读取关键信息

    404. 抱歉,您访问的资源不存在。 可能是网址有误,或者对应的内容被删除,或者处于私有状态。 代码改变世界,联系邮箱 contact@cnblogs.com 园子的商业化努力-困…

    Linux 2023年6月7日
    0115
  • 新年伊始我的centos8没法更新了

    22年春节后centos8竟然没法更新了,提示 No URLs in mirrorlist如下: yum update Repository extras is listed mo…

    Linux 2023年6月13日
    0242
  • Ubuntu2004使用dnsmasq+Clash+Iptables+Ipset

    404. 抱歉,您访问的资源不存在。 可能是网址有误,或者对应的内容被删除,或者处于私有状态。 代码改变世界,联系邮箱 contact@cnblogs.com 园子的商业化努力-困…

    Linux 2023年6月13日
    089
  • 2. 文件与I/O

    文件与I/o open 系统调用 close 系统调用 creat 系统调用 read 系统调用 write 系统调用 open&#x7CFB;&#x7EDF;&a…

    Linux 2023年6月6日
    097
  • Redis主从复制

    一、主从复制 1、redis为什么还要主从复制? 和Mysql主从复制的原因一样,Redis虽然读取写入的速度都特别快,但是也会产生读压力特别大的情况。为了分担读压力,Redis支…

    Linux 2023年5月28日
    078
  • Linux虚拟机追加扩展磁盘

    一、使用VMware给虚拟机追加磁盘 使用VMware打开虚拟机设置对话框,选择硬盘,点击右侧的扩展按钮,输入扩展后的磁盘容量。 点击扩展按钮。提示磁盘已成功扩展。 二、对闲置的空…

    Linux 2023年6月6日
    0124
  • 正则表达式 8. 特殊限制(环视否定)

    https://www.zybuluo.com/Zjmainstay/note/709093 特殊限制(环视否定) (8.1)使用\d{1,3}匹配1-999的数据,不能以0开头 …

    Linux 2023年6月13日
    0110
亲爱的 Coder【最近整理,可免费获取】👉 最新必读书单  | 👏 面试题下载  | 🌎 免费的AI知识星球