用JavaScript来实现的超炫组织结构图

  到新公司实习第七天,Boos就让我做个组织架构,用来展示人员关系图...然后就开始了我的code,不经意间在Github上看到了一个 开源的javascript类库可以生成非常酷炫的节点图形,我选择了其中一种spacetree类型做为我的组织结构图基础,这类库种类很多,功能非常强大,非常适合复杂的图形功能需求

spacetree

  这种图形可以支持一下特性:

  • 支持向上下左右四个方向展开图表
  • 支持子节点扩展
  • 支持图表拖放  
  • 支持图表缩放

 

 html: 



<!-- JIT Library File -->
<script language="javascript" type="text/javascript" src="/public/javascripts/jit.js"></script>

<!-- Example File -->
<script language="javascript" type="text/javascript" src="/public/javascripts/example1.js"></script>

<div>
    <div id="container">
        <div id="center-container">
            <div id="infovis"></div>
        </div>
        <div id="log"></div>
    </div>
</div>

<script type="text/javascript">
    window.onload=init();
</script>



var labelType, useGradients, nativeTextSupport, animate;

(function() {
  var ua = navigator.userAgent,
      iStuff = ua.match(/iPhone/i) || ua.match(/iPad/i),
      typeOfCanvas = typeof HTMLCanvasElement,
      nativeCanvasSupport = (typeOfCanvas == 'object' || typeOfCanvas == 'function'),
      textSupport = nativeCanvasSupport 
        && (typeof document.createElement('canvas').getContext('2d').fillText == 'function');
  //I'm setting this based on the fact that ExCanvas provides text support for IE
  //and that as of today iPhone/iPad current text support is lame
  labelType = (!nativeCanvasSupport || (textSupport && !iStuff))? 'Native' : 'HTML';
  nativeTextSupport = labelType == 'Native';
  useGradients = nativeCanvasSupport;
  animate = !(iStuff || !nativeCanvasSupport);
})();

var Log = {
  elem: false,
  write: function(text){
    if (!this.elem) 
      this.elem = document.getElementById('log');
    this.elem.innerHTML = text;
    this.elem.style.left = (500 - this.elem.offsetWidth / 2) + 'px';
  }
};


function init(){
    var json;
    $.ajax({
        type: "GET",
        url: "/home/spacetreeJSON",
        data: {username:$("#username").val(), content:$("#content").val()},
        dataType: "json",
        async: false,
        success: function(data){
            if(data.result=="success"){
                json=data.info.json;
            }
            console.log(data.info.json);
        }        
    });
    //init Spacetree
    //Create a new ST instance
    var st = new $jit.ST({
        //id of viz container element
        injectInto: 'infovis',
        //set duration for the animation
        duration: 800,
        //set animation transition type
        transition: $jit.Trans.Quart.easeInOut,
        //set distance between node and its children
        levelDistance: 50,
        //enable panning
        Navigation: {
          enable:true,
          panning:true
        },
        //set node and edge styles
        //set overridable=true for styling individual
        //nodes or edges
        Node: {
            height: 20,
            width: 100,
            type: 'rectangle',
            color: '#aaa',
            overridable: true
        },
        
        Edge: {
            type: 'bezier',
            overridable: true
        },
        
        onBeforeCompute: function(node){
            Log.write("loading " + node.name);
        },
        
        onAfterCompute: function(){
            Log.write("done");
        },
        
        //This method is called on DOM label creation.
        //Use this method to add event handlers and styles to
        //your node.
        onCreateLabel: function(label, node){
            label.id = node.id;            
            label.innerHTML = node.name;
            label.onclick = function(){
                // if(normal.checked) {
                  st.onClick(node.id);
                // } else {
                // st.setRoot(node.id, 'animate');
                // }
            };
            //set label styles
            var style = label.style;
            style.width = 100 + 'px';
            style.height = 17 + 'px';            
            style.cursor = 'pointer';
            style.color = '#333';
            style.fontSize = '0.8em';
            style.textAlign= 'center';
            style.paddingTop = '3px';
        },
        
        //This method is called right before plotting
        //a node. It's useful for changing an individual node
        //style properties before plotting it.
        //The data properties prefixed with a dollar
        //sign will override the global node style properties.
        onBeforePlotNode: function(node){
            //add some color to the nodes in the path between the
            //root node and the selected node.
            if (node.selected) {
                node.data.$color = "#ff7";
            }
            else {
                delete node.data.$color;
                //if the node belongs to the last plotted level
                if(!node.anySubnode("exist")) {
                    //count children number
                    var count = 0;
                    node.eachSubnode(function(n) { count++; });
                    //assign a node color based on
                    //how many children it has
                    node.data.$color = ['#aaa', '#baa', '#caa', '#daa', '#eaa', '#faa'][count];                    
                }
            }
        },
        
        //This method is called right before plotting
        //an edge. It's useful for changing an individual edge
        //style properties before plotting it.
        //Edge data proprties prefixed with a dollar sign will
        //override the Edge global style properties.
        onBeforePlotLine: function(adj){
            if (adj.nodeFrom.selected && adj.nodeTo.selected) {
                adj.data.$color = "#eed";
                adj.data.$lineWidth = 3;
            }
            else {
                delete adj.data.$color;
                delete adj.data.$lineWidth;
            }
        }
    });
    //load json data
    st.loadJSON(json);
    //compute node positions and layout
    st.compute();
    //optional: make a translation of the tree
    st.geom.translate(new $jit.Complex(-200, 0), "current");
    //emulate a click on the root node.
    st.onClick(st.root);

}



