Ztree 仿淘宝树结构完美实现 移动 右键增删改

Posted 青小稞

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Ztree 仿淘宝树结构完美实现 移动 右键增删改相关的知识,希望对你有一定的参考价值。

官网介绍 zTree 是一个依靠 jQuery 实现的多功能 “树插件”。优异的性能、灵活的配置、多种功能的组合是 zTree 最大优点。
1、zTree 是利用 JQuery 的核心代码,实现一套能完成大部分常用功能的 Tree 插件
2、zTree v3.0 将核心代码按照功能进行了分割,不需要的代码可以不用加载
3、采用了 延迟加载 技术,上万节点轻松加载,即使在 IE6 下也能基本做到秒杀
4、兼容 IE、FireFox、Chrome、Opera、Safari 等浏览器
5、支持 JSON 数据
6、支持静态 和 Ajax 异步加载节点数据
7、支持任意更换皮肤 / 自定义图标(依靠css)
8、支持极其灵活的 checkbox 或 radio 选择功能
9、提供多种事件响应回调
10、灵活的编辑(增/删/改/查)功能,可随意拖拽节点,还可以多节点拖拽哟
11、在一个页面内可同时生成多个 Tree 实例
12、简单的参数配置实现 灵活多变的功能

ztree是一个很好的树形结构组件,依赖Jquery,以其优异和完备的api可以自定义树形的多种实现,做到灵活配置,最近看了淘宝的图片空间,用的也恰好是ztree,所已就打算copy下来...

先看下效果图:



继续贴代码:
首先引入相关的配置依赖文件

1 <link href="$!webPath/resources/js/zTree/css/zTreeStyle/zTreeStyle.css" type="text/css" rel="stylesheet"/>
2 <script type="text/javascript" charset="utf-8" src="$!webPath/resources/js/zTree/js/jquery.ztree.core-3.5.js"></script>
3 <script type="text/javascript" charset="utf-8" src="$!webPath/resources/js/zTree/js/jquery.ztree.excheck-3.5.js"></script>
4 <script type="text/javascript" charset="utf-8" src="$!webPath/resources/js/zTree/js/jquery.ztree.exedit-3.5.js"></script>


相关json数据

复制代码
 1 [
 2     {
 3         "id": "1",
 4         "name": "我的图片",
 5         "isParent": true,
 6         "checked": false,
 7         "nocheck": false,
 8         "open": true,
 9         "click": "photo_manage.accessory_list(\'1\',\'common\')",
10         "children": [
11             {
12                 "id": "1753",
13                 "name": "测试_123kl",
14                 "isParent": true,
15                 "checked": false,
16                 "nocheck": false,
17                 "open": false,
18                 "click": "photo_manage.accessory_list(\'1753\',\'common\')"
19             },
20             {
21                 "id": "1478",
22                 "name": "日化家居",
23                 "isParent": true,
24                 "checked": false,
25                 "nocheck": false,
26                 "open": false,
27                 "click": "photo_manage.accessory_list(\'1478\',\'common\')"
28             },
29             {
30                 "id": "1477",
31                 "name": "美容彩妆",
32                 "isParent": true,
33                 "checked": false,
34                 "nocheck": false,
35                 "open": false,
36                 "click": "photo_manage.accessory_list(\'1477\',\'common\')"
37             },
38             {
39                 "id": "1476",
40                 "name": "母婴用品",
41                 "isParent": true,
42                 "checked": false,
43                 "nocheck": false,
44                 "open": false,
45                 "click": "photo_manage.accessory_list(\'1476\',\'common\')"
46             },
47             {
48                 "id": "1475",
49                 "name": "营养保健",
50                 "isParent": true,
51                 "checked": false,
52                 "nocheck": false,
53                 "open": false,
54                 "click": "photo_manage.accessory_list(\'1475\',\'common\')"
55             },
56             {
57                 "id": "1474",
58                 "name": "进口食品",
59                 "isParent": true,
60                 "checked": false,
61                 "nocheck": false,
62                 "open": false,
63                 "click": "photo_manage.accessory_list(\'1474\',\'common\')"
64             }
65         ]
66     }
67 ]
复制代码

 

在script脚本中定义setting和zTreeNodes

