0 Asp.Net Core 项目实战之权限管理系统(0) 无中生有

1 Asp.Net Core 项目实战之权限管理系统(1) 使用AdminLTE搭建前端

2 Asp.Net Core 项目实战之权限管理系统(2) 功能及实体设计

3 Asp.Net Core 项目实战之权限管理系统(3) 通过EntityFramework Core使用PostgreSQL

4 Asp.Net Core 项目实战之权限管理系统(4) 依赖注入、仓储、服务的多项目分层实现

5 Asp.Net Core 项目实战之权限管理系统(5) 用户登录

6 Asp.Net Core 项目实战之权限管理系统(6) 功能管理

7 Asp.Net Core 项目实战之权限管理系统(7) 组织机构、角色、用户权限

8 Asp.Net Core 项目实战之权限管理系统(8) 功能菜单的动态加载

github源码地址

0 仓储接口及实现

0.0 仓储接口

修改Fonour.Domain项目中IRepository接口,增加分页查询及根据条件删除的操作接口。

/// <summary>
/// 根据条件删除实体
/// </summary>
/// <param name="where">lambda表达式</param>
/// <param name="autoSave">是否自动保存</param>
void Delete(Expression<Func<TEntity, bool>> where, bool autoSave = true); /// <summary>
/// 分页获取数据
/// </summary>
/// <param name="startPage">起始页</param>
/// <param name="pageSize">页面条目</param>
/// <param name="rowCount">数据总数</param>
/// <param name="where">查询条件</param>
/// <param name="order">排序</param>
/// <returns></returns>
IQueryable<TEntity> LoadPageList(int startPage, int pageSize, out int rowCount, Expression<Func<TEntity, bool>> where, Expression<Func<TEntity, object>> order);

增加功能菜单管理的仓储操作接口IMenuRepository

public interface IMenuRepository : IRepository<Menu>
{
}

0.1 仓储实现

在Fonour.EntityFrameworkCore项目的Repositories\FonourRepositoryBase.cs中实现上述接口。

/// <summary>
/// 根据条件删除实体
/// </summary>
/// <param name="where">lambda表达式</param>
/// <param name="autoSave">是否自动保存</param>
public void Delete(Expression<Func<TEntity, bool>> where, bool autoSave = true)
{
_dbContext.Set<TEntity>().Where(where).ToList().ForEach(it => _dbContext.Set<TEntity>().Remove(it));
if (autoSave)
Save();
}
/// <summary>
/// 分页查询
/// </summary>
/// <param name="startPage">页码</param>
/// <param name="pageSize">单页数据数</param>
/// <param name="rowCount">行数</param>
/// <param name="where">条件</param>
/// <param name="order">排序</param>
/// <returns></returns>
public IQueryable<TEntity> LoadPageList(int startPage, int pageSize, out int rowCount, Expression<Func<TEntity, bool>> where = null, Expression<Func<TEntity, object>> order = null)
{
var result = from p in _dbContext.Set<TEntity>()
select p;
if (where != null)
result = result.Where(where);
if (order != null)
result = result.OrderBy(order);
else
result = result.OrderBy(m => m.Id);
rowCount = result.Count();
return result.Skip((startPage - ) * pageSize).Take(pageSize);
}

1 应用服务接口及实现

在Fonour.Application项目中添加MenuApp文件夹,新增IMenuAppService接口及实现MenuAppService,以及数据传输对象MenuDto。

接口主要包含一下定义,具体实现代码参见GitHub源码。

