爱上MVC3~MVC+ZTree实现对树的CURD及拖拽操作
上一讲中,我们学习了如何使用zTree对一棵大树(大数据量的树型结构的数据表,呵呵,名称有点绕,但说的是事实)进行异步加载,今天这讲,我们来说说,如何去操作这棵大树,无非就是添加子节点,删除节点,编辑节点,节点之间的拖拽,节点位置的变化(排序)等等。
事实上,jquery.ZTree的demo已经把前台JS代码给的很清晰了,我们只要稍加修改,然后把后台CS代码写好就可以了,我将demo的前台代码做了简单的处理,减化了些代码,项目架构使用microsoft mvc 3.0
前台HTML代码如下:
<link href="../../Scripts/JQuery-zTree/css/zTreeStyle/zTreeStyle.css" rel="stylesheet" /> <script src="../../Scripts/JQuery-zTree/js/jquery-1.4.4.min.js"></script> <script src="../../Scripts/JQuery-zTree/js/jquery.ztree.all-3.5.min.js"></script> <style type="text/css"> .ztree li span.button.add { margin-left: 2px; margin-right: -1px; background-position: -144px 0; vertical-align: top; *vertical-align: middle; } a { text-decoration: underline; } </style> <script type="text/javascript"> /* 动态数据测试部分开始 */ var log, className = "dark"; var treeID = "treeDemo"; var curDragNodes, autoExpandNode; var newCount = 1; //异步加载节点 var setting4 = { data: { simpleData: { enable: true, idKey: "id", pIdKey: "pId", rootPId: null } }, edit: { enable: true, editNameSelectAll: true, showRemoveBtn: showRemoveBtn, showRenameBtn: showRenameBtn, removeTitle: "删除", renameTitle: "编辑", drag: { autoExpandTrigger: true, prev: dropPrev, inner: dropInner, next: dropNext } }, view: { addHoverDom: addHoverDom, removeHoverDom: removeHoverDom, selectedMulti: false }, async: { //异步加载 enable: true, url: "/ZTree/AsyncGetNodes", autoParam: ["id", "name", "pId"] }, callback: { /*节点获取与展开回调*/ beforeExpand: beforeExpand, onAsyncSuccess: onAsyncSuccess, onAsyncError: onAsyncError, /*编辑与删除回调*/ beforeEditName: beforeEditName, beforeRemove: beforeRemove, beforeRename: beforeRename, onRemove: onRemove, onRename: onRename, /*拖动回调*/ beforeDrag: beforeDrag, beforeDrop: beforeDrop, beforeDragOpen: beforeDragOpen, onDrag: onDrag, onDrop: onDrop, onExpand: onExpand } }; function beforeEditName(treeId, treeNode) { className = (className === "dark" ? "" : "dark"); var zTree = $.fn.zTree.getZTreeObj(treeID); zTree.selectNode(treeNode); return confirm("进入节点 -- " + treeNode.name + " 的编辑状态吗?"); } function beforeRemove(treeId, treeNode) { className = (className === "dark" ? "" : "dark"); var zTree = $.fn.zTree.getZTreeObj(treeID); zTree.selectNode(treeNode); return confirm("确认删除 节点 -- " + treeNode.name + " 吗?"); } function beforeRename(treeId, treeNode, newName, isCancel) { className = (className === "dark" ? "" : "dark"); if (newName.length == 0) { alert("节点名称不能为空."); var zTree = $.fn.zTree.getZTreeObj(treeID); setTimeout(function () { zTree.editName(treeNode) }, 10); return false; } return true; } function onRemove(e, treeId, treeNode) { $.ajax({ url: '/ZTree/AsyncDelNodes', //url action是方法的名称 data: { id: treeNode.id }, type: 'POST', success: function (data) { alert(data); } }); } function onRename(e, treeId, treeNode, isCancel) { alert("编辑了节点" + treeNode.id + " " + treeNode.name); $.ajax({ url: '/ZTree/AsyncEditNodes', //url action是方法的名称 data: { id: treeNode.id, name: treeNode.name }, type: 'POST', success: function (data) { alert(data); } }); } function showRemoveBtn(treeId, treeNode) { //return !treeNode.isFirstNode; return true; } function showRenameBtn(treeId, treeNode) { // return treeNode.isLastNode; return true; } function addHoverDom(treeId, treeNode) { var sObj = $("#" + treeNode.tId + "_span"); if (treeNode.editNameFlag || $("#addBtn_" + treeNode.tId).length > 0) return; var addStr = "<span class='button add' id='addBtn_" + treeNode.tId + "' title='add node' onfocus='this.blur();'></span>"; sObj.after(addStr); var btn = $("#addBtn_" + treeNode.tId); if (btn) btn.bind("click", function () { var zTree = $.fn.zTree.getZTreeObj(treeID); zTree.addNodes(treeNode, { id: (100 + newCount), pId: treeNode.id, name: "new node" + newCount }); $.ajax({ url: '/ZTree/AsyncAddNodes', //url action是方法的名称 data: { id: (100 + newCount), pid: treeNode.id, name: "new node" + newCount }, type: 'POST', success: function (data) { //异常完成后,刷新父节点及下面所有子节点 zTree.reAsyncChildNodes(treeNode, "refresh"); } }); newCount++; return false; }); }; function removeHoverDom(treeId, treeNode) { $("#addBtn_" + treeNode.tId).unbind().remove(); }; function createTree() { $.ajax({ url: '/ZTree/AsyncGetNodes', //url action是方法的名称 data: { id: 0 }, type: 'Get', dataType: "text", //可以是text,如果用text,返回的结果为字符串;如果需要json格式的,可是设置为json success: function (data) { $.fn.zTree.init($("#" + treeID), setting4, eval('(' + data + ')')); }, error: function (msg) { alert(" 数据加载失败!" + msg); } }); } function beforeExpand(treeId, treeNode) { if (!treeNode.isAjaxing) { return true; } else { alert("zTree 正在下载数据中,请稍后展开节点。。。"); return false; } } function onAsyncSuccess(event, treeId, treeNode, msg) { } function onAsyncError() { alert(" 数据加载失败"); } $(document).ready(function () { createTree(); }); function dropPrev(treeId, nodes, targetNode) { var pNode = targetNode.getParentNode(); if (pNode && pNode.dropInner === false) { return false; } else { for (var i = 0, l = curDragNodes.length; i < l; i++) { var curPNode = curDragNodes[i].getParentNode(); if (curPNode && curPNode !== targetNode.getParentNode() && curPNode.childOuter === false) { return false; } } } return true; } function dropInner(treeId, nodes, targetNode) { if (targetNode && targetNode.dropInner === false) { return false; } else { for (var i = 0, l = curDragNodes.length; i < l; i++) { if (!targetNode && curDragNodes[i].dropRoot === false) { return false; } else if (curDragNodes[i].parentTId && curDragNodes[i].getParentNode() !== targetNode && curDragNodes[i].getParentNode().childOuter === false) { return false; } } } return true; } function dropNext(treeId, nodes, targetNode) { var pNode = targetNode.getParentNode(); if (pNode && pNode.dropInner === false) { return false; } else { for (var i = 0, l = curDragNodes.length; i < l; i++) { var curPNode = curDragNodes[i].getParentNode(); if (curPNode && curPNode !== targetNode.getParentNode() && curPNode.childOuter === false) { return false; } } } return true; } function beforeDrag(treeId, treeNodes) { className = (className === "dark" ? "" : "dark"); for (var i = 0, l = treeNodes.length; i < l; i++) { if (treeNodes[i].drag === false) { curDragNodes = null; return false; } else if (treeNodes[i].parentTId && treeNodes[i].getParentNode().childDrag === false) { curDragNodes = null; return false; } } curDragNodes = treeNodes; return true; } function beforeDragOpen(treeId, treeNode) { autoExpandNode = treeNode; return true; } function beforeDrop(treeId, treeNodes, targetNode, moveType, isCopy) { className = (className === "dark" ? "" : "dark"); return true; } function onDrag(event, treeId, treeNodes) { className = (className === "dark" ? "" : "dark"); } function onDrop(event, treeId, treeNodes, targetNode, moveType, isCopy) { className = (className === "dark" ? "" : "dark"); $.ajax({ url: '/ZTree/AsyncDragNodes', // url action是方法的名称 data: { id: treeNodes[0].id, pid: targetNode.id, name: treeNodes[0].name, moveType: moveType, isCopy: isCopy }, type: 'POST', success: function (data) { } }); } function onExpand(event, treeId, treeNode) { if (treeNode === autoExpandNode) { className = (className === "dark" ? "" : "dark"); } } </script> <div class="zTreeDemoBackground left"> <ul id="treeDemo" class="ztree"></ul> </div>
面各种JS事件,所对应的MVC代码如下:
#region 对节点的操作 /// <summary> /// 得到一级子节点 /// </summary> /// <param name="id"></param> /// <returns></returns> public string AsyncGetNodes(int? id) { var model = irepository.GetEntities() .Where(i => i.ParentID == id).OrderBy(i => i.Sortable); return model.ToJson(); } /// <summary> /// 添加节点 /// </summary> /// <param name="id"></param> /// <param name="name"></param> /// <param name="pid"></param> /// <returns></returns> public string AsyncAddNodes(int id, string name, int pid) { try { //得到父对象,但设置它的isParent属性 irepository.Modify(i => new Category { ID = pid, IsParent = true, }); var entity = new Category { Name = name, ParentID = pid, CreateDate = DateTime.Now, Level = 1, IsParent = false, }; irepository.Add(entity); return entity.ID.ToString(); } catch (Exception ex) { return ex.Message; } } /// <summary> /// 编辑节点(重命名) /// </summary> /// <param name="id"></param> /// <param name="name"></param> /// <returns></returns> public string AsyncEditNodes(int id, string name) { try { irepository.Modify(i => new Category { ID = id, Name = name, }); return "操作成功"; } catch (Exception ex) { return ex.Message; } } /// <summary> /// 删除节点 /// </summary> /// <param name="id"></param> /// <returns></returns> public string AsyncDelNodes(int id) { try { irepository.Remove(irepository.GetEntity(i => i.ID == id)); return "操作成功"; } catch (Exception ex) { return ex.Message; } } /// <summary> /// 拖拽节点 /// </summary> /// <param name="id"></param> /// <param name="pid"></param> /// <param name="sortable"></param> /// <returns></returns> public string AsyncDragNodes( int id, int pid, string name, string moveType, bool isCopy) { try { var parent = irepository.GetEntity(i => i.ID == pid); var parentSons = irepository.GetEntities(i => i.ParentID == pid); var current = irepository.GetEntity(i => i.ID == id); if (moveType == "inner") parent.isParent = true; else if (parentSons == null || parentSons.Count() == 0) parent.isParent = false; if (isCopy)//复制,前台目前不能实现 { irepository.Add(new Category { ParentID = pid, Name = name, CreateDate = DateTime.Now, }); } else { if (moveType == "inner") { current.ParentID = pid; irepository.Modify(current); } else { current.ParentID = parent.ParentID;//同级移动时,与目标节点父ID相同 current.Sortable = moveType == "next" ? parent.Sortable + 1 : parent.Sortable - 1; irepository.Modify(current); } } irepository.Modify(parent); return "操作成功"; } catch (Exception ex) { return ex.Message; } } #endregion
好了,上面的代码只是对树结果做的最基本的操作,其中拖拽时的复制操作(按着ctrl键,拖拽节点),后台没有进行实现。