x6 流程图

198 阅读1分钟

image.png

<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>509-流程图</title>
    <link rel="stylesheet" href="./assets/css/global.css" />
    <link rel="stylesheet" href="./assets/css/task.css" />
    <script src="./assets/js/vue.min.js"></script>
    <script src="./assets/js/x6.js"></script>
    <script src="./assets/js/config_ports.js"></script>
    <script src="./assets/js/component.js"></script>
</head>

<body>
    <div id="app">
        <div class="head flex  flex-align-c">
            <div class="row flex flex-align-c">
                <div class="colors">
                    <div class="diamond-color" :style="{background: COLOR}" @click="handlerColor"></div>
                    <j-config-color v-if="visible" @ok="handerOk">
                </div>
                <div class="input-title">颜色</div>
                <j-input class="flex-one" v-model="COLOR" type="text" placeholder=""></j-input>
                <j-button @click="revocation">撤销</j-button>
                <j-button color="blue" @click="printNodeList">保存</j-button>
                <j-button @click="recover">接口渲染</j-button>
            </div>

        </div>
        <div class="home flex">
            <!-- 左侧模块菜单 -->
            <div class="menu-bar">
                <!-- 模块列表 -->
                <div class="title">基础图形</div>
                <div class="line"></div>
                <!-- 模块列表 -->
                <div class="menu-list">
                    <div class="menu-list-li" v-for="item in moduleList" :key="item.id"
                        :style="{background: item.color}" draggable="true" @dragend="handleDragEnd($event, item)">
                        {{item.name}}
                    </div>
                </div>
            </div>
            <!-- 画布部分 -->
            <div class="canvas-card flex-one">
                <div id="container" @dragover="dragoverDiv"></div>
                <!-- 菜单 -->
                <div class="menu">
                    <div>全选</div>
                    <div @click="handlerEdit">编辑</div>
                    <div>复制</div>
                    <div>剪切</div>
                    <div>删除</div>
                </div>
            </div>
            <!-- 小地图 -->
            <div id="minimapContainer"></div>
        </div>

    </div>
</body>