/// <summary>
/// 获取功能列表
/// </summary>
/// <returns></returns>
List<MenuDto> GetAllList(); /// <summary>
/// 根据父级Id获取功能列表
/// </summary>
/// <param name="parentId">父级Id</param>
/// <param name="startPage">起始页</param>
/// <param name="pageSize">页面大小</param>
/// <param name="rowCount">数据总数</param>
/// <returns></returns>
List<MenuDto> GetMneusByParent(Guid parentId, int startPage, int pageSize, out int rowCount); /// <summary>
/// 新增或修改功能
/// </summary>
/// <param name="dto">实体</param>
/// <returns></returns>
bool InsertOrUpdate(MenuDto dto); /// <summary>
/// 根据Id集合批量删除
/// </summary>
/// <param name="ids">功能Id集合</param>
void DeleteBatch(List<Guid> ids); /// <summary>
/// 删除
/// </summary>
/// <param name="id">功能Id</param>
void Delete(Guid id); /// <summary>
/// 根据Id获取实体
/// </summary>
/// <param name="id">功能Id</param>
/// <returns></returns>
MenuDto Get(Guid id);

1.0 AutoMapper的使用

AutoMapper是一个轻量级的类库,主要功能是把一个对象转换成另外一个对象,而避免我们每次重复的手工去逐个属性赋值转换。在将领域模型(Menu)与数据传输对象(MenuDto)互相转化的过程中提供便利。

0 添加AutoMapper应用

通过NuGet程序包管理器安装、通过NuGet程序包控制台安装、或通过直接修改project.json都可用很方便的快速添加对AutoMapper类库的引用。

1 FonourMapper类

AutoMapper最基本的使用是首先通过CreateMap<T1,T2>()方法创建两个实体的映射关系,然后通过Mapper.Map<T1>(T2)实现实体T2到T1的转换。这里所有领域模型与Dto之间的映射关系创建都在应用服务层完成,应用服务层将接收到的Dto对象转换为领域模型后传入仓储调用,同时将仓储返回的领域模型转换为Dto,返回给界面变现层使用。

在Fonour.Application项目中新增一个名称为FonourMapper的类,里面实现一个名称为Initialize的静态方法,以后所有的领域模型与Dto的映射关系都在这个FonourMapper类里完成。

 /// <summary>
/// Enity与Dto映射
/// </summary>
public class FonourMapper
{
public static void Initialize()
{
Mapper.Initialize(cfg =>
{
cfg.CreateMap<Menu, MenuDto>();
cfg.CreateMap<MenuDto, Menu>();
});
}
}

2 Startup启动时调用

在应用程序启动入口处初始化所有领域模型与Dto对象的映射关系,修改Startup.cs类的Startup方法,增加FonourMapper类Initialize静态方法的调用。

//初始化映射关系
FonourMapper.Initialize();

3 AutoMapper进行实体转换

使用AutoMapper进行实体转换操作主要应用在应用服务接口的实现类中,如MenuAppService的GetAllList方法实现。

 public List<MenuDto> GetAllList()
{
var menus = _menuRepository.GetAllList().OrderBy(it=>it.SerialNumber);
//使用AutoMapper进行实体转换
return Mapper.Map<List<MenuDto>>(menus);
}

2 表现层实现

功能及实体设计中我们将功能管理部分设计为左右结构,左边为树形结构,右边为列表结构,当单击左边树节点时,加载选中树节点的下级数据至右侧列表中。基于此设计,必不可少的必然包含一个树形插件和一个带有分页功能的表格插件。

2.0 JsTree使用

树形插件这里选择使用JsTree,jsTree是一个 基于jQuery的Tree控件。支持XML,JSON,Html三种数据源。提供创建,重命名,移动,删除,拖"放节点操作。可以自己自定义创建,删 除,嵌套,重命名,选择节点的规则。在这些操作上可以添加多种监听事件,用以实现我们实际应用中的种种树形操作需求。

0 JsTree前端包的安装

通过Bower管理器,搜索JsTree,找到后直接安装即可。或直接修改Fonour.MVC项目中的Bower.json文件:

{
"name": "asp.net",
"private": true,
"dependencies": {
"bootstrap": "3.3.6",
"font-awesome": "4.6.1",
"iCheck": "1.0.2",
"layer": "*",
"jquery.cookie": "1.4.1",
"jstree": "3.3.0"
}
}

1 JsTree的基本使用

在布局页Shared/_Layout.cshtml中统一添加JsTree的css及js引用。

<link rel="stylesheet" href="~/lib/jstree/dist/themes/default/style.min.css">
<script src="~/lib/jstree/dist/jstree.js"></script>