复制代码
 1     var curStatus = "init", curAsyncCount = 0, asyncForAll = false, goAsync = false;
 2     var setting = {
 3         async: {
 4             enable: true,
 5             autoParam: ["id=ids"],//, "name=n", "level=lv"
 6             url: "$!webPath/xxx/xxxTreeData.htm",
 7             dataFilter: filter,
 8             type: "post"
 9         },
10         view: {
11             fontCss: getFont,
12             showLine: true,
13             expandSpeed: "",
14             addHoverDom: addHoverDom,
15             //removeHoverDom: removeHoverDom,
16             selectedMulti: false
17         },
18         edit: {
19             drag: {
20                 isCopy: true,
21                 isMove: true,
22                 prev:true,
23                 next:true
24             },
25             enable: true,
26             showRemoveBtn: false,
27             //removeTitle: "删除菜单",
28             renameTitle: "编辑菜单名称"
29         },
30         data: {
31             keep: {
32                 parent: true
33             },
34             simpleData: {
35                 enable: false
36             }
37         },
38         callback: {
39             onRightClick: onRightClick,
40             //beforeRemove: beforeRemove,    //节点被删除之前的事件,并且根据返回值确定是否允许删除操作
41             beforeRename: beforeRename,    //用于捕获节点编辑名称结束
42 
43             beforeAsync: beforeAsync,    //用于捕获异步加载之前的事件回调函数,zTree 根据返回值确定是否允许进行异步加载
44             onAsyncSuccess: onAsyncSuccess,    //用于捕获异步加载出现异常错误的事件回调函数
45             onAsyncError: onAsyncError,    //用于捕获异步加载正常结束的事件回调函数
46 
47             beforeDrag: beforeDrag,    //用于捕获节点被拖拽之前的事件回调函数,并且根据返回值确定是否允许开启拖拽操作
48             beforeDrop: beforeDrop,    //用于捕获节点拖拽操作结束之前的事件回调函数,并且根据返回值确定是否允许此拖拽操作
49             beforeDragOpen: beforeDragOpen,    //用于捕获拖拽节点移动到折叠状态的父节点后,即将自动展开该父节点之前的事件回调函数,并且根据返回值确定是否允许自动展开操作
50             onDrag: onDrag,    //用于捕获节点被拖拽的事件回调函数
51             onDrop: onDrop,    //用于捕获节点拖拽操作结束的事件回调函数
52             onExpand: onExpand    //用于捕获节点被展开的事件回调函数
53         }
54     };
复制代码


相关的html和css

复制代码
 1 <div >
 2     <div class="zTreeDemoBackground left"  >
 3         <!--<ul id="treeDemo" class="ztree"></ul>-->
 4         <ul id="zTreeMenuContent" class="ztree" style="height: 790px;"></ul>
 5     </div>
 6 </div>
 7 <style type="text/css">
 8     div#rMenu{
 9         position: fixed;
10         visibility:hidden;
11         top:0;
12         background-color: #fff;
13         text-align: left;
14         border: 1px solid #c9caca;
15         box-shadow: 0 0 2px #c9caca;
16         padding: 2px 0;
17         z-index: 999;
18     }
19 
20     div#rMenu ul li{
21         margin: 1px 0;
22         padding: 0 5px;
23         cursor: pointer;
24         list-style: none;
25         height: 30px;
26         background-color: #fff;
27 
28     }
29     div#rMenu ul li:hover{
30         background: #ddd;
31     }
32 </style>
33 
34 <div id="rMenu" style="width: 120px;height: 110px;font-size: 12px;" >
35     <ul>
36         <li id="m_add" >
37             <i class="fa fa-plus fa-lg" aria-hidden="true"></i>
38             <span style="color:#1681ff;">
39                 &nbsp;添加相册
40             </span>
41 
42         </li>
43         <li id="m_rename"  onclick="editAlbumName();">
44             <i class="fa fa-edit fa-lg" aria-hidden="true"></i>
45             <span style="color:#1681ff;">
46                 重新命名
47             </span>
48         </li>
49         <li id="m_del"  >
50             <i class="fa fa-close fa-lg" aria-hidden="true"></i>
51             <span style="color:#1681ff;">
52                 &nbsp删除相册
53             </span>
54         </li>
55     </ul>
56 </div>
复制代码


