最近公司讓學習了mxgraph,簡單總結一下
(1)mxGraph學習路徑
1)API:http://jgraph.github.io/mxgraph/docs/js-api/files/index-txt.html
2)demo:http://jgraph.github.io/mxgraph/javascript/index.html
(2)最簡單的例子(helloword)
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Hello World</title> <style> html, body { height: 100%; } #graphContainer { position: relative; overflow: hidden; width: 100%; height: 100%; background: url('./images/grid.gif'); cursor: default; } </style> </head> <body onload="main(document.getElementById('graphContainer'))"> <div id="graphContainer"></div> </body> <script> const mxBasePath = '../static/mxgraph'; </script> <script src="../mxClient.js"></script> <script> function main(container) { // 禁用鼠標右鍵 mxEvent.disableContextMenu(container); const graph = new mxGraph(container); // 設置這個屬性后節點之間才可以連接 graph.setConnectable(true); // 開啟區域選擇 new mxRubberband(graph); const parent = graph.getDefaultParent(); graph.getModel().beginUpdate(); try { const v1 = graph.insertVertex(parent, null, 'Hello,', 20, 20, 80, 30);//創建第一個節點 const v2 = graph.insertVertex(parent, null, 'World!', 200, 150, 80, 30);//創建第二個節點 const e1 = graph.insertEdge(parent, null, '30%', v1, v2);//創建連線 } finally { graph.getModel().endUpdate(); } } </script> </html>
圖:
(3)mxGraph將圖轉換成xml字符串
var graph = new mxGraph(container); var xml = mxUtils.getXml(new mxCodec().encode(graph.getModel())) ; console.log(xml)
如下,左圖可得右XML
(4)Java后台解析xml
package org.sxdata.jingwei.util.transUtil; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.UUID; import javax.xml.parsers.DocumentBuilder; import javax.xml.parsers.DocumentBuilderFactory; import javax.xml.parsers.ParserConfigurationException; import org.w3c.dom.Attr; import org.w3c.dom.Document; import org.w3c.dom.Element; import org.w3c.dom.NamedNodeMap; import org.w3c.dom.Node; import org.w3c.dom.NodeList; /** * @author sonyan * @version 2019年8月14日 下午3:09:15 * @desc */ public class DocumentUtil { private static String xmlStr = ""; private static List<Map<String, Object>> nodeMapList = new ArrayList<Map<String, Object>>(); /** * 將指定的document解析成xml字符串 * @param doc * @return */ public static String getXmlStrByDocument(Document doc) { xmlStr = ""; // 根節點名稱 String rootName = doc.getDocumentElement().getTagName(); // 遞歸解析Element Element element = doc.getDocumentElement(); return getElementStr(element); } /** * 將指定的節點解析成xml字符串 * @param element * @return */ public static String getElementStr(Element element) { String TagName = element.getTagName(); boolean flag = true; xmlStr = xmlStr + "<" + TagName; NamedNodeMap attris = element.getAttributes(); for (int i = 0; i < attris.getLength(); i++) { Attr attr = (Attr) attris.item(i); xmlStr = xmlStr + " " + attr.getName() + "=\"" + attr.getValue() + "\""; } xmlStr = xmlStr + ">" ; NodeList nodeList = element.getChildNodes(); Node childNode; for (int temp = 0; temp < nodeList.getLength(); temp++) { childNode = nodeList.item(temp); // 判斷是否屬於節點 if (childNode.getNodeType() == Node.ELEMENT_NODE) { // 判斷是否還有子節點 getElementStr((Element) childNode); if (childNode.getNodeType() != Node.COMMENT_NODE) { xmlStr = xmlStr + childNode.getTextContent(); } } } xmlStr = xmlStr + "</" + element.getTagName() + ">"; return xmlStr; } /** * 解析節點 * @param element * @param graphId 所屬圖的id */ public static void parseElement(Element element, String graphId) { NodeList nodeList = element.getChildNodes(); Node childNode; for (int temp = 0; temp < nodeList.getLength(); temp++) { childNode = nodeList.item(temp); String id= getUUID32(); String nodeId = getNodeAttrValue(childNode, "id"); if (!"0".equals(nodeId) && !"1".equals(nodeId) && "mxCell".equals(childNode.getNodeName())) { System.out.println(childNode.getNodeName()); System.out.println("graphid:" + graphId); System.out.println("nodeId:" + getNodeAttrValue(childNode, "id")); System.out.println("parent:" + getNodeAttrValue(childNode, "parent")); System.out.println("value:" + getNodeAttrValue(childNode, "value")); System.out.println("source:" + getNodeAttrValue(childNode, "source")); System.out.println("target:" + getNodeAttrValue(childNode, "target")); System.out.println("vertex:" + getNodeAttrValue(childNode, "vertex")); System.out.println("edge:" + getNodeAttrValue(childNode, "edge")); parseElement2((Element) childNode, nodeId,graphId); System.out.println("****end*****"); Map<String, Object> node = new HashMap<String, Object>(); node.put("id", id); node.put("nodeId", nodeId); node.put("graphId", graphId); node.put("parent", getNodeAttrValue(childNode, "parent")); node.put("nodeValue", getNodeAttrValue(childNode, "value")); node.put("source", getNodeAttrValue(childNode, "source")); node.put("target", getNodeAttrValue(childNode, "target")); node.put("edge", getNodeAttrValue(childNode, "edge")); node.put("vertex", getNodeAttrValue(childNode, "vertex")); node.put("style", getNodeAttrValue(childNode, "style")); node.put("ass", getNodeAttrValue(childNode, "as")); node.put("nodeName", childNode.getNodeName()); nodeMapList.add(node); } // 判斷是否屬於節點 if (childNode.getNodeType() == Node.ELEMENT_NODE) { // 判斷是否還有子節點 parseElement((Element) childNode, graphId); } } } /** * 解析mxGeometry節點 * @param element * @param parentId * @param graphId */ private static void parseElement2(Element element, String parentId, String graphId) { NodeList nodeList = element.getChildNodes(); Node childNode; for (int temp = 0; temp < nodeList.getLength(); temp++) { childNode = nodeList.item(temp); String nodeName = childNode.getNodeName(); if ("mxGeometry".equals(nodeName)) { String nodeId = getNodeAttrValue(childNode, "id"); String id = getUUID32(); System.out.println("--name:" + nodeName); System.out.println("--height:" + getNodeAttrValue(childNode, "height")); System.out.println("--width:" + getNodeAttrValue(childNode, "height")); System.out.println("--x:" + getNodeAttrValue(childNode, "x")); System.out.println("--y:" + getNodeAttrValue(childNode, "y")); System.out.println("--as:" + getNodeAttrValue(childNode, "as")); System.out.println("--relative:" + getNodeAttrValue(childNode, "relative")); Map<String, Object> node = new HashMap<String, Object>(); node.put("id", id); node.put("nodeId", nodeId); node.put("parent", parentId); node.put("nodeName", childNode.getNodeName()); node.put("height", getNodeAttrValue(childNode, "height")); node.put("width", getNodeAttrValue(childNode, "width")); node.put("x", getNodeAttrValue(childNode, "x")); node.put("y", getNodeAttrValue(childNode, "y")); node.put("ass", getNodeAttrValue(childNode, "as")); node.put("relative", getNodeAttrValue(childNode, "relative")); node.put("graphId",graphId); node.put("style", getNodeAttrValue(childNode, "style")); // node.put("value", getNodeAttrValue(childNode, "value")); // node.put("source", getNodeAttrValue(childNode, "source")); // node.put("target", getNodeAttrValue(childNode, "target")); // node.put("edge", getNodeAttrValue(childNode, "edge")); // node.put("vertex", getNodeAttrValue(childNode, "vertex")); nodeMapList.add(node); // 判斷是否屬於節點 if (childNode.getNodeType() == Node.ELEMENT_NODE) { // 判斷是否還有子節點 parseElement((Element) childNode, ""); } } } } /** * 獲取指定節點的指定屬性的值 * @param node * @param attrName * @return */ public static String getNodeAttrValue(Node node, String attrName) { NamedNodeMap attr = node.getAttributes(); if (attr != null) { Node attrNode = attr.getNamedItem(attrName); if (attrNode != null) { return attrNode.getNodeValue(); } } return ""; } /** * 獲取指定的document對象中要保存的節點對象 * @param doc * @return */ public static List<Map<String, Object>> parseDocument(Document doc) { String id ="6ed10c4036f245b8bf78e1141d85e23b";// doc.getDocumentElement().getAttribute("id"); if ("".equals(id)) { id = getUUID32(); } // 遞歸解析Element Element element = doc.getDocumentElement(); nodeMapList.clear(); parseElement(element, id); return nodeMapList; } /** * 根據圖的id獲取圖的xml字符串 * @param graphId * @return */ public static String getXmlByGraphId(String graphId){ xmlStr = ""; // 根節點名稱 /*String rootName = doc.getDocumentElement().getTagName(); // 遞歸解析Element Element element = doc.getDocumentElement();*/ return getElementStr(null); } /** * 生成32位主鍵 * @return */ public static String getUUID32() { return UUID.randomUUID().toString().replace("-", "").toLowerCase(); } public static Document createDocument() { // 初始化xml解析工廠 DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance(); // 創建DocumentBuilder DocumentBuilder builder = null; try { builder = factory.newDocumentBuilder(); } catch (ParserConfigurationException e) { e.printStackTrace(); } // 創建Document Document doc = builder.newDocument(); // standalone用來表示該文件是否呼叫其它外部的文件。若值是 ”yes” 表示沒有呼叫外部文件 doc.setXmlStandalone(true); // 創建一個根節點 // 說明: // doc.createElement("元素名")、element.setAttribute("屬性名","屬性值")、element.setTextContent("標簽間內容") Element diagram = doc.createElement("diagram"); diagram.setAttribute("id", ""); diagram.setAttribute("tcn", ""); // 創建根節點第一個子節點 Element mxGraphModel = doc.createElement("mxGraphModel"); diagram.appendChild(mxGraphModel); Element root = doc.createElement("root"); mxGraphModel.appendChild(root); Element mxCell1 = doc.createElement("mxCell"); mxCell1.setAttribute("id", "0"); root.appendChild(mxCell1); Element mxCell2 = doc.createElement("mxCell"); mxCell2.setAttribute("id", "1"); mxCell2.setAttribute("parent", "0"); root.appendChild(mxCell2); //根據圖的id獲取圖中節點 /*Element mxCell3 = doc.createElement("mxCell"); mxCell3.setAttribute("id", "2"); mxCell3.setAttribute("parent", "1"); mxCell3.setAttribute("vertex", "1"); mxCell3.setAttribute("value", "songyan"); root.appendChild(mxCell3); Element mxGeometry = doc.createElement("mxGeometry"); mxGeometry.setAttribute("x", "20"); mxGeometry.setAttribute("y", "20"); mxGeometry.setAttribute("width", "80"); mxGeometry.setAttribute("height", "30"); mxGeometry.setAttribute("as", "geometry"); mxCell3.appendChild(mxGeometry);*/ // 添加根節點 doc.appendChild(diagram); return doc; } /** * 根據圖的id獲取document對象 * @param graphId 圖的id * @return */ public static Document getDocumentByGraphId(String graphId) { // 初始化xml解析工廠 DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance(); // 創建DocumentBuilder DocumentBuilder builder = null; try { builder = factory.newDocumentBuilder(); } catch (ParserConfigurationException e) { e.printStackTrace(); } // 創建Document Document doc = builder.newDocument(); // standalone用來表示該文件是否呼叫其它外部的文件。若值是 ”yes” 表示沒有呼叫外部文件 doc.setXmlStandalone(true); // 創建一個根節點 // 說明: // doc.createElement("元素名")、element.setAttribute("屬性名","屬性值")、element.setTextContent("標簽間內容") Element diagram = doc.createElement("diagram"); diagram.setAttribute("id", ""); diagram.setAttribute("tcn", ""); // 創建根節點第一個子節點 Element mxGraphModel = doc.createElement("mxGraphModel"); diagram.appendChild(mxGraphModel); Element root = doc.createElement("root"); mxGraphModel.appendChild(root); Element mxCell1 = doc.createElement("mxCell"); mxCell1.setAttribute("id", "0"); root.appendChild(mxCell1); Element mxCell2 = doc.createElement("mxCell"); mxCell2.setAttribute("id", "1"); mxCell2.setAttribute("parent", "0"); root.appendChild(mxCell2); //根據圖的id獲取圖中節點 //List<Map<String,Object>> transList = trans // 添加根節點 doc.appendChild(diagram); return doc; } public static void main(String[] args) { Document document= createDocument(); System.out.println(getXmlStrByDocument(document)); } public static Document getDocument(List<Map<String, Object>> newNodeList,String graphId) { // 初始化xml解析工廠 DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance(); // 創建DocumentBuilder DocumentBuilder builder = null; try { builder = factory.newDocumentBuilder(); } catch (ParserConfigurationException e) { e.printStackTrace(); } // 創建Document Document doc = builder.newDocument(); // standalone用來表示該文件是否呼叫其它外部的文件。若值是 ”yes” 表示沒有呼叫外部文件 doc.setXmlStandalone(true); // 創建一個根節點 // 說明: // doc.createElement("元素名")、element.setAttribute("屬性名","屬性值")、element.setTextContent("標簽間內容") Element diagram = doc.createElement("diagram"); diagram.setAttribute("id", graphId); diagram.setAttribute("tcn", ""); // 創建根節點第一個子節點 Element mxGraphModel = doc.createElement("mxGraphModel"); diagram.appendChild(mxGraphModel); Element root = doc.createElement("root"); mxGraphModel.appendChild(root); Element mxCell1 = doc.createElement("mxCell"); mxCell1.setAttribute("id", "0"); root.appendChild(mxCell1); Element mxCell2 = doc.createElement("mxCell"); mxCell2.setAttribute("id", "1"); mxCell2.setAttribute("parent", "0"); root.appendChild(mxCell2); for (Map<String, Object> node : newNodeList) { handleNode(root,doc,node); } // 添加根節點 doc.appendChild(diagram); return doc; } private static void handleNode(Element root, Document doc, Map<String, Object> node) { Element mxCell = doc.createElement((String) node.get("nodeName")); Object as = node.get("as"); Object width = node.get("width"); Object x = node.get("x"); Object y = node.get("y"); Object style = node.get("style"); Object nodeId = node.get("nodeId"); Object height = node.get("height"); Object parent = node.get("parent"); Object relative = node.get("relative"); Object vertex = node.get("vertex"); Object value = node.get("value"); Object edge = node.get("edge"); Object source = node.get("source"); Object target = node.get("target"); if(value!=null && !"".equals(value)){ mxCell.setAttribute("value", (String) value); } if(edge!=null && !"".equals(edge)){ mxCell.setAttribute("edge", (String) edge); } if(source!=null && !"".equals(source)){ mxCell.setAttribute("source", (String) source); } if(target!=null && !"".equals(target)){ mxCell.setAttribute("target", (String) target); } if(as!=null && !"".equals(as)){ mxCell.setAttribute("as", (String) as); } if(width!=null && !"".equals(width)){ mxCell.setAttribute("width", (String) width); } if(x!=null && !"".equals(x)){ mxCell.setAttribute("x", (String) x); } if(y!=null && !"".equals(y)){ mxCell.setAttribute("y", (String) y); } if(style!=null && !"".equals(style)){ mxCell.setAttribute("style", (String) style); } if(nodeId!=null && !"".equals(nodeId)){ mxCell.setAttribute("id", (String) nodeId); } if(parent!=null && !"".equals(parent)){ mxCell.setAttribute("parent", (String) parent); } if(height!=null && !"".equals(height)){ mxCell.setAttribute("height", (String) height); } if(relative!=null && !"".equals(relative)){ mxCell.setAttribute("relative", (String) relative); } if(vertex!=null && !"".equals(vertex)){ mxCell.setAttribute("vertex", (String) vertex); } root.appendChild(mxCell); Object child = node.get("child"); if(child!=null){ List<Map<String, Object>> childNodeList = (List<Map<String, Object>>) child; for (Map<String, Object> map : childNodeList) { handleNode(mxCell, doc, map); } } } }
這個是我寫的一個工具類,包括解析xml的方法,也有些封裝成xml的方法。
下面是解析xml並將其保存到數據庫的方法
@RequestMapping(value="/save2") @ResponseBody protected void save2(HttpServletResponse response,HttpServletRequest request,@RequestParam String graphXml) throws Exception{ String xml = StringEscapeHelper.decode(graphXml); System.out.println(xml); Document current = mxXmlUtils.parseXml(xml); String graphId = "6ed10c4036f245b8bf78e1141d85e23b";//current.getDocumentElement().getAttribute("id"); System.out.println("graphId:"+graphId); transService.deleteGraphById(graphId); List<Map<String, Object>> nodeMapList = DocumentUtil.parseDocument(current); for (Map<String, Object> trans : nodeMapList) { System.out.println(trans); transService.insert(trans); } try{ PrintWriter out=response.getWriter(); String result="songyan"; out.write(result); out.flush(); out.close(); }catch (Exception e){ e.printStackTrace(); throw new Exception(e.getMessage()); } }
這里我是給每個圖一個唯一的id,每次保存的時候都把之前的節點信息刪除掉,再根據解析出來的節點信息,保存節點。
(5)節點信息封裝成mxgraph可以解析的xml
下面是從數據庫讀取節點信息,並將其轉換成xml的方法:
@ResponseBody @RequestMapping(method = RequestMethod.POST, value = "/open") protected void open(HttpServletRequest request) throws Exception { //獲取圖的id String graphId = request.getParameter("graphId"); System.out.println("graphId::"+graphId); //獲取節點對象 List<Map<String,Object>> nodeList = transService.getTransListByGraphId(graphId); List<Map<String,Object>> newNodeList = new ArrayList<Map<String,Object>>(); for (Map<String, Object> node : nodeList) { Map nodeMap = node; String nodeId = (String) node.get("nodeId"); //查詢子節點 List<Map<String,Object>> childNodeList = getChildNodes(graphId,nodeId); if(childNodeList!=null && childNodeList.size()!=0){ nodeMap.put("child", childNodeList); newNodeList.add(nodeMap); } } System.out.println(newNodeList); //獲取圖的document對象 Document document = DocumentUtil.getDocument(newNodeList,graphId); //獲取xml String graphXml = DocumentUtil.getXmlStrByDocument(document); System.out.println(graphXml); JsonUtils.responseXml(StringEscapeHelper.encode(graphXml)); }
(6)頁面渲染xml,回顯成圖(普通的html,js中顯示正常,集成到VUE里面之后發現不報錯,也不顯示,針對VUE的處理會在下面說明)
var xml = '<diagram id="" tcn=""><mxGraphModel><root><Workflow value="Diagram" id="0"><mxCell/></Workflow><Layer value="Default Layer" id="1"><mxCell parent="0"/></Layer><mxCell id="2" value="Hello, World!" parent="1" vertex="1"><mxGeometry x="120" y="90" width="80" height="40" as="geometry"/></mxCell></root></mxGraphModel></diagram>'; var doc = mxUtils.parseXml(xml); var codec = new mxCodec(doc); codec.decode(doc.documentElement.firstChild, graph.getModel());
(7)Vue引入mxGraph(引入的方法很重要)
1)在項目的src目錄下創建文件夾magraph,在mxgraph文件夾創建index.js,Graph.js兩個文件
index.js內容:
import mx from 'mxgraph' const mxgraph = mx({ mxBasePath: '/static/mxgraph' }) // decode bug https://github.com/jgraph/mxgraph/issues/49 window.mxGraph = mxgraph.mxGraph window.mxGraphModel = mxgraph.mxGraphModel window.mxEditor = mxgraph.mxEditor window.mxGeometry = mxgraph.mxGeometry window.mxDefaultKeyHandler = mxgraph.mxDefaultKeyHandler window.mxDefaultPopupMenu = mxgraph.mxDefaultPopupMenu window.mxStylesheet = mxgraph.mxStylesheet window.mxDefaultToolbar = mxgraph.mxDefaultToolbar export default mxgraph
Graph.js內容:

import mxgraph from './index'; import _ from 'lodash'; const { mxGraph, mxVertexHandler, mxConstants, mxCellState, mxPerimeter, mxCellEditor, mxGraphHandler, mxEvent, mxEdgeHandler, mxShape, mxConnectionConstraint, mxPoint, mxEventObject, mxCodec, mxObjectCodec, mxUtils, mxImageExport, mxXmlCanvas2D, mxCodecRegistry, } = mxgraph; Object.assign(mxEvent, { EDGE_START_MOVE: 'edgeStartMove', VERTEX_START_MOVE: 'vertexStartMove', }); let pokeElementIdSeed = 0; // export class PokeElement { // constructor(element) { // this.id = pokeElementIdSeed; // pokeElementIdSeed++; // this.element = element; // this.normalType = ''; // } // } export class Graph extends mxGraph { static getStyleDict(cell) { return _.compact(cell.getStyle().split(';')) .reduce((acc, item) => { const [key, value] = item.split('='); acc[key] = value; return acc; }, {}); } static convertStyleToString(styleDict) { const style = Object.entries(styleDict) .map(([key, value]) => `${key}=${value}`) .join(';') .replace(/=undefined/g, ''); return `${style};`; } static getCellPosition(cell) { return _.pick(cell.getGeometry(), ['x', 'y']); } constructor(container) { super(container); this._init(); } _init() { this._setDefaultConfig(); this._configConstituent(); this._putVertexStyle(); this._setDefaultEdgeStyle(); this._setAnchors(); this._configCustomEvent(); // this._configCoder(); } _configConstituent() { // Redirects selection to parent this.selectCellForEvent = (...args) => { const [cell] = args; if (this.isPart(cell)) { args[0] = this.model.getParent(cell); mxGraph.prototype.selectCellForEvent.call(this, args); return; } mxGraph.prototype.selectCellForEvent.apply(this, args); }; /** * Redirects start drag to parent. */ const graphHandlerGetInitialCellForEvent = mxGraphHandler.prototype.getInitialCellForEvent; mxGraphHandler.prototype.getInitialCellForEvent = function getInitialCellForEvent(...args) { // this 是 mxGraphHandler let cell = graphHandlerGetInitialCellForEvent.apply(this, args); if (this.graph.isPart(cell)) { cell = this.graph.getModel().getParent(cell); } return cell; }; } _setDefaultConfig() { this.setConnectable(true); mxEvent.disableContextMenu(this.container); // 固定節點大小 this.setCellsResizable(false); // 編輯時按回車鍵不換行,而是完成輸入 this.setEnterStopsCellEditing(true); // 編輯時按 escape 后完成輸入 mxCellEditor.prototype.escapeCancelsEditing = false; // 失焦時完成輸入 mxCellEditor.prototype.blurEnabled = true; // 禁止節點折疊 this.foldingEnabled = false; // 文本包裹效果必須開啟此配置 this.setHtmlLabels(true); // 拖拽過程對齊線 mxGraphHandler.prototype.guidesEnabled = true; // 禁止游離線條 this.setDisconnectOnMove(false); this.setAllowDanglingEdges(false); mxGraph.prototype.isCellMovable = cell => !cell.edge; // 禁止調整線條彎曲度 this.setCellsBendable(false); // 禁止從將label從線條上拖離 mxGraph.prototype.edgeLabelsMovable = false; } _putVertexStyle() { const normalTypeStyle = { [mxConstants.STYLE_SHAPE]: mxConstants.SHAPE_IMAGE, [mxConstants.STYLE_PERIMETER]: mxPerimeter.RectanglePerimeter, }; this.getStylesheet().putCellStyle('normalType', normalTypeStyle); const nodeStyle = { // 圖片樣式參考這個例子 // https://github.com/jinzhanye/mxgraph-demos/blob/master/src/06.image.html [mxConstants.STYLE_SHAPE]: mxConstants.SHAPE_LABEL, [mxConstants.STYLE_PERIMETER]: mxPerimeter.RectanglePerimeter, [mxConstants.STYLE_ROUNDED]: true, [mxConstants.STYLE_ARCSIZE]: 6, // 設置圓角程度 [mxConstants.STYLE_STROKECOLOR]: '#333333', [mxConstants.STYLE_FONTCOLOR]: '#333333', [mxConstants.STYLE_FILLCOLOR]: '#ffffff', // [mxConstants.STYLE_LABEL_BACKGROUNDCOLOR]: 'none', [mxConstants.STYLE_ALIGN]: mxConstants.ALIGN_CENTER, [mxConstants.STYLE_VERTICAL_ALIGN]: mxConstants.ALIGN_TOP, [mxConstants.STYLE_IMAGE_ALIGN]: mxConstants.ALIGN_CENTER, [mxConstants.STYLE_IMAGE_VERTICAL_ALIGN]: mxConstants.ALIGN_TOP, [mxConstants.STYLE_IMAGE_WIDTH]: '72', [mxConstants.STYLE_IMAGE_HEIGHT]: '72', [mxConstants.STYLE_SPACING_TOP]: '100', [mxConstants.STYLE_SPACING]: '8', }; this.getStylesheet().putCellStyle('node', nodeStyle); // 設置選中狀態節點的邊角為圓角,默認是直角 const oldCreateSelectionShape = mxVertexHandler.prototype.createSelectionShape; mxVertexHandler.prototype.createSelectionShape = function createSelectionShape(...args) { const res = oldCreateSelectionShape.apply(this, args); res.isRounded = true; // style 屬性來自 mxShape , mxRectangle 繼承自 mxShape res.style = { arcSize: 6, }; return res; }; } _setDefaultEdgeStyle() { const style = this.getStylesheet().getDefaultEdgeStyle(); Object.assign(style, { [mxConstants.STYLE_ROUNDED]: true, // 設置線條拐彎處為圓角 [mxConstants.STYLE_STROKEWIDTH]: '2', [mxConstants.STYLE_STROKECOLOR]: '#333333', [mxConstants.STYLE_EDGE]: mxConstants.EDGESTYLE_ORTHOGONAL, [mxConstants.STYLE_FONTCOLOR]: '#33333', [mxConstants.STYLE_LABEL_BACKGROUNDCOLOR]: '#ffa94d', }); // 設置拖拽線的過程出現折線,默認為直線 this.connectionHandler.createEdgeState = () => { const edge = this.createEdge(); return new mxCellState(this.view, edge, this.getCellStyle(edge)); }; } _setAnchors() { // 禁止從節點中心拖拽出線條 this.connectionHandler.isConnectableCell = () => false; mxEdgeHandler.prototype.isConnectableCell = () => false; // Overridden to define per-shape connection points mxGraph.prototype.getAllConnectionConstraints = (terminal) => { if (terminal != null && terminal.shape != null) { if (terminal.shape.stencil != null) { if (terminal.shape.stencil != null) { return terminal.shape.stencil.constraints; } } else if (terminal.shape.constraints != null) { return terminal.shape.constraints; } } return null; }; // Defines the default constraints for all shapes mxShape.prototype.constraints = [ new mxConnectionConstraint(new mxPoint(0, 0), true), new mxConnectionConstraint(new mxPoint(0, 1), true), new mxConnectionConstraint(new mxPoint(1, 0), true), new mxConnectionConstraint(new mxPoint(1, 1), true), new mxConnectionConstraint(new mxPoint(0.25, 0), true), new mxConnectionConstraint(new mxPoint(0.5, 0), true), new mxConnectionConstraint(new mxPoint(0.75, 0), true), new mxConnectionConstraint(new mxPoint(0, 0.25), true), new mxConnectionConstraint(new mxPoint(0, 0.5), true), new mxConnectionConstraint(new mxPoint(0, 0.75), true), new mxConnectionConstraint(new mxPoint(1, 0.25), true), new mxConnectionConstraint(new mxPoint(1, 0.5), true), new mxConnectionConstraint(new mxPoint(1, 0.75), true), new mxConnectionConstraint(new mxPoint(0.25, 1), true), new mxConnectionConstraint(new mxPoint(0.5, 1), true), new mxConnectionConstraint(new mxPoint(0.75, 1), true)]; } _configCustomEvent() { const graph = this; const oldStart = mxEdgeHandler.prototype.start; mxEdgeHandler.prototype.start = function start(...args) { oldStart.apply(this, args); graph.fireEvent(new mxEventObject(mxEvent.EDGE_START_MOVE, 'edge', this.state.cell, 'source', this.isSource, )); }; const oldCreatePreviewShape = mxGraphHandler.prototype.createPreviewShape; mxGraphHandler.prototype.createPreviewShape = function createPreviewShape(...args) { graph.fireEvent(new mxEventObject(mxEvent.VERTEX_START_MOVE)); return oldCreatePreviewShape.apply(this, args); }; } _configCoder() { const codec = new mxObjectCodec(new PokeElement()); codec.encode = function (enc, obj) { const node = enc.document.createElement('PokeElement'); mxUtils.setTextContent(node, JSON.stringify(obj)); return node; }; codec.decode = function (dec, node, into) { const obj = JSON.parse(mxUtils.getTextContent(node)); obj.constructor = PokeElement; return obj; }; mxCodecRegistry.register(codec); } getDom(cell) { const state = this.view.getState(cell); return state.shape.node; } setStyle(cell, key, value) { const styleDict = Graph.getStyleDict(cell); styleDict[key] = value; const style = Graph.convertStyleToString(styleDict); this.getModel().setStyle(cell, style); } isPart(cell) { const state = this.view.getState(cell); const style = (state != null) ? state.style : this.getCellStyle(cell); return style.constituent === 1; } deleteSubtree(cell) { const cells = []; this.traverse(cell, true, (vertex) => { cells.push(vertex); return true; }); this.removeCells(cells); } _restoreModel() { Object.values(this.getModel().cells) .forEach(cell => { if (cell.vertex && cell.data) { cell.data = JSON.parse(cell.data); } }); } // 將 data 變為字符串,否則還原時會報錯 _getExportModel() { const model = _.cloneDeep(this.getModel()); Object.values(model.cells) .forEach(cell => { if (cell.vertex && cell.data) { cell.data = JSON.stringify(cell.data); } }); return model; } importModelXML(xmlTxt) { this.getModel().beginUpdate(); try { const doc = mxUtils.parseXml(xmlTxt); const root = doc.documentElement; const dec = new mxCodec(root.ownerDocument); dec.decode(root, this.getModel()); } finally { this.getModel().endUpdate(); } this._restoreModel(); } exportModelXML() { const enc = new mxCodec(mxUtils.createXmlDocument()); const node = enc.encode(this._getExportModel()); return mxUtils.getPrettyXml(node); } exportPicXML() { const xmlDoc = mxUtils.createXmlDocument(); const root = xmlDoc.createElement('output'); xmlDoc.appendChild(root); const { scale } = this.view; // 這個項目畫布邊寬為0,可以自行進行調整 const border = 0; const bounds = this.getGraphBounds(); const xmlCanvas = new mxXmlCanvas2D(root); xmlCanvas.translate( Math.floor((border / scale - bounds.x) / scale), Math.floor((border / scale - bounds.y) / scale), ); xmlCanvas.scale(1); const imgExport = new mxImageExport(); imgExport.drawState(this.getView().getState(this.model.root), xmlCanvas); const w = Math.ceil(bounds.width * scale / scale + 2 * border); const h = Math.ceil(bounds.height * scale / scale + 2 * border); const xml = mxUtils.getPrettyXml(root); return { xml, w, h, }; } } let graph = {}; export const destroyGraph = () => { graph.destroy(); graph = {}; }; export const genGraph = (container) => { graph = new Graph(container); return graph; }; export const getGraph = () => graph;
2)在使用Vue的頁面引入
import mxgraph from "../../graph/index";
const {
mxGraph,
mxClient,
mxDragSource,
mxCell,
mxRubberband,
mxVertexHandler,
mxConstants,
mxCellState,
mxPerimeter,
mxCellEditor,
mxGraphHandler,
mxEvent,
mxEdgeHandler,
mxShape,
mxConnectionConstraint,
mxPoint,
mxEventObject,
mxCodec,
mxObjectCodec,
mxUtils,
mxImageExport,
mxXmlCanvas2D,
mxClipboard,
mxCodecRegistry
} = mxgraph;
3)cnpm安裝
xnpm install mxgraph
注:其他的方式可能也能引入但是有些功能可能不能用,比如xml渲染的時候不出錯但是也不顯示
4)關於節點樣式的處理,拖拽生成圖的處理,右鍵生成菜單,右鍵刪除節點的功能如下