在视图文件中定义要渲染JsTree的Div对象

<div id="treeDiv" class="portlet-body">
</div>

JsTree的创建主要是通过调用$('#treeDiv').jstree()方法实现,下面是一个基本的使用ajax获取json数据并绑定树结构的过程,有关JsTree的更详细的使用说明,请参看官方网站相关介绍。

//加载功能树
function initTree() {
$.jstree.destroy();
$.ajax({
type: "Get",
url: "/Menu/GetMenuTreeData", //获取数据的ajax请求地址
success: function (data) {
$('#treeDiv').jstree({ //创建JsTtree
'core': {
'data': data, //绑定JsTree数据
"multiple": false //是否多选
},
"plugins": ["state", "types", "wholerow"] //配置信息
})
$("#treeDiv").on("ready.jstree", function (e, data) { //树创建完成事件
data.instance.open_all(); //展开所有节点
});
$("#treeDiv").on('changed.jstree', function (e, data) { //选中节点改变事件
var node = data.instance.get_node(data.selected[0]); //获取选中的节点
if (node) {
selectedMenuId = node.id;
loadTables(1, 10);
};
});
}
}); }

2.1 bootstrap-paginator分页插件使用

表格的显示我们采用Bootstrap中的Table组件,并配合bootstrap-paginator提供的分页功能实现列表数据的分页展示。Bootstrap-paginator是一款基于Bootstrap的js分页插件,功能很丰富,它提供了一系列的参数用来支持用户的定制,提供了公共的方法可随时获得插件状态的改变,以及事件来监听用户的动作。由于它暂不支持Bower,所以我们只能下载这个js文件,添加到我们项目的wwwroot\js\文件夹下。同时在布局页中添加此js的引用。

<script src="~/js/bootstrap-paginator.js"></script>

一般的使用是首先在视图页中定义分页插件的容器。

<div class="paging-toolbar">
<ul id="grid_paging_part"></ul>
</div>

然后在列表数据的加载同时创建分页插件,其中page,data.rowsCount,data.pageCount等定义需要在请求的控制器方法中通过json格式返回。

var elment = $("#grid_paging_part"); //分页插件的容器id
if (data.rowCount > 0) {
var options = { //分页插件配置项
bootstrapMajorVersion: 3,
currentPage: page, //当前页
numberOfPages: data.rowsCount, //总数
totalPages: data.pageCount, //总页数
onPageChanged: function (event, oldPage, newPage) { //页面切换事件
loadTables(newPage);
}
}
elment.bootstrapPaginator(options); //分页插件初始化
}

2.2 功能实现

0 控制器

新建名称为MenuController的控制器,控制器的构造函数中通过依赖注入获取IMenuAppService的实现实例。

private readonly IMenuAppService _menuAppService;
public MenuController(IMenuAppService menuAppService)
{
_menuAppService = menuAppService;
}

控制器中主要包括以下方法。

  • Index 返回功能管理界面
  • GetMenuTreeData 返回左侧功能管理树的Json数据
  • GetMneusByParent 返回左侧功能树选中节点下级功能分页列表所需Json数据
  • Edit 功能管理的编辑
  • DeleteMuti 功能的批量删除
  • Delete 功能删除
  • Get 根据id获取功能实体

注:此处根据个人喜好,视图界面采用尽量纯净的html,操作全部采用ajax请求,控制器统一返回Json格式的数据。当然你也可以通过使用TagHelper+模型绑定+Razor机制实现(控制器获取实体或实体集合,返回View,View中得到实体,通过TagHelper实现模型绑定),可参考用户登陆实现部分。

1 视图

Views文件夹下新建Menu文件夹,在Menu文件夹下创建Index.cshtml视图,以及名称为_Edit.cshtml(新增/修改)的分部视图,同时添加Index.js文件。

在布局页_Layout.cshtml中增加RenderSection,用于渲染视图页中相关js方法。

@RenderSection("scripts", false)

Index.cshtml主要布局修改如下

