需求:在项目下有许多个模块,需要在 树形菜单中对模块进行管理(包括增加删除节点,以及修改节点文本信息)
寻找解决方法:打算用网上的插件,找了多个,觉得 ZTREE 插件比较符合我的要求,随即下载了源码包,当中有许多例子,
官方有文档和预览效果,最终 async_edit.html 这个例子作为模板。
解决过程:后台使用的springmvc ,数据库mysql ,前台jsp
在数据库中 的 module数据表 字段有 id, prarentid,name 属性
TreeNode.java 是用来传递数据的实体类,代码如下
import java.util.List;
public class TreeNode{
private Integer id;
private String name;
private Integer parentid;
private List<TreeNode> children;
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public Integer getParentid() {
return parentid;
}
public void setParentid(Integer parentid) {
this.parentid = parentid;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public List<TreeNode> getChildren() {
return children;
}
public void setChildren(List<TreeNode> children) {
this.children = children;
}
}
</pre>将数据库 module 数据表的数据通过 treenode 包装成 json数据 返回给前台,代码如下<pre name="code" class="html">/**
* 显示模块树形列表
* @param projectid
* @return
*/
@RequestMapping("/findlist")
public @ResponseBody List<TreeNode> findlist(@RequestParam(value = "projectid", required = false) Integer projectid,Model model) {
List<TreeNode> nodeList = new ArrayList<TreeNode>();
TreeNode rootnode = new TreeNode();
rootnode.setId(0);
rootnode.setName("项目模块");
rootnode.setChildren(new ArrayList<TreeNode>());
if(projectid==null){
nodeList.add(rootnode);
return nodeList;
}
List<Module> modules = moduleService.selectByProjectId(projectid);
List<TreeNode> treelist = new ArrayList<TreeNode>();
for (Module module : modules) {
TreeNode treeNode = new TreeNode();
treeNode.setId(module.getId());
treeNode.setName(module.getName());
if(module.getParentid()!=null){
treeNode.setParentid(module.getParentid());
}
treelist.add(treeNode);
}
if(!treelist.isEmpty()){
for(TreeNode node1 : treelist){
boolean mark = false;
for(TreeNode node2 : treelist){
if(node1.getParentid()!=null && node1.getParentid().equals(node2.getId())){
mark = true;
if(node2.getChildren() == null)
node2.setChildren(new ArrayList<TreeNode>());
node2.getChildren().add(node1);
break;
}
}
if(!mark){
rootnode.getChildren().add(node1);;
}
}
nodeList.add(rootnode);
}
//转为json格式
return nodeList;
}
/**
* 获取项目中模块的最大id
* @param projectid
* @return
*/
public int getMaxModuleId(){
List<Module> modules = moduleService.selectList();
int max=0;
for (Module module : modules) {
if(max<module.getId()){
max=module.getId();
}
}
return max;
}
由于没有将整个项目作为根节点保存进数据库,所以这里手动指定 rootnode 为根节点,并做了相应逻辑判断,当该项目下没有模块是,仍然显示根节点的数据(不参与数据库读写,后边存数据的时候做了剔除)
前台 js
function init() {
$('.easy-tree').EasyTree({
addable: true,
editable: true,
deletable: true
});
}
var setting = {
async: {
enable: true,
url:"project/findlist?projectid="+$("#projectid").val()+"",
autoParam:["id", "name=n", "level=lv"],
otherParam:{"otherParam":"zTreeAsyncTest"},
dataFilter: filter
},
view: {expandSpeed:"",
addHoverDom: addHoverDom,
removeHoverDom: removeHoverDom,
selectedMulti: false
},
edit: {
enable: true,
showRemoveBtn: setRemoveBtn,
showRenameBtn: setRemoveBtn
},
data: {
simpleData: {
enable: true
}
},
callback: {
beforeAsync: beforeAsync,
onAsyncSuccess: onAsyncSuccess,
onAsyncError: onAsyncError,
beforeRemove: beforeRemove,
beforeRename: beforeRename,
}
};
function setRemoveBtn(treeId, treeNode) {
if(treeNode.level == 0)
return false;
else
return true;
};
function filter(treeId, parentNode, childNodes) {
if (!childNodes) return null;
for (var i=0, l=childNodes.length; i<l; i++) {
childNodes[i].name = childNodes[i].name.replace(/\.n/g, '.');
}
return childNodes;
}
function beforeRemove(treeId, treeNode) {
var zTree = $.fn.zTree.getZTreeObj("treeDemo");
zTree.selectNode(treeNode);
return confirm("确认删除 模块 -- " + treeNode.name + " 吗?");
}
function beforeRename(treeId, treeNode, newName) {
if (newName.length == 0) {
alert("模块名称不能为空.");
return false;
}
return true;
}
var newCount = 1;
function addHoverDom(treeId, treeNode) {
var sObj = $("#" + treeNode.tId + "_span");
if (treeNode.editNameFlag || $("#addBtn_"+treeNode.tId).length>0) return;
var addStr = "<span class='button add' id='addBtn_" + treeNode.tId
+ "' title='add node' οnfοcus='this.blur();'></span>";
sObj.after(addStr);
var btn = $("#addBtn_"+treeNode.tId);
if (btn) btn.bind("click", function(){
var zTree = $.fn.zTree.getZTreeObj("treeDemo");
zTree.addNodes(treeNode, {id:(parseInt("${maxnode}") + newCount), pId:treeNode.id, name:"new node" + (newCount++)});
return false;
});
};
function removeHoverDom(treeId, treeNode) {
$("#addBtn_"+treeNode.tId).unbind().remove();
};
function beforeAsync() {
curAsyncCount++;
}
function onAsyncSuccess(event, treeId, treeNode, msg) {
var treeObj = $.fn.zTree.getZTreeObj("treeDemo");
treeObj.expandAll(true);
}
function onAsyncError(event, treeId, treeNode, XMLHttpRequest, textStatus, errorThrown) {
curAsyncCount--;
if (curAsyncCount <= 0) {
curStatus = "";
if (treeNode!=null) asyncForAll = true;
}
}
var curStatus = "init", curAsyncCount = 0, asyncForAll = false,
goAsync = false;
function expandAll() {
if (!check()) {
return;
}
var zTree = $.fn.zTree.getZTreeObj("treeDemo");
if (asyncForAll) {
$("#demoMsg").text(demoMsg.expandAll);
zTree.expandAll(true);
} else {
expandNodes(zTree.getNodes());
if (!goAsync) {
$("#demoMsg").text(demoMsg.expandAll);
curStatus = "";
}
}
}
function expandNodes(nodes) {
if (!nodes) return;
curStatus = "expand";
var zTree = $.fn.zTree.getZTreeObj("treeDemo");
for (var i=0, l=nodes.length; i<l; i++) {
zTree.expandNode(nodes[i], true, false, false);
if (nodes[i].isParent && nodes[i].zAsync) {
expandNodes(nodes[i].children);
} else {
goAsync = true;
}
}
}
function check() {
if (curAsyncCount > 0) {
$("#demoMsg").text(demoMsg.async);
return false;
}
return true;
}
function treenodeClick() {
zTree = $.fn.zTree.getZTreeObj("treeDemo");
var nodes=zTree.getNodes();
var nodes_array = zTree.transformToArray (nodes);
var tempResult = [];
var str = "";
for(var i=0;i<nodes_array.length;i++){
str = '{"id":'+nodes_array[i].id+',"name":"'+nodes_array[i].name.replace(/\s+/g,"")+'","parentid":'+nodes_array[i].pId+'}';
tempResult.push(str);
}
var treelist =eval(tempResult);
treelist = "["+treelist+"]";
$("#treejson").val(treelist);
}
edit: {
enable: true,
showRemoveBtn: setRemoveBtn,
showRenameBtn: setRemoveBtn
},
中移除了根节点的修改和删除图标,防止删除所有节点后,不能再添加新的节点。
treenodeClick()
方法中返回了修改后的 ztree,将所有节点的数据转换成了 json数据,这里碰到了些问题,最后自己拼的数据,直接用 JSON.stringify 的时候,生成的json 字符串老是多引号,像这样 ["{}","{}","{}"] 并列的对象之间多了 双引号 ,正常应该是 [{},{},{}] , 这里应该是 push进数组的时候被加上的,后来 改成了用 eval 转化成json对象,再在外边套上了 中括号,对于节点文本中的空格 在
addHoverDom
方法中做了去空格处理,然后通过表单的隐藏域将json 传回了后台,这里不去空格的话,会出现错误,空格后的字符会消失
后台代码如下
/**
* 根据属性菜单值更新模块数据
* @param projectid
* @param request
*/
public void updateModules(Integer projectid,HttpServletRequest request){
List<Module> modulelist = moduleService.selectByProjectId(projectid);
boolean flag = false;
String newtree = request.getParameter("treejson");
List<TreeNode> treelist = JSON.parseArray(newtree,TreeNode.class);
for (TreeNode node : treelist) {
if(node.getId()!=0){
for (Module m : modulelist){
if(node.getId()==m.getId()){
m.setName(node.getName());
if(node.getParentid()!=0){
m.setParentid(node.getParentid());
}
m.setProjectid(projectid);
moduleService.update(m);
flag=true;
break;
}
}
if(flag==false){
Module newmodule =new Module();
newmodule.setName(node.getName());
if(node.getParentid()!=0){
newmodule.setParentid(node.getParentid());
}
newmodule.setProjectid(projectid);
moduleService.insert(newmodule);
}
flag=false;
}
}
for (Module m : modulelist){
for (TreeNode bar : treelist) {
if(m.getId()==bar.getId()){
flag=true;
break;
}
}
if (flag==false) {
moduleService.deleteById(m.getId());
}
flag=false;
}
}
整过过程中遇到了不少问题,最后算是大部分都解决了,特此记录一下