開發組在開發過程中,都不可避免地遇到了一些困難或問題,但都最終想出辦法克服了。我們認為這樣的經驗是有必要記錄下來的,因此就有了【技術博客】。
基於JsPlumb和JQuery-UI的流程圖的保存和再生成
這篇技術博客基於軟件工程課程的VisualPytorch之上,代碼在一定程度上參考了https://www.cnblogs.com/sggx/p/3836432.html這篇博客。
VisualPytorch項目鏈接如下:VisualPytorch
前言-為什么要保存流程圖

對於這樣一個類流程圖或者模型圖,用戶會有保存、查看以及再編輯的需求,僅僅提供讓用戶拖拽連線的功能、讓用戶每次都從頭搭建是會嚴重影響用戶體驗的,因此需要能夠保存模型圖。
這篇博客主要用於提供思路,而非提供整套的解決方案,具體解決方案因人而異。
如何保存流程圖
保存流程圖是兩方面的問題:
(1)選定合適的保存格式
(2)確保保存的信息足夠來還原完整的圖
在保存時,我采用的是將整個圖分為兩個數組保存入數據庫中。圖主要由模塊(開始、reshape層)和連線兩部分組成,因此我選擇將模塊保存為一個json,而連線保存為數組。
保存模塊采用的主要方法
$("#canvas").find(".node").each(function (index, element) {
var id = $(element).attr('id');
nets[id] = {
"name": $(element).attr('name'),
"attribute": eval('(' + window.localStorage.getItem(id) + ')'),
"left": $(element).css('left'),
"top": $(element).css('top')
}
});
在畫布中遍歷所有的模塊類,nets對象的標簽為模塊的id,而內容為需要保存的屬性。需要注意的是,left與top屬性必須保存,其相當於x,y坐標,用於還原時定位div的位置。
保存連線采用的主要方法
for (var i = 0; i < conn_list.length; i++) {
var source_id = conn_list[i]["sourceId"];
var target_id = conn_list[i]["targetId"];
var conn = {
"source": {
"id": source_id,
"anchor_position": conn_list[i]["endpoints"][0]["anchor"]["type"]
},
"target": {
"id": target_id,
"anchor_position": conn_list[i]["endpoints"][1]["anchor"]["type"]
}
};
nets_conn.push(conn);
}
連線保存需要注意兩個地方,首先是conn_list的獲得,即如何得到目前的所有連線,這里我調用了JsPlumb中現有的接口
conn_list = jsPlumb.getAllConnections()
另一個地方時anchor_position。這里涉及到了JsPlumb的部分,在連線的時候,只有有anchor的地方才可以連線,為了復原整個圖,我們需要記錄下anchor的位置。
如何復原流程圖
如何將保存的數據從數據庫中取出不應該是本博客的內容,不多贅述。先貼上完整代碼
var structure = eval('(' + net_work["structure"] + ')');
var nets = structure['nets'];
var nets_conn = structure['nets_conn'];
var static_val = structure['static'];
var drop_function = $("#canvas").droppable('option', 'drop');
var event;
jQuery.each(nets, function (id, val) {
jsPlumb.ready(function () {
var ui = {
'offset': {
'left': parseInt(val['left'].split('px')) + $("#canvas").offset().left,
'top': parseInt(val['top'].split('px')) + $("#canvas").offset().top
},
'draggable': [{
"id": val['name'],
"innerHTML": $("#" + val['name'])[0].innerHTML
}],
'id': id
};
drop_function(event, ui);
});
window.localStorage.setItem(id, JSON.stringify(val['attribute']));
});
jQuery.each(nets_conn, function (id, val) {
jsPlumb.ready(function () {
jsPlumb.connect({
"source": val['source']['id'],
"target": val['target']['id'],
"anchors": [val['source']['anchor_position'], val['target']['anchor_position']],
"endpoint": ["Dot", {radius: 5}],
"paintStyle": {
stroke: "#fc2f49",
strokeWidth: 3,
},
"maxConnections": -1,
"connector": ["Flowchart", {
stub: [40, 60],
gap: 5,
cornerRadius: 5,
alwaysRespectStubs: true
}],
"overlays": [["Arrow", {width: 10, length: 10, location: 1}]],
"connectionsDetachable": true,
})
});
});
代碼看着很多,原理非常簡單,就是把用戶畫圖的操作再用js腳本做一遍。
放置模塊
首先第一點,用戶在拖拽div進入畫布時,畫布為droppable狀態,會觸發JQuery-UI 的drop事件。
$("#canvas").droppable({
scope: "ss",
drop: function (event, ui) {
.......
}
})
第一段代碼中的
var drop_function = $("#canvas").droppable('option', 'drop')
drop_function函數就對應了drop事件觸發后執行的代碼塊。
如此一來便很容易理解,只要讓每個被保存的模塊去觸發drop事件即可,參數ui用保存的屬性來自己構造,詳情參照代碼。
重新連線
重新連線部分,直接調用JsPlumb的連線方法
jsPlumb.connect()
即可。
注意我們的anchor要選擇之前保存的anchor。
