树结构
方式一:一张表
一张表中含有id pid name 等字段
后台代码
@RequestMapping("/showTree")
public ABResponse showTree(){
ABResponse groupGetAll = remoteService.group_getAll();
List<RmGroup> getAllData = (List<RmGroup>) groupGetAll.getData();
List<Menu> menuList= new ArrayList<Menu>();
for (int i = 0; i < getAllData.size(); i++) {
Menu menu = new Menu();
menu.setId(String.valueOf(getAllData.get(i).getId()));
menu.setPid(String.valueOf(getAllData.get(i).getParentId()));
menu.setText(getAllData.get(i).getName());
menuList.add(menu);
}
/*让我们创建树*/
MenuTree menuTree =new MenuTree(menuList);
menuList=menuTree.builTree();
String jsonOutput= JSON.toJSONString(menuList);
System.out.println(jsonOutput);
return ABResponse.buildSuccessData(jsonOutput);
}
工具类
import java.util.ArrayList;
import java.util.List;
public class MenuTree {
private List<Menu> menuList = new ArrayList<Menu>();
public MenuTree(List<Menu> menuList) {
this.menuList=menuList;
}
//建立树形结构
public List<Menu> builTree(){
List<Menu> treeMenus =new ArrayList<Menu>();
for(Menu menuNode : getRootNode()) {
menuNode=buildChilTree(menuNode);
treeMenus.add(menuNode);
}
return treeMenus;
}
//递归,建立子树形结构
private Menu buildChilTree(Menu pNode){
List<Menu> chilMenus =new ArrayList<Menu>();
for(Menu menuNode : menuList) {
if(menuNode.getPid().equals(pNode.getId())) {
chilMenus.add(buildChilTree(menuNode));
}
}
pNode.setNodes(chilMenus);
return pNode;
}
//获取根节点
private List<Menu> getRootNode() {
List<Menu> rootMenuLists =new ArrayList<Menu>();
for(Menu menuNode : menuList) {
if(menuNode.getPid().equals("0")) {
rootMenuLists.add(menuNode);
}
}
return rootMenuLists;
}
}
实体类
package com.abview.pojo;
import com.fasterxml.jackson.annotation.JsonProperty;
import java.util.List;
public class Menu {
private String id;
@JsonProperty(value = "pid")
private String pid;
private String text;
private String url;
private String yxbz;
public Menu() {
}
@JsonProperty(value = "nodes")
private List<Menu> nodes;
public Menu(String id, String pid, String text, String url, String yxbz) {
this.id = id;
this.pid = pid;
this.text = text;
this.url = url;
this.yxbz = yxbz;
}
/*省略get\set*/
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;
}
public List<Menu> getNodes() {
return nodes;
}
public void setNodes(List<Menu> nodes) {
this.nodes = nodes;
}
public String getText() {
return text;
}
public void setText(String text) {
this.text = text;
}
public String getUrl() {
return url;
}
public void setUrl(String url) {
this.url = url;
}
public String getYxbz() {
return yxbz;
}
public void setYxbz(String yxbz) {
this.yxbz = yxbz;
}
}
前端代码使用treeview实现
<!DOCTYPE html>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>移动到</title>
<script type="text/javascript" src="../../../js/jquery/jquery-2.1.4/jquery.min.js"></script>
<script src="../../../js/common/extends.js"></script>
<script src="../../../js/common/Utils.js"></script>
<script type="text/javascript" src="../../../js/jquery/jquery.form.js"></script>
<link rel="stylesheet" type="text/css" href="../../../js/bootstrap/css/bootstrap.min.css"/>
<script src="../../../js/bootstrap/js/bootstrap.min.js" type="text/javascript"></script>
<script type="text/javascript" src="../../../js/bootstrap/js/bootbox.min.js"></script>
<script type='text/javascript' src='../../../js/icheck-1.x/icheck.js'></script>
<link rel="stylesheet" href="../../../js/icheck-1.x/skins/icheck.css" type="text/css"/>
<script src="../../../js/bootstrap/js/bootstrapValidator.js" type="text/javascript"></script>
// 下面是treeview
<link href="../../../js/bootstrap-treeview/bootstrap-treeview.min.css" rel="stylesheet"/>
<script src="../../../js/bootstrap-treeview/bootstrap-treeview.min.js" type="text/javascript"></script>
<style>
#tv {
border: 1px solid #D6D6D6;
display: none;
width: 300px;
height: 400px;
overflow-x: hidden;
overflow-y: auto;
}
</style>
<script>
MoveDevBackUtils = {
submit: function () {
// 获取设备id和分组id
var pid = $("#pid").val();
var deviceId = $("#deviceId").val();
if (pid == null||pid === "") {
parent.parent.toastr["error"]("分组名称不存在");
return;
}
$.ajax({
url: '../../../ra/tree/moveDevice',
dataType: 'json',
data: {
pid: pid,
deviceId: deviceId
},
method: 'post',
success: function (data) {
parent.TopoListUtils.closeMoveDevice();
parent.TopoListUtils.refreshTopoList();
parent.parent.toastr["info"]("成功!");
parent.parent.parent.topoObject.flushMenu();
},
error: function () {
parent.parent.toastr["error"]("请重试!");
}
})
},
getGroupId: function () {
// 获取设备mac地址和分组id
var pid = $("#pid").val();
var mac = $("#deviceId").val();
if (pid == null||pid === "") {
parent.parent.toastr["error"]("分组名称不存在");
return;
}
return pid;
},
init: function () {
var iddata = $.UrlUtils.getParam("iddata");
$("#deviceId").val(iddata);
var nodeData = [];
$.ajax({
url: '../../../ra/tree/showTree',
type: 'post',
dataType: 'json',
async: false,
success: function (data) {
nodeData = data.data;
}
})
$('#tv').treeview({
data: nodeData, // 节点数据
levels: 1, // 节点层级数
color: "#000", // 每一级通用的 节点字体颜色
backColor: "#fff", // 每一级通用的 节点字背景色
onhoverColor: "skyblue", // 选中浮动颜色
showBorder: false, // 不显示边框
showTags: true, // 是否在每个节点的右侧显示标签。 其值必须在每个节点的数据结构中提供
highlightSelected: true, // 是否突出显示选定的节点
selectedColor: "#fff", // 设置选定节点的前景色
selectedBackColor: "skyblue", // 设置选定节点的背景色
onNodeSelected: function (event, data) {
$('#region').val(data.text);
$('#pid').val(data.id);
$('#tv').hide();
}
})
$('#region').click(function () {
$('#tv').show();
})
}
}
</script>
</head>
<body>
<div class="container">
<div class="row-fluid">
<div class="row">
<div class="col-md-offset-4 col-md-3">
<form id="moveGroupForm">
<div class="form-group">
<input id="pid" value="" hidden>
<input id="deviceId" value="" hidden>
<label for="region">分组名称</label>
<input id="region" class="form-control" value="Root" type="text" placeholder="请选择分组"
style="width:200px;" name="groupName"/>
<div id="tv"></div>
</div>
<!-- <div class="form-group" style="margin-bottom: 10px ">-->
<!-- <input class="btn btn-primary" value="确认" onclick="MoveDevUtils.submit()"/>-->
<!-- </div>-->
</form>
</div>
</div>
</div>
</div>
<script>
$(function () {
MoveDevBackUtils.init();
});
</script>
</body>
</html>
模拟数据进行展示
进行数据模拟展示
package com.example.demo2;
import com.alibaba.fastjson.JSON;
import com.example.demo.entry.Menu;
import com.example.demo.utils.MenuTreeData;
import org.junit.jupiter.api.Test;
import org.springframework.boot.test.context.SpringBootTest;
import java.util.Arrays;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.stream.Collectors;
// classes = DemoApplicationTests.class 独立运行避免出错
@SpringBootTest(classes = DemoApplicationTests.class)
class DemoApplicationTests {
// 这个速度快
@Test
void contextLoads() {
//模拟从数据库查询出来
List<Menu> menus = Arrays.asList(
new Menu(1,"商品管理",0),
new Menu(2,"会员管理",0),
new Menu(3,"系统管理",0),
new Menu(4,"系统工具",0),
new Menu(5,"用户管理",3),
new Menu(6,"角色管理",3),
new Menu(7,"菜单栏管理",3),
new Menu(8,"日志管理",4),
new Menu(9,"商品列表",1),
new Menu(10,"订单列表",1),
new Menu(11,"会员等级",2),
new Menu(12,"品牌列表",1)
);
//获取父节点
List<Menu> collect = menus.stream().filter(m -> m.getParentId() == 0).map(
(m) -> {
m.setChildList(getChildrens(m, menus));
return m;
}
).collect(Collectors.toList());
System.out.println("-------转json输出结果-------");
System.out.println(JSON.toJSON(collect));
}
/**
* 递归查询子节点
* @param root 根节点
* @param all 所有节点
* @return 根节点信息
*/
private List<Menu> getChildrens(Menu root, List<Menu> all) {
List<Menu> children = all.stream().filter(m -> {
return Objects.equals(m.getParentId(), root.getId());
}).map(
(m) -> {
m.setChildList(getChildrens(m, all));
return m;
}
).collect(Collectors.toList());
return children;
}
@Test
void contextLoads2() {
//模拟从数据库查询出来
List<Menu> menus = Arrays.asList(
new Menu(1,"商品管理",0),
new Menu(2,"会员管理",0),
new Menu(3,"系统管理",0),
new Menu(4,"系统工具",0),
new Menu(5,"用户管理",3),
new Menu(6,"角色管理",3),
new Menu(7,"菜单栏管理",3),
new Menu(8,"日志管理",4),
new Menu(9,"商品列表",1),
new Menu(10,"订单列表",1),
new Menu(11,"会员等级",2),
new Menu(12,"品牌列表",1)
);
List<Menu> list = buildDeptTreeByStream(menus);
System.out.println(list);
System.out.println(JSON.toJSON(list));
}
public static List<Menu> buildDeptTreeByStream(List<Menu> trees){
//获取parentId = 0的根节点
List<Menu> list = trees.stream().filter(item -> item.getParentId() == 0L).collect(Collectors.toList());
//根据parentId进行分组
Map<Integer, List<Menu>> map = trees.stream().collect(Collectors.groupingBy(Menu::getParentId));
recursionFnTree(list, map);
return list;
}
/**
* 递归遍历节点
* @param list
* @param map
*/
public static void recursionFnTree(List<Menu> list, Map<Integer, List<Menu>> map){
for (Menu treeSelect : list) {
List<Menu> childList = map.get(treeSelect.getId());
treeSelect.setChildList(childList);
if (null != childList && 0 < childList.size()){
recursionFnTree(childList,map);
}
}
}
@Test
void contextLoads3() {
//模拟从数据库查询出来
List<Menu> menus = Arrays.asList(
new Menu(1,"商品管理",0),
new Menu(2,"会员管理",0),
new Menu(3,"系统管理",0),
new Menu(4,"系统工具",0),
new Menu(5,"用户管理",3),
new Menu(6,"角色管理",3),
new Menu(7,"菜单栏管理",3),
new Menu(8,"日志管理",4),
new Menu(9,"商品列表",1),
new Menu(10,"订单列表",1),
new Menu(11,"会员等级",2),
new Menu(12,"品牌列表",1)
);
List<Menu> list = MenuTreeData.buildDeptTree(menus);
System.out.println(list);
System.out.println(JSON.toJSON(list));
}
}
contextLoads3 里调用的方法
package com.example.demo.utils;
import com.example.demo.entry.Menu;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
public class MenuTreeData {
/**
* 构建前端所需要树结构
*
* @param trees 列表
* @return 树结构列表
*/
public static List<Menu> buildDeptTree(List<Menu> trees) {
List<Menu> returnList = new ArrayList<Menu>();
List<Long> tempList = new ArrayList<Long>();
for (Menu dept : trees) {
tempList.add(Long.valueOf(dept.getId()));
}
for (Iterator<Menu> iterator = trees.iterator(); iterator.hasNext(); ) {
Menu treeSelect = (Menu) iterator.next();
// 如果是顶级节点, 遍历该父节点的所有子节点
if (!tempList.contains(treeSelect.getParentId())) {
recursionFn(trees, treeSelect);
returnList.add(treeSelect);
}
}
if (returnList.isEmpty()) {
returnList = trees;
}
return returnList;
}
/**
* 递归列表
*/
private static void recursionFn(List<Menu> list, Menu t) {
// 得到子节点列表
List<Menu> childList = getChildList(list, t);
t.setChildList(childList);
for (Menu tChild : childList) {
if (hasChild(list, tChild)) {
recursionFn(list, tChild);
}
}
}
/**
* 得到子节点列表
*/
private static List<Menu> getChildList(List<Menu> list, Menu t) {
List<Menu> tlist = new ArrayList<Menu>();
for (Menu n : list) {
if (n.getParentId() != null && n.getParentId() == t.getId()) {
tlist.add(n);
}
}
return tlist;
}
/**
* 判断是否有子节点
*/
private static boolean hasChild(List<Menu> list, Menu t) {
return getChildList(list, t).size() > 0;
}
}
实体类
package com.example.demo.entry;
import lombok.Builder;
import lombok.Data;
import java.util.List;
/**
* 树结构:Menu
*/
@Data
@Builder
public class Menu {
/**
* id
*/
public Integer id;
/**
* 名称
*/
public String name;
/**
* 父id ,根节点为0
*/
public Integer parentId;
/**
* 子节点信息
*/
public List<Menu> childList;
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Integer getParentId() {
return parentId;
}
public void setParentId(Integer parentId) {
this.parentId = parentId;
}
public List<Menu> getChildList() {
return childList;
}
public void setChildList(List<Menu> childList) {
this.childList = childList;
}
public Menu(Integer id, String name, Integer parentId) {
this.id = id;
this.name = name;
this.parentId = parentId;
}
public Menu(Integer id, String name, Integer parentId, List<Menu> childList) {
this.id = id;
this.name = name;
this.parentId = parentId;
this.childList = childList;
}
}
显示数据结果
[Menu(id=1, name=商品管理, parentId=0, childList=[Menu(id=9, name=商品列表, parentId=1, childList=[]), Menu(id=10, name=订单列表, parentId=1, childList=[]), Menu(id=12, name=品牌列表, parentId=1, childList=[])]), Menu(id=2, name=会员管理, parentId=0, childList=[Menu(id=11, name=会员等级, parentId=2, childList=[])]), Menu(id=3, name=系统管理, parentId=0, childList=[Menu(id=5, name=用户管理, parentId=3, childList=[]), Menu(id=6, name=角色管理, parentId=3, childList=[]), Menu(id=7, name=菜单栏管理, parentId=3, childList=[])]), Menu(id=4, name=系统工具, parentId=0, childList=[Menu(id=8, name=日志管理, parentId=4, childList=[])]), Menu(id=5, name=用户管理, parentId=3, childList=[]), Menu(id=6, name=角色管理, parentId=3, childList=[]), Menu(id=7, name=菜单栏管理, parentId=3, childList=[]), Menu(id=8, name=日志管理, parentId=4, childList=[]), Menu(id=9, name=商品列表, parentId=1, childList=[]), Menu(id=10, name=订单列表, parentId=1, childList=[]), Menu(id=11, name=会员等级, parentId=2, childList=[]), Menu(id=12, name=品牌列表, parentId=1, childList=[])]
[{"name":"商品管理","childList":[{"name":"商品列表","childList":[],"id":9,"parentId":1},{"name":"订单列表","childList":[],"id":10,"parentId":1},{"name":"品牌列表","childList":[],"id":12,"parentId":1}],"id":1,"parentId":0},{"name":"会员管理","childList":[{"name":"会员等级","childList":[],"id":11,"parentId":2}],"id":2,"parentId":0},{"name":"系统管理","childList":[{"name":"用户管理","childList":[],"id":5,"parentId":3},{"name":"角色管理","childList":[],"id":6,"parentId":3},{"name":"菜单栏管理","childList":[],"id":7,"parentId":3}],"id":3,"parentId":0},{"name":"系统工具","childList":[{"name":"日志管理","childList":[],"id":8,"parentId":4}],"id":4,"parentId":0},{"name":"用户管理","childList":[],"id":5,"parentId":3},{"name":"角色管理","childList":[],"id":6,"parentId":3},{"name":"菜单栏管理","childList":[],"id":7,"parentId":3},{"name":"日志管理","childList":[],"id":8,"parentId":4},{"name":"商品列表","childList":[],"id":9,"parentId":1},{"name":"订单列表","childList":[],"id":10,"parentId":1},{"name":"会员等级","childList":[],"id":11,"parentId":2},{"name":"品牌列表","childList":[],"id":12,"parentId":1}]
方式二 :两张表
两张表,例如:group表1中包含id pid name等字段 ;device表2中包含id groupId(表1的主键id字段) name 等其他字段
后台代码
public String device_getListAll2() {
Map<String, List<TreeData>> rtnMap = new HashMap<String, List<TreeData>>();
List<RmDevice> allDevList = deviceDao.getAll();
String groupId = "";
for (RmDevice dev : allDevList) {
TreeData map = new TreeData();
map.setId(dev.getId());
map.setGroupId(dev.getGroupId());
map.setIp(utils.ip.toFrontEnd(dev.getIp()));
map.setMac(utils.mac.toFrontEnd(dev.getMac()));
map.setName(dev.getName());
map.setModel(dev.getModel());
map.setSoftwareVersion(dev.getSoftwareVersion());
map.setSystemVersion(dev.getSystemVersion());
map.setIconCls("icon-driver");
if (dev.getGroupId() == 0) {
groupId = "1";
map.setParentId(1);
} else {
groupId = String.valueOf(dev.getGroupId());
map.setParentId(dev.getGroupId());
}
map.setMark(dev.getId() + "_" + 1);
map.setType("1");
if (rtnMap.containsKey(groupId)) {
List<TreeData> tempList = rtnMap.get(groupId);
tempList.add(map);
} else {
List<TreeData> tempList = new ArrayList<TreeData>();
tempList.add(map);
rtnMap.put(groupId, tempList);
}
}
//分组
List<RmGroup> allGroupList = groupDao.getAll();
List<TreeData> returnList = getGroupChild(allGroupList, 0, rtnMap);
return JSON.toJSONString(returnList);
}
private List<TreeData> getGroupChild(List<RmGroup> list, int root, Map<String, List<TreeData>> rtnMap) {
List<TreeData> treeList = new ArrayList<TreeData>();
for (RmGroup grp : list) {
TreeData tree = new TreeData();
tree.setId(grp.getId());
tree.setName(grp.getName());
tree.setParentId(grp.getParentId());
tree.setIconCls("icon-function");
int parentId = grp.getParentId();
int treeId = grp.getId();
if (root == parentId) {
List<TreeData> lists = getGroupChild(list, treeId, rtnMap);
if (rtnMap.get(String.valueOf(grp.getId())) != null) {
lists.addAll(rtnMap.get(String.valueOf(grp.getId())));
}
tree.setChildren(lists);
treeList.add(tree);
}
}
return treeList;
}
实体类
public class TreeData{
private static final long serialVersionUID = 1L;
private int id;
private String name;
private int groupId;
private String ip;
private String mac;
private String model;
private String softwareVersion;
private String systemVersion;
private int parentId;
private List<TreeData> children;
private String mark;
private String type;
private String iconCls;
public String getIconCls() {
return iconCls;
}
public void setIconCls(String iconCls) {
this.iconCls = iconCls;
}
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getGroupId() {
return groupId;
}
public void setGroupId(int groupId) {
this.groupId = groupId;
}
public String getIp() {
return ip;
}
public void setIp(String ip) {
this.ip = ip;
}
public String getMac() {
return mac;
}
public void setMac(String mac) {
this.mac = mac;
}
public String getSoftwareVersion() {
return softwareVersion;
}
public void setSoftwareVersion(String softwareVersion) {
this.softwareVersion = softwareVersion;
}
public String getSystemVersion() {
return systemVersion;
}
public void setSystemVersion(String systemVersion) {
this.systemVersion = systemVersion;
}
public int getParentId() {
return parentId;
}
public void setParentId(int parentId) {
this.parentId = parentId;
}
public List<TreeData> getChildren() {
return children;
}
public void setChildren(List<TreeData> children) {
this.children = children;
}
public String getMark() {
return mark;
}
public void setMark(String mark) {
this.mark = mark;
}
public String getType() {
return type;
}
public void setType(String type) {
this.type = type;
}
public String getModel() {
return model;
}
public void setModel(String model) {
this.model = model;
}
public TreeData() {
}
public TreeData(int id, String name, int groupId, String ip, String mac, String model, String softwareVersion, String systemVersion, int parentId, List<TreeData> children, String mark, String type) {
this.id = id;
this.name = name;
this.groupId = groupId;
this.ip = ip;
this.mac = mac;
this.model = model;
this.softwareVersion = softwareVersion;
this.systemVersion = systemVersion;
this.parentId = parentId;
this.children = children;
this.mark = mark;
this.type = type;
}
}
前端代码
可以使用esayui里面的treegrid表格样式,也可以用其他的。比如layui