当前位置: 首页 > 工具软件 > GoJS > 使用案例 >

GOJS使用教程

叶浩荡
2023-12-01

简介

GoJS是一个用于交互式图表和图形的JavaScript和TypeScript库。允许您为用户构建各种图表,从简单的流程图、组织图到图表、SCADA和BPMN图表、医学图表(如基因组图)等等。GoJS使用可定制的模板和布局,可以轻松构建复杂节点、链接和组的JavaScript图。
为用户提供了许多高级功能,如拖放、复制粘贴、就地文本编辑、工具提示、上下文菜单、自动布局和操作。

安装依赖

yarn add gojs@2.2.7

页面使用

<template>
    <div>
        <div id="myDiagramDiv"
             class="diagram-box"></div>
        <ul id="contextMenu"
            class="menu">
            <li id="cut"
                class="menu-item"
                @click="cxcommand($event)">Cut</li>
            <li id="copy"
                class="menu-item"
                @click="cxcommand($event)">Copy</li>
            <li id="paste"
                class="menu-item"
                @click="cxcommand($event)">Paste</li>
            <li id="delete"
                class="menu-item"
                @click="cxcommand($event)">Delete</li>
        </ul>
    </div>
</template>
import go from 'gojs';
export default {
    computed: {},
    data() {
        return {
            myDiagram: {},
        };
    },
    created() {},
    mounted() {
        this.init();
    },
    methods: {
        init() {
            /*
                图表(Diagram)
                基本元素:点(Node)、线(Link)
                点和线自由组合就是 组(Group)
                所有的元素都处在 图层(Layer) 上,并且可以对他们进行 布局(Layout)
                每一个点和线都是通过模板来描述他们的文本、形状、颜色等信息以及交互行为。每一个模板其实就是一个 面板(Panel)
                每个图表都是通过 数据模型(Model) 来填充和确定 点 的信息和 线 的所属关系
                并且我们只需创建好 点 和 线 的模板以及数据模型,其他事情都交给gojs处理
                Model又分为了以下三种类型:
                Model:最基本的(不带连线)
                GraphLinksModel :高级点的动态连线图
                TreeModel:树形图的模型
                gojs通过Model.nodeDataArray方法和GraphLinksModel.linkDataArray方法自动加载模型并构建元素
                通过ToolManager对象管理工具类(交互行为),譬如管理CommandHandler对象用来添加一些键盘命令
                gojs定义了一个用于创建GraphObject对象的静态函数GraphObject.make
                GraphObject是所有图形对象的抽象类,这个类的子类包括Panel、Shape、TextBlock、Picture和Placeholder。其中Panel派生的子类Part是Node和Link的父类
                Part是一个图表对象,它继承自Panel。它是所有用户操作级别对象的基类
                Shape:形状——Rectangle(矩形)、RoundedRectangle(圆角矩形),Ellipse(椭圆形),Triangle(三角形),Diamond(菱形),Circle(圆形)等
                TextBlock:文本域(可编辑)
                Picture:图片
                Panel:容器来保存其他Node的集合
            */
            var $ = go.GraphObject.make; // 给go.GraphObject.make起简称
            // var diagram = new go.Diagram('myDiagramDiv');
            // diagram.model = new go.GraphLinksModel([{ key: 'Hello' }, { key: 'World!' }], [{ from: 'Hello', to: 'World!' }]);
            // 创建Diagram对象 定义画布的基本属性
            var myDiagram = (this.myDiagram = $(
                go.Diagram,
                'myDiagramDiv', // 必须命名或引用DIV HTML元素 画布绑定的Div的ID
                {
                    LinkDrawn: showLinkLabel, // 监听线生成事件
                    LinkRelinked: showLinkLabel, // 线重新连接事件
                    initialContentAlignment: go.Spot.Center, // 任何初始图都位于视口中的中心
                    InitialLayoutCompleted: loadDiagramProperties, // 在加载新模型之前不要初始化一些属性
                    'toolManager.mouseWheelBehavior': go.ToolManager.WheelZoom, // 鼠标滚轮事件放大和缩小,而不是向上和向下滚动
                    // 允许通过双击创建一个新节点,与nodeTemplate里TextBlock绑定的属性值new go.Binding('text', 'key')有关
                    'clickCreatingTool.archetypeNodeData': { key: 'new node', color: 'white' },
                    'undoManager.isEnabled': true, // 允许取消和重做 undo & redo
                    allowZoom: false, // 不允许用户改变图表的规模
                    'grid.visible': true, // 画布上面是否出现网格
                }
            ));

            function loadDiagramProperties(e) {}

            // TextBlock案例
            myDiagram.add(
                $(
                    go.Part,
                    'Vertical',
                    $(
                        go.Panel,
                        'Vertical',
                        // TextBlock是用于显示文本信息的对象 font属性设置文本的字体 stroke属性设置文本字体的颜色
                        $(go.TextBlock, {
                            text: 'a Text Block\nwith three logical lines\nof text',
                            background: 'lightgreen',
                            margin: 2,
                            stroke: 'red',
                            isMultiline: false, // 设置是否开启内嵌文本中的换行符作用
                        }),
                        $(go.TextBlock, {
                            text: 'a Text Block\nwith three logical lines\nof text',
                            background: 'lightgreen',
                            margin: 2,
                            isMultiline: true,
                        }),
                        $(go.TextBlock, {
                            text: '文本块\n中有三行居中\n文本',
                            background: 'lightgreen',
                            margin: 2,
                            isMultiline: true,
                            textAlign: 'center', // 对齐方式
                            font: 'bold 14pt serif',
                        }),
                        $(go.TextBlock, {
                            text: 'a single line of centered text that should wrap because we will limit the width',
                            background: 'lightgreen',
                            margin: 2,
                            width: 80,
                            wrap: go.TextBlock.WrapFit, // 根据TextBlock的尺寸自动对文本进行换行
                            textAlign: 'center',
                        })
                    ),
                    $(
                        go.Panel,
                        'Vertical',
                        { width: 150, defaultStretch: go.GraphObject.None },
                        $(go.TextBlock, { text: 'alignment: Left', background: 'lightgreen', margin: 2, alignment: go.Spot.Left }),
                        $(go.TextBlock, { text: 'alignment: Center', background: 'lightgreen', margin: 2, alignment: go.Spot.Center }),
                        $(go.TextBlock, { text: 'alignment: Right', background: 'lightgreen', margin: 2, alignment: go.Spot.Right })
                    ),
                    // editable对TextBlock文本的编辑功能
                    // 设置TextBlock.textValidation属性编写验证规则,该属性的值为function。设置TextBlock.textEditor属性更换文本编辑器
                    $(go.TextBlock, { text: 'select and then click to edit', background: 'lightblue', editable: true, isMultiline: false }),
                    $(go.TextBlock, { text: 'this one allows embedded newlines', background: 'lightblue', editable: true })
                )
            );

            // 单独创建能够复用的对象,比如样式刷(Brush)
            var violetbrush = $(go.Brush, go.Brush.Linear, { 0.0: 'Violet', 1.0: 'Lavender' });

            // Shape案例
            myDiagram.add(
                $(
                    go.Part,
                    'Table',
                    // 通过Shape可以构建一个几何图形
                    // fill属性设置填充颜色。
                    // 如果设置为null,那么只能点击图形的轮廓才能选中,点击图形里面是无法选中的。而如设置为transparent,点击图形的任何地方都可以选中该图形。
                    $(go.Shape, 'RoundedRectangle', { row: 0, column: 0, width: 40, height: 40, margin: 4, fill: violetbrush }),
                    $(go.TextBlock, 'green', { row: 1, column: 0 }),
                    $(go.Shape, { row: 0, column: 1, figure: 'RoundedRectangle', width: 40, height: 40, margin: 4, fill: 'white' }),
                    $(go.TextBlock, 'white', { row: 1, column: 1 }),
                    $(go.Shape, { row: 0, column: 2, figure: 'Ellipse', width: 40, height: 40, margin: 4, fill: 'transparent' }),
                    $(go.TextBlock, 'transparent', { row: 1, column: 2 }),
                    // angle和.scale属性设置Shape的角度和比例
                    $(go.Shape, { row: 0, column: 3, figure: 'Ellipse', width: 40, height: 40, margin: 4, fill: null, angle: 30, scale: 1.5 }),
                    $(go.TextBlock, 'null', { row: 1, column: 3 })
                )
            );

            // 右键菜单
            var cxElement = document.getElementById('contextMenu');
            var myContextMenu = $(go.HTMLInfo, {
                show: showContextMenu,
                hide: hideContextMenu,
            });

            cxElement.addEventListener(
                'contextmenu',
                function (e) {
                    e.preventDefault();
                    return false;
                },
                false
            );

            // 显示右键菜单回调函数
            function showContextMenu(obj, diagram, tool) {
                // 只显示当前状态下的相关按钮。
                var cmd = diagram.commandHandler;
                var hasMenuItem = false;
                function maybeShowItem(elt, pred) {
                    if (pred) {
                        elt.style.display = 'block';
                        hasMenuItem = true;
                    } else {
                        elt.style.display = 'none';
                    }
                }
                // 重写键盘工具类commandHandler,去拦截调用deleteSelection 实现取消删除的功能
                maybeShowItem(document.getElementById('cut'), cmd.canCutSelection());
                maybeShowItem(document.getElementById('copy'), cmd.canCopySelection());
                maybeShowItem(document.getElementById('paste'), cmd.canPasteSelection(diagram.toolManager.contextMenuTool.mouseDownPoint));
                maybeShowItem(document.getElementById('delete'), cmd.canDeleteSelection());
                // Now show the whole context menu element
                if (hasMenuItem) {
                    cxElement.classList.add('show-menu');
                    // we don't bother overriding positionContextMenu, we just do it here:
                    var mousePt = diagram.lastInput.viewPoint;
                    cxElement.style.left = mousePt.x + 5 + 'px';
                    cxElement.style.top = mousePt.y + 'px';
                }
                // 可选:使用' window '的单击监听与事件捕获
                // 如果用户单击页面上其他地方,则删除上下文菜单
                window.addEventListener('click', hideCX, true);
            }
            function hideCX() {
                if (myDiagram.currentTool instanceof go.ContextMenuTool) {
                    myDiagram.currentTool.doCancel();
                }
            }
            // 隐藏右键菜单回调函数
            function hideContextMenu() {
                cxElement.classList.remove('show-menu');
                // 可选:使用' window '的单击监听与事件捕获
                // 如果用户单击页面上其他地方,则删除上下文菜单
                window.removeEventListener('click', hideCX, true);
            }
            // 全局(图表空白处)绑定自定义右键菜单
            myDiagram.contextMenu = myContextMenu;

            // 定义一个函数来创建一个新连线用来连接两个节点
            function makePort(name, align, spot, output, input) {
                var horizontal = align.equals(go.Spot.Top) || align.equals(go.Spot.Bottom);
                return $(go.Shape, {
                    fill: 'transparent', // 定义锚点颜色
                    strokeWidth: 0, // no stroke
                    width: horizontal ? NaN : 8, // if not stretching horizontally, just 8 wide
                    height: !horizontal ? NaN : 8, // if not stretching vertically, just 8 tall
                    alignment: align, // 规定锚点的位置
                    stretch: horizontal ? go.GraphObject.Horizontal : go.GraphObject.Vertical,
                    portId: name, // 规定锚点的名称
                    fromSpot: spot, // 输出点
                    fromLinkable: output, // 是否可以作为新连线的末节点
                    toSpot: spot, // 输入点
                    toLinkable: input, // 是否可以作为新连线的初始节点
                    cursor: 'pointer', // 显示不同的光标以指示潜在的链接点
                    mouseEnter: function (e, port) {
                        // the PORT argument will be this Shape 鼠标移入
                        if (!e.diagram.isReadOnly) port.fill = 'rgba(255,0,255,1)';
                    },
                    mouseLeave: function (e, port) {
                        port.fill = 'transparent';
                    },
                });
            }
            // 监听线生成和重新连接事件
            function showLinkLabel(e) {
                var label = e.subject.findObject('LABEL');
                if (label !== null) label.visible = e.subject.fromNode.data.category === 'Conditional';
            }

            // Link案例
            // 自定义Nodes案例
            // 节点面板子元素布局定义
            // Auto:子元素叠加布局、Vertical:子元素垂直布局、Horizontal:子元素水平布局、Position:子元素坐标布局、Table:子元素表格布局
            // Spot: Node里的元素都排列在这个Node中的各个锚点上
            myDiagram.nodeTemplate = $(
                go.Node,
                'Auto',
                new go.Binding('location', 'loc', go.Point.parse), // 申明动态绑定指定属性,将数据源中提供指定字段的值赋值给当前的指定属性
                // new go.Binding("location", "loc", go.Point.parse).makeTwoWay(go.Point.stringify) 双向绑定
                $(go.Shape, 'RoundedRectangle', {
                    fill: 'white',
                    portId: '',
                    cursor: 'pointer',
                    // 允许可以更改连线的端口,配合linkTemplate的relinkableFrom和relinkableTo属性使用
                    fromLinkable: true,
                    fromLinkableSelfNode: true,
                    fromLinkableDuplicates: true,
                    toLinkable: true,
                    toLinkableSelfNode: true,
                    toLinkableDuplicates: true,
                }),
                $(go.Picture, { maxSize: new go.Size(50, 50) }, new go.Binding('source', 'img')),
                $(go.TextBlock, { margin: new go.Margin(65, 0, 0, 0) }, new go.Binding('text', 'key')),
                {
                    // 鼠标悬浮title
                    toolTip: $(
                        go.Adornment,
                        'Auto',
                        $(
                            go.Shape,
                            { fill: '#F3F3C3' },
                            new go.Binding('visible', 'info', function (i) {
                                return i ? true : false;
                            })
                        ),
                        $(go.TextBlock, { margin: 4 }, new go.Binding('text', 'info'))
                    ),
                },
                { contextMenu: myContextMenu },
                makePort('T', go.Spot.Top, go.Spot.TopSide, false, true),
                makePort('L', go.Spot.Left, go.Spot.LeftSide, true, true),
                makePort('R', go.Spot.Right, go.Spot.RightSide, true, true),
                makePort('B', go.Spot.Bottom, go.Spot.BottomSide, true, false)
            );

            // 构建一个连线模板
            myDiagram.linkTemplate = $(
                go.Link,
                {
                    routing: go.Link.AvoidsNodes, // 设置默认规则,可被后续绑定的new go.Binding覆盖
                    curve: go.Link.JumpOver, // 当两条连线相交时,JumpOver:一条线越过了交叉的另一条线。Bezier:设置连线的弯曲度;JumpGap:在相交处会断开
                    corner: 10, // 设置连线折角的弧度
                    toShortLength: 4,
                    relinkableFrom: true, // 可以更改线的初始点
                    relinkableTo: true, // 可以更改线的末端点
                    reshapable: true, // 重塑(改变shape形状边界时使用,将影响节点大小)
                    resegmentable: true, // 连线中直线可更改
                    // mouse-overs subtly highlight links: 鼠标移入事件
                    mouseEnter: function (e, link) {
                        link.findObject('HIGHLIGHT').stroke = 'rgba(30,144,255,0.2)';
                        link.findObject('HIGHLIGHT').strokeWidth = 5;
                    },
                    mouseLeave: function (e, link) {
                        link.findObject('HIGHLIGHT').stroke = '#000';
                        link.findObject('HIGHLIGHT').strokeWidth = 1;
                    },
                    selectionAdorned: true, // 显示选中边框
                },
                { contextMenu: myContextMenu },
                $(
                    go.Shape, // 线的悬浮高亮
                    {
                        isPanelMain: true,
                        strokeWidth: 1,
                        name: 'HIGHLIGHT',
                    }
                ),
                new go.Binding('routing', 'routing'),
                $(
                    go.Shape, // 箭头OpenTriangle
                    { toArrow: 'standard', fill: null }
                ),
                $(
                    go.Panel,
                    'Auto', // 连线标签,默认不可见
                    {
                        visible: true,
                        name: 'LABEL',
                        segmentIndex: 2,
                        segmentFraction: 0.5,
                    },
                    new go.Binding('visible', 'visible').makeTwoWay(),
                    $(go.Shape, 'RoundedRectangle', { fill: '#F8F8F8', strokeWidth: 0 }),
                    $(
                        go.TextBlock,
                        '标签',
                        {
                            textAlign: 'center',
                            font: '10pt helvetica, arial, sans-serif',
                            stroke: '#333333',
                            editable: true,
                        },
                        new go.Binding('text').makeTwoWay()
                    )
                )
            );
            // 为图表背景定义行为:
            function diagramInfo(model) {
                // diagram's model工具提示信息
                return '模板:\n' + model.nodeDataArray.length + ' 节点(node), ' + model.linkDataArray.length + ' 连线(Links)';
            }
            // 当不在任何Part上时,为Diagram的背景提供工具提示
            myDiagram.toolTip = $('ToolTip', $(go.TextBlock, { margin: 4 }, new go.Binding('text', '', diagramInfo)));

            var nodeDataArray = [
                { key: 'Alpha', loc: '0 0', img: require('assets/logo.png'), info: '' },
                { key: 'Beta', loc: '200 350', img: require('assets/logo.png'), info: 'shredded curtains' },
                { key: 'Gamma', loc: '300 125', img: require('assets/logo.png'), info: 'shredded' },
            ];
            var linkDataArray = [
                { from: 'Alpha', to: 'Beta', routing: go.Link.Normal }, // routing改变连线的路径,Orthogonal:正交,AvoidsNodes:自动避开Node
                { from: 'Alpha', to: 'Beta', routing: go.Link.Normal },
                { from: 'Alpha', to: 'Beta', routing: go.Link.Normal },
                { from: 'Alpha', to: 'Gamma', routing: go.Link.Orthogonal },
                { from: 'Beta', to: 'Gamma', routing: go.Link.AvoidsNodes },
            ];
            myDiagram.model = new go.GraphLinksModel(nodeDataArray, linkDataArray);
        },
        // 右键菜单点击事件
        cxcommand(event) {
            var val = event.currentTarget.id;
            var diagram = this.myDiagram;
            switch (val) {
                case 'cut':
                    diagram.commandHandler.cutSelection();
                    break;
                case 'copy':
                    diagram.commandHandler.copySelection();
                    break;
                case 'paste':
                    diagram.commandHandler.pasteSelection(diagram.toolManager.contextMenuTool.mouseDownPoint);
                    break;
                case 'delete':
                    diagram.commandHandler.deleteSelection(); // 删除选中的节点或线
                    break;
            }
            diagram.currentTool.stopTool();
        },
    },
};
.diagram-box {
    border: solid 1px blue;
    width: 1500px;
    height: 650px;
    margin-top: 100px;
}

#myDiagramDiv {
    flex: 1;
    border: 1px solid rgb(212, 212, 212);
}
.menu {
    display: none;
    position: absolute;
    opacity: 0;
    margin: 0;
    padding: 8px 0;
    z-index: 999;
    box-shadow: 0 5px 5px -3px rgba(0, 0, 0, 0.2), 0 8px 10px 1px rgba(0, 0, 0, 0.14), 0 3px 14px 2px rgba(0, 0, 0, 0.12);
    list-style: none;
    background-color: #ffffff;
    border-radius: 4px;
}

.menu-item {
    display: block;
    position: relative;
    min-width: 60px;
    margin: 0;
    padding: 6px 16px;
    font: bold 12px sans-serif;
    color: rgba(0, 0, 0, 0.87);
    cursor: pointer;
}

.menu-item::before {
    position: absolute;
    top: 0;
    left: 0;
    opacity: 0;
    pointer-events: none;
    content: '';
    width: 100%;
    height: 100%;
    background-color: #000000;
}

.menu-item:hover::before {
    opacity: 0.04;
}

.menu .menu {
    top: -8px;
    left: 100%;
}

.show-menu,
.menu-item:hover > .menu {
    display: block;
    opacity: 1;
}

 类似资料: