知名网站建设定制先上几个网址
知名网站建设定制上面是一些用到的网址,知名网站建设定制先说下我项目中的需求,知名网站建设定制就是要自己绘制出一个知名网站建设定制我们想要的图,知名网站建设定制组态化呈现出来,知名网站建设定制然后这个图中会有很多节点,知名网站建设定制每个节点都会有自己的状态 ,状态会实时改变,状态变化的时候,对应的节点标签颜色等都要跟随变化。
那么我们该用什么绘制呢,绘制出来还要符合我们的业务需求,说白了就是要很灵活,想改什么,想用什么节点(自定义图片等),想绑定什么属性等等,我们都可以很灵活的实现。所以就基于 X6 来实现这么个事儿。
那么整体的思路就是基于AntV X6来实现这么一个工具,然后它最终是可以将我们绘制的图(节点、路径等)转为一个JSON数据,我们把这个JSON数据转递给后端,后端会实时去改变JSON数据中的具体的某些字段的值(注意不要修改JSON数据的结构),然后再返回给前端,前端会通过AntV X6将这个JSON数据在对应的页面中再渲染出来,每当JSON数据更新就实时更新来实现这么个需求
先看下最终开发实现出的这个效果图:
左边是我们的节点,就上图所示,上面是一些内置的基础的节点,下面是根据自己具体的业务需求自定义的一些图片节点,当然还要其他的一些很多,官网上都有对应示例文档,可以根据自己的需求、业务场景定制。中间就是我们操作的画板,上面是一些小工具,核心就是用到上面的 toJSON 可以将我们绘制的节点路径样式转为JSON,然后右边就是一些属性,当我们点击网格、点击节点、点击线在右侧都会有不同的属性,当然这些属性也都是自己加上的,还有很多属性都可以自定义加上,包括样式属性,节点的边框粗细、颜色,填充色,连接线的样式、动画、路由方式、连接方式、还有节点连接桩等等都是可以设置的
下面介绍下AntV\X6使用
这里只能说是说一些大致的思路方向,核心代码,很多细节不可能都能说到,尤其是右边的具体的属性,我也会将完整代码上传一下
这里是demo完整代码(同样的功能,分别用、Vue3实现):
1. 安装
- # npm
- $ npm install @antv/x6 --save
- $ npm install insert-css
-
- # yarn
- $ yarn add @antv/x6
- $ yarn add insert-css
2. 安装完成之后,使用 import
或 require
进行引用
import {Graph, Addon, FunctionExt, Shape} from '@antv/x6'
3. 布局
布局其实蛮简单的了,就正常布局即可
index.vue
- <template>
- <div class="flow">
- <div class="content">
- <!--左侧工具栏-->
- <div id="stencil" />
- <div class="panel">
- <!--流程图工具栏-->
- <div class="toolbar">
- <!-- <tool-bar v-if="isReady" /> -->
- </div>
- <!--流程图画板-->
- <div id="container" />
- </div>
- <!--右侧工具栏-->
- <div class="config">
- <!-- <config-panel v-if="isReady" /> -->
- </div>
- </div>
- </div>
- </template>
index.css
- .flow {
- width: 100vw;
- height: 100vh;
- }
-
- .content {
- width: 100%;
- height: 100%;
- display: flex;
- }
-
- #stencil {
- width: 290px;
- height: 100%;
- position: relative;
- border-right: 1px solid rgba(0, 0, 0, 0.08);
- box-sizing: border-box;
- }
-
- .panel {
- width: calc(100% - 580px);
- height: 100%;
- }
-
- .panel .toolbar {
- width: 100%;
- height: 38px;
- display: flex;
- align-items: center;
- background-color: #f7f9fb;
- border-bottom: 1px solid rgba(0, 0, 0, 0.08);
- }
-
- .panel #container {
- width: 100%;
- height: calc(100% - 38px);
- }
-
-
- .config {
- width: 290px;
- height: 100%;
- padding: 0 10px;
- border-left: 1px solid rgba(0, 0, 0, 0.08);
- box-sizing: border-box;
- }
4. 初始化流程图、画板
graph/index.js
- this.graph = new Graph({
- background: {
- color: '#a5a5a5' // 设置画布背景颜色
- },
- container: dom, //画板的dom容器
- width: width, //画板的宽度
- height: height, //画板的高度
- autoResize: true,
- grid: {
- size: 10,
- visible: true,
- type: 'doubleMesh',
- args: [
- {
- color: '#cccccc',
- thickness: 1,
- },
- {
- color: '#5F95FF',
- thickness: 1,
- factor: 4,
- },
- ],
- },
- scroller: {
- enabled: false,
- pageVisible: false,
- pageBreak: false,
- pannable: false,
- },
- // 开启画布缩放
- mousewheel: {
- enabled: true,
- modifiers: ['ctrl', 'meta'],
- minScale: 0.5,
- maxScale: 2,
- },
- connecting: {
- anchor: 'center',
- connectionPoint: 'anchor',
- allowBlank: true,
- highlight: true,
- snap: true, // 是否自动吸附
- allowMulti: true, // 是否允许在相同的起始节点和终止之间创建多条边
- allowNode: false, // 是否允许边链接到节点(非节点上的链接桩)
- allowBlank: false, // 是否允许连接到空白点
- allowLoop: false, // 是否允许创建循环连线,即边的起始节点和终止节点为同一节点,
- allowEdge: false, // 是否允许边链接到另一个边
- highlight: true, // 拖动边时,是否高亮显示所有可用的连接桩或节点
- connectionPoint: "anchor", // 指定连接点
- anchor: "center", // 指定被连接的节点的锚点
- createEdge() {
- // X6 的 Shape 命名空间中内置 Edge、DoubleEdge、ShadowEdge 三种边
- return new Shape.DoubleEdge({
- attrs: {
- // line: {
- // // stroke: '#5F95FF',
- // // strokeWidth: 4,
- // // targetMarker: {
- // // name: 'classic',
- // // size: 8,
- // // },
- // stroke: '#1890ff',
- // strokeDasharray: 5,
- // targetMarker: null,//block classic diamond cross async path circle circlePlus ellipse
- // style: {
- // animation: 'ant-line 30s infinite linear',
- // },
- // },
- line: {
- strokeWidth: 8,
- stroke: '#e54033',
- strokeDasharray: 5,
- style: {
- animation: 'ant-line 30s infinite linear',
- },
- targetMarker: null, // 去掉箭头
- },
- outline: {
- stroke: '#73d13d',
- strokeWidth: 15,
- }
- },
- router: {
- name: 'metro',
- }
- })
- },
- validateConnection({
- sourceView,
- targetView,
- sourceMagnet,
- targetMagnet,
- }) {
- if (sourceView === targetView) {
- return false
- }
- if (!sourceMagnet) {
- return false
- }
- if (!targetMagnet) {
- return false
- }
- return true
- },
- },
- highlighting: {
- magnetAvailable: {
- name: 'stroke',
- args: {
- padding: 4,
- attrs: {
- strokeWidth: 4,
- stroke: 'rgba(223,234,255)',
- },
- },
- },
- },
- // 开启拖拽平移(防止冲突,按下修饰键并点击鼠标才能触发画布拖拽)
- panning: {
- enabled: true,
- modifiers: 'shift',
- },
- resizing: true,
- rotating: true,
- selecting: {
- enabled: true,
- multiple: true,
- rubberband: true,
- movable: true,
- showNodeSelectionBox: true,
- },
- snapline: true,
- history: true,
- clipboard: {
- enabled: true,
- },
- keyboard: {
- enabled: true,
- },
- embedding: {
- enabled: true,
- findParent({ node }) {
- const bbox = node.getBBox()
- return this.getNodes().filter((node) => {
- // 只有 data.parent 为 true 的节点才是父节点
- const data = node.getData()
- if (data && data.parent) {
- const targetBBox = node.getBBox()
- return bbox.isIntersectWithRect(targetBBox)
- }
- return false
- })
- },
- },
- })
5. 初始化左侧根节点
graph/index.js
- this.stencil = new Addon.Stencil({
- target: this.graph, // 刚才初始化流程图画板的实例
- stencilGraphWidth: 280,
- search: { rect: true },
- collapsable: true,
- groups: [
- {
- name: 'basic',
- title: '基础节点',
- graphHeight: 180,
- },
- {
- name: 'custom-image',
- title: '系统设计图',
- graphHeight: 600
- },
- // {
- // name: 'combination',
- // title: '组合节点',
- // layoutOptions: {
- // columns: 1,
- // marginX: 60,
- // },
- // graphHeight: 260,
- // },
- // {
- // name: 'group',
- // title: '节点组',
- // graphHeight: 100,
- // layoutOptions: {
- // columns: 1,
- // marginX: 60,
- // },
- // },
- ],
- })
- const stencilContainer = document.querySelector('#stencil') // 左侧的节点容器
- stencilContainer?.appendChild(this.stencil.container) // 追加进去
6. 初始化具体每个根节点下不同类型节点
① 连接桩配置
ports.js
- export const basicPorts = {
- 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',
- },
- {
- group: 'right',
- },
- {
- group: 'bottom',
- },
- {
- group: 'left',
- }
- ]
- }
-
- export const customPorts = {
- groups: {
- top: {
- position: {
- name: 'absolute',
- }
- },
- right: {
- position: {
- name: 'absolute',
- }
- },
- bottom: {
- position: {
- name: 'absolute',
- }
- },
- left: {
- position: {
- name: 'absolute',
- }
- }
- },
- items: [
- {
- id: 'port1',
- group: 'top',
- // 通过 args 指定绝对位置
- args: {
- x: 25,
- y: -5,
- },
- attrs: {
- circle: {
- r: 4,
- magnet: true,
- stroke: '#5F95FF',
- strokeWidth: 1,
- fill: '#fff',
- style: {
- visibility: 'hidden',
- },
- },
- },
- },
- {
- id: 'port2',
- group: 'right',
- args: {
- x: 55,
- y: 25,
- },
- attrs: {
- circle: {
- r: 4,
- magnet: true,
- stroke: '#5F95FF',
- strokeWidth: 1,
- fill: '#fff',
- style: {
- visibility: 'hidden',
- }
- },
- }
- },
- {
- id: 'port3',
- group: 'bottom',
- args: {
- x: 25,
- y: 55,
- },
- attrs: {
- circle: {
- r: 4,
- magnet: true,
- stroke: '#5F95FF',
- strokeWidth: 1,
- fill: '#fff',
- style: {
- visibility: 'hidden',
- }
- }
- }
- },
- {
- id: 'port4',
- group: 'left',
- args: {
- x: -5,
- y: 25,
- },
- attrs: {
- circle: {
- r: 4,
- magnet: true,
- stroke: '#5F95FF',
- strokeWidth: 1,
- fill: '#fff',
- style: {
- visibility: 'hidden',
- }
- }
- }
- }
- ]
- }
② 注册节点
先注册节点,一会儿再createNode,最后再load
shape.js
- import { Graph, Dom, Node } from '@antv/x6'
- import { basicPorts, customPorts } from './ports' // 连接桩配置
-
- // 基础节点
- export const FlowChartRect = Graph.registerNode('flow-chart-rect', {
- inherit: 'rect',
- width: 80,
- height: 42,
- attrs: {
- body: {
- stroke: '#5F95FF',
- strokeWidth: 1,
- fill: '#ffffff',
- },
- fo: {
- refWidth: '100%',
- refHeight: '100%',
- },
- foBody: {
- xmlns: Dom.ns.xhtml,
- style: {
- width: '100%',
- height: '100%',
- display: 'flex',
- justifyContent: 'center',
- alignItems: 'center',
- },
- },
- 'edit-text': {
- contenteditable: 'true',
- class: 'x6-edit-text',
- style: {
- width: '100%',
- textAlign: 'center',
- fontSize: 12,
- color: 'rgba(0,0,0,0.85)',
- },
- },
- text: {
- fontSize: 12,
- fill: '#080808',
- },
- },
- markup: [
- {
- tagName: 'rect',
- selector: 'body',
- },
- {
- tagName: 'text',
- selector: 'text',
- },
- {
- tagName: 'foreignObject',
- selector: 'fo',
- children: [
- {
- ns: Dom.ns.xhtml,
- tagName: 'body',
- selector: 'foBody',
- children: [
- {
- tagName: 'div',
- selector: 'edit-text',
- },
- ],
- },
- ],
- },
- ],
- ports: { ...basicPorts },
- })
- // 组合节点
- export const FlowChartImageRect = Graph.registerNode('flow-chart-image-rect', {
- inherit: 'rect',
- width: 200,
- height: 60,
- attrs: {
- body: {
- stroke: '#5F95FF',
- strokeWidth: 1,
- fill: 'rgba(95,149,255,0.05)',
- },
- image: {
- 'xlink:href':
- 'https://gw.alipayobjects.com/zos/antfincdn/FLrTNDvlna/antv.png',
- width: 16,
- height: 16,
- x: 12,
- y: 12,
- },
- title: {
- text: 'Node',
- refX: 40,
- refY: 14,
- fill: 'rgba(0,0,0,0.85)',
- fontSize: 12,
- 'text-anchor': 'start',
- },
- text: {
- text: 'this is content text',
- refX: 40,
- refY: 38,
- fontSize: 12,
- fill: 'rgba(0,0,0,0.6)',
- 'text-anchor': 'start',
- },
- },
- markup: [
- {
- tagName: 'rect',
- selector: 'body',
- },
- {
- tagName: 'image',
- selector: 'image',
- },
- {
- tagName: 'text',
- selector: 'title',
- },
- {
- tagName: 'text',
- selector: 'text',
- },
- ],
- ports: { ...basicPorts },
- })
-
- export const FlowChartTitleRect = Graph.registerNode('flow-chart-title-rect', {
- inherit: 'rect',
- width: 200,
- height: 68,
- attrs: {
- body: {
- stroke: '#5F95FF',
- strokeWidth: 1,
- fill: 'rgba(95,149,255,0.05)',
- },
- head: {
- refWidth: '100%',
- stroke: 'transparent',
- height: 28,
- fill: 'rgb(95,149,255)',
- },
- image: {
- 'xlink:href':
- 'https://gw.alipayobjects.com/zos/antfincdn/FLrTNDvlna/antv.png',
- height: 16,
- x: 6,
- y: 6,
- },
- title: {
- text: 'Node',
- refX: 30,
- refY: 9,
- fill: '#ffffff',
- fontSize: 12,
- 'text-anchor': 'start',
- },
- text: {
- text: 'this is content text',
- refX: 8,
- refY: 45,
- fontSize: 12,
- fill: 'rgba(0,0,0,0.6)',
- 'text-anchor': 'start',
- },
- },
- markup: [
- {
- tagName: 'rect',
- selector: 'body',
- },
- {
- tagName: 'rect',
- selector: 'head',
- },
- {
- tagName: 'image',
- selector: 'image',
- },
- {
- tagName: 'text',
- selector: 'title',
- },
- {
- tagName: 'text',
- selector: 'text',
- },
- ],
- ports: { ...basicPorts },
- })
-
- export const FlowChartAnimateText = Graph.registerNode('flow-chart-animate-text', {
- inherit: 'rect',
- width: 200,
- height: 60,
- attrs: {
- body: {
- stroke: '#5F95FF',
- strokeWidth: 1,
- fill: 'rgba(95,149,255,0.05)',
- },
- text1: {
- class: 'animate-text1',
- text: 'AntV X6',
- fontSize: 32,
- },
- text2: {
- class: 'animate-text2',
- text: 'AntV X6',
- fontSize: 32,
- },
- },
- markup: [
- {
- tagName: 'rect',
- selector: 'body',
- },
- {
- tagName: 'text',
- selector: 'text1',
- },
- {
- tagName: 'text',
- selector: 'text2',
- },
- ],
- })
-
- // 自定义 系统设计图
- export const FlowChartImageRectCustom = Graph.registerNode('flow-chart-image-rect-custom', {
- inherit: 'rect',
- width: 80,
- height: 80,
- markup: [
- {
- tagName: 'rect',
- selector: 'body',
- },
- {
- tagName: 'image',
- },
- {
- tagName: 'text',
- selector: 'label',
- }
- ],
- attrs: {
- body: {
- // 节点线的颜色
- stroke: 'transparent',
- // 背景填充色
- fill: 'transparent',
- },
- // 自定义图片
- image: {
- width: 60,
- height: 60,
- refX: 0,
- refY: 0,
- },
- label: {
- refX: 3,
- refY: 2,
- textAnchor: 'left',
- textVerticalAnchor: 'top',
- fontSize: 12,
- fill: 'black',
- },
- 'edit-text': {
- contenteditable: 'true',
- class: 'x6-edit-text',
- style: {
- width: '100%',
- textAlign: 'center',
- fontSize: 12,
- color: 'rgba(0,0,0,0.85)'
- },
- },
- text: {
- fontSize: 12,
- fill: '#080808',
- },
- },
- ports: { ...customPorts },
- })
-
- // 节点组
- export class NodeGroup extends Node {
- collapsed = true
-
- postprocess() {
- this.toggleCollapse(true)
- }
-
- isCollapsed() {
- return this.collapsed
- }
-
- toggleCollapse(collapsed) {
- const target = collapsed == null ? !this.collapsed : collapsed
- if (target) {
- this.attr('buttonSign', { d: 'M 1 5 9 5 M 5 1 5 9' })
- this.resize(200, 40)
- } else {
- this.attr('buttonSign', { d: 'M 2 5 8 5' })
- this.resize(240, 240)
- }
- this.collapsed = target
- }
- }
-
- NodeGroup.config({
- shape: 'rect',
- markup: [
- {
- tagName: 'rect',
- selector: 'body',
- },
- {
- tagName: 'image',
- selector: 'image',
- },
- {
- tagName: 'text',
- selector: 'text',
- },
- {
- tagName: 'g',
- selector: 'buttonGroup',
- children: [
- {
- tagName: 'rect',
- selector: 'button',
- attrs: {
- 'pointer-events': 'visiblePainted',
- },
- },
- {
- tagName: 'path',
- selector: 'buttonSign',
- attrs: {
- fill: 'none',
- 'pointer-events': 'none',
- },
- },
- ],
- },
- ],
- attrs: {
- body: {
- refWidth: '100%',
- refHeight: '100%',
- strokeWidth: 1,
- fill: 'rgba(95,149,255,0.05)',
- stroke: '#5F95FF',
- },
- image: {
- 'xlink:href':
- 'https://gw.alipayobjects.com/mdn/rms_0b51a4/afts/img/A*X4e0TrDsEiIAAAAAAAAAAAAAARQnAQ',
- width: 16,
- height: 16,
- x: 8,
- y: 12,
- },
- text: {
- fontSize: 12,
- fill: 'rgba(0,0,0,0.85)',
- refX: 30,
- refY: 15,
- },
- buttonGroup: {
- refX: '100%',
- refX2: -25,
- refY: 13,
- },
- button: {
- height: 14,
- width: 16,
- rx: 2,
- ry: 2,
- fill: '#f5f5f5',
- stroke: '#ccc',
- cursor: 'pointer',
- event: 'node:collapse',
- },
- buttonSign: {
- refX: 3,
- refY: 2,
- stroke: '#808080',
- },
- },
- })
-
- Graph.registerNode('groupNode', NodeGroup)
③ createNode、load
graph/index.js
- const { graph } = this
- // 基础节点
- const r1 = graph.createNode({
- shape: 'flow-chart-rect',
- attrs: {
- body: {
- rx: 24,
- ry: 24,
- },
- text: {
- text: '起始节点',
- },
- },
- })
- const r2 = graph.createNode({
- shape: 'flow-chart-rect',
- attrs: {
- text: {
- text: '流程节点',
- },
- },
- })
- const r3 = graph.createNode({
- shape: 'flow-chart-rect',
- width: 52,
- height: 52,
- angle: 45,
- attrs: {
- 'edit-text': {
- style: {
- transform: 'rotate(-45deg)',
- },
- },
- text: {
- text: '判断节点',
- transform: 'rotate(-45deg)',
- },
- },
- ports: {
- groups: {
- top: {
- position: {
- name: 'top',
- args: {
- dx: -26,
- },
- },
- },
- right: {
- position: {
- name: 'right',
- args: {
- dy: -26,
- },
- },
- },
- bottom: {
- position: {
- name: 'bottom',
- args: {
- dx: 26,
- },
- },
- },
- left: {
- position: {
- name: 'left',
- args: {
- dy: 26,
- },
- },
- },
- },
- },
- })
- const r4 = graph.createNode({
- shape: 'flow-chart-rect',
- width: 70,
- height: 70,
- attrs: {
- body: {
- rx: 35,
- ry: 35,
- },
- text: {
- text: '链接节点',
- },
- },
- })
- // 组合节点
- const c1 = graph.createNode({
- shape: 'flow-chart-image-rect',
- })
- const c2 = graph.createNode({
- shape: 'flow-chart-title-rect',
- })
- const c3 = graph.createNode({
- shape: 'flow-chart-animate-text',
- })
- // 节点组
- const g1 = graph.createNode({
- shape: 'groupNode',
- attrs: {
- text: {
- text: 'Group Name',
- },
- },
- data: {
- parent: true,
- },
- })
- // 系统设计图
- const imgs = [
- {
- image: require('../../../assets/ldb.png')
- },
- {
- image: require('../../../assets/冷冻泵.png')
- },
- {
- image: require('../../../assets/冷却泵.png')
- },
- {
- image: require('../../../assets/wft1.png')
- },
- {
- image: require('../../../assets/wft2.png')
- },
- {
- image: require('../../../assets/wft3.png')
- },
- {
- image: require('../../../assets/wft4.png')
- },
- {
- image: require('../../../assets/wft5.png')
- }
- ]
- const imgNodes = imgs.map(item => {
- return graph.createNode({
- // shape: 'flow-chart-image-rect-custom',
- // attrs: {
- // image: {
- // 'xlink:href': item.image,
- // }
- // }
- shape: 'image', //可选值:Rect Circle Ellipse Polygon Polyline Path Image HTML TextBlock BorderedImage EmbeddedImage InscribedImage Cylinder
- imageUrl: item.image,
- attrs: {
- image: {
- // fill: 'yellow',
- },
- },
- width: 52,
- height: 52,
- ports: { ...customPorts }
- })
- })
- this.stencil.load([r1, r2, r3, r4], 'basic')
- this.stencil.load(imgNodes, 'custom-image')
- // this.stencil.load([c1, c2, c3], 'combination')
- // this.stencil.load([g1], 'group')
7. 根据json数据渲染
graph/index.js
this.graph.fromJSON(jsonData)
8. 鼠标的一些事件,连接桩的显示时机等
graph/index.js
- // 连接桩显示时机
- showPorts(ports, show) {
- for (let i = 0, len = ports.length; i < len; i = i + 1) {
- ports[i].style.visibility = show ? 'visible' : 'hidden'
- }
- }
-
- initEvent() {
- const { graph } = this
- const container = document.getElementById('container')
-
- graph.on('node:contextmenu', ({ cell, view }) => {
- console.log(view.container)
- const oldText = cell.attr('text/text')
- cell.attr('text/style/display', 'none')
- const elem = view.container.querySelector('.x6-edit-text')
- if (elem) {
- elem.innerText = oldText
- elem.focus()
- }
- const onBlur = () => {
- cell.attr('text/text', elem.innerText)
- }
- if(elem){
- elem.addEventListener('blur', () => {
- onBlur()
- elem.removeEventListener('blur', onBlur)
- })
- }
- })
- // 鼠标移入 显示连接桩
- graph.on('node:mouseenter', FunctionExt.debounce(() => {
- const ports = container.querySelectorAll('.x6-port-body')
- this.showPorts(ports, true)
- }), 500,)
- // 鼠标移出 隐藏连接桩
- graph.on('node:mouseleave', () => {
- const ports = container.querySelectorAll('.x6-port-body')
- this.showPorts(ports, false)
- })
-
- graph.on('node:collapse', ({ node, e }) => {
- e.stopPropagation()
- node.toggleCollapse()
- const collapsed = node.isCollapsed()
- const cells = node.getDescendants()
- cells.forEach(n => {
- if (collapsed) {
- n.hide()
- } else {
- n.show()
- }
- })
- })
- // backspace
- graph.bindKey('delete', () => {
- const cells = graph.getSelectedCells()
- if (cells.length) {
- graph.removeCells(cells)
- }
- })
- }
9. 最后再给大家上个导出的JSON数据
- export default {
- "cells": [
- {
- "position": {
- "x": 420,
- "y": 160
- },
- "size": {
- "width": 80,
- "height": 42
- },
- "attrs": {
- "text": {
- "text": "起始节点"
- },
- "body": {
- "rx": 24,
- "ry": 24
- }
- },
- "visible": true,
- "shape": "flow-chart-rect",
- "ports": {
- "groups": {
- "top": {
- "position": "top",
- "attrs": {
- "circle": {
- "r": 3,
- "magnet": true,
- "stroke": "#5F95FF",
- "strokeWidth": 1,
- "fill": "#fff",
- "style": {
- "visibility": "hidden"
- }
- }
- }
- },
- "right": {
- "position": "right",
- "attrs": {
- "circle": {
- "r": 3,
- "magnet": true,
- "stroke": "#5F95FF",
- "strokeWidth": 1,
- "fill": "#fff",
- "style": {
- "visibility": "hidden"
- }
- }
- }
- },
- "bottom": {
- "position": "bottom",
- "attrs": {
- "circle": {
- "r": 3,
- "magnet": true,
- "stroke": "#5F95FF",
- "strokeWidth": 1,
- "fill": "#fff",
- "style": {
- "visibility": "hidden"
- }
- }
- }
- },
- "left": {
- "position": "left",
- "attrs": {
- "circle": {
- "r": 3,
- "magnet": true,
- "stroke": "#5F95FF",
- "strokeWidth": 1,
- "fill": "#fff",
- "style": {
- "visibility": "hidden"
- }
- }
- }
- }
- },
- "items": [
- {
- "group": "top",
- "id": "45726225-0a03-409e-8475-07da4b8533c5"
- },
- {
- "group": "right",
- "id": "06111939-bf01-48d9-9f54-6465d9d831c6"
- },
- {
- "group": "bottom",
- "id": "6541f8dc-e48b-4b8c-a105-2ab3a47f1f21"
- },
- {
- "group": "left",
- "id": "54781206-573f-4982-a21e-5fac1e0e8a60"
- }
- ]
- },
- "id": "8650a303-3568-4ff2-9fac-2fd3ae7e6f2a",
- "zIndex": 1
- },
- {
- "position": {
- "x": 420,
- "y": 250
- },
- "size": {
- "width": 80,
- "height": 42
- },
- "attrs": {
- "text": {
- "text": "流程节点"
- }
- },
- "visible": true,
- "shape": "flow-chart-rect",
- "ports": {
- "groups": {
- "top": {
- "position": "top",
- "attrs": {
- "circle": {
- "r": 3,
- "magnet": true,
- "stroke": "#5F95FF",
- "strokeWidth": 1,
- "fill": "#fff",
- "style": {
- "visibility": "hidden"
- }
- }
- }
- },
- "right": {
- "position": "right",
- "attrs": {
- "circle": {
- "r": 3,
- "magnet": true,
- "stroke": "#5F95FF",
- "strokeWidth": 1,
- "fill": "#fff",
- "style": {
- "visibility": "hidden"
- }
- }
- }
- },
- "bottom": {
- "position": "bottom",
- "attrs": {
- "circle": {
- "r": 3,
- "magnet": true,
- "stroke": "#5F95FF",
- "strokeWidth": 1,
- "fill": "#fff",
- "style": {
- "visibility": "hidden"
- }
- }
- }
- },
- "left": {
- "position": "left",
- "attrs": {
- "circle": {
- "r": 3,
- "magnet": true,
- "stroke": "#5F95FF",
- "strokeWidth": 1,
- "fill": "#fff",
- "style": {
- "visibility": "hidden"
- }
- }
- }
- }
- },
- "items": [
- {
- "group": "top",
- "id": "d1346f43-969a-4201-af5d-d09b7ef79980"
- },
- {
- "group": "right",
- "id": "d561926a-3a24-449a-abb1-0c20bc89947e"
- },
- {
- "group": "bottom",
- "id": "0cbde5df-ef35-410e-b6c3-a6b1f5561e3f"
- },
- {
- "group": "left",
- "id": "2fceb955-f7af-41ac-ac02-5a2ea514544e"
- }
- ]
- },
- "id": "7b6fd715-83e6-4053-8c2b-346e6a857bf3",
- "zIndex": 2
- },
- {
- "shape": "edge",
- "attrs": {
- "line": {
- "stroke": "#5F95FF",
- "strokeWidth": 1,
- "targetMarker": {
- "name": "classic",
- "size": 8
- },
- "strokeDasharray": 0
- }
- },
- "id": "00f3c401-8bad-46b9-b692-232aa011d4c5",
- "router": {
- "name": "manhattan"
- },
- "zIndex": 3,
- "source": {
- "cell": "8650a303-3568-4ff2-9fac-2fd3ae7e6f2a",
- "port": "6541f8dc-e48b-4b8c-a105-2ab3a47f1f21"
- },
- "target": {
- "cell": "7b6fd715-83e6-4053-8c2b-346e6a857bf3",
- "port": "d1346f43-969a-4201-af5d-d09b7ef79980"
- }
- },
- {
- "position": {
- "x": 425,
- "y": 371
- },
- "size": {
- "width": 70,
- "height": 70
- },
- "attrs": {
- "text": {
- "text": "链接节点"
- },
- "body": {
- "rx": 35,
- "ry": 35
- }
- },
- "visible": true,
- "shape": "flow-chart-rect",
- "ports": {
- "groups": {
- "top": {
- "position": "top",
- "attrs": {
- "circle": {
- "r": 3,
- "magnet": true,
- "stroke": "#5F95FF",
- "strokeWidth": 1,
- "fill": "#fff",
- "style": {
- "visibility": "hidden"
- }
- }
- }
- },
- "right": {
- "position": "right",
- "attrs": {
- "circle": {
- "r": 3,
- "magnet": true,
- "stroke": "#5F95FF",
- "strokeWidth": 1,
- "fill": "#fff",
- "style": {
- "visibility": "hidden"
- }
- }
- }
- },
- "bottom": {
- "position": "bottom",
- "attrs": {
- "circle": {
- "r": 3,
- "magnet": true,
- "stroke": "#5F95FF",
- "strokeWidth": 1,
- "fill": "#fff",
- "style": {
- "visibility": "hidden"
- }
- }
- }
- },
- "left": {
- "position": "left",
- "attrs": {
- "circle": {
- "r": 3,
- "magnet": true,
- "stroke": "#5F95FF",
- "strokeWidth": 1,
- "fill": "#fff",
- "style": {
- "visibility": "hidden"
- }
- }
- }
- }
- },
- "items": [
- {
- "group": "top",
- "id": "089ce61a-4b17-4ed8-9c3f-5b905f484425"
- },
- {
- "group": "right",
- "id": "fd4b8c95-d1eb-41ea-b3e1-15135814b292"
- },
- {
- "group": "bottom",
- "id": "9bb8ec19-b1e2-432d-8735-b008da064948"
- },
- {
- "group": "left",
- "id": "fbf8759a-1059-47bb-b556-f0a4477e48d3"
- }
- ]
- },
- "id": "762cbe4d-fd2b-4cb2-95bb-fae3cb9ef7fc",
- "zIndex": 4
- },
- {
- "angle": 45,
- "position": {
- "x": 310,
- "y": 380
- },
- "size": {
- "width": 52,
- "height": 52
- },
- "attrs": {
- "text": {
- "text": "判断节点",
- "transform": "rotate(-45deg)"
- },
- "edit-text": {
- "style": {
- "transform": "rotate(-45deg)"
- }
- }
- },
- "visible": true,
- "shape": "flow-chart-rect",
- "ports": {
- "groups": {
- "top": {
- "position": {
- "name": "top",
- "args": {
- "dx": -26
- }
- },
- "attrs": {
- "circle": {
- "r": 3,
- "magnet": true,
- "stroke": "#5F95FF",
- "strokeWidth": 1,
- "fill": "#fff",
- "style": {
- "visibility": "hidden"
- }
- }
- }
- },
- "right": {
- "position": {
- "name": "right",
- "args": {
- "dy": -26
- }
- },
- "attrs": {
- "circle": {
- "r": 3,
- "magnet": true,
- "stroke": "#5F95FF",
- "strokeWidth": 1,
- "fill": "#fff",
- "style": {
- "visibility": "hidden"
- }
- }
- }
- },
- "bottom": {
- "position": {
- "name": "bottom",
- "args": {
- "dx": 26
- }
- },
- "attrs": {
- "circle": {
- "r": 3,
- "magnet": true,
- "stroke": "#5F95FF",
- "strokeWidth": 1,
- "fill": "#fff",
- "style": {
- "visibility": "hidden"
- }
- }
- }
- },
- "left": {
- "position": {
- "name": "left",
- "args": {
- "dy": 26
- }
- },
- "attrs": {
- "circle": {
- "r": 3,
- "magnet": true,
- "stroke": "#5F95FF",
- "strokeWidth": 1,
- "fill": "#fff",
- "style": {
- "visibility": "hidden"
- }
- }
- }
- }
- },
- "items": [
- {
- "group": "top",
- "id": "c133349e-4a4a-4d7d-9b38-26c36b3e68c5"
- },
- {
- "group": "right",
- "id": "af190d15-b0f1-4f92-85bc-e0c4df83e2a7"
- },
- {
- "group": "bottom",
- "id": "c51a4f3b-759b-47ed-9d80-fa4f6c114e64"
- },
- {
- "group": "left",
- "id": "c241a7e4-12d3-4dde-9694-0f0e5f7b9a91"
- }
- ]
- },
- "id": "ef3865af-8a91-4164-8466-3f6b4315070f",
- "zIndex": 5
- },
- {
- "shape": "edge",
- "attrs": {
- "line": {
- "stroke": "#5F95FF",
- "strokeWidth": 1,
- "targetMarker": {
- "name": "classic",
- "size": 8
- }
- }
- },
- "id": "9031a1ee-8deb-4b1e-90e6-96d40d3a8515",
- "router": {
- "name": "manhattan"
- },
- "zIndex": 7,
- "source": {
- "cell": "ef3865af-8a91-4164-8466-3f6b4315070f",
- "port": "af190d15-b0f1-4f92-85bc-e0c4df83e2a7"
- },
- "target": {
- "cell": "762cbe4d-fd2b-4cb2-95bb-fae3cb9ef7fc",
- "port": "fbf8759a-1059-47bb-b556-f0a4477e48d3"
- }
- },
- {
- "angle": 45,
- "position": {
- "x": 566,
- "y": 380
- },
- "size": {
- "width": 52,
- "height": 52
- },
- "attrs": {
- "text": {
- "text": "判断节点",
- "transform": "rotate(-45deg)"
- },
- "edit-text": {
- "style": {
- "transform": "rotate(-45deg)"
- }
- }
- },
- "visible": true,
- "shape": "flow-chart-rect",
- "ports": {
- "groups": {
- "top": {
- "position": {
- "name": "top",
- "args": {
- "dx": -26
- }
- },
- "attrs": {
- "circle": {
- "r": 3,
- "magnet": true,
- "stroke": "#5F95FF",
- "strokeWidth": 1,
- "fill": "#fff",
- "style": {
- "visibility": "hidden"
- }
- }
- }
- },
- "right": {
- "position": {
- "name": "right",
- "args": {
- "dy": -26
- }
- },
- "attrs": {
- "circle": {
- "r": 3,
- "magnet": true,
- "stroke": "#5F95FF",
- "strokeWidth": 1,
- "fill": "#fff",
- "style": {
- "visibility": "hidden"
- }
- }
- }
- },
- "bottom": {
- "position": {
- "name": "bottom",
- "args": {
- "dx": 26
- }
- },
- "attrs": {
- "circle": {
- "r": 3,
- "magnet": true,
- "stroke": "#5F95FF",
- "strokeWidth": 1,
- "fill": "#fff",
- "style": {
- "visibility": "hidden"
- }
- }
- }
- },
- "left": {
- "position": {
- "name": "left",
- "args": {
- "dy": 26
- }
- },
- "attrs": {
- "circle": {
- "r": 3,
- "magnet": true,
- "stroke": "#5F95FF",
- "strokeWidth": 1,
- "fill": "#fff",
- "style": {
- "visibility": "hidden"
- }
- }
- }
- }
- },
- "items": [
- {
- "group": "top",
- "id": "c133349e-4a4a-4d7d-9b38-26c36b3e68c5"
- },
- {
- "group": "right",
- "id": "af190d15-b0f1-4f92-85bc-e0c4df83e2a7"
- },
- {
- "group": "bottom",
- "id": "c51a4f3b-759b-47ed-9d80-fa4f6c114e64"
- },
- {
- "group": "left",
- "id": "c241a7e4-12d3-4dde-9694-0f0e5f7b9a91"
- }
- ]
- },
- "id": "9be960d0-fb75-49b1-8131-abc05b5991bd",
- "zIndex": 9
- },
- {
- "shape": "edge",
- "attrs": {
- "line": {
- "stroke": "#5F95FF",
- "strokeWidth": 1,
- "targetMarker": {
- "name": "classic",
- "size": 8
- }
- }
- },
- "id": "8af8b072-dfb9-458a-b15e-dd5d4c1863bd",
- "router": {
- "name": "manhattan"
- },
- "zIndex": 10,
- "source": {
- "cell": "7b6fd715-83e6-4053-8c2b-346e6a857bf3",
- "port": "d561926a-3a24-449a-abb1-0c20bc89947e"
- },
- "target": {
- "cell": "9be960d0-fb75-49b1-8131-abc05b5991bd",
- "port": "c133349e-4a4a-4d7d-9b38-26c36b3e68c5"
- }
- },
- {
- "shape": "edge",
- "attrs": {
- "line": {
- "stroke": "#5F95FF",
- "strokeWidth": 1,
- "targetMarker": {
- "name": "classic",
- "size": 8
- }
- }
- },
- "id": "58874c23-da0b-46e6-9124-5154e8570bfd",
- "router": {
- "name": "manhattan"
- },
- "zIndex": 11,
- "source": {
- "cell": "9be960d0-fb75-49b1-8131-abc05b5991bd",
- "port": "c241a7e4-12d3-4dde-9694-0f0e5f7b9a91"
- },
- "target": {
- "cell": "762cbe4d-fd2b-4cb2-95bb-fae3cb9ef7fc",
- "port": "fd4b8c95-d1eb-41ea-b3e1-15135814b292"
- }
- },
- {
- "shape": "edge",
- "attrs": {
- "line": {
- "stroke": "#5F95FF",
- "strokeWidth": 1,
- "targetMarker": {
- "name": "classic",
- "size": 8
- }
- }
- },
- "id": "13d78262-f889-4e6f-9980-29f9e2d87c8f",
- "router": {
- "name": "manhattan"
- },
- "zIndex": 12,
- "source": {
- "cell": "7b6fd715-83e6-4053-8c2b-346e6a857bf3",
- "port": "2fceb955-f7af-41ac-ac02-5a2ea514544e"
- },
- "target": {
- "cell": "ef3865af-8a91-4164-8466-3f6b4315070f",
- "port": "c133349e-4a4a-4d7d-9b38-26c36b3e68c5"
- }
- },
- {
- "shape": "edge",
- "attrs": {
- "line": {
- "stroke": "#5F95FF",
- "strokeWidth": 1,
- "targetMarker": {
- "name": "classic",
- "size": 8
- }
- }
- },
- "id": "9c7b7539-2f82-478d-a592-60dfceede791",
- "router": {
- "name": "manhattan"
- },
- "zIndex": 13,
- "source": {
- "cell": "7b6fd715-83e6-4053-8c2b-346e6a857bf3",
- "port": "0cbde5df-ef35-410e-b6c3-a6b1f5561e3f"
- },
- "target": {
- "cell": "762cbe4d-fd2b-4cb2-95bb-fae3cb9ef7fc",
- "port": "089ce61a-4b17-4ed8-9c3f-5b905f484425"
- }
- },
- {
- "position": {
- "x": 420,
- "y": 497
- },
- "size": {
- "width": 80,
- "height": 42
- },
- "attrs": {
- "text": {
- "text": "流程节点"
- }
- },
- "visible": true,
- "shape": "flow-chart-rect",
- "ports": {
- "groups": {
- "top": {
- "position": "top",
- "attrs": {
- "circle": {
- "r": 3,
- "magnet": true,
- "stroke": "#5F95FF",
- "strokeWidth": 1,
- "fill": "#fff",
- "style": {
- "visibility": "hidden"
- }
- }
- }
- },
- "right": {
- "position": "right",
- "attrs": {
- "circle": {
- "r": 3,
- "magnet": true,
- "stroke": "#5F95FF",
- "strokeWidth": 1,
- "fill": "#fff",
- "style": {
- "visibility": "hidden"
- }
- }
- }
- },
- "bottom": {
- "position": "bottom",
- "attrs": {
- "circle": {
- "r": 3,
- "magnet": true,
- "stroke": "#5F95FF",
- "strokeWidth": 1,
- "fill": "#fff",
- "style": {
- "visibility": "hidden"
- }
- }
- }
- },
- "left": {
- "position": "left",
- "attrs": {
- "circle": {
- "r": 3,
- "magnet": true,
- "stroke": "#5F95FF",
- "strokeWidth": 1,
- "fill": "#fff",
- "style": {
- "visibility": "hidden"
- }
- }
- }
- }
- },
- "items": [
- {
- "group": "top",
- "id": "d1346f43-969a-4201-af5d-d09b7ef79980"
- },
- {
- "group": "right",
- "id": "d561926a-3a24-449a-abb1-0c20bc89947e"
- },
- {
- "group": "bottom",
- "id": "0cbde5df-ef35-410e-b6c3-a6b1f5561e3f"
- },
- {
- "group": "left",
- "id": "2fceb955-f7af-41ac-ac02-5a2ea514544e"
- }
- ]
- },
- "id": "c7b4dfb0-2cc1-4ce1-839d-22b13bdf86e5",
- "zIndex": 14
- },
- {
- "shape": "edge",
- "attrs": {
- "line": {
- "stroke": "#5F95FF",
- "strokeWidth": 1,
- "targetMarker": {
- "name": "classic",
- "size": 8
- }
- }
- },
- "id": "9dfeb591-70e1-4b52-a463-3078a6fde579",
- "router": {
- "name": "manhattan"
- },
- "zIndex": 15,
- "source": {
- "cell": "762cbe4d-fd2b-4cb2-95bb-fae3cb9ef7fc",
- "port": "9bb8ec19-b1e2-432d-8735-b008da064948"
- },
- "target": {
- "cell": "c7b4dfb0-2cc1-4ce1-839d-22b13bdf86e5",
- "port": "d1346f43-969a-4201-af5d-d09b7ef79980"
- }
- },
- {
- "position": {
- "x": 420,
- "y": 616
- },
- "size": {
- "width": 80,
- "height": 42
- },
- "attrs": {
- "text": {
- "text": "结束节点"
- },
- "body": {
- "rx": 24,
- "ry": 24
- }
- },
- "visible": true,
- "shape": "flow-chart-rect",
- "ports": {
- "groups": {
- "top": {
- "position": "top",
- "attrs": {
- "circle": {
- "r": 3,
- "magnet": true,
- "stroke": "#5F95FF",
- "strokeWidth": 1,
- "fill": "#fff",
- "style": {
- "visibility": "hidden"
- }
- }
- }
- },
- "right": {
- "position": "right",
- "attrs": {
- "circle": {
- "r": 3,
- "magnet": true,
- "stroke": "#5F95FF",
- "strokeWidth": 1,
- "fill": "#fff",
- "style": {
- "visibility": "hidden"
- }
- }
- }
- },
- "bottom": {
- "position": "bottom",
- "attrs": {
- "circle": {
- "r": 3,
- "magnet": true,
- "stroke": "#5F95FF",
- "strokeWidth": 1,
- "fill": "#fff",
- "style": {
- "visibility": "hidden"
- }
- }
- }
- },
- "left": {
- "position": "left",
- "attrs": {
- "circle": {
- "r": 3,
- "magnet": true,
- "stroke": "#5F95FF",
- "strokeWidth": 1,
- "fill": "#fff",
- "style": {
- "visibility": "hidden"
- }
- }
- }
- }
- },
- "items": [
- {
- "group": "top",
- "id": "45726225-0a03-409e-8475-07da4b8533c5"
- },
- {
- "group": "right",
- "id": "06111939-bf01-48d9-9f54-6465d9d831c6"
- },
- {
- "group": "bottom",
- "id": "6541f8dc-e48b-4b8c-a105-2ab3a47f1f21"
- },
- {
- "group": "left",
- "id": "54781206-573f-4982-a21e-5fac1e0e8a60"
- }
- ]
- },
- "id": "5ac48b64-d507-4006-954b-f8fbf8016ad2",
- "zIndex": 16
- },
- {
- "shape": "edge",
- "attrs": {
- "line": {
- "stroke": "#5F95FF",
- "strokeWidth": 1,
- "targetMarker": {
- "name": "classic",
- "size": 8
- }
- }
- },
- "id": "f847f055-9073-4c8e-92c5-8124597d1e7e",
- "router": {
- "name": "manhattan"
- },
- "zIndex": 17,
- "source": {
- "cell": "c7b4dfb0-2cc1-4ce1-839d-22b13bdf86e5",
- "port": "0cbde5df-ef35-410e-b6c3-a6b1f5561e3f"
- },
- "target": {
- "cell": "5ac48b64-d507-4006-954b-f8fbf8016ad2",
- "port": "45726225-0a03-409e-8475-07da4b8533c5"
- }
- }
- ]
- }
以上都是核心代码了,希望帮到大家!