.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)

大家都在看

  • python入门基础知识四(字典与集合)

    dict_name = {key1:value1,key2,value2,…} 空字典:dict_name = {} or dict_name = dict() 字典的…

    Linux 2023年6月7日
    074
  • 1.2

    数字信号为什么不能远程传播?高频率->传的短 容易被干扰 答案是可以的。 数字信号传输编码的目的:保证数据传送的可靠性 数据传输的关键指标: 延迟和吞吐量 posted @2…

    Linux 2023年6月6日
    098
  • 线程池如何保证核心线程一直存活

    转载请注明出处: 查看 ThreadPoolExecutor 类中的 getTask 方法,这个方法可以保持核心线程在没有任务的时候也可以一直处于存活状态 核心在于 workQue…

    Linux 2023年6月14日
    0163
  • Ansible Playbook概览

    Ansible playbook 执行需要三步路执行: 1.编写playbook 2.定义主机清单文件 3.设置运行环境,写入配置文件 1.编写playbook Playbook使…

    Linux 2023年6月6日
    085
  • VR(虚拟现实)开发资源汇总

    Daydream Gear VR Algorithm ATW Bluetooth Blog Latency Tools Touch Unity Qualcomm EGL Origi…

    Linux 2023年6月7日
    095
  • Centos 6 DNS 配置 解决 Unknown host

    测试服务器Maven 打包时遇到了如下的错误 看上去应该是对 maven.aliyun.com的DNS 域名解析出问题了。 登录到服务器上 ping maven.aliyun.co…

    Linux 2023年5月27日
    0108
  • (十)redis源码解读

    一、redis工作机制 redis是 单线程,所有命令(set,get等)都会加入到队列中,然后一个个执行。 二、为什么redis速度快? 1、基于内存 2、redis协议resp…

    Linux 2023年5月28日
    0114
  • linux磁盘配额管理

    磁盘配额是一种磁盘空间的管理机制,使用磁盘配额可限制用户或组在某个特定文件系统中能使用的最大空间 1、查看内核是否支持磁盘配额 grep “CONFIG_QUOTA&#…

    Linux 2023年5月27日
    0104
  • 请求方式

    题目如下 题目描述为请求方式,HTTP的请求方式一共有八种,读者自行去查 打开靶场如下 题目的意思需要以CTF**B为请求方式,由于平台名为CTFHUB,于是试了一下 接着抓包,推…

    Linux 2023年6月7日
    0108
  • (读书笔记)基于CMMI的软件工程及实训指导 第13-16章

    一、软件测试 软件测试是为了发现程序中的错误而执行的过程。测试只能证明软件有错,而不能保证软件程序没错。 1. 软件版本 Alpha版 公司内测版本 Beta版 对外公测版本 发布…

    Linux 2023年6月14日
    088
  • Golang 实现 Redis(5): 使用跳表实现 SortedSet

    本文是使用 golang 实现 redis 系列的第五篇, 将介绍如何使用跳表实现有序集合(SortedSet)的相关功能。 跳表(skiplist) 是 Redis 中 Sort…

    Linux 2023年5月28日
    0102
  • QLabel文字内容行间距

    故事背景:最近做项目升级,需要界面上展示升级更新内容,用QLabel展示,字符串是这样的”1、XXXXXXX;2、XXXXXXX;3、XXXXXXX”,一个…

    Linux 2023年6月13日
    0102
  • 浅谈DDD中的聚合

    DDD分为战略部分跟战术部分,相信大家都认同DDD的核心在战略而非战术。而战略方面的核心我认为在业务建模,领域划分、统一语言等都在为业务建模服务。 为什么业务建模重要? 以前的开发…

    Linux 2023年6月8日
    0105
  • protobuf 的交叉编译使用(C++)

    为了提高通信效率,可以采用 protobuf 替代 XML 和 Json 数据交互格式,protobuf 相对来说数据量小,在进程间通信或者设备之间通信能够提高通信速率。下面介绍 …

    Linux 2023年6月7日
    0148
  • 使用SpringBoot校验客户端传来的数据

    前端的数据校验都是辣鸡!后端天下第一! 如果想完美地贯彻原则, 理论上来说就应该让前端那边少传数据过来, 有些的东西能查的就自己查出来。 常用的数据校验like this: /**…

    Linux 2023年6月14日
    080
  • RNN循环神经网络

    1.为什么还会有RNN? CNN(卷积神经网络)我们会发现, 他们的输出都是只考虑前一个输入的影响而不考虑其它时刻输入的影响, 比如简单的猫,狗,手写数字等单个物体的识别具有较好的…

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