1. 程式人生 > >十分好用的拓撲圖外掛JTopo

十分好用的拓撲圖外掛JTopo

<%@ page contentType="text/html;charset=UTF-8"%>
<%@ include file="/WEB-INF/views/include/taglib.jsp"%>
<html>
<head>
<title>建立拓撲圖</title>
<meta name="decorator" content="default" />
    <script type="text/javascript" src="${ctxStatic}/common/uuid.js"></script>
    <script type="text/javascript" src="${ctxStatic}/topo/jtopo/js/jtopo-min.js"></script>
    <script type="text/javascript" src="${ctxStatic}/topo/jtopo/js/toolbar.js"></script>
    <script type="text/javascript" src="${ctxStatic}/topo/jtopo/js/jtopo-common.js"></script>

<script type="text/javascript" src="${ctxStatic}/jQuery-slimScroll-1.3.0/jquery.slimscroll.min.js"></script>
    <script type="text/javascript" src="${ctxStatic}/jQuery-slimScroll-1.3.0/slimScrollHorizontal.min.js"></script>
<style>
.contextmenu {
    border: 1px solid #aaa;
    border-bottom: 0;
    background: #eee;
    position: absolute;
    list-style: none;
    margin: 0;
    padding: 0;
    display: none;
}
                                                                           
.contextmenu li a {
    display: block;
    padding: 10px;
    border-bottom: 1px solid #aaa;
    cursor: pointer;
}
                                                                           
.contextmenu li a:hover {
    background: #fff;
}
</style>
<script type="text/javascript">
$(document).ready(function(){
    $('.scroller').each(function() {
        $(this).slimScroll({
            size : '7px',
            color : '#a1b2bd',
            height : $(this).attr("data-height"),
            alwaysVisible : ($(this).attr("data-always-visible") == "1" ? true : false),
            railVisible : ($(this).attr("data-rail-visible") == "1" ? true : false),
            railOpacity : 0.1,
            disableFadeOut : true
        });
    });
    //載入素材圖片
    $.ajax({
        url : '${ctx}/topo/topoRes/treeRes?extId=' + new Date().getTime(),
        async : false, //同步執行 
        data : {},
        success : function(r) {
            if (r && r.length > 0) {
                for (var i = 0; i < r.length; i = i + 1) {
                    if (r[i]) {
                        var sel = '<div id="'+r[i].id+'">' + r[i].name;
                        for (var j = 0; j < r[i].childList.length; j = j + 1) {

                            if (r[i].childList[j] && r[i].childList[j].resPath != NaN && r[i].childList[j].resPath != '') {
                                if (r[i].childList[j].code == 'line') {
                                    sel += 
                                    '<div style="margin-top:2px;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;'+
                                        '<a href="#" onclick="addTopoNodes(\'' + r[i].childList[j].code + '\',\'' + r[i].childList[j].resPath + '\')" >' +
                                         r[i].childList[j].name + '</a>&nbsp;&nbsp;'+
                                         '<img style="width:32px;height:3px;" src="${ctxStatic}/topo/res/' + r[i].childList[j].resPath + '"'+ 
                                         'onclick="addTopoNodes(\'' + r[i].childList[j].code + '\',\'' + r[i].childList[j].resPath + '\')"/></div>';
                                } else{
                                    sel +=
                                    '<div style="margin-top:2px;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;'+
                                        '<a href="#" onclick="addTopoNodes(\'' + r[i].childList[j].code + '\',\'' + r[i].childList[j].resPath + '\')" >' + 
                                        r[i].childList[j].name + '</a>&nbsp;&nbsp;<img style="width:32px;height:32px;" src="${ctxStatic}/topo/res/' + r[i].childList[j].resPath + '" '+
                                        ' onclick="addTopoNodes(\'' + r[i].childList[j].code + '\',\'' + r[i].childList[j].resPath + '\')"/></div>';
                                }
                            }
                        }
                        sel = sel + '</div>';
                        jQuery("#topoRes").before(sel);
                    }
                }
            }
        }
    });
    initTopo();
    bindEvent();
    contextMenuClick();
});
//畫布場景
var scene;
var stage;
//拓撲圖狀態 1:可編輯 2:關聯資產 0:只讀
var editStatus = 1;
var beginNode = null;
var currentNode = null;
var lineColor = "128,0,0";

