EasyUI、权限管理 这是个都快被搞烂了的组合,但是easyui的确好用,权限管理在项目中的确实用。一直以来博客园里也不少朋友分享过,但是感觉好的要不没源码,要不就是过度设计写的太复杂看不懂,也懒得去看懂,还有一些不是在推广自己的代码生成器就是在卖权限组件,看着漂亮的UI和完善的功能就是没源码学习,真是恼人。

前段时间公司项目阶段性结束了,就抽空把权限控制的部分抽取出来写了个html+js+ashx+ado.net的权限管理系统分享给一些初学者,这个权限系统demo没有MVC、没有ORM、数据库表都没设外键关系、级联删除等,所有需要级联操作的地方都是事务提交。界面上的所有操作基本都是jquery发ajax请求ashx处理,ashx处理后输出json前台接收处理并配合easyui的组件响应给用户。基本没什么门槛,比较适合初学者。先看项目结构图:

项目结构基本就是模仿PetShop的,简单的7层,接口里定义方法,不同数据库不同实现,工厂负责创建访问数据库的对象,具体访问哪个数据库写在配置文件,都是老东西了也没什么说的。Model、BLL、SQLServerDAL等类库里的类都跟数据库表名保持一致,新建类库的时候修改了默认的命名空间(右键类库 – 属性 – 应用程序 – 程序集名称&默认命名空间),调用的时候用解决方案名.类库名.类名,这是我的个人习惯。具体查看源码

接下来简单分享下代码和贴图演示,懒的听我啰嗦的直接跳转到文章结尾下载源码。戳我

登陆就是用框架提供的FormsAuthentication类来做的,基本就是写cookie了,用户登录成功就加密下票证写到cookie里,简单的SetAuthCookie方法有点太简单了,只能写用户名到cookie里。我一般用FormsAuthenticationTicket类来做,可以把整个用户对象(userData)都写到cookie里。如果只把用户名写到cookie里,这样用户在别的浏览器登录然后执行修改密码操作,过来之前登陆过的浏览器,虽然改了密码,但还是可以继续保持登陆状态(博客园就是),这显然不符合常理。我的是把用户名和密码都保存到cookie里,然后用户每次访问都取出用户名和密码去数据库验证,如果找不到记录就干掉cookie。说到这肯定有人疑惑,把用户密码写到cookie里会不会不安全,用户密码本身就是不可逆的md5,写入cookie之前也再次进行了加密,我个人相信是比较安全的,且只有你自己看到cookie,如果担心有人抓包,可以把登陆功能部署到https上(个人想法,欢迎拍砖)。

简单来说,我的登陆逻辑:用户访问登陆页面就ajax请求后台验证cookie,只有用户名和密码匹配上(用户没修改密码)、状态IsAble可用(管理员没在后台禁用此用户)等等的情况下直接跳到首页,其他都干掉cookie。这样做的好处就是管理员可以很方便的控制一个用户的状态,就算他保存了cookie,因为服务端每次都有验证IsAble字段,管理员也可以很方便的禁用这个用户。还有不影响登陆的情况:比如用户修改了自己的姓名等情况也得重写cookie,否则从cookie里取出来的用户名显示到欢迎区域就不准确了,这里用FormsAuthenticationTicket就完美了,userData参数可以存很多东西。