<div class="row">
<div class="col-md-3">
<div class="box box-primary">
<div class="box-header with-border">
<h3 class="box-title">功能管理</h3>
</div>
<div class="box-body">
<div id="treeDiv" class="portlet-body">
</div>
</div>
</div>
</div>
<div class="col-md-9">
<div class="box box-primary">
<div class="box-header with-border">
<h3 class="box-title">功能列表</h3>
<div class="pull-right box-tools">
<button id="btnAddRoot" class="btn btn-primary" type="button" data-original-title="新增顶级功能" data-toggle="tooltip" data-widget="">
<i class="fa fa-plus-square"></i>&nbsp;&nbsp;新增顶级
</button>
<button id="btnAdd" title="" class="btn btn-primary" type="button" data-original-title="新增功能" data-toggle="tooltip" data-widget="">
<i class="fa fa-plus-circle"></i>&nbsp;&nbsp;新增
</button>
<button id="btnDelete" title="" class="btn btn-danger" type="button" data-original-title="删除功能" data-toggle="tooltip" data-widget="">
<i class="fa fa-times"></i>&nbsp;&nbsp;删除
</button>
<button id="btnLoadRoot" title="" class="btn btn-success" type="button" data-original-title="加载顶级功能" data-toggle="tooltip" data-widget="">
<i class="fa fa-list"></i>&nbsp;&nbsp;加载顶级
</button>
</div>
</div>
<div class="box-body">
<div class="table-scrollable">
<table class="table table-striped table-bordered table-hover dataTable no-footer" role="grid" aria-describedby="sample_2_info">
<thead>
<tr role="row">
<th class="table-checkbox" style="width:40px;text-align:center;"><input id="checkAll" class="group-checkable" type="checkbox"></th>
<th tabindex="0" aria-label="">
功能名称
</th>
<th tabindex="0" aria-label="">
功能编码
</th>
<th tabindex="0" aria-label="">
地址
</th>
<th tabindex="0" aria-label="">
类型
</th>
<th tabindex="0" aria-label="">
功能描述
</th>
<th tabindex="0" style="width: 230px;" aria-label="">
操作
</th>
</tr>
</thead>
<tbody id="tableBody"></tbody>
</table>
</div>
</div>
<div class="paging-toolbar">
<ul id="grid_paging_part"></ul>
</div>
</div>
</div>
</div>
@Html.Partial("_Edit")
@section scripts{
<script src="~/Views/Menu/Index.js"></script>
}

_Edit.cshtm采用Bootstrap的Modal弹出框组件,做为功能管理的新增和编辑界面

<div id="addRootModal" tabindex="-1" class="modal fade in" aria-hidden="true">
<div class="modal-dialog">
<div class="box box-info">
<div class="box-header with-border">
<h5 class="box-title" id="Title"></h5>
<button class="close" aria-label="Close" type="button" data-dismiss="modal">
<span aria-hidden="true">×</span>
</button>
</div>
<form class="form-horizontal">
<input type="hidden" id="Id" />
<input type="hidden" id="ParentId" />
<div class="box-body">
<div class="form-group">
<label class="col-sm-2 control-label" for="">功能名称</label>
<div class="col-sm-10">
<input class="form-control" id="Name" type="text" placeholder="功能名称">
</div>
</div>
<div class="form-group">
<label class="col-sm-2 control-label" for="inputPassword3">功能编码</label>
<div class="col-sm-10">
<input class="form-control" id="Code" type="text" placeholder="功能编码">
</div>
</div>
<div class="form-group">
<label class="col-sm-2 control-label" for="inputPassword3">功能类型</label>
<div class="col-sm-10">
<select class="form-control" id="Type">
<option value="0">功能菜单</option>
<option value="1">操作按钮</option>
</select>
</div>
</div>
<div class="form-group">
<label class="col-sm-2 control-label" for="">链接地址</label>
<div class="col-sm-10">
<input class="form-control" id="Url" type="text" placeholder="链接地址">
</div>
</div>
<div class="form-group">
<label class="col-sm-2 control-label" for="">功能图标</label>
<div class="col-sm-10">
<input class="form-control" id="Icon" type="text" placeholder="功能图标">
</div>
</div>
<div class="form-group">
<label class="col-sm-2 control-label" for="">功能序号</label>
<div class="col-sm-10">
<input class="form-control" id="SerialNumber" type="text" placeholder="功能序号">
</div>
</div>
<div class="form-group">
<label class="col-sm-2 control-label" for="">功能描述</label>
<div class="col-sm-10">
<textarea id="Remarks" class="form-control" rows="3"></textarea>
</div>
</div>
</div>
<div class="box-footer">
<div class="pull-right box-tools">
<button id="btnSave" class="btn btn-primary" type="button">保存</button>
<button class="btn btn-default" type="button" data-dismiss="modal">取消</button>
</div>
</div>
</form>
</div>
</div>
</div>

