.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<int> { 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> TPrimaryKey可以是long、int等值类型,必须包含IsDeleted、DeleterUserId、DeletionTime,其中这个接口
还继承了AuditedEntity<TPrimaryKey>, IFullAudited, IAudited, ICreationAudited, IHasCreationTime, IModificationAudited, IHasModificationTime, IDeletionAudited, IHasDeletionTime, ISoftDelete,这些父类型、接口的定义自己F12就可以看到
AbpLearn.EntityFrameworkCore/EntityFrameworkCore/AbpLearnDbContext.cs增加DbSet
public class AbpLearnDbContext : AbpZeroDbContext<Tenant, Role, User, AbpLearnDbContext> { /* Define a DbSet for each entity of the application */ public AbpLearnDbContext(DbContextOptions<AbpLearnDbContext> options) : base(options) { } public DbSet<AbpMenus> 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文件夹
第三步:添加控制器和前台页面、js
Controller文件,MenusController.cs
前台添加Menus及对应的js文件,可以简单省事的把其他文件夹复制粘贴一份,然后关键词修改下
这些文件太多了,我会把这套代码上传到github中,文章最低部会把链接挂出来
添加完之后我们就可以生成预览一下Menus,因为SetNavigation中未将Menus的url加进去,我们自己手打链接进入
此时, 我们的菜单这一块的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<ComboboxItemDto> TeneacyItems { get; set; } }
public async Task<IActionResult> 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
@using Abp.Authorization.Users@using Abp.MultiTenancy@using AbpLearn.MultiTenancy@using AbpLearn.Web.Models.Common.Modals@model int@{ Layout = null;}<div class="modal fade" id="MenuCreateModal" tabindex="-1" role="dialog" aria-labelledby="MenuCreateModalLabel" data-backdrop="static"> <div class="modal-dialog modal-lg" role="document"> <div class="modal-content"> @await Html.PartialAsync("~/Views/Shared/Modals/_ModalHeader.cshtml", new ModalHeaderViewModel(L("CreateNewMenu"))) <form name="systemMenuCreateForm" role="form" class="form-horizontal"> <div class="modal-body"> <div class="form-group row required"> <label class="col-md-3 col-form-label">@L("MenuName")</label> <div class="col-md-9"> <input type="text" name="MenuName" class="form-control" required minlength="2"> </div> </div> <div class="form-group row required"> <label class="col-md-3 col-form-label">@L("LName")</label> <div class="col-md-9"> <input type="text" name="LName" class="form-control" required> </div> </div> <div class="form-group row required"> <label class="col-md-3 col-form-label">@L("Url")</label> <div class="col-md-9"> <input type="text" name="Url" class="form-control"> </div> </div> <div class="form-group row"> <label class="col-md-3 col-form-label">@L("PageName")</label> <div class="col-md-9"> <input type="text" name="PageName" class="form-control"> </div> </div> <div class="form-group row"> <label class="col-md-3 col-form-label">@L("ParentId")</label> <div class="col-md-9"> <input type="text" name="ParentId" class="form-control"> </div> </div> <div class="form-group row"> <label class="col-md-3 col-form-label">@L("Orders")</label> <div class="col-md-9"> <input type="text" name="Orders" class="form-control"> </div> </div> <div class="form-group row"> <label class="col-md-3 col-form-label" for="CreateMenuIsActive">@L("IsActive")</label> <div class="col-md-9"> <input id="CreateMenuIsActive" type="checkbox" name="IsActive" value="true" checked /> </div> </div> </div> <input type="hidden" name="TenantId" value="@(Model)" /> @await Html.PartialAsync("~/Views/Shared/Modals/_ModalFooterWithSaveAndCancel.cshtml") </form> </div> </div></div>View Code
修改_EditModal.cshtml
@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")))<form name="MenuEditForm" role="form" class="form-horizontal"> <input type="hidden" name="Id" value="@Model.Id" /> <div class="modal-body"> <div class="form-group row required"> <label class="col-md-3 col-form-label" for="tenancy-name">@L("MenuName")</label> <div class="col-md-9"> <input id="tenancy-name" type="text" class="form-control" name="MenuName" value="@Model.MenuName" required maxlength="64" minlength="2"> </div> </div> <div class="form-group row required"> <label class="col-md-3 col-form-label" for="name">@L("LName")</label> <div class="col-md-9"> <input id="name" type="text" class="form-control" name="LName" value="@Model.LName" required maxlength="128"> </div> </div> <div class="form-group row required"> <label class="col-md-3 col-form-label" for="name">@L("Url")</label> <div class="col-md-9"> <input id="name" type="text" class="form-control" name="Url" value="@Model.Url" required maxlength="128"> </div> </div> <div class="form-group row required"> <label class="col-md-3 col-form-label" for="name">@L("PageName")</label> <div class="col-md-9"> <input id="name" type="text" class="form-control" name="PageName" value="@Model.PageName" required maxlength="128"> </div> </div> <div class="form-group row required"> <label class="col-md-3 col-form-label" for="name">@L("ParentId")</label> <div class="col-md-9"> <input id="name" type="text" class="form-control" name="ParentId" value="@Model.ParentId" required maxlength="128"> </div> </div> <div class="form-group row required"> <label class="col-md-3 col-form-label" for="name">@L("Orders")</label> <div class="col-md-9"> <input id="name" type="text" class="form-control" name="Orders" value="@Model.Orders" required maxlength="128"> </div> </div> <div class="form-group row"> <label class="col-md-3 col-form-label" for="isactive">@L("IsActive")</label> <div class="col-md-9"> <input id="isactive" type="checkbox" name="IsActive" value="true" @(Model.IsActive ? "checked" : "") /> </div> </div> </div> @await Html.PartialAsync("~/Views/Shared/Modals/_ModalFooterWithSaveAndCancel.cshtml")</form><script src="~/view-resources/Views/Menus/_EditModal.js" asp-append-version="true"></script>View Code
修改Index.AdvancedSearch.cshtml
@using AbpLearn.Web.Views.Shared.Components.TenantChange@using Abp.Application.Services.Dto@model ChangeModalViewModel <div class="abp-advanced-search"> <form id="MenusSearchForm" class="form-horizontal"> <input type="hidden" name="TenantId" value="@Model.TenantId" /> </form> <div class="form-horizontal"> <div class="form-group"> @Html.DropDownList( "ChangeTenancyNames", Model.TeneacyItems.Select(i => i.ToSelectListItem()), new { @class = "form-control edited", id = "ChangeTenancyName" }) </div> </div> </div>
因为在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 查询全部菜单 /// <summary> /// 查询全部菜单 /// </summary> /// <param name="input"></param> /// <returns></returns> public override async Task<PagedResultDto<MenuDto>> GetAllAsync(MenusPagedResultRequestDto input) { IQueryable<AbpMenus> 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<MenuDto>( totalCount, entities.Select(MapToEntityDto).ToList() ); }
No comments:
Post a Comment