</html>
<script>
    var ACTIONNODE = null
    var app = new Vue({
        el: "#app",
        data() {
            return {
                // 颜色识别器
                COLOR: 'linear-gradient(to left, #f00, #ff0, #0f0, #0ff, #00f, #f0f)',
                h: 0,
                visible: false,
                actionNode: null,//当前节点
                graph: null, // 画布实例对象
                ifEXistCircle:null, // 是否存在圆球运动轨迹
                curSelectNode: null, // 当前选中的节点
                moduleList: [
                    {
                        id: 1,
                        name: "1",
                        color: '#fff',
                        shape: 'custom-rect',
                        attrs: {
                            body: {
                                rx: 20,
                                ry: 26,
                            },
                        },
                    },
                    {
                        id: 2,
                        name: "测试",
                        color: '#fff',
                        shape: 'custom-rect',
                        attrs: {
                            body: {
                            },
                        },
                    },
                    {
                        id: 3,
                        color: '#fff',
                        name: "3",
                        shape: 'custom-rect',
                        attrs: {
                            body: {
                                rx: 6,
                                ry: 6,
                            },
                        },
                    },

                    {
                        id: 4,
                        name: "4",
                        color: '#fff',
                        shape: 'custom-polygon',
                        attrs: {
                            body: {
                                refPoints: '0,10 10,0 20,10 10,20',
                            },
                        },
                    },
                    {
                        id: 5,
                        color: '#fff',
                        name: "5",
                        shape: 'custom-polygon',
                        attrs: {
                            body: {
                                refPoints: '10,0 40,0 30,20 0,20',
                            },
                        },
                    },
                    {
                        id: 6,
                        color: '#fff',
                        name: "6",
                        shape: 'custom-circle',
                        attrs: {
                            body: {}
                        }
                    },
                ], // 列表可拖动模块
            };
        },
        created() {
            window.addEventListener("resize", this.getViweHieth, false);
        },
        destroyed() {
            window.removeEventListener('resize', this.getViweHieth, false)
        },
        mounted() {
            this.getViweHieth()
            this.init();
            this.dblclick() // 双击改变
            this.graph.on('node:contextmenu', function (event) {
                this.actionNode = event.node;
                ACTIONNODE = event.node
                console.log(this.actionNode);
                const menu = document.getElementsByClassName('menu')[0]
                menu.style.display = 'block'
                menu.style.left = event.x + 10 + 'px'
                menu.style.top = event.y + 'px'
                document.body.addEventListener('click', function () {
                    menu.style.display = 'none'
                })
            })
        },
        methods: {
            // 初始化流程图画布
            init() {
                this.initGraph() // 初始化X6
                this.registerNode() // 自定义节点样式
                this.LinkPegShow() // 节点桩显示隐藏
                this.nodeAddEvent() // 显示边框删除节点
                this.shortcutKey() // 快捷键
            },
            initGraph() {
                let container = document.getElementById("container"); // 地图
                let minimapContainer = document.getElementById("minimapContainer"); //小地图
                // 构建X6配置
                this.graph = new X6.Graph({
                    history: true,
                    container: container, // 画布容器
                    width: container.offsetWidth, // 画布宽
                    height: container.offsetHeight, // 画布高
                    background: this.color, // 背景(透明)
                    snapline: true, // 对齐线
                    height: this.h,
                    highlighting: {
                        magnetAvailable: magnetAvailabilityHighlighter,
                        magnetAdsorbed: {
                            name: 'stroke',
                            args: {
                                attrs: {
                                    fill: '#fff',
                                    stroke: '#31d0c6',
                                },
                            },
                        },
                    },
                    grid: {
                        type: 'mesh',
                        size: 20,      // 网格大小 10px
                        visible: true, // 渲染网格背景
                        args: {
                            color: '#eeeeee', // 网格线/点颜色
                            thickness: 2,     // 网格线宽度/网格点大小
                        },
                    },
                    mousewheel: {
                        enabled: true, // 支持滚动放大缩小
                    },
                    panning: {
                        enabled: true, // 是否拖动
                    },
                    scroller: {
                        enabled: true,
                    },
                    resizing: true, // 缩放节点
                    rotating: true,
                    selecting: {
                        enabled: true,
                        rubberband: true,
                        showNodeSelectionBox: true,
                    },
                    snapline: true,
                    keyboard: true,
                    clipboard: true,
                    minimap: {
                        enabled: true,
                        container: minimapContainer,
                        width: 250,
                        height: 250,
                    },
                    connecting: {
                        router: {
                            name: 'manhattan',
                            args: {
                                padding: 1,
                            },
                        },
                        connector: {
                            name: 'rounded',
                            args: {
                                radius: 8,
                            },
                        },
                        anchor: 'center',
                        connectionPoint: 'anchor',
                        snap: {
                            radius: 20,
                        },
                        createEdge() {
                            return new X6.Shape.Edge({
                                attrs: {
                                    line: {
                                        stroke: '#77DDFF',
                                        strokeWidth: 2,
                                        targetMarker: {
                                            name: 'block',
                                            width: 12,
                                            height: 8,
                                        },
                                    },
                                },
                                zIndex: 0,
                            })
                        },
                        allowBlank: false, // 是否允许连接到画布空白位置的点,默认为 true。
                        validateConnection(e) {
                            return validate(e)
                        },
                    },
                })
            },
            // 拖动节点到画布中鼠标样式变为可拖动状态
            dragoverDiv(ev) {
                ev.preventDefault();
            },
            // 拖动后松开鼠标触发事件
            handleDragEnd(e, item) {
                item.width = item.shape == 'custom-polygon' ? 100 : e.target.offsetWidth
                this.addHandleNode(e.pageX - 300, e.pageY - 60, item)
            },
            // 添加节点
            addHandleNode(x, y, item) {
                item.attrs.label = {
                    fontSize: 18,
                    stroke: 'black'
                }
                item.attrs.body.fill = item.color
                item.attrs.body.stroke = 'black'
                item.attrs.body.strokeWidth = 2
                let node = {
                    shape: item.shape, // 指定使用何种图形,默认值为 'rect'
                    x: x,
                    y: y,
                    id: item.id, // String,可选,节点的唯一标识
                    width: item.width,
                    label: item.name,
                    height: 40,
                    type: item.type,
                    parentId: item.parentId,
                    children: item.children || [],
                    attrs: item.attrs,
                    ports
                }
                if (node.shape == 'custom-polygon') {
                    node.ports.items = node.ports.items.filter(item => item.group == 'top' || item.group == 'bottom')
                }
                this.graph.addNode(node)

            },

            LinkPegShow() {
                // 控制连接桩显示/隐藏
                const showPorts = (ports, show) => {
                    for (let i = 0, len = ports.length; i < len; i = i + 1) {
                        ports[i].style.visibility = show ? 'visible' : 'hidden'
                    }
                }
                this.graph.on('node:mouseenter', () => {
                    const container = document.getElementById('container')
                    const ports = container.querySelectorAll(
                        '.x6-port-body',
                    )
                    showPorts(ports, true)
                })
                this.graph.on('node:mouseleave', (e) => {
                    const container = document.getElementById('container')
                    const ports = container.querySelectorAll(
                        '.x6-port-body',
                    )
                    showPorts(ports, false)
                })
            },
            // 1
            nodeAddEvent() {
                // 节点绑定点击事件
                this.graph.on('node:click', ({ e, x, y, node, view }) => {
                    // 判断是否有选中过节点
                    if (this.curSelectNode) {
                        // 移除选中状态
                        this.curSelectNode.removeTools()
                        // 判断两次选中节点是否相同
                        if (this.curSelectNode !== node) {
                            node.addTools([{
                                name: 'boundary',
                                args: {
                                    attrs: {
                                        fill: '#16B8AA',
                                        stroke: '#2F80EB',
                                        strokeWidth: 1,
                                        fillOpacity: 0.1
                                    }
                                }
                            }, {
                                name: 'button-remove',
                                args: {
                                    x: '100%',
                                    y: 0,
                                    offset: {
                                        x: 0,
                                        y: 0
                                    }
                                }
                            }])
                            this.curSelectNode = node
                        } else {
                            this.curSelectNode = null
                        }
                    } else {
                        this.curSelectNode = node
                        node.addTools([{
                            name: 'boundary',
                            args: {
                                attrs: {
                                    fill: '#16B8AA',
                                    stroke: '#2F80EB',
                                    strokeWidth: 1,
                                    fillOpacity: 0.1
                                }
                            }
                        }, {
                            name: 'button-remove',
                            args: {
                                x: '100%',
                                y: 0,
                                offset: {
                                    x: 0,
                                    y: 0
                                }
                            }
                        }])
                    }
                })
                this.graph.on('cell:mouseleave', ({ cell }) => {
                    if (cell.shape === 'edge') {
                        cell.removeTools()
                        cell.setAttrs({
                            line: {
                                stroke: '#275da3',
                            },
                        })
                        cell.zIndex = 1
                    }
                })
                // 连线绑定悬浮事件
                this.graph.on('cell:mouseenter', ({ cell }) => {
                    if (cell.shape == 'edge') {
                        cell.addTools([
                            {
                                name: 'button-remove',
                                args: {
                                    x: '100%',
                                    y: 0,
                                    offset: {
                                        x: 0,
                                        y: 0
                                    },
                                },
                            }
                        ])
                        cell.setAttrs({
                            line: {
                                stroke: '#409EFF',
                            },
                        })
                        cell.zIndex = 99
                    }
                })

            },
            getViweHieth() {
                this.h = window.innerHeight - 60
            },
            registerNode() {
                X6.Graph.registerNode(
                    'custom-rect',
                    {
                        inherit: 'rect',
                        width: 66,
                        height: 36,
                        attrs: {
                            body: {
                                strokeWidth: 1,
                                stroke: '#5F95FF',
                                fill: '#EFF4FF',
                            },
                            text: {
                                fontSize: 12,
                                fill: '#262626',
                            },
                        },
                    },
                    true,
                )

                X6.Graph.registerNode(
                    'custom-polygon',
                    {
                        inherit: 'polygon',
                        width: 66,
                        height: 36,
                        attrs: {
                            body: {
                                strokeWidth: 1,
                                stroke: '#5F95FF',
                                fill: '#EFF4FF',
                            },
                            text: {
                                fontSize: 12,
                                fill: '#262626',
                            },
                        },
                    },
                    true,
                )
                X6.Graph.registerNode(
                    'custom-rect',
                    {
                        inherit: 'rect',
                        width: 66,
                        height: 36,
                        attrs: {
                            body: {
                                strokeWidth: 1,
                                stroke: '#5F95FF',
                                fill: '#EFF4FF',
                            },
                            text: {
                                fontSize: 12,
                                fill: '#262626',
                            },
                        },
                    },
                    true,
                )
                X6.Graph.registerNode(
                    'custom-circle',
                    {
                        inherit: 'circle',
                        width: 45,
                        height: 45,
                        attrs: {
                            body: {
                                strokeWidth: 1,
                                stroke: '#5F95FF',
                                fill: '#EFF4FF',
                            },
                            text: {
                                fontSize: 12,
                                fill: '#262626',
                            },
                        },
                    },
                    true,
                )
            },
            handerOk(e) {
                this.visible = false
                this.graph.drawBackground({
                    color: e,
                })
                this.COLOR = e
                document.getElementsByClassName('x6-widget-minimap-viewport')[0].style.border = '2px solid ' + e
            },
            handlerColor() {
                this.visible = true
            },

            dblclick() {
                this.graph.on('cell:dblclick', (event) => {
                    this.actionNode = event.node;
                    this.actionNode.label = '123'
                    console.log(event.node.label);
                })
            },
            // 撤销
            revocation() {
                this.graph.undo()
            },
            // 快捷键
            shortcutKey() {
                this.graph.bindKey(['meta+c', 'ctrl+c'], () => {
                    console.log('cells');
                    const cells = this.graph.getSelectedCells()
                    if (cells.length) {
                        this.graph.copy(cells)
                    }
                    return false
                })
                this.graph.bindKey(['meta+v', 'ctrl+v'], () => {
                    if (!this.graph.isClipboardEmpty()) {
                        const cells = this.graph.paste({ offset: 32 })
                        this.graph.cleanSelection()
                        this.graph.select(cells)
                    }
                    return false
                })
                this.graph.bindKey(['meta+x', 'ctrl+x'], () => {
                    const cells = this.graph.getSelectedCells()
                    if (cells.length) {
                        this.graph.cut(cells)
                    }
                    return false
                })
                // select all
                this.graph.bindKey(['meta+a', 'ctrl+a'], () => {
                    const nodes = this.graph.getNodes()
                    if (nodes) {
                        this.graph.select(nodes)
                    }
                })
                this.graph.bindKey('delete', () => {
                    const cells = this.graph.getSelectedCells()
                    if (cells.length) {
                        this.graph.removeCells(cells)
                    }
                })
                this.graph.bindKey(['meta+z', 'ctrl+z'], () => {
                    if (this.graph.history.canUndo()) {
                        this.graph.history.undo()
                    }
                    return false
                })
            },
            printNodeList() {
                console.log(JSON.stringify(this.graph.toJSON({ diff: true })));
            },
            recover() {
                let axios = { "cells": [{ "position": { "x": 124, "y": 82 }, "size": { "width": 100, "height": 40 }, "attrs": { "text": { "text": "测试" }, "body": { "strokeWidth": 2, "stroke": "black", "fill": "#fff" }, "label": { "fontSize": 18, "stroke": "black" } }, "shape": "custom-rect", "id": 2, "children": [], "ports": { "groups": { "top": { "position": "top", "attrs": { "circle": { "r": 4, "magnet": true, "stroke": "#5F95FF", "strokeWidth": 1, "fill": "#fff", "style": { "visibility": "hidden" } } } }, "right": { "position": "right", "attrs": { "circle": { "r": 4, "magnet": true, "stroke": "#5F95FF", "strokeWidth": 1, "fill": "#fff", "style": { "visibility": "hidden" } } } }, "bottom": { "position": "bottom", "attrs": { "circle": { "r": 4, "magnet": true, "stroke": "#5F95FF", "strokeWidth": 1, "fill": "#fff", "style": { "visibility": "hidden" } } } }, "left": { "position": "left", "attrs": { "circle": { "r": 4, "magnet": true, "stroke": "#5F95FF", "strokeWidth": 1, "fill": "#fff", "style": { "visibility": "hidden" } } } } }, "items": [{ "group": "top", "id": "8f529e62-b338-4531-8db5-7fa7d5f6253f" }, { "group": "right", "id": "e60838df-6c5c-4f5d-bed4-849089431b79" }, { "group": "bottom", "id": "162fd067-7f26-4912-a3f1-4eb8371089f6" }, { "group": "left", "id": "3ab8ef93-adce-44b2-b462-6deb738d67bf" }] }, "zIndex": 1 }] }
                this.graph.fromJSON(axios)
            },
            handlerEdit() {
                let input = document.createElement('input')
                let canvas = document.getElementsByClassName('canvas-card')[0]
                let { x, y } = ACTIONNODE.position()
                input.className = 'ces'
                input.style.border='none'
                input.style.position = 'absolute'
                input.style.width = '110px'
                input.style.height = '50px'
                input.style.left = x - 9 + 'px'
                input.style.top = y - 9 + 'px'
                input.placeholder = ACTIONNODE.label
                canvas.appendChild(input)
                this.graph.mousewheel.enabled = false
                this.graph.panning.enabled = false
                input.focus()
                input.addEventListener('blur', function (e) {
                    ACTIONNODE.label = e.target.value ||  ACTIONNODE.label
                    canvas.removeChild(input)
                })
            
            }
        },
    });
  
</script>