Index.js文件主要定义视图文件与服务器的交互操作,主要包括以下定义。

前面我们已经介绍过静态文件的使用需要在Startup中的Configure方法中增加

//使用静态文件
app.UseStaticFiles();

这样就可以访问所有wwwroot目录下的静态文件,但是若想访问Views/Menu/Index.js文件,还需要在Configure方法中增加

app.UseStaticFiles(new StaticFileOptions()
{
FileProvider = new PhysicalFileProvider(Directory.GetCurrentDirectory())
});

这样在客户端就可以成功访问Views/Menu/Index.js文件,调用相关方法了。

3 界面预览

完成的功能管理界面大体如下。

主界面

编辑界面

删除提示

4 总结

本次我们通过使用AutoMapper、JsTree、Bootstrap-paginator等技术实现了功能管理的开发,接下来要实现组织机构管理功能的开发。

Asp.Net Core 项目实战之权限管理系统(6) 功能管理的更多相关文章

  1. Asp.Net Core 项目实战之权限管理系统(4) 依赖注入、仓储、服务的多项目分层实现

    0 Asp.Net Core 项目实战之权限管理系统(0) 无中生有 1 Asp.Net Core 项目实战之权限管理系统(1) 使用AdminLTE搭建前端 2 Asp.Net Core 项目实战之 ...

  2. Asp.Net Core 项目实战之权限管理系统(0) 无中生有

    0 Asp.Net Core 项目实战之权限管理系统(0) 无中生有 1 Asp.Net Core 项目实战之权限管理系统(1) 使用AdminLTE搭建前端 2 Asp.Net Core 项目实战之 ...

  3. Asp.Net Core 项目实战之权限管理系统(1) 使用AdminLTE搭建前端

    0 Asp.Net Core 项目实战之权限管理系统(0) 无中生有 1 Asp.Net Core 项目实战之权限管理系统(1) 使用AdminLTE搭建前端 2 Asp.Net Core 项目实战之 ...

  4. Asp.Net Core 项目实战之权限管理系统(2) 功能及实体设计

    0 Asp.Net Core 项目实战之权限管理系统(0) 无中生有 1 Asp.Net Core 项目实战之权限管理系统(1) 使用AdminLTE搭建前端 2 Asp.Net Core 项目实战之 ...

  5. Asp.Net Core 项目实战之权限管理系统(3) 通过EntityFramework Core使用PostgreSQL

    0 Asp.Net Core 项目实战之权限管理系统(0) 无中生有 1 Asp.Net Core 项目实战之权限管理系统(1) 使用AdminLTE搭建前端 2 Asp.Net Core 项目实战之 ...

  6. Asp.Net Core 项目实战之权限管理系统(5) 用户登录

    0 Asp.Net Core 项目实战之权限管理系统(0) 无中生有 1 Asp.Net Core 项目实战之权限管理系统(1) 使用AdminLTE搭建前端 2 Asp.Net Core 项目实战之 ...

  7. Asp.Net Core 项目实战之权限管理系统(7) 组织机构、角色、用户权限

    0 Asp.Net Core 项目实战之权限管理系统(0) 无中生有 1 Asp.Net Core 项目实战之权限管理系统(1) 使用AdminLTE搭建前端 2 Asp.Net Core 项目实战之 ...

  8. Asp.Net Core 项目实战之权限管理系统(8) 功能菜单的动态加载

    0 Asp.Net Core 项目实战之权限管理系统(0) 无中生有 1 Asp.Net Core 项目实战之权限管理系统(1) 使用AdminLTE搭建前端 2 Asp.Net Core 项目实战之 ...

  9. Net Core 项目实战之权限管理系统(0)

    0 前言 Net Core 项目实战之权限管理系统(0) 无中生有   0 http://www.cnblogs.com/fonour/p/5848933.html 学习的最好方法就是动手去做,这里以 ...

