在前面随笔《ABP开发框架前后端开发系列---(9)ABP框架的权限控制管理》中介绍了基于ABP框架服务构建的Winform客户端,客户端通过Web API调用的方式进行获取数据,从而实现了对组织机构、角色、用户、权限等管理,其中没有涉及菜单部分,本篇随笔介绍在ABP框架中实现菜单的管理,菜单是作为Winform或者Web动态构建界面的一个重要元素,同时也是作为角色权限控制的部分资源。

1、菜单的列表展示和管理

一般情况下,菜单的树形列表的显示可以分为多个节点,节点可以收缩也可以展开,当然节点是有不同的图标的了。这样就可以把很多功能点整合在一个树列表里面了,树的节点也可以分为很多级别,很多层次

  

如果我们想按照业务的范畴来区分,也可以分为多个模块展示,类似选项卡的方式,一个模块的功能菜单列表集合在一起展示,如下所示。

上面是我Winform开发框架和混合式开发框架的Winform界面中呈现菜单的界面,对于ABP开发框架来说,我们也只是获取数据方式不同,业务范畴的管理也没有什么不一样,我们依旧可以在服务器端配置好系统的菜单记录,然后基于ABP的Winform界面,同样管理这些内容即可。

下面是ABP框架中对于菜单资源管理的列表界面。

左边我们通过TreeList列表进行展示,右侧通过分页控件列表的方式进行展示,还是比较标准的Winform界面展示。

编辑或者创建菜单的界面如下所示。

菜单对于角色来说,应该是一种界面资源,可以通过配置进行管理对应角色用户的菜单。

2、菜单模块的实现逻辑

为了开发菜单模块,我们需要先定义好菜单的存储数据表,定义菜单表和角色菜单的中间关系表如下所示。

这个菜单模块定位为Web和Winform都通用的,因此菜单表中增加多了一些字段信息。

在数据库里增加这两个表后,就可以使用代码生成工具进行框架代码的生成和Winform界面代码的生成了。

生成框架后,对应的应用服务层类代码如下所示。

这个生成的类,默认具有基类的增删改查分页等接口方法,同时我们也会生成对应的Web API Caller层的类代码,代码如下所示。

Winform界面生成标准界面后进行布局的一定调整,左侧增加TreeList控件,设计界面如下所示。

获取列表数据的函数定义在GetData函数里面,函数代码如下所示。

        /// <summary>
/// 获取数据
/// </summary>
/// <returns></returns>
private async Task<IPagedResult<MenuDto>> GetData()
{
MenuPagedDto pagerDto = null;
if (advanceCondition != null)
{
pagerDto = new MenuPagedDto(this.winGridViewPager1.PagerInfo);
pagerDto = dlg.GetPagedResult(pagerDto);
}
else if(!IsNormalSearch && this.tree.FocusedNode != null)
{
//构建分页的条件和查询条件
pagerDto = new MenuPagedDto(this.winGridViewPager1.PagerInfo)
{
PID = string.Concat(this.tree.FocusedNode.GetValue(Id_FieldName))
};
}
else
{
//构建分页的条件和查询条件
pagerDto = new MenuPagedDto(this.winGridViewPager1.PagerInfo)
{
//添加所需条件
Name = this.txtName.Text.Trim(),
WinformType = this.txtWinformType.Text.Trim()
};
} var result = await MenuApiCaller.Instance.GetAll(pagerDto);
return result;
}

分页控件的数据绑定代码如下所示,这些都是根据Winform界面配置自动生成的代码。

            this.winGridViewPager1.DisplayColumns = "EmbedIcon,Name,Seq,Visible,Expand,WinformType,Tag,CreationTime";
this.winGridViewPager1.ColumnNameAlias = await MenuApiCaller.Instance.GetColumnNameAlias();//字段列显示名称转义 //获取分页数据列表
var result = await GetData(); //设置所有记录数和列表数据源
this.winGridViewPager1.PagerInfo.RecordCount = result.TotalCount; //需先于DataSource的赋值,更新分页信息
this.winGridViewPager1.DataSource = result.Items;

而TreeList列表是我们后来增加上去的,需要额外进行数据的绑定和处理,初始化树列表处理代码如下所示。

        /// <summary>
/// 初始化树控件
/// </summary>
private void InitTree()
{
this.tree.Columns.Clear();
//控件扩展函数封装处理
this.tree.CreateColumn("Name", "菜单名称", , true);
this.tree.InitTree("Id", "PID", null, false, false); //设置树的图标集合及逐级图标
this.tree.SelectImageList = this.imageCollection1;
this.tree.CustomDrawNodeImages += (object sender, CustomDrawNodeImagesEventArgs e) =>
{
int maxCount = this.imageCollection1.Images.Count;
var index = e.Node.Level < maxCount ? e.Node.Level : ;
e.SelectImageIndex = index;
};
//初始化树节点选择事件
this.tree.FocusedNodeChanged += delegate (object sender, FocusedNodeChangedEventArgs e)
{
this.FocusedNodeChanged();
};
}