首次访问登陆页面判断是否登陆和用户点击登陆按钮的示例代码:

  1. case "iflogin":
  2. //System.Threading.Thread.Sleep(5000);
  3. if (context.Request.IsAuthenticated)
  4. {
  5. FormsIdentity id = (FormsIdentity)context.User.Identity;
  6. FormsAuthenticationTicket tickets = id.Ticket;
  7. //获取票证里序列化的用户对象(反序列化)
  8. ZGZY.Model.User userCheck = new JavaScriptSerializer().Deserialize<ZGZY.Model.User>(tickets.UserData);
  9. //执行登录操作
  10. ZGZY.Model.User userReLogin = new ZGZY.BLL.User().UserLogin(userCheck.UserId, userCheck.UserPwd);
  11. if (userReLogin == null)
  12. {
  13. FormsAuthentication.SignOut();
  14. context.Response.Write("{\"msg\":\"用户名或密码错误!\",\"success\":false}");
  15. }
  16. else if (!userReLogin.IsAble)
  17. {
  18. FormsAuthentication.SignOut();
  19. context.Response.Write("{\"msg\":\"用户已被禁用!\",\"success\":false}");
  20. }
  21. else
  22. {
  23. //记录登录日志
  24. ZGZY.Model.LoginLog loginInfo = new Model.LoginLog();
  25. loginInfo.UserIp = context.Request.UserHostAddress;
  26. loginInfo.City = context.Request.Params["city"] ?? "未知"; //访问者所处城市
  27. loginInfo.UserName = context.User.Identity.Name;
  28. loginInfo.Success = true;
  29. new ZGZY.BLL.LoginLog().WriteLoginLog(loginInfo);
  30. context.Response.Write("{\"msg\":\"已登录过,正在跳转!\",\"success\":true}");
  31. }
  32. }
  33. else
  34. context.Response.Write("{\"msg\":\"nocookie\",\"success\":false}");
  35. break;
  36. case "login":
  37. //System.Threading.Thread.Sleep(5000);
  38. string userIp = context.Request.UserHostAddress;
  39. string city = context.Request.Params["city"] ?? "未知";
  40. string remember = context.Request.Params["remember"] ?? ""; //记住密码天数
  41. string name = context.Request.Params["loginName"];
  42. string pwd = ZGZY.Common.Md5.GetMD5String(context.Request.Params["loginPwd"]); //md5加密
  43. DateTime? lastLoginTime;
  44. if (new ZGZY.BLL.LoginLog().CheckLogin(userIp, out lastLoginTime) != null)
  45. {
  46. DateTime dtNextLogin = Convert.ToDateTime(lastLoginTime);
  47. context.Response.Write("{\"msg\":\"密码错误次数达到5次,请在" + dtNextLogin.AddMinutes(30).ToShortTimeString() + "之后再登陆!\",\"success\":false}");
  48. }
  49. else
  50. {
  51. ZGZY.Model.LoginLog loginInfo = new Model.LoginLog();
  52. loginInfo.UserName = name;
  53. loginInfo.UserIp = userIp;
  54. loginInfo.City = city;
  55. ZGZY.Model.User currentUser = new ZGZY.BLL.User().UserLogin(name, pwd);
  56. if (currentUser == null)
  57. {
  58. context.Response.Write("{\"msg\":\"用户名或密码错误!\",\"success\":false}");
  59. loginInfo.Success = false;
  60. new ZGZY.BLL.LoginLog().WriteLoginLog(loginInfo);
  61. }
  62. else if (currentUser.IsAble == false)
  63. {
  64. context.Response.Write("{\"msg\":\"用户已被禁用!\",\"success\":false}");
  65. loginInfo.Success = false;
  66. new ZGZY.BLL.LoginLog().WriteLoginLog(loginInfo);
  67. }
  68. else
  69. {
  70. //记录登录日志
  71. loginInfo.Success = true;
  72. new ZGZY.BLL.LoginLog().WriteLoginLog(loginInfo);
  73. context.Response.Write("{\"msg\":\"登录成功!\",\"success\":true}");
  74. DateTime dateCookieExpires; //cookie有效期
  75. switch (remember)
  76. {
  77. case "notremember":
  78. dateCookieExpires = new DateTime(9999, 12, 31); //默认时间
  79. break;
  80. case "oneday":
  81. dateCookieExpires = DateTime.Now.AddDays(1);
  82. break;
  83. case "sevenday":
  84. dateCookieExpires = DateTime.Now.AddDays(7);
  85. break;
  86. case "onemouth":
  87. dateCookieExpires = DateTime.Now.AddDays(30);
  88. break;
  89. case "oneyear":
  90. dateCookieExpires = DateTime.Now.AddDays(365);
  91. break;
  92. default:
  93. dateCookieExpires = new DateTime(9999, 12, 31);
  94. break;
  95. }
  96. FormsAuthenticationTicket ticket = new FormsAuthenticationTicket
  97. (
  98. 2,
  99. currentUser.UserId,
  100. DateTime.Now,
  101. dateCookieExpires,
  102. false,
  103. new JavaScriptSerializer().Serialize(currentUser) //序列化当前用户对象
  104. );
  105. string encTicket = FormsAuthentication.Encrypt(ticket);
  106. HttpCookie cookie = new HttpCookie(FormsAuthentication.FormsCookieName, encTicket);
  107. if (dateCookieExpires != new DateTime(9999, 12, 31)) //不是默认时间才设置过期时间,否则会话cookie
  108. cookie.Expires = dateCookieExpires;
  109. context.Response.Cookies.Add(cookie);
  110. }
  111. }
  112. break;

