十分好用的拓撲圖外掛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;"> '+
'<a href="#" onclick="addTopoNodes(\'' + r[i].childList[j].code + '\',\'' + r[i].childList[j].resPath + '\')" >' +
r[i].childList[j].name + '</a> '+
'<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;"> '+
'<a href="#" onclick="addTopoNodes(\'' + r[i].childList[j].code + '\',\'' + r[i].childList[j].resPath + '\')" >' +
r[i].childList[j].name + '</a> <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);
}