目录
echarts官网:
树图网址:
echarts图例参数介绍网址:
功能实现
1、先实现查询数据库返回数据,将数据处理成树形结构返回给页面。
1.1、数据结构:子节点的PID等于父节点的ID。
1.2、代码逻辑
主要功能设置:
一、将节点名称的文字纵向立着显示。:
二、根据查询条件传入的参数改变节点名称的颜色变化:
三、根据查询条件传入的参数控制节点是否展开:
完整的Java逻辑代码和 js处理的html:
Java代码:
html代码:
该文档echarts树图知识点:
1、将文字纵向立着显示。
2、根据查询条件传入的参数改变节点名称的颜色变化。
3、根据查询条件传入的参数控制节点是否展开。
效果图如下:分别输入a和b看展示结果。
echarts官网:
http://echarts.apache.org/zh/index.html
树图网址:
https://echarts.apache.org/examples/zh/index.html#chart-type-tree
echarts图例参数介绍网址:
使用echarts配置图需要下载echarts.js包,用到什么图就下载什么的echarts.js包就行。下载完在html引入就可以用了。jquery的js也需要引。
功能实现
1、先实现查询数据库返回数据,将数据处理成树形结构返回给页面。
数据库的测试数据:
1.1、数据结构:子节点的PID等于父节点的ID。
1.2、代码逻辑
这里通过递归将数据处理成树形图结构(有好的方法欢迎留言)。
Java 返回结果实体类:
@Data
public class TestTreeResult implements Serializable {
private Long id;
private Long pid;
private String name;
//子节点集合
private List<TestTreeResult> children;
//查询数据库数据接收返回结果用到
public TestTreeResult(Long id, Long pid, String name) {
this.id = id;
this.pid = pid;
this.name = name;
}
}
查询数据将结果返回到实体类集合。
这里用到第一个方法:
public interface TestTreeExchange extends JpaRepository<TestTree,Long> {
//查询所有数据(展示树图用)
@Query(value = "select new com.picclife.platform.model.TestTreeResult(t.id,t.pid,t.name) from TestTree t order by t.id")
List<TestTreeResult> getAllTreeList();
//通过查询条件返回pid(处理根据查询条件控制节点收展时用到)
@Query(value = "select distinct t.pid from TEST_TREE t where t.name like %?1%",nativeQuery = true)
TreeSet<String> getPidListByName(String name);
}
树形结构处理逻辑 (因为是测试没用到service,直接在action写逻辑了)
/**
* 获取树形结构数据
* @return
*/
@RequestMapping("/getSystemTreeInfo")
@ResponseBody
public List<TestTreeResult> getRootTree(){
//存放根节点数据集合
List<TestTreeResult> rootList = new ArrayList<>();
//查询所有数据
List<TestTreeResult> allTreeList = testTreeExchange.getAllTreeList();
if(allTreeList !=null && allTreeList.size()>0){
for(TestTreeResult t: allTreeList){
//父节点是0的,为根节点。
if(t.getPid()== 0){
//添加到根节点集合
rootList.add(t);
}
}
}
//获取根节点下的所有子节点
if(rootList !=null && rootList.size()>0){
for(TestTreeResult root:rootList){
List<TestTreeResult> childTree = getChildTree(root.getId(), allTreeList);
if(childTree !=null && childTree.size()>0){
root.setChildren(childTree);
}
}
allList = allTreeList;
allTrees = rootList;
return rootList;
}else{
return null;
}
}
/**
* 获取子节点
* @param rootId 父节点id
* @param allList 所有数据列表
* @return 每个根节点下,所有子数据列表
*/
public List<TestTreeResult> getChildTree(Long rootId,List<TestTreeResult> allList){
//子数据集合
List<TestTreeResult> childList = new ArrayList<TestTreeResult>();
// 遍历所有节点,将所有数据的父id与传过来的根节点的id比较
if(allList !=null && allList.size()>0){
for(TestTreeResult t:allList){
//相等说明:为该根节点的子节点。
if(rootId == t.getPid()){
childList.add(t);
}
}
}
if(childList !=null && childList.size()>0){
//递归子节点集合,循环获取子节点
for(TestTreeResult child:childList){
List<TestTreeResult> childTreeNext = getChildTree(child.getId(), allList);
child.setChildren(childTreeNext);
}
}else{
return null;
}
return childList;
}
html 先定义个容器,用于展示树图:
option设置
function getOption(data,name){
option = {
tooltip: {
trigger: 'item',
triggerOn: 'mousemove'
},
series:[
{
type: 'tree',
data: [data],
left: '2%',
right: '2%',
top: '8%',
bottom: '20%',
symbol: 'emptyCircle',//标记的形状
initialTreeDepth: 2, //图形初始展开层级,默认2.
orient: 'vertical',//图形显示方向
expandAndCollapse: true,
roam: true,//缩放和平移
label: {
position: 'top',
rotate: 0,
offset:[10,-10],
verticalAlign: 'middle',
align: 'center',
fontSize: 12,
/*文字内容显示设置*/
formatter:function (param) {
var split = param.name.split('');
if(name !='' && param.name.match(name)){
return '{a|'+param.name.split('').join('\n')+'}'
}else{
return split.join('\n');
}
},
rich:{
a:{
color: 'red',
lineHeight:10
}
}
},
leaves: {
label: {
position: 'bottom',
rotate: 0,
verticalAlign: 'middle',
align: 'center',
fontSize: 12
}
},
animationDurationUpdate: 750
}
]
};
return option;
};
主要功能设置:
一、将节点名称的文字纵向立着显示。:
在lable标签里边添加formatter方法,param.name得到的就是节点名称,将名称按字分组,每个字添加“\n”换行即可。如下图红框(如果没有查询条件则不用写if判断,直接返回就行)。
二、根据查询条件传入的参数改变节点名称的颜色变化:
这个需要formatter方法配合rich标签使用。如上图:rich配置字体颜色亮度等,formatter方法if 里边返回根据查询参数改变节点字体的颜色,调用rich标签里的 a。
三、根据查询条件传入的参数控制节点是否展开:
这个稍微有点复杂,先整理下思路。因为是根据查询条件控制是否展开节点,首先想到几个问题:
1、如何控制节点是否展开?
2、当前节点是否包含该参数?
3、当前节点的父节点是否包含该参数?
最终目的就是当子节点包含该查询参数,不论父节点是否包含该参数都必须展开节点。如果当前节点的所有子节点都不包含该参数,则当前节点需要关闭。因为树形结构不容易操作父节点及父父节点,所以我取了所有包含查询条件参数的父节点ID放到一个集合,最终通过判断每个节点ID是否和集合中的ID相等。如果相等则把当前节点展开。(如果有更简单方法希望留下宝贵建议或意见)
查看官网发现这段代码就是控制节点是否展开。
主要是:collapsed = true 就是关闭节点,等于false就是打开节点。
js代码:获取包含查询参数的父节点ID,及判断节点是否展开的方法:
/*条件搜索*/
$("#sButton").click(function () {
var name = $("#reportName").val();
if(name != "" && name.trim().length>0){
/*获取所有树形结构数据*/
$.post(baseUrl + '/testTree/getTreeList', function (data) {
myChart.hideLoading();
if(data){
getIds(data[0],name);
}
});
}else{
myChart.setOption(getOption(resData,''));
}
});
/*根据条件name参数,获取所有匹配name的父类ids*/
function getIds(data,name){
$.post(baseUrl + '/testTree/getidListByName',{name:name}, function (ids) {
if(ids){
getChild(data,ids);
myChart.setOption(getOption(data,name));
}else{
myChart.setOption(getOption(resData,''));
}
});
}
/*递归根据 id判断当前节点是否展开*/
function getChild(data,ids){
if(ids.length>0 && data.children){
ids.forEach(item => {
if (item == data.id) {
data.collapsed = false;//展开节点
}
});
echarts.util.each(data.children, function (datum, index) {
if(datum.children){
return getChild(datum,ids);
}
});
}else{
return null;
}
}
Java代码:
/**
* 获取所有包含name参数 的id。
* @param name
* @return
*/
@RequestMapping("/getidListByName")
@ResponseBody
public Set<String> getidListByName(@RequestParam(value = "name",defaultValue = "") String name){
allId.clear();
TreeSet<String> pidList = testTreeExchange.getPidListByName(name);
Set<String> treeIds = getTreeId(allList, pidList);
return treeIds;
}
/**
* 获取所有树集合
* @return
*/
@RequestMapping("/getTreeList")
@ResponseBody
public List<TestTreeResult> getTreeList(){
return allTrees;//一开始将所有数据处理成树形结构的集合。
}
/**
* 获取pids节点的id。
* @return
*/
public Set<String> getTreeId(List<TestTreeResult> list,Set<String> pids){
//存包含name参数 对象pid
Set<String> tset = new TreeSet();
if(pids.size()>0){
Iterator<String> it = pids.iterator();
while (it.hasNext()){
String pid = String.valueOf(it.next());
if(list !=null && list.size()>0){
for(TestTreeResult t:list){
if(String.valueOf(t.getId()).equals(pid)){
tset.add(String.valueOf(t.getPid()));
allId.add(String.valueOf(t.getId()));
}
}
}
}
if(tset.size()>0){
getTreeId(list,tset);
}
}
System.out.println(allId);
return allId;
}
完整的Java逻辑代码和 js处理的html:
Java代码:
@RequestMapping("testTree")
@Controller
public class TestTreeController {
@Autowired
private TestTreeExchange testTreeExchange;
private List<TestTreeResult> allList = null;
private List<TestTreeResult> allTrees= null;
private Set<String> allId = new TreeSet();
/**
* 获取树形结构数据
* @return
*/
@RequestMapping("/getSystemTreeInfo")
@ResponseBody
public List<TestTreeResult> getRootTree(){
//存放根节点数据集合
List<TestTreeResult> rootList = new ArrayList<>();
//查询所有数据
List<TestTreeResult> allTreeList = testTreeExchange.getAllTreeList();
if(allTreeList !=null && allTreeList.size()>0){
for(TestTreeResult t: allTreeList){
//父节点是0的,为根节点。
if(t.getPid()== 0){
//添加到根节点集合
rootList.add(t);
}
}
}
//获取根节点下的所有子节点
if(rootList !=null && rootList.size()>0){
for(TestTreeResult root:rootList){
List<TestTreeResult> childTree = getChildTree(root.getId(), allTreeList);
if(childTree !=null && childTree.size()>0){
root.setChildren(childTree);
}
}
allList = allTreeList;
allTrees = rootList;
return rootList;
}else{
return null;
}
}
/**
* 获取子节点
* @param rootId 父节点id
* @param allList 所有数据列表
* @return 每个根节点下,所有子数据列表
*/
public List<TestTreeResult> getChildTree(Long rootId,List<TestTreeResult> allList){
//子数据集合
List<TestTreeResult> childList = new ArrayList<TestTreeResult>();
// 遍历所有节点,将所有数据的父id与传过来的根节点的id比较
if(allList !=null && allList.size()>0){
for(TestTreeResult t:allList){
//相等说明:为该根节点的子节点。
if(rootId == t.getPid()){
childList.add(t);
}
}
}
if(childList !=null && childList.size()>0){
//递归子节点集合,循环获取子节点
for(TestTreeResult child:childList){
List<TestTreeResult> childTreeNext = getChildTree(child.getId(), allList);
child.setChildren(childTreeNext);
}
}else{
return null;
}
return childList;
}
/**
* 获取所有包含name参数 的id。
* @param name
* @return
*/
@RequestMapping("/getidListByName")
@ResponseBody
public Set<String> getidListByName(@RequestParam(value = "name",defaultValue = "") String name){
allId.clear();
TreeSet<String> pidList = testTreeExchange.getPidListByName(name);
Set<String> treeIds = getTreeId(allList, pidList);
return treeIds;
}
/**
* 获取所有树集合
* @return
*/
@RequestMapping("/getTreeList")
@ResponseBody
public List<TestTreeResult> getTreeList(){
return allTrees;
}
/**
* 获取pids节点的id。
* @return
*/
public Set<String> getTreeId(List<TestTreeResult> list,Set<String> pids){
//存包含name参数 对象pid
Set<String> tset = new TreeSet();
if(pids.size()>0){
Iterator<String> it = pids.iterator();
while (it.hasNext()){
String pid = String.valueOf(it.next());
if(list !=null && list.size()>0){
for(TestTreeResult t:list){
if(String.valueOf(t.getId()).equals(pid)){
tset.add(String.valueOf(t.getPid()));
allId.add(String.valueOf(t.getId()));
}
}
}
}
if(tset.size()>0){
getTreeId(list,tset);
}
}
System.out.println(allId);
return allId;
}
}
html代码:
<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="utf-8"/>
<meta name="viewport" content="width=device-width, initial-scale=1"/>
<title>PICC - 系统导航</title>
<link th:href="@{/assets/style/bootstrap.min.css}" rel="stylesheet"/>
<link th:href="@{/assets/style/reset.css}" rel="stylesheet"/>
<link th:href="@{/assets/style/font-awesome.min.css}" rel="stylesheet"/>
<link th:href="@{/assets/style/zTreeStyle.css}" rel="stylesheet"/>
<link th:href="@{/assets/style/navCss.css}" rel="stylesheet"/>
<link th:href="@{/assets/style/sweetalert2.min.css}" rel="stylesheet"/>
<script th:src="@{/assets/javascript/jquery-1.11.1.min.js}"></script>
<script th:src="@{/plugins/echarts.js}"></script>
</head>
<body>
<div class="content">
<div class="navbar-header">
<div class="flex-between padding20">
<span>
系统导航
</span>
<span>
<input id="reportName" type="text" width="150px">
<input id="sButton" class="btn btn-info btn-sm" type="button" value="搜索">
</span>
</div>
</div>
<div class="flex1BC" id="container"></div>
</div>
<script type="text/javascript">
//系统路径
var baseUrl = '[(${#servletContext.contextPath})]';
var dom = document.getElementById("container");
var myChart = echarts.init(dom);
myChart.showLoading();
var resData = null;/*所有数据*/
$().ready(function() {
$.post(baseUrl + '/testTree/getSystemTreeInfo', function (data) {
myChart.hideLoading();
if(data != null && data !='undefined' && data !=''){
resData = data[0];
}
myChart.setOption(getOption(resData,''));
});
});
var option;
function getOption(data,name){
option = {
tooltip: {
trigger: 'item',
triggerOn: 'mousemove'
},
series:[
{
type: 'tree',
data: [data],
left: '2%',
right: '2%',
top: '8%',
bottom: '20%',
symbol: 'emptyCircle',//标记的形状
initialTreeDepth: 2, //图形初始展开层级,默认2.
orient: 'vertical',//图形显示方向
expandAndCollapse: true,
roam: true,//缩放和平移
label: {
position: 'top',
rotate: 0,
offset:[10,-10],
verticalAlign: 'middle',
align: 'center',
fontSize: 12,
/*文字内容显示设置*/
formatter:function (param) {
var split = param.name.split('');
if(name !='' && param.name.match(name)){
return '{a|'+param.name.split('').join('\n')+'}'
}else{
return split.join('\n');
}
},
rich:{
a:{
color: 'red',
lineHeight:10
}
}
},
leaves: {
label: {
position: 'bottom',
rotate: 0,
verticalAlign: 'middle',
align: 'center',
fontSize: 12
}
},
animationDurationUpdate: 750
}
]
};
return option;
};
/*监听页面窗口变化*/
window.onresize = function () {
myChart.resize();
}
/*条件搜索*/
$("#sButton").click(function () {
var name = $("#reportName").val();
if(name != "" && name.trim().length>0){
/*获取所有树形结构数据*/
$.post(baseUrl + '/testTree/getTreeList', function (data) {
myChart.hideLoading();
if(data){
getIds(data[0],name);
}
});
}else{
myChart.setOption(getOption(resData,''));
}
});
/*根据条件name参数,获取所有匹配name的父类ids*/
function getIds(data,name){
$.post(baseUrl + '/testTree/getidListByName',{name:name}, function (ids) {
if(ids){
getChild(data,ids);
myChart.setOption(getOption(data,name));
}else{
myChart.setOption(getOption(resData,''));
}
});
}
/*递归根据 id判断当前节点是否展开*/
function getChild(data,ids){
if(ids.length>0 && data.children){
ids.forEach(item => {
if (item == data.id) {
data.collapsed = false;
}
});
echarts.util.each(data.children, function (datum, index) {
if(datum.children){
return getChild(datum,ids);
}
});
}else{
return null;
}
}
</script>
</body>
</html>