随机推荐

  1. 苹果强制使用HTTPS传输了怎么办?——关于HTTPS,APP开发者必须知道的事

    WeTest 导读 2017年1月1日起,苹果公司将强制使用HTTPS协议传输.本文通过对HTTPS基础原理和通信过程内容的讲解,介绍APP开发者在这个背景下的应对办法. 几周前,我们在<htt ...

  2. 趣说游戏AI开发:曼哈顿街角的A*算法

    0x00 前言 请叫我标题党!请叫我标题党!请叫我标题党!因为下面的文字既不发生在美国曼哈顿,也不是一个讲述美国梦的故事.相反,这可能只是一篇没有那么枯燥的关于算法的文章.A星算法,这个在游戏寻路开发 ...

  3. 2016/12/31_Python

    今天学习主要内容: Python: 1.with语句(补充昨天的文件操作) 用with打开的文件在脚本结束会自动关闭,以防普通打开方式忘记关闭文件连接 语法: with open("demo ...

  4. [转载]网站地址栏小图标favicon.ico的制作方法

    有人也许会好奇,有的网址前面有个漂亮的小图标而且有的网站图标还会动,这是怎么做到的呢? 如下图所示: 那个小图标有个名字叫favicon.ico,网站图标虽小但可以起到很好的点缀作用,尤其是当浏览者将 ...

  5. arcgis api for js入门开发系列四地图查询(含源代码)

    备注:由于实现本篇功能的需求,修改了地图数据的dlsearch.mxd,然后更新了地图服务,需要的在文章最后有提供最新的mxd以及源代码下载的 上一篇实现了demo的地图工具栏,本篇新增地图查询功能, ...

  6. 如何使用本地账户"完整"安装 SharePoint Server 2010+解决“New-SPConfigurationDatabase : 无法连接到 SharePoint_Config 的 SQL Server 的数据 库 master。此数据库可能不存在,或当前用户没有连接权限。”

    注:目前看到的解决本地账户完整安装SharePoint Server 2010的解决方案如下,但是,有但是的哦: 当我们选择了"完整"模式安装SharePointServer201 ...

  7. .net core和angular2之前端篇—1

    2016-10-20更新 今天的这篇文章还是一篇"Hello World",只不过开发环境有所改变--Visual Studio Code+Angular2+Webapck,也算是 ...

  8. 热修复-Nuwa学习篇

    nuwa热修复是基于qq空间团队的思路,最近的热度话题了,很多种方案,自己先研究几种方案,基本上都各有优势,学习肯定得先挑个软柿子捏了,自己对比了一下,发现nuwa代码量少点,所以就决定了,先研究nu ...

  9. HTML5游戏源码 飞翔的字母 可自定义内容

    相信大家都玩过飞翔的小鸟吧,当然,可能已经有很多人因为这个游戏砸了不少手机.吼吼. 废话不多说,回到主题,源码如下. 博客园上传空间大小有限制,没法上传了,需要打包源码的朋友们请留言邮箱地址.当然还有 ...

  10. (一)Spark简介-Java&Python版Spark

    Spark简介 视频教程: 1.优酷 2.YouTube 简介: Spark是加州大学伯克利分校AMP实验室,开发的通用内存并行计算框架.Spark在2013年6月进入Apache成为孵化项目,8个月 ...