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 添加相册
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  删除相册
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节点增删改