获取列表数据并绑定树列表的数据源如下所示

        /// <summary>
/// 绑定树的数据源
/// </summary>
private async Task BindTree()
{
var pageDto = new MenuPagedDto();
var result = await MenuApiCaller.Instance.GetAll(pageDto); this.tree.DataSource = result.Items;
this.tree.ExpandAll();
}

而界面显示的时候,加载并显示左侧树列表数据如下代码所示。

        private async void FrmMenu_Load(object sender, EventArgs e)
{
//列表信息
InitTree();
InitSearchControl();
await BindTree();
}

删除菜单的时候,我们一般想把当前菜单和下面的子菜单一并级联删除,实现这个方法,我们需要在服务端自定义实现,如下是应用服务层的实现方法。

        /// <summary>
/// 移除节点和子节点
/// </summary>
/// <param name="input"></param>
/// <returns></returns>
[UnitOfWork]
public virtual async Task DeleteWithSubNode(EntityDto<string> input)
{
var children = await _repository.GetAllListAsync(ou => ou.PID == input.Id);
foreach (var child in children)
{
await DeleteWithSubNode(new EntityDto<string>(child.Id));//递归删除
} await _repository.DeleteAsync(input.Id);
}

我们这里显示声明了UnitOfWork标记,说明这个操作的原子性,全部成功就成功,否则失败的处理。

而客户端的Web API 封装调用类,对这个Web API接口的封装,根据上篇随笔《ABP开发框架前后端开发系列---(10)Web API调用类的简化处理》简化后的处理代码如下所示。

3、角色菜单管理

菜单的管理整体操作和常规的业务表处理一样,没有太多特殊的地方,下面介绍一下角色包含菜单的管理操作。

前面介绍了角色包含菜单的管理界面如下所示。

界面主要是列出所有菜单,并勾选上该角色可以使用的菜单。这个角色包含的菜单和角色包含的权限处理上比较相似。

首先我们需要定义一个角色DTO对象中的菜单集合属性,如下所示。

在界面上获取勾选上的权限和菜单ID集合,存储在对应的列表里面。

        /// <summary>
/// 编辑或者保存状态下取值函数
/// </summary>
/// <param name="info"></param>
private void SetInfo(RoleDto info)
{
info.DisplayName = txtDisplayName.Text;
info.Name = txtName.Text;
info.Description = txtDescription.Text; info.Permissions = GetNodeValues(this.tree, "Name");
info.Menus = GetNodeValues(this.treeMenu, "Id");
}

在应用服务层的RoleAppService类里面,我们创建或者更新角色的时候,需要更新它的权限和菜单资源,如下代码是创建角色的函数。

        /// <summary>
/// 创建角色对象
/// </summary>
/// <param name="input"></param>
/// <returns></returns>
public override async Task<RoleDto> Create(CreateRoleDto input)
{
CheckCreatePermission(); var role = ObjectMapper.Map<Role>(input);
role.SetNormalizedName(); CheckErrors(await _roleManager.CreateAsync(role));
await CurrentUnitOfWork.SaveChangesAsync(); //It's done to get Id of the role. await UpdateGrantedPermissions(role, input.Permissions);
await UpdateGrantedMenus(role, input.Menus); return MapToEntityDto(role);
}

同理,更新角色一样处理这两个部分的资源

        /// <summary>
/// 更新角色对象
/// </summary>
/// <param name="input"></param>
/// <returns></returns>
public override async Task<RoleDto> Update(RoleDto input)
{
CheckUpdatePermission(); var role = await _roleManager.GetRoleByIdAsync(input.Id); ObjectMapper.Map(input, role); CheckErrors(await _roleManager.UpdateAsync(role)); await UpdateGrantedPermissions(role, input.Permissions);
await UpdateGrantedMenus(role, input.Menus); return MapToEntityDto(role);
}

以上就是菜单的管理,和角色包含菜单的维护操作,整个开发过程主要就是使用代码生成工具来快速生成框架各个层的代码,以及Winform界面的代码,这样在进行一定的函数扩展以及界面布局调整,就可以非常方便、高效的完整一个业务模块的开发工作了。

