echarts-tree:树图(带查询条件的导航图)

目录

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图例参数介绍网址:

https://echarts.apache.org/zh/option.html#series-tree

使用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>

 

 

  • 6
    点赞
  • 20
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值