需求:
1.根据无限级的树形结构的json生成树菜单
2.树样式可以是图标类型和简单类型
3.可以自定义节点的图标
4.支持复选框
5.支持懒加载方式请求数据
6.支持节点点击事件
7.只有右键菜单【未实现】
8.支持拖拽调整节点【未实现】
实现图例
客户代码
1 <body> 2 <div id="Container" style="padding:10px; margin:0 auto;width:800px;height:300px;padding-top:10px;padding-left:100px"> 3 <ul id="tree"></ul> 4 </div> 5 <script type="text/javascript"> 6 function getAllCheckedNodes() { 7 var nodes = tree.tree(‘getCheckNodes‘, ‘‘); 8 alert(JSON.stringify(nodes)); 9 } 10 var data = [{ 11 "id1": "0", 12 "text1": "菜单", 13 "checked": true, 14 "iconCls": "icon-ok", 15 "children": [{ 16 "id1": "0_1", 17 "text1": "子菜单1", 18 "checked": true, 19 "iconCls": "icon-save" 20 }, { 21 "id1": "0_2", 22 "text1": "子菜单2", 23 "checked": true, 24 "iconCls": "icon-ok" 25 } 26 ] 27 },{ 28 "id1": ‘2‘, 29 "text1": "计算机语言", 30 "closed": false, 31 "children": [{ 32 "id1": "2_1", 33 "text1": "Java", 34 "children": [{ 35 "id1": ‘2_1_1‘, 36 "text1": ‘j2ee‘ 37 }, { 38 "id1": ‘2_1_2‘, 39 "text1": ‘j2me‘, 40 "checked": true, 41 "iconCls": "icon-ok" 42 }, { 43 "id1": ‘2_1_3‘, 44 "text1": ‘jsp‘ 45 }] 46 }, { 47 "id1": "2_2", 48 "text1": "C#" 49 }] 50 }]; 51 var tree; 52 $(function () { 53 tree = $("#tree").tree({ 54 onClick: function (data) { 55 console.log(JSON.stringify(data)); 56 alert("click"); 57 }, 58 animate: true, 59 isPlain: false, 60 checkbox: true, 61 textField: ‘text1‘, 62 idField: ‘id1‘, 63 //data: data 64 lazy:true, 65 url: ‘testServer/jsonQuestTest.ashx?flag=tree‘, 66 onLoadSuccess:function(data){ 67 console.log("服务器数据返回:"+ JSON.stringify(data)); 68 } 69 }); 70 }); 71 </script> 72 </body>
组件代码:
1 /************************************************************** 2 *作者:hjwen 3 *电邮:[email protected] 4 *版本:1.0 5 *版权许可:中国通用开源许可协议V1.0 6 *说明:tree组件 7 ***************************************************************/ 8 (function ($) { 9 var isPlain = false, checkbox = false, animate = false, lazy = false; textField = ‘text‘, idField = ‘id‘, url=""; 10 var treeDataObj = null; var treeOpts = null; 11 var onLoadSuccess = null;//加载成功 12 function renderHtml(target) { 13 target.addClass(‘tree‘); 14 treeOpts = target.data(‘settings‘); 15 if (treeOpts.data == null) { 16 alert("treeOpts.data 是必须的!"); 17 return; 18 } 19 treeDataObj = treeOpts.data; 20 target.parent().css("overflow", "auto"); 21 isPlain = treeOpts.isPlain; 22 checkbox = treeOpts.checkbox; 23 animate = treeOpts.animate; 24 lazy = treeOpts.lazy; 25 url = treeOpts.url; 26 textField = treeOpts.textField; 27 idField = treeOpts.idField; 28 var treeJson = treeOpts.data; 29 var ctlData={isRoot:true,path:‘‘,pid:‘‘,isLastf:false,isFirstf:false,isRootFirst:false,isRootLast:false}; 30 loopInitCreate(treeJson, 1, target, ctlData); 31 }; 32 function loopInitCreate(treeJson, treeDeep, target,ctlData) { 33 var lastItem; 34 $.each(treeJson, function (i, node) { 35 var children = node.children; 36 node.idx = i; 37 node.pid = ctlData.pid; 38 var controlData = {//控制参数 39 isRoot: ctlData.isRoot,//是否是树根 40 isFirst: false,//是否是第一个节点 41 isLast: false,//是否是最后一个节点 42 path: ctlData.path + i,//树路径,用于数据搜索,避免全树扫描性能问题 43 isLeaf: false,//是否是子叶 44 isLastf: ctlData.isLastf,//父元素是否为最后一个 ,第一级没有父元素为false 45 isFirstf: ctlData.isFirstf,//父元素是否为第一个 ,第一级没有父元素为false 46 treeDeep: treeDeep,//树深度 47 isRootFirst: ctlData.isRootFirst,//是否是根第一个节点,用于控制节点图标和补充线的样式 48 isRootLast: ctlData.isRootLast//是否是根最后一个节点,用于控制节点图标和补充线的样式 49 }; 50 if (i == 0) { 51 if (ctlData.isRoot) 52 controlData.isRootFirst = true; 53 controlData.isFirst = true; 54 } 55 if (i == treeJson.length - 1) { 56 controlData.isLast = true; 57 if (ctlData.isRoot) 58 controlData.isRootLast = true; 59 } 60 if (typeof children != ‘undefined‘ && $.isArray(children)) { 61 var li = $("<li class=\"tree_li\"></li>").appendTo(target); 62 if (children.length == 0) 63 node.closed = true; 64 lastItem = loopCreateTree(node, li, controlData); 65 } else {//子叶 66 controlData.isLeaf = true; 67 lastItem = createLeafNode(node, target, controlData); 68 } 69 }); 70 var tmpDeep = lastItem.attr("treedeep"); 71 var isleaf = lastItem.attr("isleaf"); 72 if (isleaf == ‘true‘){ 73 if(lastItem.attr("isrootlast")==‘true‘) 74 lastItem.children("div:lt(" + tmpDeep + ")").removeClass("tree_line_all").addClass("tree_line_up"); 75 }else { 76 if (lastItem.attr("isclose") == ‘true‘){ 77 lastItem.children("div:lt(" + tmpDeep + ")").removeClass("tree_collapsable_center").addClass("tree_collapsable_up"); 78 lastItem.children("div:lt(" + (parseInt(tmpDeep)-1) + ")").removeClass("tree_line_all").addClass("tree_line_up"); 79 } 80 else 81 lastItem.children("div:lt(" + tmpDeep + ")").removeClass("tree_expandable_center").addClass("tree_expandable_up"); 82 } 83 } 84 /**********私有方法开始********************/ 85 /****远程加载数据*****/ 86 function queryData(params,loadingContaner, fn) { 87 var ajaxopt = { 88 url: url, 89 params: params, 90 loadingContainer: loadingContaner, 91 okdeal: function (data) { 92 var arr = eval(data); 93 fn(arr); 94 if (typeof onLoadSuccess === ‘function‘) { 95 onLoadSuccess(arr); 96 } 97 } 98 }; 99 $.myui.ajaxRequest(ajaxopt); 100 }; 101 /***** 102 *修改树数据的checked属性,注意非子叶节点的checked只做参考 103 *@param path :节点的树路径,根据路径查找节点,避免全树搜索的性能开销 104 *@param checked 是否选中 105 ******/ 106 function modifyCheckAttr(path, checked) { 107 var pathArr = path.split(‘_‘); 108 //利用setTimeout模拟开启一个更新数据的线程,加快处理速度 109 setTimeout(function () { 110 //console.log("点击前数据=" + JSON.stringify(treeDataObj)); 111 loopCheckAttr(treeDataObj, pathArr, checked == ‘true‘); 112 //console.log("更新后数据=" + JSON.stringify(treeDataObj)); 113 },0); 114 }; 115 function loopCheckAttr(dataArr, pathArr, checked) { 116 for (var i = 0, len = dataArr.length; i < len; ++i) { 117 if (pathArr[0] == i) {//已经找到 118 dataArr[i].checked = checked; 119 if (pathArr.length > 1) {//根据path往下搜寻到末节点 120 if (typeof dataArr[i].children != ‘undefined‘ && $.isArray(dataArr[i].children)) {//如果有子集合 121 var tempArr = []; 122 for (var j = 0; j < pathArr.length; j++) { 123 if (j > 0) 124 tempArr.push(pathArr[j]); 125 } 126 loopCheckAttr(dataArr[i].children, tempArr, checked); 127 } 128 } else {//如果已经找到路径的末点,并且点击的是非子叶节点则需要再往下更新数据 129 if (typeof dataArr[i].children != ‘undefined‘ && $.isArray(dataArr[i].children)) { 130 loopChildrenAttr(dataArr[i].children, checked); 131 } 132 } 133 break; 134 } 135 } 136 }; 137 /****根据path获取点击节点的数据***/ 138 function getNodeByPath(dataArr, pathArr) { 139 for (var i = 0, len = dataArr.length; i < len; ++i) { 140 if (pathArr[0] == i) {//已经找到 141 if (pathArr.length > 1) {//根据path往下搜寻到末节点 142 if (typeof dataArr[i].children != ‘undefined‘ && $.isArray(dataArr[i].children)) { 143 var tempArr = []; 144 for (var j = 0; j < pathArr.length; j++) { 145 if (j > 0) 146 tempArr.push(pathArr[j]); 147 } 148 return getNodeByPath(dataArr[i].children, tempArr); 149 } 150 } else { 151 return dataArr[i]; 152 } 153 break; 154 } 155 } 156 }; 157 /********根据path获取checked=true节点的数据***********/ 158 function getCheckedNodeByPath(dataArr, pathArr) { 159 var res = []; 160 if (pathArr.length ==1 && pathArr[0] == "") { 161 $.each(dataArr, function (i, node) { 162 if (typeof node.children != ‘undefined‘ && $.isArray(node.children)) { 163 loopCheckedChildren(res, node.children); 164 } else { 165 if (node.checked) 166 res.push(node); 167 } 168 }); 169 } else { 170 var node = getNodeByPath(dataArr, pathArr); 171 if (typeof node.children != ‘undefined‘ && $.isArray(node.children)) { 172 loopCheckedChildren(res, node.children); 173 } else { 174 if (node.checked) 175 res.push(node); 176 } 177 } 178 return res; 179 }; 180 function loopCheckedChildren(res, nodes) { 181 $.each(nodes, function (i,node) { 182 if (typeof node.children != ‘undefined‘ && $.isArray(node.children)) { 183 loopCheckedChildren(res, node.children); 184 } else { 185 if (node.checked) 186 res.push(node); 187 } 188 }); 189 }; 190 function loopChildrenAttr(children, checked) { 191 $.each(children, function (j, node) { 192 node.checked = checked; 193 if (typeof node.children != ‘undefined‘ && $.isArray(node.children)) { 194 loopChildrenAttr(node.children, checked); 195 } 196 }); 197 }; 198 /**** 199 *复选框点击 200 ****/ 201 function chkClick(e) { 202 var t = $(this); 203 var checked; 204 var chkedString=‘true‘; 205 var p = t.parent("div"); 206 if (t.hasClass("tree_chk_check_all"))//注意点击时,发现为选中状态的,将会变更为非选择状态 207 chkedString = ‘false‘; 208 modifyCheckAttr(p.attr("path"), chkedString); 209 if (p.attr("isleaf") == ‘true‘) {//子叶点击 210 if (t.hasClass("tree_chk_check_all")) { 211 checked = false; 212 p.attr("checkstate", "-1"); 213 t.removeClass("tree_chk_check_all").addClass("tree_chk_uncheck"); 214 } else { 215 checked = true; 216 p.attr("checkstate", "1"); 217 t.removeClass("tree_chk_uncheck").addClass("tree_chk_check_all"); 218 } 219 loopCheckStyle(p.parent("li"), checked, 1); 220 } else { 221 var chkstate = p.attr("checkstate"); 222 if (chkstate == "1") {//如果是全选,在变为全取消 223 checked = false; 224 p.attr("checkstate", "-1"); 225 p.attr("checkedcount", "0"); 226 t.removeClass("tree_chk_check_all").addClass("tree_chk_uncheck"); 227 //向下修改子节点 228 loopCheckChildStyle(p.next().children("li"), checked); 229 //向上修改父节点 230 if (p.attr("isroot")==‘false‘) 231 loopCheckStyle(p.parent("li"), checked, 1) 232 } else {//变为全选状态 233 checked = true; 234 p.attr("checkstate", "1"); 235 p.attr("checkedcount", p.attr("childcount")); 236 t.removeClass("tree_chk_uncheck").removeClass("tree_chk_check").addClass("tree_chk_check_all"); 237 //向下修改子节点 238 loopCheckChildStyle(p.next().children("li"), checked); 239 //向上修改父节点 240 if (p.attr("isroot") == ‘false‘) 241 loopCheckStyle(p.parent("li"), checked, 1) 242 } 243 } 244 if (e && e.stopPropagation) 245 e.stopPropagation(); 246 else 247 window.event.cancelBubble = true; 248 }; 249 function loopCheckChildStyle(childrens,checked) { 250 $.each(childrens, function (i, node) { 251 var $node = $(node).children("div"); 252 var chkIoc = $node.children(".tree_chk_ioc"); 253 if (checked) { 254 $node.attr("checkstate","1"); 255 chkIoc.removeClass("tree_chk_uncheck").removeClass("tree_chk_check").addClass("tree_chk_check_all"); 256 if ($node.attr("isleaf") == ‘false‘) {//非子叶节点 257 $node.attr("checkedcount", $node.attr("childcount")); 258 loopCheckChildStyle($node.next().children("li"), checked); 259 } 260 } else { 261 $node.attr("checkstate", "-1"); 262 chkIoc.removeClass("tree_chk_check_all").removeClass("tree_chk_check").addClass("tree_chk_uncheck"); 263 if ($node.attr("isleaf") == ‘false‘) {//非子叶节点 264 $node.attr("checkedcount", "0"); 265 loopCheckChildStyle($node.next().children("li"), checked); 266 } 267 } 268 }); 269 } 270 /*** 271 *递归check样式检查,并根据check状态修改图标样式 272 *@param curLi 子叶项标签 273 *@param checked 复选框 选中/不选中 274 *@param count 加 减数量 275 ****/ 276 function loopCheckStyle(curLi, checked, count) { 277 var p_ul = curLi.parent("ul"); 278 var titleObj = p_ul.prev(); 279 var fchkCount = parseInt(titleObj.attr(‘checkedcount‘)); 280 var fchildCount = parseInt(titleObj.attr(‘childcount‘)); 281 var fchkState = titleObj.attr(‘checkstate‘); 282 var chkBoxDiv = titleObj.children(".chk_ioc"); 283 if (checked) {//如果是选中 284 fchkCount = fchkCount + count; 285 if (fchkCount > fchildCount) 286 fchkCount = fchildCount; 287 titleObj.attr(‘checkedcount‘, fchkCount); 288 // 修改父元素的 checkedcount checkstate 289 if (fchildCount == fchkCount) { 290 chkBoxDiv.removeClass("tree_chk_check"); 291 chkBoxDiv.removeClass("tree_chk_uncheck"); 292 chkBoxDiv.addClass("tree_chk_check_all"); 293 titleObj.attr(‘checkstate‘, ‘1‘); 294 } else { 295 chkBoxDiv.removeClass("tree_chk_uncheck"); 296 chkBoxDiv.removeClass("tree_chk_check_all"); 297 chkBoxDiv.addClass("tree_chk_check"); 298 titleObj.attr(‘checkstate‘, ‘0‘); 299 } 300 } else {//取消选中 301 fchkCount = fchkCount - count; 302 if (fchkCount < 0) 303 fchkCount = 0; 304 titleObj.attr(‘checkedcount‘, fchkCount); 305 if (fchkCount == 0) { 306 titleObj.attr(‘checkstate‘, ‘-1‘); 307 chkBoxDiv.removeClass("tree_chk_check"); 308 chkBoxDiv.removeClass("tree_chk_check_all"); 309 chkBoxDiv.addClass("tree_chk_uncheck"); 310 } else { 311 chkBoxDiv.removeClass("tree_chk_uncheck"); 312 chkBoxDiv.removeClass("tree_chk_check_all"); 313 chkBoxDiv.addClass("tree_chk_check"); 314 titleObj.attr(‘checkstate‘, ‘0‘); 315 } 316 } 317 //递归修改上一级父元素的状态 318 if (titleObj.attr("isroot") == ‘false‘) { 319 count = 0; 320 if (titleObj.attr("checkstate") == "1" || titleObj.attr("checkstate") == "-1") { 321 count = 1; 322 } 323 loopCheckStyle(titleObj.parent("li"), checked, count); 324 } 325 }; 326 /**** 327 *节点点击,展开/收起 328 *****/ 329 function nodeClick(e) { 330 var t = $(this); 331 var p = t.parent("div"); 332 var ul = p.next(); 333 if (p.attr("isclose") == ‘false‘) {//收起 334 p.children(".ioc_ioc").removeClass("tree_node_file_open").addClass("tree_node_file_close");//改变文件夹样式 335 if (p.attr("isrootfirst") == ‘true‘ && p.attr("treedeep") == ‘1‘) {//如果是根节点 336 if (p.attr("islast")==‘true‘) 337 t.addClass("tree_collapsable_all"); 338 else 339 t.addClass("tree_collapsable_down"); 340 341 t.removeClass("tree_expandable_down"); 342 } else { 343 t.removeClass("tree_expandable_center"); 344 if (p.attr("isrootlast") == ‘true‘ && p.attr("islast") == ‘true‘) { 345 if (p.attr("isfirst") == ‘true‘) 346 t.addClass("tree_collapsable_all"); 347 else 348 t.addClass("tree_collapsable_up"); 349 } else { 350 t.addClass("tree_collapsable_center"); 351 } 352 } 353 if (p.attr("isrootlast") == ‘true‘ && p.attr("islast") == ‘true‘ && p.attr("islastf") == ‘true‘) { 354 p.children(".line_ioc").removeClass("tree_line_all").addClass("tree_line_up"); 355 } 356 if (animate) { 357 ul.hide(300); 358 } else { 359 ul.hide(); 360 } 361 p.attr("isclose", ‘true‘); 362 } else {//展开 363 p.children(".ioc_ioc").removeClass("tree_node_file_close").addClass("tree_node_file_open"); 364 if (p.attr("isrootfirst") == ‘true‘ && p.attr("treedeep") == ‘1‘) {//如果是根节点 365 t.removeClass("tree_collapsable_down"); 366 t.addClass("tree_expandable_down"); 367 } else { 368 if (p.attr("islast") == "true"){ 369 t.removeClass("tree_collapsable_up"); 370 t.prevAll().removeClass("tree_line_up").addClass("tree_line_all"); 371 } 372 else 373 t.removeClass("tree_collapsable_center"); 374 t.addClass("tree_expandable_center"); 375 } 376 if (p.attr("isrootlast") == ‘true‘ && p.attr("islast") == ‘true‘ && p.attr("islastf") == ‘true‘) { 377 p.children(".line_ioc").removeClass("tree_line_up").addClass("tree_line_all"); 378 } 379 if (animate) { 380 ul.show(300); 381 } else { 382 ul.show(); 383 } 384 p.attr("isclose", ‘false‘); 385 if (lazy&&url!="") { 386 if (p.attr("childcount") == ‘0‘) { 387 var target = p.next("ul"); 388 var loadingContaner = $("<li></li>").appendTo(target); 389 queryData("pid=" + p.attr("id"), loadingContaner, function (data) { 390 //将查询到的数据补充到treeDataObj,采用settimeout 391 setTimeout(function () { 392 var pathArr = p.attr("path").split("_"); 393 //console.log("添加前数据=" + JSON.stringify(treeDataObj)); 394 var findedNode = getNodeByPath(treeDataObj, pathArr); 395 findedNode.children = data; 396 //console.log("添加后数据=" + JSON.stringify(treeDataObj)); 397 }, 0); 398 loadingContaner.remove(); 399 p.attr("childcount", data.length); 400 var ctlData = { 401 isRoot: false, 402 path: p.attr("path") + "_", 403 pid: p.attr("id"), 404 isLastf: p.attr("islast")==‘true‘, 405 isFirstf: p.attr("isfirst") == ‘true‘, 406 isRootFirst: p.attr("isrootfirst") == ‘true‘, 407 isRootLast: p.attr("isrootlast") == ‘true‘ 408 }; 409 if (p.attr("isroot") == ‘true‘) { 410 ctlData.isRootFirst = ctlData.isFirstf; 411 ctlData.isRootLast = ctlData.isLastf; 412 } 413 loopInitCreate(data, parseInt(p.attr("treeDeep")) + 1, target, ctlData); 414 }); 415 } 416 } 417 } 418 if (e && e.stopPropagation) 419 e.stopPropagation(); 420 else 421 window.event.cancelBubble = true; 422 } 423 /**** 424 *创建一个子节点 425 *@param node 节点数据 426 *@param target 节点目标容器 427 *@param controlData 控制ui的数据 428 *****/ 429 function createLeafNode(node, target, controlData) { 430 /***********线样式***************/ 431 //根据树深度补充外围线 432 var lineCls = ‘‘, help = 1, fixLineDiv = ‘‘; 433 if (controlData.isLast && controlData.isFirst && controlData.isRoot) {//只有一个子叶节点 434 lineCls = ""; 435 } else { 436 if (controlData.isLast && controlData.isRoot) { 437 lineCls = "tree_line_up"; 438 } else if (controlData.isFirst && controlData.isRoot) { 439 lineCls = "tree_line_down"; 440 } else { 441 if (controlData.isLast) { 442 lineCls = "tree_line_up"; 443 } else { 444 lineCls = "tree_line_center"; 445 } 446 } 447 } 448 while (help <= controlData.treeDeep) { 449 if (help == controlData.treeDeep) {//自身列 450 fixLineDiv = fixLineDiv + "<div class=\"tree_ioc_item tree_line_ioc " + lineCls + "\"></div>"; 451 } else { 452 fixLineDiv = fixLineDiv + "<div class=\"tree_ioc_item tree_line_ioc tree_line_all\"></div>"; 453 } 454 help++; 455 } 456 /**************图标样式****************/ 457 var iconDiv = ‘‘; 458 if (!isPlain) {//不是简单样式 459 var iconCls = ‘tree_file_ioc tree_node_leaf‘;//默认的图标样式 460 if (typeof node.iconCls == ‘string‘) { 461 iconCls = node.iconCls; 462 iconDiv = "<div style=\"background-position:0 2px;\" class=\"tree_ioc_item " + iconCls + "\"></div>"; 463 } else { 464 iconDiv = "<div class=\"tree_ioc_item " + iconCls + "\"></div>"; 465 } 466 } 467 /**************复选框样式***************/ 468 var chkBoxDiv = ‘‘, checkstate = ‘-1‘; 469 if (checkbox) { 470 var checked = node.checked; 471 var chkCls = ‘tree_chk_uncheck‘; 472 if (typeof checked == ‘boolean‘ && checked) { 473 chkCls = ‘tree_chk_check_all‘; 474 checkstate = ‘1‘; 475 } else { 476 node.checked = false;//客户代码中没有checked,则补充上 477 } 478 chkBoxDiv = "<div class=‘tree_ioc_item tree_chk_ioc " + chkCls + "‘></div>"; 479 } 480 var id = node[idField]; 481 var text = node[textField]; 482 var li_html = "<li class=\"tree_li\"><div treedeep=‘" + controlData.treeDeep + "‘ isfirstf=‘" + controlData.isFirstf + "‘ islastf=‘" + controlData.isLastf + "‘ isrootlast=‘" + controlData.isRootLast + "‘ isrootfirst=‘" + controlData.isRootFirst + "‘ path=‘" + controlData.path + "‘ checkstate=‘" + checkstate + "‘ pid=‘" + node.pid + "‘ isleaf=‘true‘ isLast=‘" + controlData.isLast + "‘ isfirst=‘" + controlData.isFirst + "‘ isroot=‘" + controlData.isRoot + "‘ treedeep=‘" + controlData.treeDeep + "‘ id=‘" + id + "‘ class=‘tree_ioc_wrap‘>" + fixLineDiv + iconDiv + chkBoxDiv + "<div class=‘tree_li_text‘>" + text + "</div></div></li>"; 483 var li = $(li_html).appendTo(target); 484 var wrap = li.children("div"); 485 if (typeof treeOpts.onClick == ‘function‘) { 486 wrap.bind(‘click‘, function () { 487 var data = getNodeByPath(treeDataObj,$(this).attr("path").split("_")); 488 treeOpts.onClick(data); 489 }); 490 } 491 if (checkbox) { 492 var checked = node.checked; 493 if (typeof checked == ‘boolean‘ && checked) { 494 loopCheckStyle(li, true, 1); 495 } 496 wrap.children(".tree_chk_ioc").bind(‘click‘, chkClick); 497 } 498 return li.children("div"); 499 }; 500 501 /**** 502 *循环递归创建一个父节点,这里肯定是子树 503 *@param node 节点数据 504 *@param target 节点目标容器 505 *@param controlData 控制ui的数据 506 *****/ 507 function loopCreateTree(nodef, target, controlData) { 508 var treeJson = nodef.children; 509 var iconDiv = ""; 510 var closed = false; 511 if (typeof nodef.closed == ‘boolean‘) 512 closed = nodef.closed; 513 /************文件夹样式*****************/ 514 var lineCls, hideCls, help = 1, fixLineDiv = ‘‘, fixLineCls = ‘tree_line_all‘; 515 if (closed) {//闭合状态 516 hideCls = ‘display:none‘; 517 if (controlData.isRoot && controlData.isFirst) {//根目录第一个节点 518 lineCls = ‘tree_collapsable_down‘; 519 } else { 520 if (controlData.isFirst) { 521 lineCls = ‘tree_collapsable_down‘; 522 } else { 523 lineCls = ‘tree_collapsable_center‘; 524 } 525 } 526 //闭合状态下,要处理最后一个关闭节点的外围补充线样式问题 527 if (controlData.isRootLast && controlData.isLast) { 528 fixLineCls = ‘tree_line_up‘; 529 lineCls = ‘tree_collapsable_up‘; 530 } 531 } else {//打开状态 532 hideCls = ‘display:block‘; 533 if (controlData.isRoot && controlData.isFirst) {//根目录第一个节点 534 lineCls = ‘tree_expandable_down‘; 535 } else { 536 if (controlData.isFirst) { 537 lineCls = ‘tree_expandable_center‘; 538 } else { 539 lineCls = ‘tree_expandable_center‘; 540 } 541 } 542 } 543 //根据树深度补充外围线 544 while (help <= controlData.treeDeep) { 545 if (help == controlData.treeDeep) { 546 fixLineDiv = fixLineDiv + "<div class=\"node_ioc tree_ioc_item tree_line_ioc " + lineCls + "\"></div>" 547 } else { 548 fixLineDiv = fixLineDiv + "<div class=\"line_ioc tree_ioc_item tree_line_ioc " + fixLineCls + "\"></div>"; 549 } 550 help++; 551 } 552 /**************图标样式 *****************/ 553 if (!isPlain) {//不是简单样式 554 var tmpCls; 555 if (closed) { 556 tmpCls = "tree_node_file_close"; 557 } else { 558 tmpCls = "tree_node_file_open"; 559 } 560 var iconCls = ‘tree_file_ioc ‘ + tmpCls;//默认的图标样式 561 iconDiv = "<div class=\"ioc_ioc tree_ioc_item " + iconCls + "\"></div>"; 562 } 563 /**************复选框******************/ 564 var chkBoxDiv = ‘‘; 565 var checkstate = "-1"; //-1不选择,0部分选择,1全部选择 566 var chkAll = false; 567 if (checkbox) { 568 var chkCls = ‘tree_chk_uncheck‘; 569 chkBoxDiv = "<div class=‘chk_ioc tree_ioc_item tree_chk_ioc " + chkCls + "‘></div>"; 570 if (typeof nodef.checked == ‘boolean‘) 571 chkAll = nodef.checked; 572 } 573 var id = nodef[idField]; 574 var text = nodef[textField]; 575 var headHtml = "<div treedeep=‘" + controlData.treeDeep + "‘ isclose=‘" + closed + "‘ isfirstf=‘" + controlData.isFirstf + "‘ islastf=‘" + controlData.isLastf + "‘ isrootlast=‘" + controlData.isRootLast + "‘ isrootfirst=‘" + controlData.isRootFirst + "‘ path=‘" + controlData.path + "‘ checkstate=‘" + checkstate + "‘ checkedcount=‘0‘ childcount=‘" + treeJson.length + "‘ isleaf=‘false‘ pid=‘" + nodef.pid + "‘ islast=‘" + controlData.isLast + "‘ isfirst=‘" + controlData.isFirst + "‘ isroot=‘" + controlData.isRoot + "‘ treedeep=‘" + controlData.treeDeep + "‘ id=‘" + id + "‘ class=\"tree_ioc_wrap\">" + fixLineDiv + iconDiv + chkBoxDiv + "<div class=‘tree_li_text‘>" + text + "</div></div>"; 576 var headObj = $(headHtml).appendTo(target); 577 if (typeof treeOpts.onClick == ‘function‘) { 578 headObj.bind(‘click‘, function () { 579 var data = getNodeByPath(treeDataObj,$(this).attr("path").split("_")); 580 treeOpts.onClick(data); 581 }); 582 } 583 if (checkbox) { 584 headObj.children(".chk_ioc").bind(‘click‘, chkClick); 585 } 586 headObj.children(".node_ioc").bind(‘click‘, nodeClick); 587 var ul = $("<ul style=‘" + hideCls + "‘></ul>").appendTo(target); 588 var chkCount = 0; 589 $.each(treeJson, function (i, node) { 590 var children = node.children; 591 node.pid = id; 592 node.idx = i; 593 var cdata = { //控制参数 594 isRoot: false, 595 isFirst: false, 596 isLast: false, 597 path: controlData.path + "_" + i, 598 isLeaf: false, 599 isLastf: false, 600 isFirstf: false, 601 treeDeep: controlData.treeDeep + 1, 602 isRootFirst: controlData.isRootFirst, 603 isRootLast: controlData.isRootLast 604 }; 605 if (checkbox) { 606 if (chkAll) 607 node.checked = true; 608 if (node.checked) 609 chkCount++; 610 } 611 if (controlData.isLast) 612 cdata.isLastf = true; 613 if (controlData.isFirst) 614 cdata.isFirstf = true; 615 if (i == treeJson.length - 1) 616 cdata.isLast = true; 617 if (i == 0) 618 cdata.isFirst = true; 619 if (typeof children != ‘undefined‘ && $.isArray(children)) { 620 var li = $("<li class=\"tree_li\"></li>").appendTo(ul); 621 if (children.length == 0) 622 node.closed = true; 623 headObj = loopCreateTree(node, li, cdata); 624 } else { 625 headObj = createLeafNode(node, ul, cdata); 626 } 627 }); 628 if (chkCount == treeJson.length) 629 nodef.checked = true; 630 else 631 nodef.checked = false; 632 return headObj; 633 }; 634 /**********私有方法结束*******************/ 635 var methods = { 636 init: function (options) { 637 if ($.isArray(options)) { 638 options = { 639 data: options 640 }; 641 } 642 return this.each(function () { 643 var $this = $(this); 644 var settings = $this.data(‘settings‘); 645 if (typeof (settings) == ‘undefined‘) { 646 settings = $.extend({}, $.fn.tree.defaults, options); 647 $this.data(‘settings‘, settings); 648 } else { 649 settings = $.extend({}, settings, options); 650 } 651 //创建ui布局 652 if (settings.url != ‘‘) {//远程请求数据 653 onLoadSuccess = settings.onLoadSuccess; 654 url = settings.url; 655 queryData(‘‘,$this, function (data) { 656 settings.data = data; 657 renderHtml($this); 658 }); 659 } else { 660 renderHtml($this); 661 } 662 if ($.myui.isDebug) { 663 $.myui.log("jQuery.tree init finish......"); 664 } 665 }); 666 }, 667 destroy: function (options) { 668 return $(this).each(function () { 669 var $this = $(this); 670 $this.removeData(‘settings‘); 671 }); 672 }, 673 /**** 674 *@params path 节点路径,path为空时则获取整个树所有checked状态的节点 675 &@return 节点数组 676 *****/ 677 getCheckNodes: function (path) { 678 var pathArr = path.split(‘_‘); 679 return getCheckedNodeByPath(treeDataObj, pathArr); 680 } 681 }; 682 /******************** 683 *组件的构造函数 684 *********************/ 685 $.fn.tree = function () { 686 var method = arguments[0]; 687 if (methods[method]) { 688 method = methods[method]; 689 arguments = Array.prototype.slice.call(arguments, 1); 690 } else if (typeof (method) == ‘object‘ || !method) { 691 if ($.myui.isDebug) { 692 $.myui.log("jQuery.tree init....."); 693 } 694 method = methods.init; 695 } else { 696 $.error(‘Method ‘ + method + ‘ does not exist on jQuery.tree‘); 697 return this; 698 } 699 return method.apply(this, arguments); 700 }; 701 /******************** 702 *组件的默认配置值 703 *********************/ 704 $.fn.tree.defaults = { 705 textField: ‘text‘,//菜单名称字段,默认为text 706 idField: ‘id‘,//菜单id字段,默认为id 707 url: ‘‘,//远程加载数据地址 708 lazy:false,//延时加载,当设置为true时,点击节点展开时,如果子元素为空则根据节点id发起请求加载子节点集合 709 data: null,//树json数据 710 isPlain: false,//true 为简单无图标样式 711 checkbox: false,//是否需要选择框 712 animate: true,//是否需要动画 713 onLoadSuccess: null,//加载成功 714 onClick: null,//点击事件 715 isDrag: false,//是否可以拖拽调整节点 716 onPreDrag: null,//拖拽前 未实现 717 onDrag: null,//拖拽释放后未实现, 718 contextMenu: false //右键菜单未实现 719 }; 720 })(jQuery);
我的开源框架之树控件
时间: 2024-10-13 18:44:46