如上js代码 是用的spacetree方式,重写的jit.ST方法,这里有一点需要注意,就是获取json的时候 建议ajax异步获取数据改为同步获取,即 async: false。

keynote插入组织架构图 keynote做组织架构图_json

 

在做以上工作的时候,非常简单,在后台整理对数据库中数据整理成json数组的时候,出现了一些问题,使用递归算法结合数据库解析成JSON树形结构,就这点小问题,让我弄了老长时间┭┮﹏┭┮,我就说一下我解决的思路把

  先上需要整理出来的json格式




keynote插入组织架构图 keynote做组织架构图_数据库_02

keynote插入组织架构图 keynote做组织架构图_keynote插入组织架构图_03

{
    "id": "node02", 
    "name": "0.2", 
    "data": { }, 
    "children": [
        {
            "id": "node13", 
            "name": "1.3", 
            "data": { }, 
            "children": [
                {
                    "id": "node24", 
                    "name": "2.4", 
                    "data": { }, 
                    "children": [
                        {
                            "id": "node35", 
                            "name": "3.5", 
                            "data": { }, 
                            "children": [
                                {
                                    "id": "node46", 
                                    "name": "4.6", 
                                    "data": { }, 
                                    "children": [ ]
                                }
                            ]
                        }, 
                        {
                            "id": "node37", 
                            "name": "3.7", 
                            "data": { }, 
                            "children": [
                                {
                                    "id": "node48", 
                                    "name": "4.8", 
                                    "data": { }, 
                                    "children": [ ]
                                }, 
                                {
                                    "id": "node49", 
                                    "name": "4.9", 
                                    "data": { }, 
                                    "children": [ ]
                                }, 
                                {
                                    "id": "node410", 
                                    "name": "4.10", 
                                    "data": { }, 
                                    "children": [ ]
                                }, 
                                {
                                    "id": "node411", 
                                    "name": "4.11", 
                                    "data": { }, 
                                    "children": [ ]
                                }
                            ]
                        }, 
                        {
                            "id": "node312", 
                            "name": "3.12", 
                            "data": { }, 
                            "children": [
                                {
                                    "id": "node413", 
                                    "name": "4.13", 
                                    "data": { }, 
                                    "children": [ ]
                                }
                            ]
                        }, 
                        {
                            "id": "node314", 
                            "name": "3.14", 
                            "data": { }, 
                            "children": [
                                {
                                    "id": "node415", 
                                    "name": "4.15", 
                                    "data": { }, 
                                    "children": [ ]
                                }, 
                                {
                                    "id": "node416", 
                                    "name": "4.16", 
                                    "data": { }, 
                                    "children": [ ]
                                }
                            ]
                        }
                    ]
                }
            ]
        }
    ]
}


View Code


   1.先获取数据库中层级最高的数据(本项目设计,最顶层级id为1) (User user)

    select * from user where id='id'

  2.在获取id为'id'的所有子节点 (List <User>)

    select * from user where learderid='id'

  3.然后for循环 遍历id='id'的子节点

    注意:for循环中 要是同递归算法了, 因为 节点的字节点一直往里延伸 你不知道有多少层,所以用递归算法是正解!

 

  以上是解法思路,还有一个难点(我认为~~)就是将数据转换成json了

    1.因为是键值对 所以用Map是王道, 运行框架 用的是play!框架,也都是封装好的 ,直接调用方法就可以

    2.要在获取当前节点后,立刻将需要的值放到map中

    3.在for循环中,要new map 用来传递给递归函数的map

    3.要在for循环之前new List  用来存储你for循环中 创建的map

  直接上代码吧



1 public static void spacetreeJSON(){
 2     ResultInfo result = new ResultInfo();
 3     String sql="select * from user where id=1";
 4     List<User> userlist = new ArrayList<User>();
 5     userlist=UserController.query(sql, User.class);
 6     
 7     Map<String, Object> json = new HashMap<String, Object>();
 8     Map<String, Object> map = new HashMap<String, Object>();
 9     recursiveTree(Long.parseLong("1"), map);
10     json.put("json", map);
11     renderJSON(result.success(json, refreshExpire()));    
12 }
13 public static User recursiveTree(Long id,Map map){    
14     User usernode = User.findById(id);
15     map.put("id",usernode.id+"");
16     map.put("name",usernode.name);
17     List<User> list = UserController.query("select * from user where leaderid='"+id+"'", User.class);
18     
19     List childlist= new ArrayList();
20     for(User u : list){
21         Map<String, Object> map_u = new HashMap<String, Object>();
22         User n = recursiveTree(u.id,map_u); //递归
23         childlist.add(map_u);
24     }
25     map.put("children",childlist);    
26     return usernode;
27 }



  

  小弟技术比较渣,写的东西 含量也不是很高,就算是写给自己看的吧...可以半年后 再回来看 ,这些全是笑话  哈哈哈~~