var rectNodes = new Array();
var eleNodes = new Array();
//初始化拓撲圖
function initTopo(){
    //參考:http://180.76.135.17/api.html
    var canvas = document.getElementById('canvas');
    stage = new JTopo.Stage(canvas);
    //顯示工具欄
    showJTopoToobar(stage);
    scene = new JTopo.Scene();    
    //scene.background = '${ctxStatic}/common/img/map_bg.png';
    scene.backgroundColor = "255,255,255";
    stage.add(scene);
    loadTopoData();
}
//初始化拓撲圖元素
function loadTopoData(){
    if("${fn:length(jtopo.elements)}"!="0"){
         var elements = new Array();  
          <c:forEach items="${jtopo.elements}" var="element">  
              var ele = { "type":"${element.type}",
                           "url":"${element.url}",
                           "x":parseInt("${element.x}"),
                           "y":parseInt("${element.y}"),
                         "width":parseInt("${element.width}"),
                         "height":parseInt("${element.height}"),
                         "code":"${element.code}",
                         "remarks":"${element.remarks}",
                         "fillColor":"${element.fillColor}",
                         "zindex":"${element.zindex}",
                         "alarm":"${element.alarm}",
                         "alpha":"${element.alpha}",
                         "rotate":"${element.rotate}"}; 
              elements.push(ele);  
          </c:forEach>
          var links = new Array();
          <c:forEach items="${jtopo.links}" var="element">  
              var ele = { "type":"${element.type}",
                         "code":"${element.code}",
                         "remarks":"${element.remarks}",
                         "fillColor":"${element.fillColor}"}; 
              links.push(ele);  
          </c:forEach>
          for(var i in elements){
             var ele = elements[i];
             if(ele.type !=undefined){
                 ele.x = getDefualtParam(ele.x,0);
                 ele.y = getDefualtParam(ele.y,0);
                 ele.width = getDefualtParam(ele.width,100);
                 ele.height = getDefualtParam(ele.height,100);
                  initTopoNodes(ele);
             }
          }
          for(var i in links){
              var ele = links[i];
              if(ele.type == 'link'){
                  initTopoLink(ele.code,ele.fillColor);
              }
          }
    }
}
function addTopoNodes(type,path){
    var ele = { 
            "type":type,
               "url":path,
               "x":0,
               "y":0,
             "width":68,
             "height":68};
    var node = null;
    if(type == "text"){//新增文字
        node = addTextNode(ele);
    }else if(type == "rect"){//新增文字
        ele.fillColor = "128,128,128";
        node = addRectNode(ele);
        rectNodes.push(node);
    }else if(type == "line"){//改變連線顏色
        if(path == "line2.png"){
            lineColor = "128,0,0";
            alert("連線切換為紅色");
        }else{
            lineColor = "0,0,255";
            alert("連線切換為藍色");
        }
    }else if(path.indexOf(".")>0) {//新增節點
        node = addImageNode(ele);
    }
    if(node !=null){
        node.addEventListener('mouseup', function(event){
            currentNode = this;
            menuEvent(event,1);
        });
        scene.add(node);
    }
}
//新增物件方法
function initTopoNodes(ele){
    var node = null;
    if(ele.type == "text"){//新增文字
        node = addTextNode(ele);
    }else if(ele.type == "rect"){//新增文字
        node = addRectNode(ele);
        rectNodes.push(node);
    }else if(ele.url.indexOf(".")>0) {//新增節點
        node = addImageNode(ele);
        if(ele.code !=null && ele.code !=""){
            eleNodes.push({"code":ele.code,"node":node});
        }
    }
    if(node !=null){
        node.id = ele.code;
        node.addEventListener('mouseup', function(event){
            currentNode = this;
            menuEvent(event,1);
        });
        scene.add(node);
    }
}

