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 {
         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返回的空的反馈结果,将被转换为子节点,而不再显示为打开样式。

原文地址:https://www.cnblogs.com/kxdblog/p/4738281.html