jstree树形菜单
final 用于声明属性、方法和类,分别表示属性不可变,方法不可重写,类不可继承。
其实可以参考用easyui的tree 和 ztree
参考:
https://www.jstree.com/demo/
https://www.jstree.com/plugins/
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta http-equiv="X-UA-Compatible" content="IE=edge"> <meta name="viewport" content="width=device-width, initial-scale=1"> <title>菜单配置页面</title> <!-- css代码 --> <link rel="stylesheet" type="text/css" href="/css/bootstrap.min.css"> <!-- <link rel="stylesheet" type="text/css" href="/css/default/style.min.css"> --> <style type="text/css"> .demo { width: 250px; margin: 0 17px 17px 0; float: left; border: 1px solid #ebebeb; height: 197px; } .last { margin-right: 0; } </style> <!-- 引入外部js --> <script type="text/javascript" src="/js/fe/jquery-2.1.4.min.js"></script> <script type="text/javascript" src="/js/fe/bootstrap.min.js"></script> <script type="text/javascript" src="/js/fe/jquery.jstree.js"></script> <script type="text/javascript" src="/js/fe/jquery.hotkeys.js"></script> <script type="text/javascript" src="/js/fe/jquery.cookie.js"></script> <script type="text/javascript" src="/js/fe/jstree.min.js"></script> <!-- js代码 --> <script type="text/javascript"> // jQuery(document).ready(function() { $(function() { var selectRole = ""; // 初始化jstree $("#menuTreeContainer").jstree({ "core": { "strings": { loading: "Loading ..." } }, "json_data": { "ajax": { "dataType": 'json', // 使用ajax加载数据,如果和data同时使用则只在打开未加载的子节点时起作用 "url": "/config/queryAllMenuNodes.json", "async": false, "success": function(data) { if (data.success) { return _callBack(data.content); } } } }, "ui" : { "initially_select" : [] }, "themes": { "icons": false }, "plugins": ["themes", "json_data", "ui","search"] }) .bind("loaded.jstree", function(e, data) { //初始打开第一个叶子节点所在目录 $("#menuTreeContainer").jstree("open_all"); }) .bind("select_node.jstree", function(e, data) { $("#currentPath").val(data.rslt.obj.data("path")); $("#currentNode_id").val(data.rslt.obj.data("id")); $("#currentNode_name").val(data.rslt.obj.data("name")); $("#currParentPath").val(data.rslt.obj.data("parentPath")); }); }); // }); function _callBack(data) { var res = [], expIds = [], attr = {}; jQuery.each(data, function(i) { var childData = data[i].children; if (!childData || jQuery.trim(childData).length == 0) { childData = ""; } // var state = "open"; var href = ""; var image = ""; res.push({ "attr": { "id": data[i].id }, "data": { "title": data[i].name }, "children": _callBack(data[i].children), "metadata": { "id": data[i].id, "name": data[i].name, "parentId": data[i].parentId, "path": data[i].path, "parentPath": data[i].parentPath }, // "state": state, "icon": image }); }); return res; }; function menuCreate() { var ref = $('#menuTreeContainer').jstree(true), sel = ref.get_selected(); if (!sel.length) { return false; } sel = sel[0]; sel = ref.create_node(sel, { "type": "file" }); if (sel) { ref.edit(sel); } }; function menuRename() { var ref = $('#menuTreeContainer').jstree(true), sel = ref.get_selected(); if (!sel.length) { return false; } sel = sel[0]; ref.edit(sel); }; //打开/关闭所有节点 var openAllNode = function(e) { $("#menuTreeContainer").jstree("open_all"); } var hideAllNode = function(e) { $("#menuTreeContainer").jstree("close_all"); } function showParentPath() { jQuery.ajax({ dataType: 'json', type: 'POST', async: false, url: 'getMenuParentPath.json', data: param, success: function(data) { if (data.success) { _.each(data.content, function(v) { jQuery("select[name=add-columnsecuritylevel-select]").append("<option value='" + v.code + "'>" + v.code + "</option>"); jQuery("select[name=modify-columnsecuritylevel-select]").append("<option value='" + v.code + "'>" + v.code + "</option>"); }); } else { alert(data.message); } } }); } var modifyMenu = function() { var nodeParentPath = $("#currParentPath").val(), nodeParentId=$("#currentNode_id").val(), nodeName=$("#currentNode_name").val(); $("#nodeOpera_parentId").val($("#currentNode_id").val()); $("#nodeOpera_parentPath").val($("#currentPath").val()); jQuery.ajax({ dataType: 'json', type: 'POST', url: 'modifyMenuTree.json', data: { "nodeParentId":nodeParentId, "nodeName":nodeName, "nodeParentPath":nodeParentPath, "type":"type" }, success: function(data) { if (data.success) { alert("修改成功"); } else { alert(data.message); } } }); }; var delMenuNode = function() { var nodeId=$("#currentNode_id").val(); jQuery.ajax({ dataType: 'json', type: 'POST', url: 'delMenuNode.json', data: { "nodeId":nodeId, }, success: function(data) { if (data.success) { $("#menuTreeContainer").jstree("close_all"); $("#menuTreeContainer").jstree("open_all"); alert("删除成功"); } else { alert(data.message); } } }); }; var addSubNode = function() { $("#nodeOpera_path").val($("#currentPath").val() +"-"+ $("#nodeOpera_name").val()); $("#nodeOpera_parentPath").val($("#currentPath").val()); var nodeName = $("#nodeOpera_name").val(); var nodeParentId = $("#currentNode_id").val(); var nodePath=$("#nodeOpera_path").val(); var parentPath = $("#nodeOpera_parentPath").val(); alert("nodeName"+nodeName); alert("nodeParentId"+nodeParentId); alert("nodePath"+nodePath); alert("parentPath"+parentPath); jQuery.ajax({ dataType: 'json', type: 'POST', url: 'addMenuSubNode.json', data: { "nodeName":nodeName, "nodeParentId":nodeParentId, "nodePath":nodePath, "parentPath":parentPath }, success: function(data) { if (data.success) { $("#menuTreeContainer").jstree("close_all"); $("#menuTreeContainer").jstree("open_all"); alert("增加子目录成功"); } else { alert(data.message); } } }); }; var setValue = function(){ var nodeName = jQuery("#nodeOpera_name").val(); jQuery("#nodeOpera_parentPath").val(tableName); } var addRootNode = function() { $("#nodeOpera_parentId").val("0"); $("#nodeOpera_parentPath").val("菜单"); $("#nodeOpera_path").val($("#nodeOpera_name").val()); var nodeName = $("#nodeOpera_name").val(); var nodeParentId = $("#nodeOpera_parentId").val(); var nodePath=$("#nodeOpera_path").val(); var parentPath = $("#nodeOpera_parentPath").val(); if(!nodeName || jQuery.trim(nodeName).length == 0) { alert("节点名称不能为空"); return; } jQuery.ajax({ dataType: 'json', type: 'POST', url: 'addMenuRootNode.json', data: { "nodeName":nodeName, "nodeParentId":nodeParentId, "nodePath":nodePath, "parentPath":parentPath }, success: function(data) { if (data.success) { $("#menuTreeContainer").jstree("close_all"); $("#menuTreeContainer").jstree("open_all"); alert("增加根目录成功"); } else { alert(data.message); } } }); } var searchMenu = function(e) { var searchContent = $("#treeSearchInput").val(); alert("条件:" + searchContent); $("#menuTreeContainer").jstree("search",searchContent); }; </script> </head> <body> <!-- HTML布局 --> <div class="main-container warp"> <div class="col-md-4 col-sm-8 col-xs-8" style="float:bottom"> <button type="button" class="btn btn-success btn-sm" onclick="menuCreate();">Create</button> <button type="button" class="btn btn-warning btn-sm" onclick="menuRename();">Rename</button> <button type="button" class="btn btn-danger btn-sm" onclick="menuDelete();"> Delete</button> </div> <div> <form onsubmit="return false"> <input id="treeSearchInput" type="search" maxlength="20" class="input-medium search-query" /> <input id="SearchSubmit" class="btn" type="submit" onclick ="searchMenu()" value="搜索" /> </form> </div> <div id="menuTreeContainer" class="fh-leftList demo last" style="font-size:15px;backgroud: #ffffff"></div> </div> <div class="span8" style="float:left"> <form class="form-horizontal"> <div class="control-group"> <div id="nodeOpera_buttons" class="controls"> <input type="button" class="btn" id="nodeOpera_add_root" onclick="addRootNode()" value="新增根目录" /> <input type="button" class="btn" id="nodeOpera_add_sub" onclick="addSubNode()" value="新增子目录" /> <input type="button" class="btn" id="nodeOpera_modify" onclick="modifyMenu()" value="保存修改" /> <input type="button" class="btn" id="nodeOpera_delete" onclick="delMenuNode()" value="删除目录" /> <input type="button" class="btn" onclick="openAllNode()" value="全部展开" /> <input type="button" class="btn" onclick="hideAllNode()" value="全部隐藏" /> </div> </div> <div id="currentNode" style=""> <input type="hidden" id="currentNode_parentId" /> <div class="control-group"> <p style="font-size: 20px;color: red;" class="controls validateTips" id="validateTips_modify"></p> </div> <div class="control-group"> <label class="control-label">序号</label> <div class="controls"> <input type="text" id="currentNode_id" readonly="readonly" maxlength="9" /> </div> </div> <div class="control-group"> <label class="control-label">名称</label> <div class="controls"> <input type="text" id="currentNode_name" maxlength="20" /> </div> </div> <div class="control-group"> <label class="control-label">挂载菜单点</label> <div class="controls"> <input type="text" id="currParentPath" maxlength="20" /> </div> </div> <div class="control-group"> <label class="control-label">当前路径</label> <div class="controls"> <input type="text" id="currentPath" maxlength="20" /> </div> </div> </div> </form> <div id="nodeOpera_data" style=""> <!-- <input type="hidden" id="nodeOpera_parentId" /> <p class="validateTips" id="validateTips_add" style="color: red;"></p> --> <label>父节点ID</label><input type="text" id="nodeOpera_parentId" maxlength="500" /><br> <label>父节点路径</label><input type="text" id="nodeOpera_parentPath" onlyNumber="true" maxlength="9" /><br> <label>名称</label><input type="text" id="nodeOpera_name" onkeyup="setValue()"/><br> <label>所在路径</label><input type="text" id="nodeOpera_path" maxlength="20" /><br> </div> </div> </div> </body> </html>
后台构造函数
private List<TreeKey> convertTree(List<MdMenuTree> rst) { List<TreeKey> treeAttrs = new ArrayList<TreeKey>(); for (MdMenuTree menuTree : rst) { TreeKey node = new TreeKey(); node.setId(menuTree.getId()); node.setName(menuTree.getName()); node.setParentPath(menuTree.getParentPath()); node.setParentId(menuTree.getParentId()); node.setPath(menuTree.getPath()); treeAttrs.add(node); } return treeAttrs; } private List<TreeKey> loadTree(List<TreeKey> treeAttrs, long parentId) { List<TreeKey> nodeList = new ArrayList<TreeKey>(); for (TreeKey node2 : treeAttrs) { if ((parentId == node2.getParentId())) { List<TreeKey> childNodes = loadTree(treeAttrs, node2.getId()); node2.setChildren(childNodes); nodeList.add(node2); } } return nodeList; }
踩过的坑
会无线循环下去,我的初步想法是去掉那个虚线的图标,或者在虚线那个“+”和“-”上加个控制事件,但是,这个办法行不通
解决答案:
根节点有 state='closed' 属性。
去掉那个state="closed"(注意,改成open是不行的),否则这个节点会被视为还有子节点,jstree会再次调用你的ajax配置的url以加载子节点的数据。 你也可以修改你的url的服务器实现,根据父节点的id返回不同的元素以实现逐级打开的效果。 并设置correct_state标志以实现节点状态的自动更正。
correct_state属性:
如果设定为true,对于ajax返回的空的反馈结果,将被转换为子节点,而不再显示为打开样式。
坚持不懈