ABP开发框架前后端开发系列---(11)菜单的动态管理的更多相关文章

  1. ABP开发框架前后端开发系列---(14)基于Winform的ABP快速开发框架

    前面介绍了很多ABP系列的文章,一步一步的把我们日常开发中涉及到的Web API服务构建.登录日志和操作审计日志.字典管理模块.省份城市的信息维护.权限管理模块中的组织机构.用户.角色.权限.菜单等内 ...

  2. ABP开发框架前后端开发系列---(9)ABP框架的权限控制管理

    在前面两篇随笔<ABP开发框架前后端开发系列---(7)系统审计日志和登录日志的管理>和<ABP开发框架前后端开发系列---(8)ABP框架之Winform界面的开发过程>开始 ...

  3. ABP开发框架前后端开发系列---(8)ABP框架之Winform界面的开发过程

    在前面随笔介绍的<ABP开发框架前后端开发系列---(7)系统审计日志和登录日志的管理>里面,介绍了如何改进和完善审计日志和登录日志的应用服务端和Winform客户端,由于篇幅限制,没有进 ...

  4. ABP开发框架前后端开发系列---(3)框架的分层和文件组织

    在前面随笔<ABP开发框架前后端开发系列---(2)框架的初步介绍>中,我介绍了ABP应用框架的项目组织情况,以及项目中领域层各个类代码组织,以便基于数据库应用的简化处理.本篇随笔进一步对 ...

  5. ABP开发框架前后端开发系列---(2)框架的初步介绍

    在前面随笔<ABP开发框架前后端开发系列---(1)框架的总体介绍>大概介绍了这个ABP框架的主要特点,以及介绍了我对这框架的Web API应用优先的一些看法,本篇继续探讨ABP框架的初步 ...

  6. ABP开发框架前后端开发系列---(10)Web API调用类的简化处理

    在较早期的随笔<ABP开发框架前后端开发系列---(5)Web API调用类在Winform项目中的使用>已经介绍了Web API调用类的封装处理,虽然这些调用类我们可以使用代码生成工具快 ...

  7. ABP开发框架前后端开发系列---(5)Web API调用类在Winform项目中的使用

    在前面几篇随笔介绍了我对ABP框架的改造,包括对ABP总体的介绍,以及对各个业务分层的简化,Web API 客户端封装层的设计,使得我们基于ABP框架的整体方案越来越清晰化, 也越来越接近实际的项目开 ...

  8. ABP开发框架前后端开发系列---(4)Web API调用类的封装和使用

    在前面随笔介绍ABP应用框架的项目组织情况,以及项目中领域层各个类代码组织,以及简化了ABP框架的各个层的内容,使得我们项目结构更加清晰.上篇随笔已经介绍了字典模块中应用服务层接口的实现情况,并且通过 ...

  9. ABP开发框架前后端开发系列---(12)配置模块的管理

    一般来说,一个系统或多或少都会涉及到一些系统参数或者用户信息的配置,而ABP框架也提供了一套配置信息的管理模块,ABP框架的配置信息,必须提前定义好配置的各项内容,然后才能在系统中初始化或者通过接口查 ...

随机推荐

  1. 阿里云CentOS7系统搭建JavaWeb环境

    一,准备工作 1,安装目录 我们创建如下路径/usr/develop,然后在develop目录下面创建java,tomcat和mysql三个目录即可. 二,配置JDK 1.理解wget命令 wget命 ...

  2. [Example of Sklearn] - 分类对比

    refrence :http://cloga.info/python/2014/02/07/classify_use_Sklearn/ 加载数据集 这里我使用pandas来加载数据集,数据集采用kag ...

  3. Massive Collection Of Design Patterns, Frameworks, Components, And Language Features For Delphi

    Developer beNative over on GitHub has a project called Concepts which is a massive collection of Del ...

  4. 关于iis里面 .net framework 版本的切换

    然而,即便这样还是不够的 在iis上浏览网站会报以下错误: 这是什么原因呢?这是因为应用程序池中没有使用asp.net v4.0 网站程序是用v4.0写的,所以我们要用4.0的程序池版本,当前应用程序 ...

  5. Leetcode 136 Single Number 亦或

    题意就是从一堆数中找出唯一的一个只存在一个的数.除了那个数,其他的数都是两个相同的数. 通过亦或的性质: 1)a^a = 0 0^a = a 2)交换律 a^b = b^ a 3)结合律 (a^b)^ ...

  6. WPF 3D动态加载模型文件

    原文:WPF 3D动态加载模型文件 这篇文章需要读者对WPF 3D有一个基本了解,至少看过官方的MSDN例子. 一般来说关于WPF使用3D的例子,都是下面的流程: 1.美工用3DMAX做好模型,生成一 ...

  7. [Songqw.Net 基础]WPF插件化中同步Style

    原文:[Songqw.Net 基础]WPF插件化中同步Style 版权声明:本文为博主原创文章,未经博主允许可以随意转载 https://blog.csdn.net/songqingwei1988/a ...

  8. OpenGL(十一) BMP真彩文件的显示和复制操作

    glut窗口除了可以绘制矢量图之外,还可以显示BMP文件,用函数glDrawPixels把内存块中的图像数据绘制到窗口上,glDrawPixels函数原型: glDrawPixels (GLsizei ...

  9. android该怎么办iphone那种画面抖动的动画效果(含有button和EditText)

    首先在效果图: 要做到抖动效果按钮,能够这样做.设定anim房源res以下.创建一个button_shake.xml <? xml version="1.0" encodin ...

  10. Python科学计算(两)——时域波形和正弦信号的频谱

    Python科学计算(两)-- 时域和频域波形为正弦波形信号生成.计算和显示 # -*- coding: utf-8 -*- import numpy as np import matplotlib. ...