一、什么是Mxgraph

mxgraph是一个前端流程图绘制工具。mxGraph支持拖动、复制图元、重新调整大小、重新构造,连接和断开,从外部源的拖放和删除,编辑图元 标签中的位置等。我这里学习mxgraph主要是利用拖拽来生成后台流程。

二、如果使用

1、在vue中引入

在控制台执行命令:npm install mxgraph

2.1 vue html部分
<!-- 定义一个画布容器-->
<div id="graphContainer"></div>
<!-- 定义一个缩略图导航器 -->
<div id="graphOutline"/>
<!-- 定义左侧栏图标 -->
    <h4>元素</h4>
    <div v-for="item in image" style="display: flex;align-items: center;">
        <img class="element-img" :id="item.id" :name="item.name" :src="gtImgUrl(item.icon)" :nodeName="item.nodeName" style="width: 30px;hight: 30px;">
        <span style="font-size:20px;">
            {{item.name}}
        </span>
    </div>
</div>
2.2 js部分
2.2.1 创建画布容器并初始化设置
// 创建一个画布容器
mkGraphContainer() {
    // 在容器中创建图表
    let container = document.getElementById("graphContainer");
    this.graph = new mxGraph(container);
    // 创建缩略图
    this.outline = new mxOutline(this.graph, document.getElementById('graphOutline'));
    this.graph.getModel().beginUpdate();
    // 定义布局算法
    this.layOut = new mxHierarchicalLayout(this.graph);
    // 禁止在mxgraph图中使用鼠标右键
    mxEvent.disableContextMenu(document.getElementById("graphContainer"));
    // 设置节点可连接
    this.graph.setConnectable(true);
    this.graph.setTooltips(true);
    this.graph.setAllowDanglingEdges(false);
    this.graph.setMultigraph(false);
    this.graph.getView().updateStyle = true;
    // 允许调整线条弯曲度
    this.graph.setCellsBendable(true);
2.2.2 添加左侧拖拽图标
dragOutImg(sourceEles, graph) {
    const dropValidate = function (evt) {
        const x = mxEvent.getClientX(evt);
        const y = mxEvent.getClientY(evt);
        // 获取 x,y 所在的元素
        const elt = document.elementFromPoint(x, y);
        // 如果鼠标落在graph容器
        if (mxUtils.isAncestorNode(graph.container, elt)) {
            return graph;
        // 鼠标落在其他地方
        return null;
    // drop成功后新建一个节点
    const dropSuccessCb = function (_graph, evt, target, x, y) {
        var img = this.element.getAttribute('src');
        var name = this.element.getAttribute('name');
        var id = this.element.getAttribute('id');
        var nodeName = this.element.getAttribute('nodeName');
        // 定义节点的样式。
        var nodeStyle = {};
        // 形状
        //nodeStyle[mxConstants.STYLE_SHAPE] = mxConstants.SHAPE_CYLINDER;
        nodeStyle[mxConstants.STYLE_SHAPE] = mxConstants.SHAPE_LABEL; // 背景图片只有在长方形形状才会展示
        // 周长
        nodeStyle[mxConstants.STYLE_PERIMETER] = mxPerimeter.RectanglePerimeter;
        // 对齐方式
        nodeStyle[mxConstants.STYLE_ALIGN] = mxConstants.ALIGN_CENTER;
        // 垂直对齐
        nodeStyle[mxConstants.STYLE_VERTICAL_ALIGN] = mxConstants.ALIGN_MIDDLE;
        nodeStyle[mxConstants.STYLE_GRADIENTCOLOR] = '#41B9F5';
        // 填充色
        nodeStyle[mxConstants.STYLE_FILLCOLOR] = '#8CCDF5';
        // 连接线颜色
        nodeStyle[mxConstants.STYLE_STROKECOLOR] = '#1B78C8';
        nodeStyle[mxConstants.STYLE_FONTCOLOR] = '#000000';
        nodeStyle[mxConstants.STYLE_ROUNDED] = true;
        // 阴影效果
        nodeStyle[mxConstants.STYLE_SHADOW] = true;
        // 透明度
        nodeStyle[mxConstants.STYLE_OPACITY] = '80';
        // 中间字体的大小 例如:鼠标双击输入
        nodeStyle[mxConstants.STYLE_FONTSIZE] = '12';
        // 字体样式(斜体、下划线、中划线等)
        nodeStyle[mxConstants.STYLE_FONTSTYLE] = 0;
        // 设置背景图片
        //nodeStyle[mxConstants.STYLE_IMAGE]= 'src/assets/mysql.png';
        nodeStyle[mxConstants.STYLE_IMAGE] = img;
        nodeStyle[mxConstants.STYLE_IMAGE_WIDTH] = '32';
        nodeStyle[mxConstants.STYLE_IMAGE_HEIGHT] = '32';
        nodeStyle[mxConstants.STYLE_SPACING_TOP] = '2';
        nodeStyle[mxConstants.STYLE_SPACING_LEFT] = '50';
        nodeStyle[mxConstants.STYLE_SPACING] = '8';
        graph.getStylesheet().putCellStyle("nodeStyle", nodeStyle);
        //const cell = new mxCell('鼠标双击输入', new mxGeometry(0, 0, 150, 135),'nodeStyle');
        const cell = new mxCell(name, new mxGeometry(0, 0, 150, 35),'nodeStyle');
        //const cell = new mxCell('鼠标双击输入', new mxGeometry(0, 0, 150, 35),'nodeStyle');
        //const cell = new mxCell('鼠标双击', new mxGeometry(0, 0, 150, 35), 'nodeStyle');
        cell.vertex = true;
        // 自定义数据,将后端需要的数据封装到该节点中。
        cell.data = {
            name:name,
            id:id,
            nodeName: nodeName,
            fullPath: '',
            context: '',
            operation: '',
            pass: 'true'
        // 鼠标落在哪里,节点就停在哪里
        const cells = graph.importCells([cell], x, y, target);
        if (cells != null && cells.length > 0) {
            graph.setSelectionCells(cells);
    // 设置连接线条的样式,如果不设置默认是斜线连接。  
    var edgeStyle = graph.getStylesheet().getDefaultEdgeStyle();
    edgeStyle[mxConstants.STYLE_EDGE] = 'orthogonalEdgeStyle';
    mxGraphHandler.prototype.guidesEnabled = true;
    Array.from(sourceEles).forEach((ele) => {
        // 拖拽时的虚影
        const dragElt = document.createElement('img');
        dragElt.setAttribute('src', ele.getAttribute('src'));
        // 拖拽虚影的大小
        dragElt.setAttribute('style', 'width:50px;height:50px;');
        mxUtils.makeDraggable(ele, dropValidate, dropSuccessCb, dragElt,
            null, null, null, true);

三、最终效果

代码中只列出了关键的代码,省略了部分。

目前遇到的问题:如何将xml导出,然后再回显回来?目前测试下来,总是会提示节点Id冲突。还在想办法解决中。

开启掘金成长之旅!这是我参与「掘金日新计划 · 2 月更文挑战」的第1天,点击查看活动详情