<template> <el-container style="height: 500px; border: 1px solid #eee"> <el-aside width="200px" style="background-color: rgb(238, 241, 246)"> <el-header style="text-align: center; font-size: 16px;" class="left_header"> <b>轉換管理</b> </el-header> <el-menu :default-openeds="['1']"> <el-submenu index="1"> <template slot="title"> <i class="el-icon-menu"></i>輸入 </template> <el-menu-item-group> <el-menu-item> <span id="in1" class="itemstyle"></span> <span unselectable="on" qtip="從一個微軟的Excel文件里讀取數據. 兼容Excel 95, 97 and 2000." id="extdd-81" >Excel輸入</span> </el-menu-item> <el-menu-item> <span id="in2" class="itemstyle"></span> <span unselectable="on" qtip="從一個文本文件(幾種格式)里讀取數據{0}這些數據可以被傳遞到下一個步驟里..." id="extdd-84" >文本文件輸入</span> </el-menu-item> <el-menu-item> <span id="in3" class="itemstyle"></span> <span unselectable="on" qtip="產生一些空記錄或相等的行." id="extdd-87">生成記錄</span> </el-menu-item> <el-menu-item> <span id="in4" class="itemstyle"></span> <span unselectable="on" qtip="Generate random value" id="extdd-90">生成隨機數</span> </el-menu-item> <el-menu-item> <span id="in5" class="itemstyle"></span> <span unselectable="on" qtip="Enter rows of static data in a grid, usually for testing, reference or demo purpose" id="extdd-93" >自定義常量數據</span> </el-menu-item> <el-menu-item> <span id="in6" class="itemstyle"></span> <span unselectable="on" qtip="獲取系統信息,例如時間、日期." id="extdd-96">獲取系統信息</span> </el-menu-item> <el-menu-item> <span id="in7" class="itemstyle"></span> <span unselectable="on" qtip="從數據庫表里讀取信息." id="extdd-99">表輸入</span> </el-menu-item> </el-menu-item-group> </el-submenu> <el-submenu index="2"> <template slot="title"> <i class="el-icon-menu"></i>輸出 </template> <el-menu-item-group> <el-menu-item> <span id="out1"></span> <span unselectable="on" qtip="Stores records into an Excel (XLS) document with formatting information." id="extdd-102" >Excel輸出</span> </el-menu-item> <el-menu-item> <span id="out2"></span> <span unselectable="on" qtip="Output SQL INSERT statements to file" id="extdd-105" >SQL 文件輸出</span> </el-menu-item> <el-menu-item> <span id="out3"></span> <span unselectable="on" qtip="基於關鍵字刪除記錄" id="extdd-108">刪除</span> </el-menu-item> <el-menu-item> <span id="out4"></span> <span unselectable="on" qtip="基於關鍵字更新或插入記錄到數據庫." id="extdd-90">插入 / 更新</span> </el-menu-item> <el-menu-item> <span id="out5"></span> <span unselectable="on" qtip="This step perform insert/update/delete in one go based on the value of a field. " id="extdd-93" >數據同步</span> </el-menu-item> <el-menu-item> <span id="out6"></span> <span unselectable="on" qtip="寫記錄到一個文本文件." id="extdd-96">文本文件輸出</span> </el-menu-item> <el-menu-item> <span id="out7"></span> <span unselectable="on" qtip="基於關鍵字更新記錄到數據庫" id="extdd-99">更新</span> </el-menu-item> <el-menu-item> <span id="out8"></span> <span unselectable="on" qtip="寫信息到一個數據庫表" id="extdd-102">表輸出</span> </el-menu-item> </el-menu-item-group> </el-submenu> <el-submenu index="3"> <template slot="title"> <i class="el-icon-menu"></i>轉換 </template> <el-menu-item-group> <el-menu-item> <span id="trans1"></span> <span unselectable="on" qtip="Maps values of a certain field from one value to another" id="extdd-105" >值映射</span> </el-menu-item> <el-menu-item> <span id="trans2"></span> <span unselectable="on" qtip="Denormalises rows by looking up key-value pairs and by assigning them to new fields in the輸出 rows.{0}This method aggregates and needs the輸入 rows to be sorted on the grouping fields" id="extdd-108" >列轉行</span> </el-menu-item> <el-menu-item> <span id="trans3"></span> <span unselectable="on" qtip="Strings cut (substring)." id="extdd-111">剪切字符串</span> </el-menu-item> <el-menu-item> <span id="trans4"></span> <span unselectable="on" qtip="去除重復的記錄行,保持記錄唯一{0}這個僅僅基於一個已經排好序的輸入.{1}如果輸入沒有排序, 僅僅兩個連續的記錄行被正確處理." id="extdd-114" >去除重復記錄</span> </el-menu-item> <el-menu-item> <span id="trans5"></span> <span unselectable="on" qtip="給記錄增加一到多個常量" id="extdd-117">增加常量</span> </el-menu-item> <el-menu-item> <span id="trans6"></span> <span unselectable="on" qtip="從序列獲取下一個值" id="extdd-120">增加序列</span> </el-menu-item> <el-menu-item> <span id="trans7"></span> <span unselectable="on" qtip="Add a checksum column for each input row" id="extdd-123" >增加校驗列</span> </el-menu-item> <el-menu-item> <span id="trans8"></span> <span unselectable="on" qtip="選擇或移除記錄里的字。{0}此外,可以設置字段的元數據: 類型, 長度和精度." id="extdd-126" >字段選擇</span> </el-menu-item> <el-menu-item> <span id="trans9"></span> <span unselectable="on" qtip="Replace all occurences a word in a string with another word." id="extdd-129" >字符串替換</span> </el-menu-item> <el-menu-item> <span id="trans10"></span> <span unselectable="on" qtip="基於字段值把記錄排序(升序或降序)" id="extdd-132">排序記錄</span> </el-menu-item> <el-menu-item> <span id="trans11"></span> <span unselectable="on" qtip="Flattens consequetive rows based on the order in which they appear in the輸入 stream" id="extdd-135" >行扁平化</span> </el-menu-item> <el-menu-item> <span id="trans12"></span> <span unselectable="on" qtip="De-normalised information can be normalised using this step type." id="extdd-138" >行轉列</span> </el-menu-item> </el-menu-item-group> </el-submenu> <el-submenu index="4"> <template slot="title"> <i class="el-icon-menu"></i>腳本 </template> <el-menu-item-group> <el-menu-item> <span id="scp1"></span> <span unselectable="on" qtip="This is a modified plugin for the Scripting Values with improved interface and performance. Written & donated to open source by Martin Lange, Proconis : http://www.proconis.de" id="extdd-81" >JavaScript代碼</span> </el-menu-item> <el-menu-item> <span id="scp2"></span> <span unselectable="on" qtip="執行一個SQL腳本, 另外,可以使用輸入的記錄作為參數" id="extdd-84">執行SQL腳本</span> </el-menu-item> </el-menu-item-group> </el-submenu> </el-menu> </el-aside> <el-container> <el-header style="text-align: left; font-size: 12px" class="right_header"> <el-button type="primary" @click="clearGraph">清空</el-button> <el-button type="primary" @click="lookXML">查看xml</el-button> <el-button type="primary" @click="save">保存</el-button> </el-header> <el-main> <div ref="graph_container" id="right"></div> </el-main> </el-container> </el-container> <!-- <div ref="graph_container"></div> --> </template> <script> import mxgraph from "../../graph/index"; const { mxGraph, mxClient, mxDragSource, mxCell, mxRubberband, mxVertexHandler, mxConstants, mxCellState, mxPerimeter, mxCellEditor, mxGraphHandler, mxEvent, mxEdgeHandler, mxShape, mxConnectionConstraint, mxPoint, mxEventObject, mxCodec, mxObjectCodec, mxUtils, mxImageExport, mxXmlCanvas2D, mxClipboard, mxCodecRegistry } = mxgraph; import api from "@/utils/api"; export default { data() { return { graph: null, undoMng: null }; }, name: "HelloWorld", props: { // msg: String }, created() {}, methods: { save() { var xml = '<diagram id="" tcn="">' + mxUtils.getXml(new mxCodec().encode(this.graph.getModel())) + "</diagram>"; api("appserver/getAppServer", "post", { gid: "01" }) .then(response => { console.log("response"); consoele.log(response); /* this.temp.name = response.name this.temp.details = response.details this.temp.iconurl = response.iconurl this.temp.operateType = 'U' this.temp.flowtype = response.flowtype this.imageUrl = response.iconshowurl */ // this.isLoading = false }) .catch(() => { //this.isLoading = false }); }, clearGraph(graph) { alert("clear"); this.undoMng.undo(); this.undoMng.redo(); }, createDragSource(graph) { //創建輸入拖動源 var in1 = this.createDragImage( graph, "in1", "./images/XLI.png", "EXCEL輸入" ); var in2 = this.createDragImage( graph, "in2", "./images/TFI.png", "文本文件輸入" ); var in3 = this.createDragImage( graph, "in3", "./images/GEN.png", "生成記錄" ); var in4 = this.createDragImage( graph, "in4", "./images/RVA.png", "生成隨機數" ); var in5 = this.createDragImage( graph, "in5", "./images/GNR.png", "自定義常量數據" ); var in6 = this.createDragImage( graph, "in6", "./images/SYS.png", "獲取系統信息" ); var in7 = this.createDragImage( graph, "in7", "./images/TIP.png", "表輸入" ); //創建輸出拖動源 var out1 = this.createDragImage( graph, "out1", "./images/XLO.png", "Excel輸出" ); var out2 = this.createDragImage( graph, "out2", "./images/SFO.png", "SQL 文件輸出" ); var out3 = this.createDragImage( graph, "out3", "./images/Delete.png", "刪除" ); var out4 = this.createDragImage( graph, "out4", "./images/INU.png", "插入 / 更新" ); var out5 = this.createDragImage( graph, "out5", "./images/SAM.png", "數據同步" ); var out6 = this.createDragImage( graph, "out6", "./images/TFO.png", "文本文件輸出" ); var out7 = this.createDragImage( graph, "out7", "./images/UPD.png", "更新" ); var out8 = this.createDragImage( graph, "out8", "./images/TOP.png", "表輸出" ); //創建轉換拖動源 var trans1 = this.createDragImage( graph, "trans1", "./images/VMAP.png", "值映射" ); var trans2 = this.createDragImage( graph, "trans2", "./images/UNP.png", "列轉行" ); var trans3 = this.createDragImage( graph, "trans3", "./images/SRC.png", "剪切字符串" ); var trans4 = this.createDragImage( graph, "trans4", "./images/UNQ.png", "去除重復記錄" ); var trans5 = this.createDragImage( graph, "trans5", "./images/CST.png", "增加常量" ); var trans6 = this.createDragImage( graph, "trans6", "./images/SEQ.png", "增加序列" ); var trans7 = this.createDragImage( graph, "trans7", "./images/CSM.png", "增加校驗列" ); var trans8 = this.createDragImage( graph, "trans8", "./images/SEL.png", "字段選擇" ); var trans9 = this.createDragImage( graph, "trans9", "./images/RST.png", "字符串替換" ); var trans10 = this.createDragImage( graph, "trans10", "./images/SRT.png", "排序記錄" ); var trans11 = this.createDragImage( graph, "trans11", "./images/FLA.png", "行扁平化" ); var trans12 = this.createDragImage( graph, "trans12", "./images/NRM.png", "行轉列" ); //創建腳本拖動源 var scp1 = this.createDragImage( graph, "scp1", "./images/SCR_mod.png", "scp1" ); var scp2 = this.createDragImage( graph, "scp2", "./images/SQL.png", "scp2" ); }, paseXML(graph) { var xml = '<mxGraphModel> <root> <mxCell id="0"/> <mxCell id="1" parent="0"/> <mxCell id="2" value="EXCEL輸入" style="in1" vertex="1" parent="1"> <mxGeometry x="100" y="110" width="50" height="50" as="geometry"/> </mxCell> <mxCell id="3" value="文本文件輸入" style="in2" vertex="1" parent="1"> <mxGeometry x="380" y="130" width="50" height="50" as="geometry"/> </mxCell> <mxCell id="4" value="生成隨機數" style="in4" vertex="1" parent="1"> <mxGeometry x="210" y="350" width="50" height="50" as="geometry"/> </mxCell> <mxCell id="6" edge="1" parent="1" source="2" target="4"> <mxGeometry relative="1" as="geometry"/> </mxCell> <mxCell id="7" edge="1" parent="1" source="4" target="3"> <mxGeometry relative="1" as="geometry"/> </mxCell> </root> </mxGraphModel>'; var doc = mxUtils.parseXml(xml); var codec = new mxCodec(doc); codec.decode(doc.documentElement, graph.getModel()); }, RightMenu(graph) { var container = document.getElementById("right"); //禁用瀏覽器自帶的右鍵事件 mxEvent.disableContextMenu(container); // 使用本地函數安裝彈出菜單處理程序 graph.popupMenuHandler.factoryMethod = function(menu, cell, evt) { return createPopupMenu(graph, menu, cell, evt); }; //創建右鍵的菜單 function createPopupMenu(graph, menu, cell, evt) { if (cell != null) { menu.addItem("復制步驟", "", function() { var cells = new Array(); cells = graph.getSelectionCells(); console.log("graph"); console.log(graph); console.log("cells"); console.log(cells); console.log("mxClipboard", mxClipboard); mxClipboard.copy(graph, cells); }); } menu.addSeparator(); menu.addItem("刪除步驟", "", function() { var cells = new Array(); cells = graph.getSelectionCells(); graph.removeCells(cells); }); } }, lookXML() { var encoder = new mxCodec(); var node = encoder.encode(this.graph.getModel()); const h = this.$createElement; this.$msgbox({ title: "xml", message: h("p", null, [ h( "div", { style: "overflow-y:auto; overflow-x:auto; width:400px; max-height:400px;" }, mxUtils.getPrettyXml(node) ) ]), showCancelButton: true, confirmButtonText: "確定", cancelButtonText: "取消" }); }, getStyle(graph, url, styleName) { // 聲明一個object var style = {}; // 克隆一個object style = mxUtils.clone(style); style[mxConstants.STYLE_SHAPE] = mxConstants.SHAPE_LABEL; // 不設置這個屬性 背景圖片不出來 // 邊框顏色 style[mxConstants.STYLE_STROKECOLOR] = "#15428b"; //圓角 style[mxConstants.STYLE_ROUNDED] = "1"; // 邊框大小 style[mxConstants.STYLE_STROKEWIDTH] = "0.5px"; // 字體顏色 style[mxConstants.STYLE_FONTCOLOR] = "#000"; // 文字水平方式 style[mxConstants.STYLE_ALIGN] = mxConstants.ALIGN_right; // 文字垂直對齊 style[mxConstants.STYLE_VERTICAL_ALIGN] = mxConstants.ALIGN_TOP; // 字體大小 style[mxConstants.STYLE_FONTSIZE] = 14; style[mxConstants.STYLE_FILLCOLOR] = "transparent"; // 底圖水平對齊 style[mxConstants.STYLE_IMAGE_ALIGN] = mxConstants.ALIGN_LEFT; // 底圖垂直對齊 style[mxConstants.STYLE_IMAGE_VERTICAL_ALIGN] = mxConstants.ALIGN_TOP; // 圖片路徑 //style[mxConstants.STYLE_IMAGE] = 'images/icons48/gear.png'; style[mxConstants.STYLE_IMAGE] = url; //'./images/i_assignedSlave.png'; // 背景圖片寬 style[mxConstants.STYLE_IMAGE_WIDTH] = 40; // 背景圖片高 style[mxConstants.STYLE_IMAGE_HEIGHT] = 40; // 上間距設置 // 即使下邊定義了全局設置,但這里單獨設置上邊間距仍單獨有效 // style[mxConstants.STYLE_SPACING_TOP] = 'spacingTop'; // 四邊間距設置 style[mxConstants.STYLE_SPACING] = 1; style[mxConstants.STYLE_VERTICAL_LABEL_POSITION] = mxConstants.ALIGN_BOTTOM; graph.getStylesheet().putCellStyle(styleName, style); }, createDragImage(graph, id, image, text) { var img = mxUtils.createImage(image); img.style.width = "30px"; img.style.height = "30px"; img.style.margin = "0px"; document.getElementById(id).appendChild(img); var style = this.getStyle(graph, image, id); // 檢查圖形中是否包含對應的elt節點 function containsElt(graph, elt) { while (elt != null) { if (elt == graph.container) { return true; } elt = elt.parentNode; } return false; } // 在給定的位置插入一個元素 var funct1 = function(graph, evt, target, x, y) { /* var prefix = ''; prefix = prefix+'verticalLabelPosition=bottom;verticalAlign=top;STYLE_STROKEWIDTH=1px;'; */ var cell = new mxCell(text, new mxGeometry(0, 0, 50, 50), id); cell.vertex = true; var cells = graph.importCells([cell], x, y, target); //插入元素、位置、大小 console.log(cells.getStyle); }; // 禁用IE瀏覽器中的DnD功能(這是為了跨瀏覽器平台而設計的,見下文) if (mxClient.IS_IE) { mxEvent.addListener(img, "dragstart", function(evt) { evt.returnValue = false; }); } // 創建拖動源的預覽 var dragElt = document.createElement("div"); dragElt.style.border = "dashed black 1px"; dragElt.style.width = "120px"; dragElt.style.height = "40px"; // 在點擊拖動源圖標時提供預覽。 預覽是提供的僅僅是拖動源的圖片 // 只有拖動源到容器內時才會顯示元素的坐標預覽 var ds = mxUtils.makeDraggable( img, graph, funct1, dragElt, null, null, graph.autoscroll, true ); //從拖動源拖動時顯示導航線。 //注意,對圖形中已存在的元素拖動時顯示導航線不在本方法約束范圍。 ds.isGuidesEnabled = function() { return graph.graphHandler.guidesEnabled; }; //從拖動源拖動元素到圖形以外的區域時,顯示拖動源圖片預覽 ds.createDragElement = mxDragSource.prototype.createDragElement; } }, // /item1 mounted() { var graph = new mxGraph(this.$refs.graph_container); graph.setConnectable(true); new mxRubberband(graph); var parent = graph.getDefaultParent(); this.graph = graph; //創建拖拽源 this.createDragSource(graph); //頁面初始化 this.paseXML(graph); //右鍵菜單定義 this.RightMenu(graph); } }; </script> <style scoped> .left_header { font-weight: 500; margin: 0 auto; padding: 15px; } .right_header { margin: auto 0; background-color: #e9edf1; padding: 10px; padding-left: 15px; } #right { height: 100%; position: relative; overflow: hidden; width: 100%; height: 100%; background: url("/images/grid.gif"); cursor: default; } .el-menu-item-group__title { padding: 0px 0 7px 20px; line-height: normal; font-size: 12px; color: #909399; } </style>
效果圖