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

大家都在看

  • linux自动备份mysql数据库

    备份脚本记录一下–(单个数据库) 2021-11-15 1.新建shell脚本:vim **.sh #!/bin/bashCKUP=/data/backup/db #获…

    Linux 2023年5月27日
    0108
  • 【小记】解决 Windows Update 更新 WSL 内核出现 0x80070643 错误问题 (Windows Subsystem for Linux Update)

    问题原因很简单:之前已经安装的 WSL 内核包因为不明原因损坏了,因为无法在后台卸载,就没有办法安装新的。 问题解决:(Win10、Win11、WinServer2019、WinS…

    Linux 2023年6月13日
    0187
  • Linux 磁盘挂载

    具体操作是: 1.先对磁盘进行格式化; 挂载磁盘到需要的挂载点; 3.添加分区启动表 1、首先查看系统中磁盘信息 命令为:fdisk -l 2、格式化要挂载的磁盘 格式化命令为:m…

    Linux 2023年6月7日
    085
  • docker 安装nextcloud+onlyoffice+mysql

    环境 类目 版本 备注 操作系统 centos 7 64位 Docker 最新版 Docker compose 最新版 暂时没有用上可以不安装 nextcloud 最新版 only…

    Linux 2023年5月27日
    091
  • 配置免密登陆服务器

    前言 原来自己学习的时候在阿里云买自己的学习机,一台主机自己瞎折腾。但是参加工作以后管理的主机越来越多了,上服务器看的频率也越来越频繁,虽然有时候shell管理工具可以很方便的保存…

    Linux 2023年5月27日
    0122
  • redis八种基本数据类型及其应用

    NoSQL 开发中或多或少都会用到,也是面试必问知识点。最近这几天的面试每一场都问到了。但是感觉回答的并不好,还有很多需要梳理的知识点。这里通过几篇 Redis 笔记整个梳理一遍,…

    Linux 2023年5月28日
    081
  • 通过示例学习PYTORCH

    核心是:PyTorch提供了两个主要的特性: 一个n维的Tensor,与Numpy相似但可以在GPU上运行 构建和训练神经网络的自动微分 我们将使用一个三阶多项式拟合 (y=sin…

    Linux 2023年6月14日
    0104
  • 批量新建域用

    前几个章节我们讲到Windows Server 2016-图形化新建域用户(一),本章节我们简单讲解下如何通过命令批量创建域用户,以便高效完成日常工作中实际批量创建用户需求,内容涉…

    Linux 2023年6月13日
    089
  • 手套—牛客网

    left[i] 和right[i] 是匹配的数值 0 和 1 左边0个 右边1个 因为数据中有0这种数值 说明有不能匹配的手套。所以这些手套必须加上 避免没有全拿时 拿到了不匹配的…

    Linux 2023年6月13日
    090
  • LeetCode-补充题9. 36进制加法

    题目来源 题目详情 36进制由0-9,a-z,共36个字符表示。 要求按照加法规则计算出任意两个36进制正整数的和,如1b + 2x = 48 (解释:47+105=152) 要求…

    Linux 2023年6月7日
    098
  • 【EventOS Nano】EventOS Nano初步了解

    EventOS Nano是什么? EventOS Nano是一个面向单片机、事件驱动的、分布式的、可跨平台开发的嵌入式开发平台。主要有两大技术特色: 事件驱动和 超轻量 Event…

    Linux 2023年6月13日
    082
  • 通过配置文件(.htaccess)实现文件上传

    一·什么是服务器配置文件.htaccess许多服务器还允许开发人员在各个目录中创建特殊的配置文件,以便覆盖或添加一个或多个全局设置。例如,Apache 服务器将从一个名为(.hta…

    Linux 2023年6月7日
    074
  • 【设计模式】Java设计模式-命令模式

    Java设计模式 – 命令模式 😄生命不息,写作不止🔥 继续踏上学习之路,学之分享笔记👊 总有一天我也能像各位大佬一样🏆 一个有梦有戏的人 @怒放吧德德🌝分享学习心得,…

    Linux 2023年6月6日
    079
  • 【Linux】【专项突破】CentOS下软件安装

    rpm yum软件仓库 配置文件 缓存处理 清理缓存 重构缓存 查询包的依赖关系 rpm 普通下载安装 rpm -ivh 包名 更新 rpm -Uvh 包全名 查询 rpm -q …

    Linux 2023年6月14日
    0116
  • Ubuntu16.04部署django+nginx项目

    项目使用django+nginx部署。这个项目断断续续地部署4遍了。感觉每次部署都挺费时间的(找各种配置的资料),于是写一个博客总结一下。 安装vsftpd $ sudo apt-…

    Linux 2023年6月7日
    062
  • ajax的发展

    ajax(Asynchronous Javascript and XML)异步javascrip和XMl. ajax只是一种web交互方法。在客户端(浏览器)和服务区段之间传输少量…

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