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;
}

 

踩过的坑

jstree树形菜单

会无线循环下去,我的初步想法是去掉那个虚线的图标,或者在虚线那个“+”和“-”上加个控制事件,但是,这个办法行不通

解决答案: 

根节点有 state='closed' 属性。

去掉那个state="closed"(注意,改成open是不行的),否则这个节点会被视为还有子节点,jstree会再次调用你的ajax配置的url以加载子节点的数据。 你也可以修改你的url的服务器实现,根据父节点的id返回不同的元素以实现逐级打开的效果。 并设置correct_state标志以实现节点状态的自动更正。

correct_state属性:

  如果设定为true,对于ajax返回的空的反馈结果,将被转换为子节点,而不再显示为打开样式。

上一篇:EA+svn实现UML的版本号控制


下一篇:Java EE 学习(8):IDEA + maven + spring 搭建 web(4)- 用户管理