本文主要记录自己在实际开发中,遇到的一个问题:需要数据库中读取部门数据,并生成部门管理树形结构图,对部门进行操作,显示效果如下图所示:
由于涉及到公司商业机密,因此在此自己将这个模块单独提炼出来并做了一些修改,并只贴出主要代码。
1.后台是基于ssm框架(Spring MVC+Spring+Mybatis);
2.使用mysql数据库;
3.前端使用easyUI+freeMark。
4.项目使用maven构建,Eclipse开发;
5.dao、model、mapper使用mybatis-generator自动生成
6.创建一个名为“treedemo”的数据库及名为“dept”的表,sql语句如下:
CREATE DATABASE treedemo;
USE treedemo;
/*
Navicat MySQL Data Transfer
Source Server : mysql
Source Server Version : 50022
Source Host : localhost:3306
Source Database : treedemo
Target Server Type : MYSQL
Target Server Version : 50022
File Encoding : 65001
*/
SET FOREIGN_KEY_CHECKS=0;
-- ----------------------------
-- Table structure for dept
-- ----------------------------
DROP TABLE IF EXISTS `dept`;
CREATE TABLE `dept` (
`id` varchar(32) NOT NULL,
`pid` varchar(32) NOT NULL,
`name` varchar(50) NOT NULL,
`type` varchar(32) ,
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
-- ----------------------------
-- Records of dept
-- ----------------------------
INSERT INTO `dept` VALUES ('0a22ff1186f24a6b9189ae0bccc37528', '9ebd264055aa4f5b9112db58c338e94e', '广告部1','dept');
INSERT INTO `dept` VALUES ('2c9153620f6344bda08ed1b238a4cdf0', 'e99e1e819fce4dac8054d8c42e4854ab', '业务部','dept');
INSERT INTO `dept` VALUES ('2f83c97cb5464890bba8291b51bd61a8', 'e99e1e819fce4dac8054d8c42e4854ab', '研发部','dept');
INSERT INTO `dept` VALUES ('33f0fbc0243743cfa615714b1764fcf7', 'da7b002944dd41d0bf7c00120b94bb48', '财务部','dept');
INSERT INTO `dept` VALUES ('3b70590f02e1486a967b2eb3463d024c', '779df9d28040485781768a08ead27567', '活动部','dept');
INSERT INTO `dept` VALUES ('779df9d28040485781768a08ead27567', 'e99e1e819fce4dac8054d8c42e4854ab', '企划部','dept');
INSERT INTO `dept` VALUES ('7ac8139930fb4a5fb4fd5412e2aa87bf', 'e99e1e819fce4dac8054d8c42e4854ab', '销售部','dept');
INSERT INTO `dept` VALUES ('9ebd264055aa4f5b9112db58c338e94e', 'df6253b3095c4d2c8083ef9cd4074b40', '广告部','dept');
INSERT INTO `dept` VALUES ('b7091fb6a38149079d07911d4b5a9537', '9ebd264055aa4f5b9112db58c338e94e', '广告部2','dept');
INSERT INTO `dept` VALUES ('da7b002944dd41d0bf7c00120b94bb48', 'e99e1e819fce4dac8054d8c42e4854ab', '运营部','dept');
INSERT INTO `dept` VALUES ('df6253b3095c4d2c8083ef9cd4074b40', '33f0fbc0243743cfa615714b1764fcf7', '策划部','dept');
INSERT INTO `dept` VALUES ('e99e1e819fce4dac8054d8c42e4854ab', '00000000000000000000000000000000', '示例单位','unit');
后台代码结构:
与数据库中的dept表对应,其如下代码:
package com.beauxie.model;
public class Dept {
/**
* This field was generated by MyBatis Generator.
* This field corresponds to the database column dept.id
*
* @mbggenerated
*/
private String id;
/**
* This field was generated by MyBatis Generator.
* This field corresponds to the database column dept.pid
*
* @mbggenerated
*/
private String pid;
/**
* This field was generated by MyBatis Generator.
* This field corresponds to the database column dept.name
*
* @mbggenerated
*/
private String name;
/**
* This field was generated by MyBatis Generator.
* This field corresponds to the database column dept.type
*
* @mbggenerated
*/
private String type;
/**
* This method was generated by MyBatis Generator.
* This method returns the value of the database column dept.id
*
* @return the value of dept.id
*
* @mbggenerated
*/
public String getId() {
return id;
}
/**
* This method was generated by MyBatis Generator.
* This method sets the value of the database column dept.id
*
* @param id the value for dept.id
*
* @mbggenerated
*/
public void setId(String id) {
this.id = id == null ? null : id.trim();
}
/**
* This method was generated by MyBatis Generator.
* This method returns the value of the database column dept.pid
*
* @return the value of dept.pid
*
* @mbggenerated
*/
public String getPid() {
return pid;
}
/**
* This method was generated by MyBatis Generator.
* This method sets the value of the database column dept.pid
*
* @param pid the value for dept.pid
*
* @mbggenerated
*/
public void setPid(String pid) {
this.pid = pid == null ? null : pid.trim();
}
/**
* This method was generated by MyBatis Generator.
* This method returns the value of the database column dept.name
*
* @return the value of dept.name
*
* @mbggenerated
*/
public String getName() {
return name;
}
/**
* This method was generated by MyBatis Generator.
* This method sets the value of the database column dept.name
*
* @param name the value for dept.name
*
* @mbggenerated
*/
public void setName(String name) {
this.name = name == null ? null : name.trim();
}
/**
* This method was generated by MyBatis Generator.
* This method returns the value of the database column dept.type
*
* @return the value of dept.type
*
* @mbggenerated
*/
public String getType() {
return type;
}
/**
* This method was generated by MyBatis Generator.
* This method sets the value of the database column dept.type
*
* @param type the value for dept.type
*
* @mbggenerated
*/
public void setType(String type) {
this.type = type == null ? null : type.trim();
}
}
主要提供一个查询方法,用以查询单位或部门:
//中间无关代码省略...
package com.beauxie.dao;
import java.util.List;
import java.util.Map;
import com.beauxie.model.Dept;
public interface DeptMapper {
/**
* This method was generated by MyBatis Generator.
* This method corresponds to the database table dept
*
* @mbggenerated
*/
int deleteByPrimaryKey(String id);
/**
* This method was generated by MyBatis Generator.
* This method corresponds to the database table dept
*
* @mbggenerated
*/
int insert(Dept record);
/**
* This method was generated by MyBatis Generator.
* This method corresponds to the database table dept
*
* @mbggenerated
*/
int insertSelective(Dept record);
/**
* This method was generated by MyBatis Generator.
* This method corresponds to the database table dept
*
* @mbggenerated
*/
Dept selectByPrimaryKey(String id);
/**
* This method was generated by MyBatis Generator.
* This method corresponds to the database table dept
*
* @mbggenerated
*/
int updateByPrimaryKeySelective(Dept record);
/**
* This method was generated by MyBatis Generator.
* This method corresponds to the database table dept
*
* @mbggenerated
*/
int updateByPrimaryKey(Dept record);
/**
* @param para
* @return List
*/
List list(Map para);
}
是一个service层的接口,里面只有一个抽象方法,如下:
package com.beauxie.service;
import com.beauxie.util.TreeModel;
public interface DeptServiceI {
/**
* 返回所有的单位及部门的树形结构
* @param id 部门id,即作为下一级子节点的pid
* @param containsDept 是否包含子节点
* @return TreeModel
*/
TreeModel selectTree(String id, boolean containsDept);
}
该类实现了DeptServiceI接口,是构造组织树的主要代码,如下:
package com.beauxie.service.impl;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import javax.annotation.Resource;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;
import com.beauxie.dao.DeptMapper;
import com.beauxie.model.Dept;
import com.beauxie.service.DeptServiceI;
import com.beauxie.util.TreeModel;
import com.beauxie.util.TreeUtil;
@Service
public class DeptServiceImpl implements DeptServiceI{
@Resource//或者@Autowired
private DeptMapper deptMapper;
/******************************************************** 构造单位及部门树形结构 ********************************************************/
private List selectChildren(String id, String type) {
Map para = new HashMap();
para.put("type", type);
para.put("pid", id);
return deptMapper.list(para);
}
public TreeModel selectTree(String id, boolean containsDept) {
TreeModel tm = new TreeModel();
//在此只保留部门或单位的id及name属性,可拓展
String[] s = new String[] { "getId", "getName" };
String type = containsDept ? "%" : "unit";
List li = this.selectChildren(id, type);
this.tree(tm, li, s, containsDept);
return tm;
}
// 构造组织机构树数据
private void tree(TreeModel tm, List li, String[] s,
boolean containsDept) {
if (!CollectionUtils.isEmpty(li)) {
for (int i = 0, l = li.size(); i < l; i++) {
TreeModel ntm = new TreeModel();
Dept dept = li.get(i);
TreeUtil.copyModel(ntm, dept, s);// 复制值到TreeModel
tm.getChildren().add(ntm);// 添加到孩子节点列表
String type = containsDept ? "%" : "unit";
List list = this.selectChildren(dept.getId(), type);
tree(ntm, list, s, containsDept);// 递归,实现无限层级
}
}
}
/******************************************************** end ********************************************************/
}
自定义的树的数据模型,代码如下:
package com.beauxie.util;
import java.util.ArrayList;
import java.util.List;
/**
* 树的数据模型
*/
public class TreeModel {
private String id;
private String text;// 名称
private String iconCls;// 图标
private String linkUrl;// 链接地址
// 状态(closed折叠、open展开)
// private String state = "closed";
private List children;// 孩子节点集合
public TreeModel() {
this.children = new ArrayList();
}
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
public String getText() {
return text;
}
public void setText(String text) {
this.text = text;
}
public String getLinkUrl() {
return linkUrl;
}
public void setLinkUrl(String linkUrl) {
this.linkUrl = linkUrl;
}
public String getIconCls() {
return iconCls;
}
public void setIconCls(String iconCls) {
this.iconCls = iconCls;
}
public List getChildren() {
return children;
}
public void setChildren(List children) {
this.children = children;
}
}
该类是一个工具类,用以构造树,代码如下所示:
package com.beauxie.util;
import java.lang.reflect.Method;
/**
* 树结构处理工具类
*/
public class TreeUtil {
/**
* 复制数据到树model
* @param tm 树model
* @param o 待复制的对象
* @param s 变长参数,方法的名称字符串
* 约定第1个为id,第2个为text,
* 第3个为linkUrl,第4个为iconCls,
* 第5个为splitNum
*/
public static void copyModel(TreeModel tm, Object o, String... s) {
Class> clazz = o.getClass();// 获取类包名
int l = s.length;
try {
if(l - 0 > 0 && s[0] != null) {
Method id = clazz.getMethod(s[0]);// 约定第1个为id
tm.setId(String.valueOf(id.invoke(o)));
}
if(l - 1 > 0 && s[1] != null) {
Method text = clazz.getMethod(s[1]);// 约定第2个为text
tm.setText(String.valueOf(text.invoke(o)));
}
if(l - 2 > 0 && s[2] != null) {
Method url = clazz.getMethod(s[2]);// 约定第3个为funcUrl
tm.setLinkUrl(String.valueOf(url.invoke(o)));
}
if(l - 3 > 0 && s[3] != null) {
Method cls = clazz.getMethod(s[3]);// 约定第4个为iconCls
tm.setIconCls(String.valueOf(cls.invoke(o)));
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
package com.beauxie.controller;
import java.util.List;
import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;
import com.beauxie.service.DeptServiceI;
import com.beauxie.util.TreeModel;
@Controller
@RequestMapping("/deptController")
public class DeptController {
@Resource
private DeptServiceI deptService;
// 表示根目录的pid
public static final String ROOT = "00000000000000000000000000000000";
/**
* 单位部门树(含部门)json
*/
@RequestMapping("/deptTree")
@ResponseBody
// 返回的是JSON格式
public List deptTree(HttpServletRequest request) {
// 默认从根节点开始
String id = ROOT;
TreeModel tm = deptService.selectTree(id, true);
return tm.getChildren();
}
}
向后台发送数据请求,并接收、处理后台传回来的数据,代码如下:
var treeUrl = '/treedemo/deptController/deptTree.do';
var tree = null;//表示树
$(function() {
tree = $('#vl');
initTree();//初始化树
});
function initTree() {
tree.tree({
url: treeUrl,
animate: false,
lines : true,
checkbox : false,//是否显示复选框
onClick: function(node) {
//nodeClick(node);
},
onLoadSuccess: function(data) {
alert("加载成功!");
}
});
}
显示树,代码如下:
treedemo
1.前台代码并不复杂,只是一个控件,关键是后台的逻辑代码;
2.首先自己定义了一个树的数据模型,然后再从数据库中查找出数据,通过工具方法将数据封装到树model里,默认从根节点开始查找。
2018/01/09更新说明
由于最近很多人想要demo源码,而csdn下载需要积分。。。
因此已将源码发布到github上。
地址:https://github.com/Beauxie/projects-demo/tree/master/treedemo
(如果有问题勿喷,世界和平要紧~如果有所帮助给颗star呗,谢谢~)