一、前言
权限系统设计中,授权代码是用来控制数据访问权限的。授权代码说白了只是一树型结构的数据,没有什么其它的业务意义。那么这个页面的功能也就非常简单授权代码维护:新增、修改、删除授权代码数据。
二、正文
我们实际上就是要实现一个treegrid的增删改的功能,技术上很容易实现。
1、新建控制器 PermissionControlle.cs
public class PermissionController : Controller { public ActionResult Index(){return View();} }
mvc控制器中不需要写任何的代码,就这样就ok
2、创建view
@{ViewBag.Title = "授权代码";Layout = "~/Views/Shared/_Layout.cshtml"; }@section scripts{<script src="~/Areas/Sys/ViewModels/Permission.js"></script><script type="text/javascript">
using(['combotree'],easyuifix.datagrid_editor_extend); ko.bindingViewModel(new viewModel());var formatterParent = function (value, row) { return row.ParentName };</script> }<div class="z-toolbar"><a id="a_refresh" href="#" plain="true" class="easyui-linkbutton" icon="icon-arrow_refresh" title="刷新" data-bind="click:refreshClick">刷新</a><a id="a_add" href="#" plain="true" class="easyui-linkbutton" icon="icon-add" title="新增" data-bind="click:addClick">新增</a><a id="a_edit" href="#" plain="true" class="easyui-linkbutton" icon="icon-edit" data-bind="click:editClick" title="编辑">编辑</a><a id="a_del" href="#" plain="true" class="easyui-linkbutton" icon="icon-cross" title="删除" data-bind="click:deleteClick">删除</a><a id="a_save" href="#" plain="true" class="easyui-linkbutton" icon="icon-save" data-bind="click:saveClick" title="保存">保存</a></div><table data-bind="treegrid:grid"><thead> <tr> <th field="_id" hidden="true"></th> <th field="PermissionName" align="left" width="150" editor="{type:'validatebox',options:{required: true }}">授权名称 </th> <th field="PermissionCode" align="left" width="80" editor="{type:'validatebox',options:{required: true }}">授权代码 </th> <th field="ParentCode" align="left" width="150" editor="combotree" formatter="formatterParent">上级授权 </th></tr> </thead> </table>
这个view相对于其它页面来说也是相当的简洁了
3、前端的实现,主要是实现easyui treegrid的在线编辑功能及按钮的交互逻辑
/** * 模块名:mms viewModel * 程序名: Permission.js * Copyright(c) 2013-2015 liuhuisheng [ liuhuisheng.xm@gmail.com ] **/function viewModel() {var self = this;this.grid = {size: { w: 4, h: 40 },url: '/api/sys/permission',idField: '_id',queryParams: ko.observable(),treeField: 'PermissionName',loadFilter: function (d) {d = utils.copyProperty(d.rows || d, ["PermissionCode"], ["_id"], false);return utils.toTreeData(d, '_id', 'ParentCode', "children");} };this.refreshClick = function () {window.location.reload();};this.addClick = function () {if (self.grid.onClickRow()) {var row = { _id: utils.uuid(), PermissionCode: '', PermissionName: '' };self.grid.treegrid('append', { parent: '', data: [row] });self.grid.treegrid('select', row._id);self.grid.$element().data("datagrid").insertedRows.push(row);self.editClick();}};this.editClick = function () {var row = self.grid.treegrid('getSelected');if (row) {//取得父节点数据var treeData = JSON.parse(JSON.stringify(self.grid.treegrid('getData')).replace(/_id/g, "id").replace(/PermissionName/g, "text"));treeData.unshift({ "id": 0, "text": "" });//设置上级菜单下拉树var gridOpt = $.data(self.grid.$element()[0], "datagrid").options;var col = $.grep(gridOpt.columns[0], function (n) { return n.field == 'ParentCode' })[0];col.editor = { type: 'combotree', options: { data: treeData } };col.editor.options.onBeforeSelect = function (node) {var isChild = utils.isInChild(treeData, row._id, node.id);com.messageif(isChild, 'warning', '不能将自己或下级设为上级节点');return !isChild;};//开始编辑行数据self.grid.treegrid('beginEdit', row._id);self.edit_id = row._id;}};this.grid.OnBeforeDestroyEditor = function (editors, row) {row.ParentName = editors['ParentCode'].target.combotree('getText');};this.deleteClick = function () {var row = self.grid.treegrid('getSelected');if (row) {self.grid.$element().treegrid('remove', row._id);self.grid.$element().data("datagrid").deletedRows.push(row);}};this.grid.onDblClickRow = self.editClick;this.grid.onClickRow = function () {var edit_id = self.edit_id;if (!!edit_id) {if (self.grid.treegrid('validateRow', edit_id)) { //通过验证self.grid.treegrid('endEdit', edit_id);self.edit_id = undefined;}else { //未通过验证self.grid.treegrid('select', edit_id);return false;}}return true;};this.saveClick = function () {self.grid.onClickRow();var post = {};post.list = new com.editTreeGridViewModel(self.grid).getChanges(['_id', 'PermissionCode', 'PermissionName', 'ParentCode']);if (self.grid.onClickRow() && post.list._changed) {com.ajax({url: '/api/sys/permission/edit',data: ko.toJSON(post),success: function (d) {com.message('success', '保存成功!');self.grid.treegrid('acceptChanges');self.grid.queryParams({});}});}}; }
4、现在大家看我的东西都觉得是在看前端文章,实际上我在后台框架也做的很强大。现在重点放在后台,请大家注意我后端的写法,我们现在看web api中的处理。我这里用到了两个web api
1、取得授权代码数据: GET /api/sys/permission
2、保存treegrid中的修改(包括新增、修改、删除的数据) POST /api/sys/permission
大家注意下WebApi实现,超级简洁的代码实现
public class PermissionApiController : ApiController {//创建数据服务实例sys_permissionService service = new sys_permissionService();public IEnumerable<dynamic> Get(){//构建查询参数var pQuery = ParamQuery.Instance().Select("A.*,B.PermissionName as ParentName").From(@"sys_permission A left join sys_permission B on B.PermissionCode = A.ParentCode");//调用服务基类中的共通方法返回查询结果return service.GetDynamicList(pQuery);}[HttpPost]public void Edit(dynamic data){//构建编辑的参数 传入的数据结构为data={deleted:[...],inserted:[...],updated:[...]}; var listWrapper = RequestWrapper.Instance().LoadSettingXmlString(@" <settings> <table>sys_permission</table> <where><field name='PermissionCode' cp='equal' variable='_id'></field> </where> </settings>");//调用服务基类中的共通方法处理保存service.Edit(null, listWrapper, data);} }
以上的两个方法就已经实现了全部的功能了,这里我们好像觉得都是调用service中的方法,那我们再看看service类
public class sys_permissionService : ServiceBase<sys_permission> {}
这个数据服务类是空的,没有任何方法,只是继承了我的service基类,拥有了基类中定义的方法。
这里再简单的介绍下我的服务基类,只要服务类继承了服务基类后,就拥有以下方法
//服务构造函数 public ServiceBase(); public ServiceBase(string moduleName);//查询方法: public List<dynamic> GetDynamicList(ParamQuery param = null); //取得动态数据列表 public dynamic GetDynamicListWithPaging(ParamQuery param = null); //取得动态数据列表(带分页)public List<T> GetModelList(ParamQuery param = null); //取得Model列表数据 public dynamic GetModelListWithPaging(ParamQuery param = null); //取得Model列表数据(带分页)public T GetModel(ParamQuery param); //取得单model数据 public dynamic GetDynamic(ParamQuery param); //取得动态对象public TField GetField<TField>(ParamQuery param); //取得字段的值//数据插入:提供数据插入前后事件定义 protected virtual bool OnBeforeInsert(InsertEventArgs arg); public int Insert(ParamInsert param); protected virtual void OnAfterInsert(InsertEventArgs arg);//数据更新: protected virtual bool OnBeforeUpdate(UpdateEventArgs arg); public int Update(ParamUpdate param); protected virtual void OnAfterUpdate(UpdateEventArgs arg);//数据删除: protected virtual bool OnBeforeDelete(DeleteEventArgs arg); public int Delete(ParamDelete param); protected virtual void OnAfterDelete(DeleteEventArgs arg);//存储过程执行: public int StoredProcedure(ParamSP param);//数据编辑(主从表在同一个事务中保存): protected virtual bool OnBeforEdit(EditEventArgs arg); protected virtual bool OnBeforEditMaster(EditEventArgs arg); protected virtual bool OnBeforEditDetail(EditEventArgs arg); public int Edit(RequestWrapper formWrapper, RequestWrapper listWrapper, JObject data); protected virtual void OnAfterEdit(EditEventArgs arg); protected virtual void OnAfterEditMaster(EditEventArgs arg); protected virtual void OnAfterEditDetail(EditEventArgs arg);
查询、新增、修改、删除、存储过程等每一个对象我都对应一个参数构造器,查询对应的是ParamQuery,每一个写法都可以像linq一样,比较方简洁。
好了,回过头再看看我的webapi中的代码,是不是每个方法只有两行代码,而且已经实现了很复杂的操作。
正是得利于我框架的这一点,才把我从后台中解放出来,有更多的时间精力去研究前端。
三、效果图
简单的几句代码就搞定了这个功能,我们来看看实现的效果
新增、修改、删除测试都ok