//繫結事件
function bindEvent(){
    scene.mouseup(function(e){
        if(e.button == 2){
            return;
        }
        if(e.target != null && e.target instanceof JTopo.Node){
            if(beginNode == null){
                beginNode = e.target;
            }else if(beginNode !== e.target){
                var endNode = e.target;
                if(isEditMode()){
                    if(!rectNodes.contains(beginNode) && !rectNodes.contains(endNode)){
                        addLink(beginNode,endNode,lineColor);
                    }
                }
                beginNode = null;
            }else{
                beginNode = null;
            }
        }
    });
    stage.click(function(event){
        if(event.button == 0){// 右鍵
            // 關閉彈出選單(div)
            $("#contextmenu").hide();
            $("#linemenu").hide();
            $("#layoutmenu").hide();
        }
    });
    bindTextEvent();
}

function bindTextEvent(){
    //文字輸入事件繫結
    var textfield = $("#jtopo_textfield");
    scene.dbclick(function(event){
        if(event.target == null || !isEditMode()){ 
            return;
        }
        var e = event.target;
        textfield.css({
            top: event.pageY,
            left: event.pageX - e.width/2
        }).show().attr('value', e.text).focus().select();
        e.text = "";
        textfield[0].JTopoNode = e;
    });
    $("#jtopo_textfield").blur(function(){
        textfield[0].JTopoNode.text = textfield.hide().val();
    });
}
//右鍵選單事件
function menuEvent(event,type){
    if(event.button == 2){// 右鍵
        var model = $("input[name='modeRadio']:checked").val();
        if('select' == model){
             $("#layoutmenu").css({
                 top: event.pageY,
                 left: event.pageX
             }).show(); 
        }else{
            // 當前位置彈出選單(div)
            var menId = "contextmenu";
            if(type == 2){
                menId = "linemenu";
                $("#contextmenu").hide();
            }else{
                $("#linemenu").hide();
            }
            $("#"+menId).css({
                top: event.pageY,
                left: event.pageX
            }).show();   
        } 
    }
}
function contextMenuClick(){
     var colors = [];
     colors.push("128,128,128");
     colors.push("135,206,250");
     colors.push("65,105,225");
     colors.push("255,255,255");
    /* 右鍵選單處理 */    
    $("#contextmenu a").click(function(){
        var text = $(this).text();
        if(text == '刪除'){
            scene.remove(currentNode);
            currentNode = null;
        }if(text == '撤銷上一次操作'){
            currentNode.restore();
        }else{
           // currentNode.save();
        }
        if(text == '刪除連線'){
            var lines = currentNode.inLinks;
            for(i = 0;i<lines.length;i++){
                scene.remove(lines[i]);
            }
        }
        if(text == '變換背景色'){
            var colorVal = colors[0];
            for(var i=0;i<colors.length;i++){
                if(currentNode.fillColor == colors[i]){
                    if((i+1) == colors.length){
                        colorVal = colors[0];
                        break;
                    }
                    colorVal = colors[i+1];
                    break;
                }
            }
            currentNode.fillColor = colorVal;
        }else if(text == '順時針旋轉'){
            currentNode.rotate += 0.5;
        }else if(text == '逆時針旋轉'){
            currentNode.rotate -= 0.5;
        }else if(text == '放大'){
            currentNode.scaleX += 0.2;
            currentNode.scaleY += 0.2;
        }else if(text == '底層'){
            currentNode.zIndex = 10;
        }else if(text == '頂層'){
            currentNode.zIndex = 20;
        }
        $("#contextmenu").hide();
    });
    var lineColors = [];
    lineColors.push("128,0,0");
    lineColors.push("0,0,255");
    lineColors.push("105,105,105");
    lineColors.push("255,255,255");
    /* 右鍵選單處理 */    
    $("#linemenu a").click(function(){
        var text = $(this).text();
        if(text == '刪除連線'){
           scene.remove(currentNode);
        }else if(text == '變換顏色'){
             var colorVal = lineColors[0];
             for(var i=0;i<lineColors.length;i++){
                 if(currentNode.strokeColor == lineColors[i]){
                     if((i+1) == lineColors.length){
                         colorVal = lineColors[0];
                         break;
                     }
                     colorVal = lineColors[i+1];
                     break;
                 }
             }
            currentNode.strokeColor = colorVal;
        }
        $("#linemenu").hide();
    });
    $("#layoutmenu a").click(function(){
        var text = $(this).text();
        var elements = scene.selectedElements;
        if(text == '垂直對齊'){
            var x1 = elements[0].x;
            var y1 = elements[0].y;
            var y2 = elements[elements.length-1].y;
            var avgY = (y2-y1)/(elements.length-1);
            for(var i=1;i<elements.length;i++){
                 var ele = elements[i];
                 ele.x = x1;
                 ele.y = y1+avgY*i;
            }
        }else if(text == "水平對齊"){
            var x1 = elements[0].x;
            var y1 = elements[0].y;
            var x2 = elements[elements.length-1].x;
            var avgX = (x2-x1)/(elements.length-1);
            for(var i=1;i<elements.length;i++){
                 var ele = elements[i];
                 ele.x = x1+avgX*i;
                 ele.y = y1;
            }
        }else if(text == "縮小"){
            for(var i in elements){
                 var ele = elements[i];
                 ele.width = ele.width*0.8;
                 ele.height = ele.height*0.8;
            }
        }else if(text == "放大"){
            for(var i in elements){
                 var ele = elements[i];
                 ele.width = ele.width*1.2;
                 ele.height = ele.height*1.2;
            }
        }
        $("#layoutmenu").hide();
    });
}
//儲存拓撲圖
function savePojo(){
    var formData ={};
    //獲取場景中的所有物件
    var elements = scene.getDisplayedElements();
    var topoId = $("#id").val();
    formData["id"]=topoId;
    for(var i in elements){
        var element = elements[i];
        if(element.elementType != undefined){
            if(element.elementType == "node"){
                formData["elements["+i+"].jtopoId"]=topoId;
                formData["elements["+i+"].code"]=element.id;
                formData["elements["+i+"].x"]=element.x;
                formData["elements["+i+"].y"]=element.y;
                formData["elements["+i+"].rotate"]=element.rotate;
                formData["elements["+i+"].scaleX"]=element.scaleX;
                formData["elements["+i+"].scaleY"]=element.scaleY;
                if(rectNodes.contains(element)){
                    formData["elements["+i+"].type"]="rect";
                    formData["elements["+i+"].zindex"]=element.zIndex;
                    formData["elements["+i+"].fillColor"]=element.fillColor;
                    formData["elements["+i+"].alpha"]=element.alpha;
                }else{
                    formData["elements["+i+"].type"]="node";
                    var url = element.image.src;
                    formData["elements["+i+"].url"]=url.replace(getRootPath()+"/static/topo/res/","");
                }
                formData["elements["+i+"].width"]=element.width;
                formData["elements["+i+"].height"]=element.height;
            }else if(element.elementType == "link"){
                formData["elements["+i+"].jtopoId"]=topoId;
                formData["elements["+i+"].type"]="link";
                formData["elements["+i+"].code"]=element.nodeA.id+","+element.nodeZ.id;
                formData["elements["+i+"].fillColor"]=element.strokeColor;
            }else if(element.elementType == "TextNode"){
                formData["elements["+i+"].jtopoId"]=topoId;
                formData["elements["+i+"].type"]="text";
                formData["elements["+i+"].code"]=element.id;
                formData["elements["+i+"].x"]=element.x;
                formData["elements["+i+"].y"]=element.y;
            }
            if(element.text != undefined){
                formData["elements["+i+"].remarks"]=element.text;
            }
        }
    }
    $.ajax({
        url : '${ctx}/jtopo/jtopoElement/save?extId=' + new Date().getTime(),
        async : true, //同步執行 
        data : formData,
        type:"post",
        success : function(r) {
            alert(r);
            window.location.reload();
        }
    });    
}
</script>
</head>
<body>
<div class="container-fluid">
        <div class="row-fluid">
            <fieldset>
                <legend> 建立網路拓撲 </legend>
            </fieldset>
        </div>
        <sys:message content="${message}" />
        <form:form id="inputForm" modelAttribute="jtopo" action="${ctx}/jtopo/jtopoElement/save" method="post" class="form-horizontal">
            <input type="hidden" name="id" id="id" value="${jtopo.id }" />
        </form:form>
        <input type="text" id="jtopo_textfield" style="position: absolute;display: none;">
        <ul id="contextmenu" class="contextmenu" style="top: 424px; left: 642px; display: none;">    
            <li><a>順時針旋轉</a></li>
            <li><a>逆時針旋轉</a></li>    
            <li><a>刪除連線</a></li>
            <li><a>刪除</a></li>
            <li><a>變換背景色</a></li>
            <li><a>頂層</a></li>
            <li><a>底層</a></li>
          </ul>
          <ul id="linemenu" class="contextmenu" style="top: 424px; left: 642px; display: none;">
            <li><a>刪除連線</a></li>
            <li><a>變換顏色</a></li>
          </ul>
           <ul id="layoutmenu" class="contextmenu" style="top: 424px; left: 642px; display: none;">
            <li><a>垂直對齊</a></li>
            <li><a>水平對齊</a></li>
            <li><a>縮小</a></li>
            <li><a>放大</a></li>
          </ul>
        <div class="row-fluid">
            <div class="span2" style="border:none;">
                <div class="scroller" data-height="600px" data-always-visible="1" data-rail-visible="1">
                    <div id="topoRes" style="display: none"></div>
                </div>
            </div>
            <div class="span10" style="border:none;">
                <div class="widget-content " style="height: 610px; margin-top: -10px;">
                    <div id="topo_content">
                        <canvas width="960" height="550" id="canvas" style="border: 1px solid #b0b0b0;"></canvas>
                    </div>
                </div>
            </div>
        </div>
    </div>