博客园的做法是访问登陆页面就把cookie干掉(如果有),我个人还是觉得有cookie再访问登陆页面就跳转到首页比较好。你可以访问博客园的登陆页面试试(慎点,会干掉你的cookie)

另外登陆功能还调用了sina的api获取用户登录城市、同一个ip连续5次输错密码就30分钟之内不让登陆,详细请自己查看源码。

权限控制基本就是用户拥有角色(可以多角色)、角色拥有菜单不同按钮的权限(浏览、增加、修改、删除等)。这样基本做到了单用户多角色,界面上的操作按钮根据用户拥有的权限显示或者不显示。先添加一个用户,默认密码123:

“已经改密”如果不勾选上,那么下次这个用户登录就会弹框让他修改密码(这个功能是跟添加用户默认密码是123相互呼应的)。直接用添加的用户登录会什么都没有,因为此用户没有任何菜单权限:

左侧的目录树是EasyUI的Tree组件,打开页面的时候ajax取出当前用户拥有的菜单权限然后展示出来,不同用户看到的菜单是不一样的。后台操作基本就是一个连表查询,DataTable取出来然后遍历构建这个Tree:

  1. /// <summary>
  2. /// 根据用户主键id查询用户可以访问的菜单
  3. /// </summary>
  4. public DataTable GetUserMenu(int id)
  5. {
  6. StringBuilder strSql = new StringBuilder();
  7. strSql.Append("select distinct(m.Name) menuname,m.Id menuid,m.Icon icon,u.Id userid,u.UserId username,m.ParentId menuparentid,m.Sort menusort,m.LinkAddress linkaddress from tbUser u");
  8. strSql.Append(" join tbUserRole ur on u.Id=ur.UserId");
  9. strSql.Append(" join tbRoleMenuButton rmb on ur.RoleId=rmb.RoleId");
  10. strSql.Append(" join tbMenu m on rmb.MenuId=m.Id");
  11. strSql.Append(" where u.Id=@Id order by m.ParentId,m.Sort");
  12. return ZGZY.Common.SqlHelper.GetDataTable(ZGZY.Common.SqlHelper.connStr, CommandType.Text, strSql.ToString(), new SqlParameter("@Id", id));
  13. }

重新登陆下管理员账户添加一个浏览角色:

给角色授权:

角色授权使的也是EasyUI的Tree组件,初始化的时候就连表查出了这个角色已经有的按钮权限,输出json绑定Tree的时候已经有的按钮权限加了:“checked”:true 就自动完成勾选了(当然,菜单和按钮的对应事先已经在数据库里对应上了。so,“按钮管理”里就不会有“角色设置”)。连表查询:

  1. /// <summary>
  2. /// 根据角色id获取此角色可以访问的菜单和菜单下的按钮(编辑角色-菜单使用)
  3. /// </summary>
  4. public DataTable GetAllMenu(int roleId)
  5. {
  6. StringBuilder sqlStr = new StringBuilder();
  7. sqlStr.Append("select m.Id menuid,m.Name menuname,m.ParentId parentid,m.Icon menuicon,mb.ButtonId buttonid,b.Name buttonname,b.Icon buttonicon,rmb.RoleId roleid,case when isnull(rmb.ButtonId , 0) =
    0 then \'false\' else \'true\' end checked");
  8. sqlStr.Append(" from tbMenu m");
  9. sqlStr.Append(" left join tbMenuButton mb on m.Id=mb.MenuId");
  10. sqlStr.Append(" left join tbButton b on mb.ButtonId=b.Id");
  11. sqlStr.Append(" left join tbRoleMenuButton rmb on(mb.MenuId=rmb.MenuId and mb.ButtonId=rmb.ButtonId and rmb.RoleId = @RoleId)");
  12. sqlStr.Append(" order by m.ParentId,m.Sort,b.Sort");
  13. return ZGZY.Common.SqlHelper.GetDataTable(ZGZY.Common.SqlHelper.connStr, CommandType.Text, sqlStr.ToString(), new SqlParameter("@RoleId", roleId));
  14. }

拼接json:

  1. /// <summary>
  2. /// 根据角色id获取此角色可以访问的菜单和菜单下的按钮(编辑角色-菜单使用)
  3. /// </summary>
  4. public string GetAllMenu(int roleId)
  5. {
  6. DataTable dt = dal.GetAllMenu(roleId);
  7. StringBuilder sb = new StringBuilder();
  8. sb.Append("[");
  9. DataRow[] rows = dt.Select("parentid = 0");
  10. if (rows.Length > 0)
  11. {
  12. DataView dataView = new DataView(dt);
  13. DataTable dtDistinct = dataView.ToTable(true, new string[] { "menuname", "menuid", "parentid" }); //distinct取不重复的子节点
  14. for (int i = 0; i < rows.Length; i++)
  15. {
  16. sb.Append("{\"id\":\"" + rows[i]["menuid"].ToString() + "\",\"text\":\"" + rows[i]["menuname"].ToString() + "\",\"children\":[");
  17. DataRow[] r_list = dtDistinct.Select(string.Format("parentid = {0}", rows[i]["menuid"])); //取子节点
  18. if (r_list.Length > 0) //根节点下有子节点
  19. {
  20. for (int j = 0; j < r_list.Length; j++)
  21. {
  22. sb.Append("{\"id\":\"" + r_list[j]["menuid"].ToString() + "\",\"text\":\"" + r_list[j]["menuname"].ToString() + "\",\"children\":[");
  23. DataRow[] r_listButton = dt.Select(string.Format("menuid = {0}", r_list[j]["menuid"])); //子子节点
  24. if (r_listButton.Length > 0) //有子子节点就遍历进去
  25. {
  26. for (int k = 0; k < r_listButton.Length; k++)
  27. {
  28. sb.Append("{\"id\":\"" + roleId + "\",\"text\":\"" + r_listButton[k]["buttonname"].ToString() + "\",\"checked\":" + r_listButton[k]["checked"].ToString() + ",\"attributes\":{\"menuid\":\"" + r_listButton[k]["menuid"].ToString() + "\",\"buttonid\":\"" + r_listButton[k]["buttonid"].ToString() + "\"}},");
  29. }
  30. sb.Remove(sb.Length - 1, 1);
  31. sb.Append("]},");
  32. }
  33. else
  34. {
  35. sb.Append("]},"); //跟上面if条件之外的字符串拼上
  36. }
  37. }
  38. sb.Remove(sb.Length - 1, 1);
  39. sb.Append("]},");
  40. }
  41. else //根节点下没有子节点
  42. {
  43. sb.Append("]},"); //跟上面if条件之外的字符串拼上
  44. }
  45. }
  46. sb.Remove(sb.Length - 1, 1);
  47. sb.Append("]");
  48. }
  49. else
  50. {
  51. sb.Append("]");
  52. }
  53. return sb.ToString();
  54. }

勾选好按钮后点击授权就是用ajax把菜单id和按钮id带到后台跟角色对应上插入角色菜单按钮id表中即可,这种角色授权的做法有很多种,对应上就好了:

上图中:7、3、4等都是菜单id、1是按钮id(1是浏览权限)。最后给用户角色:

当然这里是可以多角色的,这里演示只给上浏览权限。重新登陆下就有菜单显示了,但是没有按钮权限也不会显示增加/编辑/删除等任何按钮:

实现要点:用过EasyUI的都知道,这种展示的图标肯定用的是datagrid组件,datagrid组件带toolbar,就是顶部的工具栏区域。EasyUI组件的组合非常灵活,要做的就是请求的时候根据用户的id查出其权限决定显示或者不显示操作按钮即可:

  1. $(function () {
  2. $.ajax({ //请求当前用户可以操作的按钮
  3. url: "ashx/bg_button.ashx?menucode=user&pagename=ui_user",
  4. type: "post",
  5. data: { "action": "getbutton" },
  6. dataType: "json",
  7. timeout: 5000,
  8. success: function (data) {
  9. if (data.success) {
  10. var toolbar = getToolBar(data); //common.js
  11. if (data.browser) { //判断是否有浏览权限
  12. $("#ui_user_dg").datagrid({ //初始化datagrid
  13. url: "ashx/bg_user.ashx?action=search",
  14. striped: true, rownumbers: true, pagination: true, pageSize: 20,
  15. idField: \'Id\', //这个idField必须指定为输出的id,输出的是Id就必须是Id,不能小写
  16. sortName: \'AddDate\',
  17. sortOrder: \'desc\',
  18. pageList: [20, 40, 60, 80, 100],
  19. frozenColumns: [[
  20. { field: \'ck\', checkbox: true },
  21. {
  22. width: 100,
  23. title: \'登录名\',
  24. field: \'UserId\',
  25. sortable: true,
  26. formatter: function (value, row, index) {
  27. return value.length > 8 ? \'<span title="\' + value + \'">\' + value + \'</span>\' : value;
  28. }
  29. }, {
  30. width: 80,
  31. title: \'姓名\',
  32. field: \'UserName\',
  33. sortable: true,
  34. formatter: function (value, row, index) {
  35. return value.length > 5 ? \'<span title="\' + value + \'">\' + value + \'</span>\' : value;
  36. }
  37. }]],
  38. columns: [[
  39. { field: \'UserRole\', title: \'角色\', width: 180,
  40. formatter: function (value, row, index) {
  41. return value.length > 12 ? \'<span title="\' + value + \'">\' + value + \'</span>\' : value;
  42. }
  43. },
  44. { field: \'UserDepartment\', title: \'部门\', width: 150,
  45. formatter: function (value, row, index) {
  46. return value.length > 10 ? \'<span title="\' + value + \'">\' + value + \'</span>\' : value;
  47. }
  48. },
  49. { field: \'IsAble\', title: \'启用\', sortable: true, width: 60, align: \'center\',
  50. formatter: function (value, row, index) {
  51. return value ? \'<img src="themes/icon/chk_checked.gif" alt="已启用" title="用户已启用" />\' : \'<img src="themes/icon/chk_unchecked.gif" alt="未启用" title="用户未启用" />\';
  52. }
  53. },
  54. { field: \'IfChangePwd\', title: \'改密\', sortable: true, width: 60, align: \'center\',
  55. formatter: function (value, row, index) {
  56. return value ? \'<img src="themes/icon/chk_checked.gif" alt="已改密" title="用户已改密" />\' : \'<img src="themes/icon/chk_unchecked.gif" alt="未改密" title="用户未改密" />\';
  57. }
  58. },
  59. { field: \'AddDate\', title: \'添加时间\', sortable: true, width: 130 },
  60. { field: \'Description\', title: \'简介\', sortable: true, width: 260,
  61. formatter: function (value, row, index) {
  62. return value.length > 15 ? \'<span title="\' + value + \'">\' + value + \'</span>\' : value;
  63. }
  64. }
  65. ]],
  66. toolbar: toolbar.length == 0 ? null : toolbar, //请求回来的当前用户可以操作的按钮
  67. onDblClickRow: function (rowIndex, rowData) { //双击行弹框编辑
  68. //被编辑列高亮,其他列去除高亮
  69. $("#ui_user_dg").datagrid(\'clearSelections\').datagrid(\'clearChecked\').datagrid(\'checkRow\', rowIndex);
  70. ui_user_edit();
  71. }
  72. });
  73. }
  74. else {
  75. $("#ui_user_layout").layout("remove", "east"); //如果没有浏览权限就移除搜索
  76. $.show_warning("提示", "无权限,请联系管理员!");
  77. }
  78. } else {
  79. $.show_warning("错误", data.result);
  80. }
  81. },
  82. error: function (XMLHttpRequest, textStatus, errorThrown) {
  83. if (textStatus == "timeout") {
  84. $.show_warning("提示", "请求超时,请刷新当前页重试!");
  85. }
  86. else {
  87. $.show_warning("错误", textStatus + ":" + errorThrown);
  88. }
  89. }
  90. })

页面加载的时候先去请求当前登陆用户拥有的按钮权限,有就展示在toolBar上,没有就什么都不显示。这就是动态绑定EasyUI datagrid/treeGrid系列组件的toolBar:

  1. toolbar: toolbar.length == 0 ? null : toolbar, //请求回来的当前用户可以操作的按钮

因为EasyUI官方并没有提供toolBar的动态添加和删除方法,当时还发了几封邮件给他们了,最后回复也不理想。没办法,官方不提供只能自己写toolBar的动态绑定了,这里其实走了一些弯路(直接输出数组实现toolbar初始化、页面加载的时候在toolbar区域追加html等等都不行),最后群里的风骑士帮我写了段js搞定了,大家可以自己试着动态绑定下toolBar试试,我这只是一个简单的方法。getToolBar函数:

  1. /**
  2. * @author 风骑士
  3. * @requires jQuery,EasyUI
  4. * 初始化 datagrid toolbar
  5. */
  6. getToolBar = function (data) {
  7. if (data.toolbar != undefined && data.toolbar != \'\') {
  8. var toolbar = [];
  9. $.each(data.toolbar, function (index, row) {
  10. var handler = row.handler;
  11. row.handler = function () { eval(handler); };
  12. toolbar.push(row);
  13. });
  14. return toolbar;
  15. } else {
  16. return [];
  17. }
  18. }

输出的toolbar是这样的(我用这个工具格式化JSON):

  1. "toolbar": [
  2. {
  3. "text": "添加",
  4. "iconCls": "icon-add",
  5. "handler": "ui_user_add();"
  6. },
  7. {
  8. "text": "修改",
  9. "iconCls": "icon-application_edit",
  10. "handler": "ui_user_edit();"
  11. },
  12. {
  13. "text": "删除",
  14. "iconCls": "icon-delete",
  15. "handler": "ui_user_delete();"
  16. },
  17. {
  18. "text": "角色设置",
  19. "iconCls": "icon-user_key",
  20. "handler": "ui_user_role();"
  21. },
  22. {
  23. "text": "部门设置",
  24. "iconCls": "icon-group",
  25. "handler": "ui_user_department();"
  26. }
  27. ],

这是EasyUI官方提供DataGrid组件ToolBar的绑定实例,拼成这样不能实现动态绑定,我很苦恼:

  1. $(\'#dg\').datagrid({
  2. toolbar: [{
  3. iconCls: \'icon-edit\',
  4. handler: function(){alert(\'edit\')}
  5. },\'-\',{
  6. iconCls: \'icon-help\',
  7. handler: function(){alert(\'help\')}
  8. }]
  9. });

输出的handler不带function就可以了,出来的时候eval绑定下就好了,其实就是展示按钮,并为操作按钮绑定js方法。当然这些按钮对应的js是实现写好在页面里的,如果你不点按钮而是console调出这些添加、修改框,其实也是操作不了的,后台有权限验证:

firebug都自动为我们提示页面有的函数了,不点按钮调出添加用户操作框试试:

点击“添加”:

后台操作日志也记录了:

不仅按钮的权限是及时监控的,就连浏览权限也是即时的,先登录管理员账号:

登陆后再开一个标签页,访问首页或者登陆页,这个时候都会定位到首页,cookie里的用户是管理员。在新标签页里退出管理员用户登陆一个没有“用户管理”页面浏览权限的用户,再回到之前第一个标签页刷新“用户管理”,会出现:

右上角虽然显示的用户是管理员,但是cookie里的用户已经变成了重新登陆的用户了,刷新标签页会重新请求,然后通过输出的json里的”browser”: true 来判断当前用户是否有浏览权限,这东西用mvc的过滤器来控制就非常简单了,后续会有分享我的MVC权限系统。

部门管理功能基本一样,用的是EasyUI的treeGrid组件做的,有兴趣的自己下载源码看看吧。

菜单和按钮的CRUD我都没写,这两项我自己倒是更喜欢直接到库里手动添加,有兴趣的朋友可以补上代码跟员工、角色、部门的CRUD一模一样。

第一、使用dialog一定要记得destroy,否则一直存在页面的html里。这个系统的所有弹框添加、编辑、授权等都是用的EasyUI的dialog组件,dialog初始化引用的是一个html,大概是这样:

  1. $(\'#dd\').dialog({
  2. title: \'My Dialog\',
  3. width: 400,
  4. height: 200,
  5. closed: false,
  6. cache: false,
  7. href: \'get_content.html\', //使用页面来展示内容
  8. modal: true
  9. });

这样做的好处是不用事先把这些编辑的html元素埋在页面的html里。因为总体的页面只有一个,所有的员工管理/部门管理/角色管理等都是以tab的形式展示的,如果所有页面的所有增删改都埋在页面里,那么就首页就特别臃肿了。写成单独的页面调用的时候去取,用完调用dialog的destroy方法销毁dialog框,否则一直存在页面的html里,用完怎么找到这个dialog框呢,初始化的时候给dialog框一个id值即可:

  1. function ui_user_add() {
  2. $("<div/>").dialog({
  3. id: "ui_user_add_dialog",
  4. href: "html/ui_user_add.html",
  5. title: "添加用户",
  6. height: 350,
  7. width: 460,
  8. modal: true,
  9. buttons: [{
  10. id: "ui_user_add_btn",
  11. text: \' \',
  12. handler: function () {
  13. $("#ui_user_useraddform").form("submit", {
  14. url: "ashx/bg_user.ashx",
  15. onSubmit: function (param) {
  16. $(\'#ui_user_add_btn\').linkbutton(\'disable\'); //点击就禁用按钮,防止连击
  17. param.action = \'add\';
  18. if ($(this).form(\'validate\'))
  19. return true;
  20. else {
  21. $(\'#ui_user_add_btn\').linkbutton(\'enable\'); //恢复按钮
  22. return false;
  23. }
  24. },
  25. success: function (data) {
  26. var dataJson = eval(\'(\' + data + \')\'); //转成json格式
  27. if (dataJson.success) {
  28. $("#ui_user_add_dialog").dialog(\'destroy\'); //销毁dialog对象
  29. $.show_warning("提示", dataJson.msg);
  30. $("#ui_user_dg").datagrid("reload").datagrid(\'clearSelections\').datagrid(\'clearChecked\');
  31. } else {
  32. $(\'#ui_user_add_btn\').linkbutton(\'enable\'); //恢复按钮
  33. $.show_warning("提示", dataJson.msg);
  34. }
  35. }
  36. });
  37. }
  38. }],
  39. onLoad: function () {
  40. $("#ui_user_userid_add").focus();
  41. },
  42. onClose: function () {
  43. $("#ui_user_add_dialog").dialog(\'destroy\'); //销毁dialog对象
  44. }
  45. });
  46. }

这是一个标准的添加函数,dialog在用户点击确定或者点dialog的叉叉都会destroy掉;

第二、其次需要主要的就是函数的命名。建议用页面的名字加方法名字,如果员工/部门/角色的添加函数都叫add(),那么同时打开多个标签页再点其中的添加就会出错,毕竟所有标签页都在一个页面,上面的用户添加函数名字就是ui_user_add();

第三、最后就是按钮禁用。由于用户的网速或者服务端数据库响应速度的问题,避免用户多次提交表单就是用户点了“提交”就禁用“提交”按钮,响应回来了再开启“提交”按钮。就比如登陆功能,用户输入用户名和密码之后点“登陆”,你不禁用“登陆”按钮,网速问题登陆比较慢,他可能连续点几次“登陆”,这个时候你如果记录了登陆日志那一次登陆会有多个登陆日志。

日常使用的各大网站也都是这么做的:百度的登陆页面加载的时候登陆框是没有的,它取回来结果之后再给你显示登陆框,如果已经登陆直接被带到了首页:

支付宝的是你输入用户名和密码之后点“登陆”,密码框会被清空,同时登陆按钮变成“正在进入…”,这个时候你无论如何也连击不了:

上网的时候多留心其实会有很多发现,我上网的时候一般都开着firebug看各种请求。

以下是对我写这个小的权限demo有影响的资料(排名有先后):

1.jQuery EasyUI  EasyUI官网,EasyUI的学习主要都是在这;

2.发一个easyui+ajax+ashx权限管理框架  这个作者没写完,我主要是借鉴了他的思想;

3.SyPro  这个很强大,有java版、php版,就是没.net版;

4.郝冠军的PetShop讲解视频  这个demo的项目结构完全从郝冠军视频里学的;

5..Net项目分层与文件夹结构大全(最佳架子奖,吐槽奖,阴沟翻船奖揭晓)  看看高人的架子,不能老局限于UI+BLL+DAL

感谢阅读,源码请点我

如果觉得还可以,请不吝给我点个“赞”,谢谢!

 

—————————————————————–

2014.04.15更新:

有不少园友都反馈了一个问题,IE9以上版本登录或者增加、编辑等提交表单时提示保存json文件,再要不就是点击了确定按钮dialog框也不销毁等等。我的公司同事也跟我说这个问题了,公司自用的后台系统我也懒得去解决这个问题了。建议大家用火狐浏览器(Chrome也可以),这个demo我全程开发和调试都是火狐浏览器,基本上firebug不会报一个错,IE所有版本基本都会有这样或那样的问题。

2014.04.18更新:

由于demo里没写菜单和按钮的CRUD,这里补充如何在数据库里添加菜单和按钮:

添加菜单:到tbMenu表里添加一条记录,是父级菜单ParentId就写0,不是就找到父级菜单的Id填成当前添加子菜单的ParentId,Code随便填,之后的html请求按钮时跟这个Code保持一致即可,LinkAddress就是点击菜单打开的html页面,Icon到css里找个图标,Sort是排序,控制菜单的顺序的;

添加按钮:先到tbButton表里添加一条记录,跟添加菜单一样,Code、Icon、Sort等等,然后到tbMenuButton里添加一条记录,表示哪个菜单有这个按钮,这个是通过主键Id关联起来的。至此界面上授权的时候就可以看到新的菜单和按钮了。但是给角色授权并给用户赋予角色后,用户登录上来还是看不到新添加的按钮。这时得到项目的Common类库里找到ToolBarHelper类,向switch结构里添加按钮,这里是通过Code关联的,添加后就会输出新的按钮了,界面上也就能看到了,之后这个按钮实现的功能再html页面里写js方法就可以了。

学习demo,切勿用于生产环境。

版权声明:本文为oppoic原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://www.cnblogs.com/oppoic/p/html_js_ashx_easyui_authorize.html