ASP.NET MVC4 +EF+ EasyUI实现 动态菜单
这段时间没事,做了一个动态菜单的实现。有很多的地方想的不是很全,做的不够完善。欢迎大神们拍砖和指点!
话不多说,进入正题:
设计了一个数据库表:
在设计的时候每一个菜单的子菜单最多能添加 99 个,子菜单编号根据父菜单编号拼接上同级子菜单的个数。
这里因为要用到Easyui,一些子段是按照easyui属性设计的。
EF使用的是数据库优先方式创建。
贴上代码:
1.HTML代码:
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 <style type="text/css"> 2 header { 3 height:60px; 4 } 5 aside{ 6 float:left; 7 width:200px; 8 } 9 article{ 10 margin-left:204px 11 } 12 </style> 13 <body> 14 <header> 15 EasyUI treegrid 实现动态菜单 16 </header> 17 18 <aside> 19 @*树形菜单*@ 20 <ul id="tt"></ul> 21 </aside> 22 <article> 23 24 @*treegrid数据表格*@ 25 26 <table id="treegrid" style="width: 600px; height: 400px"></table> 27 28 @*treegrid的右键菜单*@ 29 <div id="tree_menu" class="easyui-menu" style="width: 120px;"> 30 <div> 31 <span>数据操作</span> 32 <div style="width: 120px;"> 33 <div id="add" data-options="iconCls:'icon-add0'">添加节点</div> 34 <div id="edit" data-options="iconCls:'icon-edit0'">修改节点</div> 35 <div id="delete" data-options="iconCls:'icon-delete0'">删除节点</div> 36 </div> 37 </div> 38 <div id="ref" data-options="iconCls:'icon-new0'">刷 新</div> 39 <div class="menu-sep"></div> 40 <div>Exit</div> 41 </div> 42 @*添加,修改表单(弹窗模式)*@ 43 <div class ="treegrid_dialog" style="display: none;"> 44 <form id="treegrid_form"> 45 @*菜单主键*@ 46 <input type="hidden" name ="MId" /> 47 @*父菜单编号*@ 48 <input type="hidden" name ="pid" id="menuPid" /> 49 @*菜单编号*@ 50 <input type="hidden" name ="id" /> 51 <table> 52 <tr> 53 <td>菜单名称</td><td><input class="easyui-validatebox" name="text" data-options="required:true" /></td> 54 </tr> 55 <tr> 56 <td>菜单图标</td><td><input id="icon" name="iconCls" editable="false"/></td> 57 </tr> 58 <tr> 59 <td>菜单路径</td><td><input class="easyui-validatebox" name="url" data-options="required:true" /></td> 60 </tr> 61 <tr> 62 <td>菜单是否选中</td> 63 <td> 64 <input id="yes" type="radio" name="checked" value="1" /><label for="yes">是</label> 65 <input id="no" type="radio" name="checked" value="0" /><label for="no">否</label> 66 </td> 67 </tr> 68 </table> 69 </form> 70 </div> 71 </article> 72 </body>
2.JavaScript代码:
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 var treegrid; 2 var treegrid_dialog; 3 var iconData; 4 $(function () { 5 //绑定菜单tree数据 6 $('#tt').tree({ 7 url: '/System/GetTree' 8 }); 9 10 iconData = [{ 11 value: '', 12 text: '默认' 13 }, { 14 value: 'icon-add', 15 text: 'icon-add' 16 }, { 17 value: 'icon-edit', 18 text: 'icon-edit' 19 }, { 20 value: 'icon-remove', 21 text: 'icon-remove' 22 }, { 23 value: 'icon-save', 24 text: 'icon-save' 25 }, { 26 value: 'icon-cut', 27 text: 'icon-cut' 28 }, { 29 value: 'icon-ok', 30 text: 'icon-ok' 31 }, { 32 value: 'icon-no', 33 text: 'icon-no' 34 }, { 35 value: 'icon-cancel', 36 text: 'icon-cancel' 37 }, { 38 value: 'icon-reload', 39 text: 'icon-reload' 40 }, { 41 value: 'icon-search', 42 text: 'icon-search' 43 }, { 44 value: 'icon-print', 45 text: 'icon-print' 46 }, { 47 value: 'icon-help', 48 text: 'icon-help' 49 }, { 50 value: 'icon-undo', 51 text: 'icon-undo' 52 }, { 53 value: 'icon-redo', 54 text: 'icon-redo' 55 }, { 56 value: 'icon-back', 57 text: 'icon-back' 58 }, { 59 value: 'icon-sum', 60 text: 'icon-sum' 61 }, { 62 value: 'icon-tip', 63 text: 'icon-tip' 64 }]; 65 66 treegrid= $('#treegrid').treegrid({ 67 url: '/System/GetTree', 68 idField: 'id', 69 treeField: 'text', 70 columns: [[ 71 { title: '菜单名称', field: 'text', width: 180 }, 72 { 73 title: '菜单图标', field: 'iconCls', width: 100, 74 formatter: function (value) { 75 if (value!=null) { 76 return '<span class="' + value+ '"></span>'; 77 } 78 } 79 }, 80 { title: '菜单路径', field: 'url', width: 100 }, 81 { title: '是否选中', field: 'checked', width: 100 } 82 ]], 83 onContextMenu: function (e, row) { 84 e.preventDefault(); 85 $(this).treegrid('unselectAll'); 86 $(this).treegrid('select', row.id); 87 $('#tree_menu').menu('show', { 88 left: e.pageX, 89 top: e.pageY 90 }); 91 } 92 }); 93 //刷新treegrid数据 94 $("#ref").click(function () { 95 treegrid.treegrid('reload'); 96 }); 97 //添加节点 98 $("#add").click(function () { 99 //取得选中节点 100 var node = treegrid.treegrid('getSelected'); 101 //清空表单数据 102 $("#treegrid_form").form('clear'); 103 $("#no").attr("checked", "true");//添加节点--设置表单(菜单是否选中)--默认选择‘否’ 104 $("#menuPid").val(node.id); //设置将要添加节点 的 父节点编号 105 $("#icon").combobox({ 106 //url: '../../Content/data/icondata.json', 107 //valueField: 'id', 108 //textField: 'text' 109 data: iconData 110 }); 111 treegrid_dialog = $(".treegrid_dialog").show().dialog({ 112 modal: true, 113 title: '添加 ' + node.text + ' 节点的子节点', 114 closed: false, 115 width: 400, 116 height: 250, 117 cache: false, 118 collapsible: true, 119 maximizable: true, 120 resizable: true, 121 buttons: [{ 122 text: '保 存', 123 plain:true, 124 iconCls: 'icon-save', 125 handler: function () { 126 //数据验证 通过与否 127 var validate = $("#treegrid_form").form('validate'); 128 if (validate == false) { 129 return false; 130 } 131 132 $.ajax({ 133 url: '/System/AddMenu', 134 data: $("#treegrid_form").serialize(), 135 type: 'POST', 136 cache: false, 137 dataType: 'json', 138 success: function (data) { 139 if (data && data.Success) { 140 $.messager.show({ title: '提示', msg: '' + data.Msg + '', timeout: 5000, showType: 'slide' }); 141 treegrid_dialog.dialog('close'); 142 treegrid.treegrid('reload'); 143 } 144 else { 145 $.messager.show({ title: '提示', msg: '' + data.Msg + '', timeout: 5000, showType: 'slide' }); 146 treegrid_dialog.dialog('close'); 147 treegrid.treegrid('reload'); 148 } 149 } 150 }); 151 } 152 }, { 153 text: '取 消', 154 iconCls: 'icon-redo', 155 plain: true, 156 handler: function () { 157 treegrid_dialog.dialog('close'); 158 } 159 }] 160 }); 161 }); 162 //编辑节点 163 $("#edit").click(function () { 164 //取得选中节点 165 var node = treegrid.treegrid('getSelected'); 166 //清空表单数据 167 $("#treegrid_form").form('clear'); 168 treegrid_dialog = $(".treegrid_dialog").show().dialog({ 169 modal: true, 170 title: '编辑 ' + node.text + ' 节点', 171 closed: false, 172 width: 400, 173 height: 250, 174 cache: false, 175 collapsible: true, 176 maximizable: true, 177 resizable: true, 178 buttons: [{ 179 text: '保 存', 180 plain:true, 181 iconCls: 'icon-save', 182 handler: function () { 183 //数据验证 通过与否 184 var validate = $("#treegrid_form").form('validate'); 185 if (validate == false) { 186 return false; 187 } 188 $.ajax({ 189 url: '/System/EditMenu', 190 data: $("#treegrid_form").serialize(), 191 type: 'POST', 192 cache: false, 193 dataType: 'json', 194 success: function (data) { 195 if (data && data.Success) { 196 $.messager.show({ title: '提示', msg: '' + data.Msg + '', timeout: 5000, showType: 'slide' }); 197 treegrid_dialog.dialog('close'); 198 treegrid.treegrid('reload'); 199 } 200 else { 201 $.messager.show({ title: '提示', msg: '' + data.Msg + '', timeout: 5000, showType: 'slide' }); 202 treegrid_dialog.dialog('close'); 203 treegrid.treegrid('reload'); 204 } 205 } 206 }); 207 } 208 }, { 209 text: '取 消', 210 iconCls: 'icon-redo', 211 plain: true, 212 handler: function () { 213 treegrid_dialog.dialog('close'); 214 } 215 }] 216 }); 217 //绑定编辑窗口表单数据 218 $("#treegrid_form").form('load', node); 219 }); 220 //删除节点 221 $("#delete").click(function () { 222 //取得选中节点 223 var node = treegrid.treegrid('getSelected'); 224 $.post('/System/DeleteMenu', { MId: node.MId }, function (data) { 225 if (data && data.Success) { 226 $.messager.show({ title: '提示', msg: '' + data.Msg + '', timeout: 5000, showType: 'slide' }); 227 treegrid.treegrid('reload'); 228 } 229 else { 230 $.messager.show({ title: '提示', msg: '' + data.Msg + '', timeout: 5000, showType: 'slide' }); 231 treegrid.treegrid('reload'); 232 } 233 }); 234 }); 235 });
3.后台Controller方法
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 private SYS_UserLimitEntities db = new SYS_UserLimitEntities(); 2 3 public ActionResult Index() 4 { 5 return View(); 6 } 7 8 #region 取得树形菜单 + ActionResult GetTree(string id) 9 /// <summary> 10 /// 取得树形菜单 11 /// </summary> 12 /// <param name="id">用户点击tree时,向后台访问父菜单编号</param> 13 /// <returns></returns> 14 [HttpPost] 15 public ActionResult GetTree(string id) 16 { 17 try 18 { 19 List<Menu> list = null; 20 //判断是否执行点击tree时,按需加载要扩展的菜单 21 if (id != null) 22 { 23 list = db.Menu.Where(a => a.pid == id).ToList(); 24 } 25 else 26 { 27 list = db.Menu.Where(a => a.pid == "000").ToList();//取得根菜单 28 } 29 return Json(list);//返回easyui展示数据的json数据 30 } 31 catch (Exception) 32 { 33 throw; 34 } 35 } 36 #endregion 37 38 #region 添加菜单操作 + ActionResult AddMenu(Menu menu) 39 /// <summary> 40 /// 添加菜单操作 41 /// </summary> 42 /// <param name="menu">收集到的Menu菜单各个属性的值</param> 43 /// <returns></returns> 44 [HttpPost] 45 public ActionResult AddMenu(Menu menu) 46 { 47 Messages m = new Messages(); 48 try 49 { 50 string newid = null;//将要添加菜单的编号 51 //判断将要添加子菜单的父菜单状态是否为父菜单状态/否则修改 52 Menu me = db.Menu.Single(a => a.id == menu.pid); 53 if (me.state == "open") 54 { 55 me.state = "closed"; 56 } 57 //取得将要添加子菜单同级菜单-----并倒序排列 58 var mlist = db.Menu.Where(a => a.pid == menu.pid).OrderByDescending(a => a.MId).ToList(); 59 //判断与将要添加的子菜单的同级菜单数目是否为0 60 if (mlist.Count > 0) 61 { 62 //取得同级菜单编号最大的的最后 两位 以便计算出将要添加菜单编号 63 string id = mlist[0].id; 64 int i = Convert.ToInt32(id.Substring(id.Length - 2, 2)) + 1; 65 //限定每一个子菜单的数目不得超过99个 66 if (i == 100) 67 { 68 m.Msg = "该菜单的子菜单已经达到最大数目了,你可以选择创建其他菜单选项"; 69 return Json(m); 70 } 71 //判断子菜单编号后两位是否为‘01’两位形式不是则补填为两位 72 newid = i > 9 ? mlist[0].pid + i.ToString() : mlist[0].pid + "0" + i.ToString(); 73 } 74 else 75 { 76 newid = menu.pid + "01";//没有同级菜单时候就默认添加菜单的编号为 父菜单编号+‘01’ 77 } 78 //修改将要添加菜单的菜单编号 79 menu.id = newid; 80 menu.state = "open"; 81 //写入数据库 82 db.Menu.Add(menu); 83 84 db.SaveChanges(); 85 m.Msg = "成功的添加["+menu.text+"]的菜单节点信息!"; 86 m.Success = true; 87 return Json(m); 88 } 89 catch (Exception ex) 90 { 91 m.Msg = "添加失败!" + ex.Message; 92 return Json(m); 93 } 94 } 95 #endregion 96 97 #region 编辑菜单操作 + ActionResult EditMenu(Menu menu) 98 /// <summary> 99 /// 编辑菜单操作 100 /// </summary> 101 /// <param name="menu">收集表单Menu数据</param> 102 /// <returns></returns> 103 [HttpPost] 104 public ActionResult EditMenu(Menu menu) 105 { 106 Messages m = new Messages(); 107 try 108 { 109 //将要修改的菜单添加到EF容器 110 DbEntityEntry<Menu> entry = db.Entry<Menu>(menu); 111 //设置包装类对象状态为unchanged 112 entry.State = System.Data.EntityState.Unchanged; 113 //设置改变的属性 114 entry.Property(a => a.text).IsModified = true; 115 entry.Property(a => a.url).IsModified = true; 116 entry.Property(a => a.@checked).IsModified = true; 117 entry.Property(a => a.iconCls).IsModified = true; 118 //提交数据库 119 db.SaveChanges(); 120 m.Msg = "节点["+menu.text+"]的信息修改成功!"; 121 m.Success = true; 122 return Json(m); 123 } 124 catch (Exception ex) 125 { 126 m.Msg = "修改失败!" + ex.Message; 127 return Json(m); 128 } 129 } 130 #endregion 131 132 #region 删除菜单操作 + ActionResult DeleteMenu(int MId) 133 /// <summary> 134 /// 删除菜单操作 135 /// </summary> 136 /// <param name="MId">将要删除的菜单编号</param> 137 /// <returns></returns> 138 [HttpPost] 139 public ActionResult DeleteMenu(int MId) 140 { 141 Messages m = new Messages(); 142 try 143 { 144 //取得将要删除的实体对象 145 Menu menu = db.Menu.Single(a => a.MId == MId); 146 // 删除项有--子菜单时 147 /* 148 * 从选中的节点开始,将其节点以及其一下节点都删除 149 * --无论从那个节点开始删除都能自如的将其子菜单以及子子菜单删除,以此类推 150 */ 151 List<Menu> mlist = db.Menu.Where(a => a.id.IndexOf(menu.id)>=0).ToList(); 152 if (mlist.Count>0) 153 { 154 foreach (var item in mlist) 155 { 156 db.Menu.Remove(item); 157 } 158 } 159 db.Menu.Remove(menu); 160 db.SaveChanges(); 161 m.Msg = "节点已经被成功删除!"; 162 m.Success = true; 163 return Json(m); 164 } 165 catch (Exception ex) 166 { 167 m.Msg = "删除失败!" + ex.Message; 168 return Json(m); 169 } 170 } 171 #endregion
效果图如下:
实例下载地址:
http://pan.baidu.com/share/link?shareid=512055&uk=2804979851
功能自己捣鼓,不足之处还望各位看客指出修正。