</body>
</html>

jtopo-common.js內容

function isEditMode(){
    var model = $("input[name='modeRadio']:checked").val();
    if('edit' == model){
        return true;
    }
    return false;
}
//自定義是否包含方法
Array.prototype.contains = function(needle) {
  for (i in this) {
    if (this[i] == needle) return true;
  }
  return false;
}
//初始化連線
function initTopoLink(code,color){
    var codes = code.split(",");
    var elements = eleNodes;
    for(i in elements){
        var e = elements[i];
        if(code.indexOf(e.code)>=0){
            if(beginNode == null){
                beginNode = e;
            }else{
                addLink(beginNode.node,e.node,color);
                beginNode = null;
            }
        }
    }
}
function addRectNode(ele){
    if(null ==ele.remarks || ele.remarks == ""){
        ele.remarks = "矩形框";
    }
    var rectNode = new JTopo.Node(ele.remarks);
    rectNode.setImage("", false);
    rectNode.setLocation(ele.x, ele.y);
    rectNode.setSize(ele.width, ele.height);
    if(ele.fillColor =="" || typeof(ele.alpha) == "undefined"){
        ele.fillColor = "128,128,128";
    }
    if(ele.alpha =="" || typeof(ele.alpha) == "undefined"){
        ele.alpha = 1;
    }
    rectNode.alpha = ele.alpha;
    if(ele.fillColor !="none"){
        rectNode.fillColor = ele.fillColor;//底色
    }
    rectNode.textPosition = 'Top_Left';//標題顯示位置
    //向左偏移 20畫素
    rectNode.textOffsetX = 15*ele.remarks.length+15;
    //向下偏移 25畫素
    rectNode.textOffsetY = 30;
    if(ele.zindex =="" || typeof(ele.zindex) == "undefined"){
        ele.zindex = 11;
    }
    rectNode.zIndex = ele.zindex;
    if(editStatus!=1){
        rectNode.editAble = false;
        rectNode.dragable = false;
    }
    rectNode.font='bold 16px 宋體';
    if(ele.fontColor =="" || typeof(ele.fontColor) == "undefined"){
        ele.fontColor = '0,0,0';
    }
    if(ele.borderColor =="" || typeof(ele.borderColor) == "undefined"){
        ele.borderColor = '0,0,0';
    }
    rectNode.fontColor = ele.fontColor;
    rectNode.borderWidth = 1; // 邊框的寬度
    rectNode.borderColor = ele.borderColor; //邊框顏色 
    return rectNode;
}
function addTextNode(ele){
    if(null ==ele.remarks || ele.remarks == ""){
        ele.remarks = "請雙擊輸入文字";
    }
    var textNode = new JTopo.TextNode(ele.remarks);
    textNode.setLocation(ele.x, ele.y);
    textNode.zIndex = 21;
    if(editStatus!=1){
        textNode.editAble = false;
        textNode.dragable = false;
    }
    textNode.font = "bold 16px 宋體";
    textNode.fontColor = "0,0,0";
    return textNode;
}
//新增連線
function addLink(beginNode,endNode,color){
    var link = new JTopo.Link(beginNode, endNode);
    link.strokeColor = color;
    link.type = 'loop'; // 自定義屬性
    link.zIndex = 21;
    if(editStatus==1){
        link.addEventListener('mouseup', function(event){
            currentNode = this;
            menuEvent(event,2);
        });
    }
    scene.add(link);
    return link;
}
//新增節點圖片
function addImageNode(ele,isEdit){
    var imgNode = new JTopo.Node();
    //設定圖片的路徑和是否改變大小
    imgNode.setImage(getImgPath(ele.url), false);
    imgNode.setBound(ele.x, ele.y, ele.width, ele.height);
    imgNode.zIndex = 20;
    imgNode.rotate = ele.rotate;
    if(editStatus!=1){
        imgNode.editAble = false;
        imgNode.dragable = false;
    }
    if(null != ele.alarm && ele.alarm !=""){
        imgNode.alarm = ele.alarm;
        imgNode.alarmColor = '255,0,0';
    }
    if(null != ele.remarks){
        imgNode.text = ele.remarks;
        imgNode.font = "12px 宋體";
        if(ele.fontColor =="" || typeof(ele.fontColor) == "undefined"){
            ele.fontColor = '0,0,255';
        }
        imgNode.fontColor = ele.fontColor;
    }
    return imgNode;
}
function getImgPath(resPath) {
    if(resPath.indexOf("http://")>=0 || resPath.indexOf("https://")>=0){
        return resPath;
    }
    return getRootPath() + '/static/topo/res/' + resPath;
}
function getRootPath() {
    // 獲取當前網址,如: http://localhost:8083/uimcardprj/share/meun.jsp
    var curWwwPath = window.document.location.href;
    // 獲取主機地址之後的目錄,如: uimcardprj/share/meun.jsp
    var pathName = window.document.location.pathname;
    var pos = curWwwPath.indexOf(pathName);
    // 獲取主機地址,如: http://localhost:8083
    var localhostPath = curWwwPath.substring(0, pos);
    // 獲取帶"/"的專案名,如:/uimcardprj
    var projectName = pathName.substring(0, pathName.substr(1).indexOf('/') + 1);
    if (projectName == '/a')
        return localhostPath;
    return (localhostPath + projectName);
}
function getDefualtParam(value,def){
    if(value ==null || value ==""){
        value = def;
    }
    return parseInt(value);
}