基本增删改和右键功能

复制代码
  1     //节点数据过滤
  2     function filter(treeId, parentNode, childNodes) {
  3         if (!childNodes) {
  4             return null;
  5         }
  6         for (var i = 0, l = childNodes.length; i < l; i++) {
  7             childNodes[i].name = childNodes[i].name.replace(/\\.n/g, \'.\');
  8         }
  9         return childNodes;
 10     }
 11 
 12     function expandNodes(nodes) {
 13         if (!nodes) {
 14             return;
 15         }
 16 
 17         curStatus = "expand";
 18         var zTree = jQuery.fn.zTree.getZTreeObj("zTreeMenuContent");
 19         if (zTree != null) {
 20             for (var i = 0, l = nodes.length; i < l; i++) {
 21                 zTree.expandNode(nodes[i], true, false, false);
 22                 if (nodes[i].isParent && nodes[i].zAsync) {
 23                     expandNodes(nodes[i].children);
 24                 } else {
 25                     goAsync = true;
 26                 }
 27             }
 28         }
 29     }
 30 
 31     //字体设置
 32     function getFont(treeId, node) {
 33         return node.font ? node.font : {};
 34     }
 35 
 36     ////////////////////下面是处理增删改节点//////////////////
 37 
 38     //节点被删除之前的事件,并且根据返回值确定是否允许删除操作
 39     function beforeRemove(treeId, treeNode) {
 40 
 41     }
 42 
 43     //用于捕获节点编辑名称
 44     function beforeRename(treeId, treeNode, newName, isCancel) {
 45 
 46         if (treeNode.id === "1") {
 47             layer.msg(\'根目录不能编辑!\', {icon: 3});
 48             return true;
 49         } else {
 50             if (treeNode.name == newName) {
 51                 return true;
 52             } else if (newName.length == 0) {
 53                 layer.msg(\'菜单名称不能为空!\', {icon: 3});
 54                 treeNode.name = treeNode.name;
 55                 return false;
 56             } else {
 57 
 58                 var cn_pattern= /[\\u4e00-\\u9fa5]/g;
 59                 var g = newName.match(cn_pattern);
 60                 var cn_length = newName.length;
 61                 if(g!=null){
 62                     var cn_numbers = g.length;
 63                     cn_length = cn_numbers*2+(cn_length-cn_numbers);
 64                 }
 65 
 66                 if(cn_length>99){
 67                     layer.msg(\'名称不能超过99个字符(1个汉字为2个字符)\', {icon: 7});
 68                     return false;
 69                 }else{
 70                     var param_data = {"album_id": treeNode.id, "album_name": newName};
 71                     var reData = \'\';
 72                     jQuery.ajax({
 73                         type: "post",
 74                         async: false,
 75                         url: "$!webPath/xxx/xxx_ajax_update.htm",
 76                         data: param_data,
 77                         success: function (save_id) {
 78                             reData = save_id;
 79                         }
 80                     });
 81                     if (reData == treeNode.id) {
 82                         return true;
 83                     } else {
 84                         layer.msg("修改<" + treeNode.name + ">菜单名称失败!", {icon: 3});
 85                         return false;
 86                     }
 87                 }
 88 
 89             }
 90         }
 91     }
 92 
 93     //添加菜单
 94     var newCount = 1;
 95     function addHoverDom(treeId, treeNode) {
 96         var sObj = $("#" + treeNode.tId + "_span");
 97         if (treeNode.editNameFlag || $("#addBtn_" + treeNode.tId).length > 0) {
 98             return;
 99         }
100     };
101 
102 
103     function dropNext(treeId, nodes, targetNode) {
104         var pNode = targetNode.getParentNode();
105         if (pNode && pNode.dropInner === false) {
106             return false;
107         } else {
108             for (var i = 0, l = curDragNodes.length; i < l; i++) {
109                 var curPNode = curDragNodes[i].getParentNode();
110                 if (curPNode && curPNode !== targetNode.getParentNode() && curPNode.childOuter === false) {
111                     return false;
112                 }
113             }
114         }
115         return true;
116     }
117 
118     function dropPrev(treeId, nodes, targetNode) {
119         var pNode = targetNode.getParentNode();
120         if (pNode && pNode.dropInner === false) {
121             return false;
122         } else {
123             for (var i = 0, l = curDragNodes.length; i < l; i++) {
124                 var curPNode = curDragNodes[i].getParentNode();
125                 if (curPNode && curPNode !== targetNode.getParentNode() && curPNode.childOuter === false) {
126                     return false;
127                 }
128             }
129         }
130         return true;
131     }
132 
133     function dropInner(treeId, nodes, targetNode) {
134         if (targetNode && targetNode.dropInner === false) {
135             return false;
136         } else {
137             for (var i = 0, l = curDragNodes.length; i < l; i++) {
138                 if (!targetNode && curDragNodes[i].dropRoot === false) {
139                     return false;
140                 } else if (curDragNodes[i].parentTId && curDragNodes[i].getParentNode() !== targetNode && curDragNodes[i].getParentNode().childOuter === false) {
141                     return false;
142                 }
143             }
144         }
145         return true;
146     }
147 
148     //className = "dark",
149     //用于捕获节点被拖拽之前的事件回调函数,并且根据返回值确定是否允许开启拖拽操作
150     var log, curDragNodes, autoExpandNode;
151     function beforeDrag(treeId, treeNodes) {
152         //className = (className === "dark" ? "" : "dark");
153         for (var i = 0, l = treeNodes.length; i < l; i++) {
154             if (treeNodes[i].drag === false) {
155                 curDragNodes = null;
156                 return false;
157             } else if (treeNodes[i].parentTId && treeNodes[i].getParentNode().childDrag === false) {
158                 curDragNodes = null;
159                 return false;
160             }
161         }
162         curDragNodes = treeNodes;
163         return true;
164     }
165 
166 
167     //用于捕获节点被拖拽的事件回调函数
168     function onDrag(event, treeId, treeNodes) {
169         //className = (className === "dark" ? "" : "dark");
170     }
171 
172     //用于捕获节点拖拽操作结束的事件回调函数
173     function onDrop(event, treeId, treeNodes, targetNode, moveType, isCopy) {
174 
175         if (treeNodes.length > 0 && targetNode) {
176             var dragId = treeNodes[0].id;//被拖拽菜单
177             var targetId = targetNode.id;//拖拽到的目标菜单
178 
179             //判断是否同级拖动
180             var treeNodes_parentId  = treeNodes[0].parentTId;
181             var targetNode_parentId = targetNode.parentTId;
182             var is_child_move = false;
183             if(treeNodes_parentId==targetNode_parentId && moveType!="inner"){
184                 is_child_move = true;
185             }
186             var data = { "album_id" : dragId, "target_id" : targetId ,"moveType":moveType , "is_child_move" : is_child_move};
187             jQuery.ajax({
188                 type: "post",
189                 async: false,
190                 url: "$!webPath/xxx/xxx_ajax_drag_update.htm",
191                 data: data,
192                 success: function(save_id){
193                     layer.msg(\'移动成功!\', {icon: 1});
194                 },
195                 error: function () {
196                     layer.msg(\'服务器内部异常!\', {icon: 1});
197                 }
198             });
199         }
200     }
201 
202     //用于捕获节点拖拽操作结束之前的事件回调函数,并且根据返回值确定是否允许此拖拽操作
203     function beforeDrop(treeId, treeNodes, targetNode, moveType, isCopy) {
204         if(targetNode){
205             var targetNode_parentId = targetNode.parentTId;
206             if(targetNode_parentId==null){
207                 return false;
208             }else{
209                 return true;
210             }
211         }else{
212             return false;
213         }
214     }
215 
216     //用于捕获节点被展开的事件回调函数
217     function onExpand(event, treeId, treeNode) {
218         if (treeNode === autoExpandNode) {
219             //className = (className === "dark" ? "" : "dark");
220         }
221     }
222 
223     function beforeAsync() {
224         layer.msg(\'加载中\', {time: 6000,icon: 16});
225         curAsyncCount++;
226     }
227 
228     function onAsyncSuccess(event, treeId, treeNode, msg) {
229 
230         curAsyncCount--;
231         if (curStatus == "expand") {
232             expandNodes(treeNode.children);
233         } else if (curStatus == "async") {
234             asyncNodes(treeNode.children);
235         }
236         //console.info(curStatus);
237         if (curAsyncCount <= 0) {
238             if (curStatus != "init" && curStatus != "") {
239                 asyncForAll = true;
240             }
241             curStatus = "";
242         }
243         layer.closeAll();
244     }
245 
246     function asyncNodes(nodes) {
247         if (!nodes) {
248             return;
249         }
250         curStatus = "async";
251         var zTree = jQuery.fn.zTree.getZTreeObj("zTreeMenuContent");
252         for (var i = 0, l = nodes.length; i < l; i++) {
253             if (nodes[i].isParent && nodes[i].zAsync) {
254                 asyncNodes(nodes[i].children);
255             } else {
256                 goAsync = true;
257                 zTree.reAsyncChildNodes(nodes[i], "refresh", true);
258             }
259         }
260     }
261 
262     function asyncAll() {
263         if (!check()) {
264             return;
265         }
266         var zTree = jQuery.fn.zTree.getZTreeObj("zTreeMenuContent");
267         if (asyncForAll) {
268 
269         } else {
270             asyncNodes(zTree.getNodes());
271             if (!goAsync) {
272                 curStatus = "";
273             }
274         }
275     }
276 
277     //用于捕获异步加载正常结束的事件回调函数
278     function onAsyncError(event, treeId, treeNode, XMLHttpRequest, textStatus, errorThrown) {
279         curAsyncCount--;
280         if (curAsyncCount <= 0) {
281             curStatus = "";
282             if (treeNode != null) asyncForAll = true;
283         }
284     }
285 
286     //用于捕获拖拽节点移动到折叠状态的父节点后,即将自动展开该父节点之前的事件回调函数,并且根据返回值确定是否允许自动展开操作
287     function beforeDragOpen(treeId, treeNode) {
288         autoExpandNode = treeNode;
289         return true;
290     }
291 
292 
293     //字体设置
294     function getFont(treeId, node) {
295         return node.font ? node.font : {};
296     }
297 
298     //初始化
299     var rMenu,zTree;
300     $(document).ready(function () {
301         zTree = jQuery.fn.zTree.init($("#zTreeMenuContent"), setting);
302         $("#callbackTrigger").bind("change", {}, setTrigger);    //拖拽节点时自动展开父节点是否触发
303         rMenu = $("#rMenu");
304     });
305 
306     function reset() {
307         if (!check()) {
308             return;
309         }
310         asyncForAll = false;
311         goAsync = false;
312         jQuery.fn.zTree.init($("#zTreeMenuContent"), setting);
313     }
314 
315     function check() {
316         if (curAsyncCount > 0) {
317             //$("#demoMsg").text("正在进行异步加载,请等一会儿再点击...");
318             return false;
319         }
320         return true;
321     }
322 
323 
324     function expandAll() {
325         if (!check()) {
326             return;
327         }
328         var zTree = jQuery.fn.zTree.getZTreeObj("zTreeMenuContent");
329         if (zTree == null)
330             return false;
331 
332         if (asyncForAll) {
333             zTree.expandAll(true);
334         } else {
335             expandNodes(zTree.getNodes());
336             if (!goAsync) {
337                 curStatus = "";
338             }
339         }
340     }
341     function setTrigger() {
342         var zTree = jQuery.fn.zTree.getZTreeObj("zTreeMenuContent");
343         zTree.setting.edit.drag.autoExpandTrigger = $("#callbackTrigger").attr("checked");
344     }
345     //默认打开所有子级
346 //    setTimeout(function () {
347 //        expandAll();
348 //    }, 500);
349 
350     //初始化关闭所有子级
351     expandAll();
352 
353 
354 
355     //鼠标右键功能
356     function onRightClick(event, treeId, treeNode) {
357 
358         //var x = event.clientX+48;
359         //var y = event.clientY-132;
360         var x = event.clientX;
361         var y = event.clientY;
362         if (!treeNode && event.target.tagName.toLowerCase() != "button" && $(event.target).parents("a").length == 0) {
363             zTree.cancelSelectedNode();
364             showRMenu("root", x, y);
365         } else if (treeNode && !treeNode.noR) {
366             zTree.selectNode(treeNode);
367             showRMenu("node", x, y);
368         }
369     }
370 
371     function editAlbumName(){
372         hideRMenu();
373         zTree = jQuery.fn.zTree.getZTreeObj("zTreeMenuContent");
374         var nodes = zTree.getSelectedNodes();
375         zTree.editName(nodes[0]);
376         return true;
377     }
378 
379 
380 
381     function showRMenu(type, x, y) {
382         $("#rMenu ul").show();
383         if (type=="root") {
384             $("#m_del").hide();
385             $("#m_rename").hide();
386         } else {
387             $("#m_del").show();
388             $("#m_rename").show();
389             $("#m_add").show();
390         }
391         rMenu.css({"top":y+"px", "left":x+"px", "visibility":"visible"});
392         $("body").bind("mousedown", onBodyMouseDown);
393     }
394 
395     function hideRMenu() {
396         if (rMenu) rMenu.css({"visibility": "hidden"});
397         jQuery("body").unbind("mousedown", onBodyMouseDown);
398     }
399     function onBodyMouseDown(event){
400         if (!(event.target.id == "rMenu" || $(event.target).parents("#rMenu").length>0)) {
401             rMenu.css({"visibility" : "hidden"});
402         }
403     }
复制代码
复制代码
 1         photo_del:function(){       /*删除相册*/
 2             $("#syncBtndel,#m_del").unbind("click").click(function(){
 3 
 4                 var treeObj = jQuery.fn.zTree.getZTreeObj("zTreeMenuContent");
 5                 var nodes = treeObj.getSelectedNodes(true);
 6                 if(nodes.length<=0){
 7                     layer.msg(\'请选择一个相册\', {icon: 3});
 8                     return false;
 9                 }
10                 var node = nodes[0];
11                 if(node.id==1){
12                     layer.msg(\'根目录不能删除\', {icon: 3});
13                     return false;
14                 }
15 
16                 layer.confirm(\'确定要删除(\'+node.name+\')相册吗,该相册下的图片不会被删除?\', {
17                     btn: [\'确定\',\'取消\'] //按钮
18                 }, function(){
19 
20                     var data = { "album_id" : node.id };
21                     var reData = \'\';
22                     jQuery.ajax({
23                         type: "post",
24                         async: false,
25                         url: web_Path+"/xxx/xxx_album_del.htm",
26                         data: data,
27                         success: function(delete_id){
28                             reData = delete_id;
29                             //刷新树节点
30                             //treeObj.reAsyncChildNodes(null, "refresh");
31 
32                             //移除删除的节点
33                             treeObj.removeNode(node);
34 
35                             //刷新相册
36                             photo_manage.accessory_list(null,"delce");
37 
38                         }
39                     });
40                     if(reData != node.id){
41                         layer.msg("删除<" + node.name + ">菜单失败!", {icon: 3});
42                     }
43                     layer.closeAll();
44                 }, function(){
45                     layer.msg(\'已取消\', {time: 2000, });
46                 });
47             });
48         },
复制代码
复制代码
 1         photo_add:function(){      /*添加相册*/
 2 
 3             var photo_name = $("#groupTitle").val();
 4             if(isEmpty(photo_name)){
 5                 layer.msg(\'相册名称不能为空\', {icon: 7});
 6                 return false;
 7             }
 8 
 9             //相册名称格式验证
10             //var pattern = new RegExp("^[\\u4E00-\\u9FA5A-Za-z0-9]{1,20}$");
11             //if(!pattern.test(photo_name)){
12             //    layer.msg(\'请输入正确的格式(中文、英文、数字)\', {icon: 7});
13             //    return false;
14             //}
15 
16             //相册名称长度验证{名称不能超过20个字符(1个汉字为2个字符)}
17             var cn_pattern= /[\\u4e00-\\u9fa5]/g;
18             var g = photo_name.match(cn_pattern);
zTree节点增删改

ztree树的使用

ztree实现拖拽移动和复制

ztree : 增删改功能demo与自定义DOM功能demo的结合

ztree树在SSSM中的增删改查

Django---进阶5