文章目录
- 一、简介
- 二、前端渲染效果
- 三、实现步骤
- 1、数据库表结构
- 2、引入zTree插件
- 3、树形结构实体类SysModule
- 4、表示层代码
- 5、js渲染部分
- 1、树初始化配置
- 2、加载数据树
- 4、控制器关键代码
- 5、业务逻辑层代码:
- 6、数据访问层代码:
- 四、碰到的bug及解决方案
- 1、指定结点选中无效
- 2、mybatis多对多关系的处理较为麻烦
一、简介
zTree 是一个依靠 jQuery 实现的多功能 “树插件”。优异的性能、灵活的配置、多种功能的组合是 zTree 最大优点。
官方文档:http://www.treejs.cn/v3/api.php
功能很强大,API都是中文的,但是在样式上面稍有欠缺,且容易受到开发环境版本的影响。
二、前端渲染效果
三、实现步骤
1、数据库表结构
2、引入zTree插件
<link rel="stylesheet"
href="/ccms/commons/jslib/ztreeV3.5.15/css/zTreeStyle/zTreeStyle.css" type="text/css">
<script type="text/javascript"
src="/ccms/commons/jslib/ztreeV3.5.15/jquery.ztree.all-3.5.js"></script>
<script type="text/javascript"
src="/ccms/commons/jslib/js-gmxt-define/ztreeTool.js"></script>
3、树形结构实体类SysModule
省略get和set方法
public class SysModule {
/**模板编码*/
private String moduleCode;
/**模板名称*/
private String moduleName;
/**模板路径*/
private String modulePath;
/**父级模板编号*/
private String parentCode;
/**是否为叶子节点*/
private int isLeaf;
/**同级排序编号*/
private int sortNumber;
}
树形结构辅助类:
package com.ccms.tools;
import java.util.List;
public class Tree {
private String id;
private String pId;
private String name;
private boolean isParent;
private boolean open;
private boolean checked;
private boolean nocheck;
private List<Tree> children;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public boolean getParent() {
return isParent;
}
public void setParent(boolean isParent) {
this.isParent = isParent;
}
public boolean isOpen() {
return open;
}
public void setOpen(boolean open) {
this.open = open;
}
public boolean isChecked() {
return checked;
}
public void setChecked(boolean checked) {
this.checked = checked;
}
public List<Tree> getChildren() {
return children;
}
public void setChildren(List<Tree> children) {
this.children = children;
}
public boolean isNocheck() {
return nocheck;
}
public void setNocheck(boolean nocheck) {
this.nocheck = nocheck;
}
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
public String getpId() {
return pId;
}
public void setpId(String pId) {
this.pId = pId;
}
private String attributes;
public void setAttributes(String attributes) {
this.attributes = attributes;
}
public String getAttributes() {
return attributes;
}
/* (non-Javadoc)
* @see java.lang.Object#hashCode()
*/
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + ((id == null) ? 0 : id.hashCode());
return result;
}
/* (non-Javadoc)
* @see java.lang.Object#equals(java.lang.Object)
*/
@Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
final Tree other = (Tree) obj;
if (id == null) {
if (other.id != null)
return false;
} else if (!id.equals(other.id))
return false;
return true;
}
}
4、表示层代码
<div class="col-sm-3">
<ul id="protree" class="ztree" style="background: #fff"></ul>
</div>
5、js渲染部分
1、树初始化配置
var setting = {
check : {
enable : true,
autoCheckTrigger : true, //触发事件回调函数
chkStyle : "checkbox", //勾选框类型:checkbox
chkboxType : {
"Y" : "s", //checkbox被勾选后,s标识操作会影响父级节点
"N" : "ps"
}
},
data : {
simpleData : {
enable : false,
}
},
edit : {
enable : true,
showRemoveBtn : false,
showRenameBtn : false,
drag : {
autoExpandTrigger : true,
prev : false,
inner : true,
next : false
}
},
callback : {
onClick : zTreeOnClick,
onDrop : zTreeOnDrop,
beforeDrop : zTreeBeforeDrop
}
};
//节点点击事件
function zTreeOnClick(event, treeId, treeNode) {
// 隐藏添加功能模块表单
$('#addwin').hide();
if (isSelected("protree")) {
if (getSelected("protree").id != "null") {
// 显示修改功能模块表单
$('#upwin').show();
// 在修改功能模块表单中,绑定修改前的数据
loadSingleData(getSelected("protree").id);
} else {
$("#addwin").hide();
$("#upwin").hide();
}
}
}
//加载单条数据
function loadSingleData(id) {
$.ajax({
url : '/ccms/module/getSingleData',
dataType : 'json',
data : {
id : id
},
type : 'post',
success : function(data) {
$("#pname_edit").val(data.moduleData.parentModuleName);
$("#moduleCode_edit").val(data.moduleData.moduleCode);
$("#moduleName_edit").val(data.moduleData.moduleName);
$("#modulePath_edit").val(data.moduleData.modulePath);
$("#parentCode_edit").val(data.moduleData.parentCode);
$("#isLeaf_edit>input[name='isLeaf']:checked").prop(
'checked', false);
$("#isLeaf" + data.moduleData.isLeaf + "_edit").prop(
'checked', true);
$("#sortNumber_edit").val(data.moduleData.sortNumber);
if (data.moduleData.moduleCode == '0') {
$("#moduleName_edit").attr('disabled', true);
$("#moduleCode_edit").attr('disabled', true);
$("#modulePath_edit").attr('disabled', true);
$("#parentCode_edit").attr('disabled', true);
$("#isLeaf_edit input[name='isLeaf']").attr('disabled',
true);
$("#sortNumber_edit").attr('disabled', true);
} else {
$("#moduleName_edit").attr('disabled', false);
$("#moduleCode_edit").attr('disabled', false);
$("#modulePath_edit").attr('disabled', false);
$("#parentCode_edit").attr('disabled', false);
$("#isLeaf_edit input[name='isLeaf']").attr('disabled', false);
$("#sortNumber_edit").attr('disabled', false);
}
},
error : function() {
swal('系统提示', '抱歉,数据加载失败。', 'info');
}
});
}
function zTreeOnDrop(event, treeId, treeNodes, targetNode, moveType) {
var selfId = treeNodes[0].id;
var parentId = targetNode.id;
$.ajax({
url : '/ccms/manager/ModuleController/changeJoin',
dataType : 'json',
data : {
selfId : selfId,
parentId : parentId
},
type : 'post',
success : function(jsonData) {
if (jsonData.rtnCode = 0) {
swal('系统提示', "修改层级关系成功", 'info');
loadProTree();
}
},
error : function() {
swal('系统提示', "修改层级关系失败~~", 'info');
}
});
};
function zTreeBeforeDrop(treeId, treeNodes, targetNode, moveType) {
if (jsonToObj(targetNode.attributes).isLeaf == '1') {
swal('系统提示', "目标节点已是末节点,不可进行拖拽~~", 'info');
return false;
} else {
return true;
}
};
2、加载数据树
//加载数据树
function loadProTree() {
$.ajax({
url : '/ccms/module/getTreeList',
type : 'post',
async : 'true',
cache : false,
data : {},
dataType : 'json',
success : function(data) {
$.fn.zTree.init($("#protree"), setting, data.zNodes);
}
});
}
4、控制器关键代码
// 获取module树
@RequestMapping(value = "/getTreeList", method = { RequestMethod.GET, RequestMethod.POST })
@ResponseBody
public Map<String, Object> getTreeList(HttpServletRequest req, HttpServletResponse resp)
throws UnsupportedEncodingException, IOException {
List<Tree> tree = moduleService.getProtocolTree();
Map<String, Object> result = new HashMap<String, Object>();
result.put("zNodes", tree);
return result;
}
// 根据id获取单条数据
@RequestMapping(value = "/getSingleData", method = { RequestMethod.GET, RequestMethod.POST })
@ResponseBody
public Map<String, Object> getSingleData(String id, HttpServletRequest req, HttpServletResponse resp)
throws UnsupportedEncodingException, IOException {
SysModule info = moduleService.getSingleDate(id);
Map<String, Object> result = new HashMap<String, Object>();
result.put("moduleData", info);
return result;
}
5、业务逻辑层代码:
public interface ModuleService {
//加载module树
public List<Tree> getProtocolTree();
// 查询是否有重复值,存在 exit,不存在 no
public String isExistName(String moduleName, String moduleCode);
//根据id获取module
public SysModule getSingleDate(String moduleCode);
}
实现类:
package com.ccms.service.impl;
import com.ccms.dao.ModuleDao;
import com.ccms.pojo.SysModule;
import com.ccms.service.ModuleService;
import com.ccms.tools.Tree;
import com.ccms.tools.UUIDGenerator;
import org.json.simple.JSONObject;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Isolation;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;
import java.util.ArrayList;
import java.util.List;
@Service("moduleService")
@Transactional(propagation = Propagation.REQUIRED, isolation = Isolation.DEFAULT)
public class ModuleServiceImpl implements ModuleService {
@Autowired
ModuleDao moduleDao;
//加载module树
@Override
public List<Tree> getProtocolTree() {
List<String> attributesList = new ArrayList<String>();
attributesList.add("isLeaf");
// 获取功能模块的所有父节点
List<SysModule> parentModuleLst = moduleDao.getParentTree();
List<Tree> parentTreesLst = new ArrayList<Tree>();
// 将List<SysModule>类型转换成List<Tree>
for (SysModule module : parentModuleLst) {
Tree rootTreeData = new Tree();
rootTreeData.setId(module.getModuleCode());
rootTreeData.setName(module.getModuleName());
rootTreeData.setOpen(true);
rootTreeData.setChecked(false);
rootTreeData.setParent(false);
rootTreeData.setNocheck(false);
JSONObject attributesJO = new JSONObject();
attributesJO.put("isLeaf", module.getIsLeaf());
rootTreeData.setAttributes(attributesJO.toString());
parentTreesLst.add(rootTreeData);
}
if (parentTreesLst != null) {
// 遍历List<Tree>类型对象parentTreesLst
for (Tree parentData : parentTreesLst) {
// 获取当前父节点模块编号
String moduleCode = parentData.getId();
// 根据当前父节点模块编号,获取其所包含的功能模块
List<SysModule> list = moduleDao.getTreeList(moduleCode);
List<Tree> treeList = new ArrayList<Tree>();
// 将List<SysModule>类型转换成List<Tree>
for (SysModule sysModule : list) {
Tree tempTreeData = new Tree();
tempTreeData.setId(sysModule.getModuleCode());
tempTreeData.setName(sysModule.getModuleName());
tempTreeData.setOpen(true);
tempTreeData.setChecked(false);
tempTreeData.setParent(false);
tempTreeData.setNocheck(false);
JSONObject attributesJO = new JSONObject();
attributesJO.put("isLeaf", sysModule.getIsLeaf());
tempTreeData.setAttributes(attributesJO.toString());
List childList = new ArrayList<Tree>();
childList = getChildTreeById(sysModule.getModuleCode());
tempTreeData.setChildren(childList);
treeList.add(tempTreeData);
}
parentData.setChildren(treeList);
}
}
return parentTreesLst;
}
// 查询是否有重复值,存在 exit,不存在 no
@Override
public String isExistName(String moduleName, String moduleCode) {
String result;
int i = moduleDao.existSameModuleName(moduleName, moduleCode);
if (i > 0) {
result = "exit";
} else {
result = "no";
}
return result;
}
//根据id获取module
@Override
public SysModule getSingleDate(String moduleCode) {
return moduleDao.getSingleDataById(moduleCode);
}
// 根据父节点CodeId获取子节点数据列表
private List getChildTreeById(String codeId) {
List<Tree> treeList = new ArrayList<Tree>();
List<SysModule> list = moduleDao.getTreeList(codeId);
for (SysModule sysModule : list) {
Tree tempTreeData = new Tree();
tempTreeData.setId(sysModule.getModuleCode());
tempTreeData.setName(sysModule.getModuleName());
tempTreeData.setOpen(true);
tempTreeData.setChecked(false);
tempTreeData.setParent(false);
tempTreeData.setNocheck(false);
JSONObject attributesJO = new JSONObject();
attributesJO.put("isLeaf", sysModule.getIsLeaf());
tempTreeData.setAttributes(attributesJO.toString());
List childList = new ArrayList<Tree>();
childList = getChildTreeById(sysModule.getModuleCode());
tempTreeData.setChildren(childList);
treeList.add(tempTreeData);
}
return treeList;
}
}
6、数据访问层代码:
package com.ccms.dao;
import com.ccms.pojo.SysModule;
import org.apache.ibatis.annotations.*;
import java.util.List;
public interface ModuleDao {
// 查询所有模块
@Select("select * from sys_module order by sortNumber asc")
public List<SysModule> getAllModule();
// 查询用户可以操作的模块编号集合
@Select("select distinct moduleCode from sys_role_module where roleCode "
+ "in (select roleCode from sys_user_role where userCode=#{userCode})")
List<String> getmoduleCodes(@Param("userCode") String userCode);
// 获取树形结构所有父节点
@Select("select * from sys_module where parentCode is null order by sortNumber asc")
public List<SysModule> getParentTree();
// 根据功能模块编号,获取其所包含的功能模块
@Select("select * from sys_module where parentCode = #{moduleCode} order by sortNumber asc")
public List<SysModule> getTreeList(@Param("moduleCode") String moduleCode);
// 根据id查询重复name
@Select("select ifnull(count(*),0) from sys_module where moduleName=#{moduleName} and moduleCode <> #{moduleCode}")
public int existSameModuleName(@Param("moduleName") String moduleName, @Param("moduleCode") String moduleCode);
// 根据功能模块编号获取数据
@Select("select cm.*, (case when cm.parentCode is null then '无' " + " else pm.moduleName end) as parentModuleName"
+ " from sys_module cm" + " left join sys_module pm" + " on cm.parentCode=pm.moduleCode"
+ " where cm.moduleCode= #{moduleCode}")
public SysModule getSingleDataById(@Param("moduleCode") String moduleCode);
}
四、碰到的bug及解决方案
1、指定结点选中无效
描述:checkNode(treeNode,checked,checkTypeFlag,callbackFlag)
方法不能指定某个结点选中
解决:
若想在树加载之后设置指定的Id选中,请将ajax的async的值设为false,即同步加载(也就是说先要等树加载完再指定结点选中,要不会出现未知错误,很难发现)
2、mybatis多对多关系的处理较为麻烦