知名网站建设定制Vue使用AntV X6绘制流程图(组态呈现)

知名网站建设定制先上几个网址

知名网站建设定制上面是一些用到的网址,知名网站建设定制先说下我项目中的需求,知名网站建设定制就是要自己绘制出一个知名网站建设定制我们想要的图,知名网站建设定制组态化呈现出来,知名网站建设定制然后这个图中会有很多节点,知名网站建设定制每个节点都会有自己的状态 ,状态会实时改变,状态变化的时候,对应的节点标签颜色等都要跟随变化。

那么我们该用什么绘制呢,绘制出来还要符合我们的业务需求,说白了就是要很灵活,想改什么,想用什么节点(自定义图片等),想绑定什么属性等等,我们都可以很灵活的实现。所以就基于 X6 来实现这么个事儿。

那么整体的思路就是基于AntV X6来实现这么一个工具,然后它最终是可以将我们绘制的图(节点、路径等)转为一个JSON数据,我们把这个JSON数据转递给后端,后端会实时去改变JSON数据中的具体的某些字段的值(注意不要修改JSON数据的结构),然后再返回给前端,前端会通过AntV X6将这个JSON数据在对应的页面中再渲染出来,每当JSON数据更新就实时更新来实现这么个需求

先看下最终开发实现出的这个效果图: 

左边是我们的节点,就上图所示,上面是一些内置的基础的节点,下面是根据自己具体的业务需求自定义的一些图片节点,当然还要其他的一些很多,官网上都有对应示例文档,可以根据自己的需求、业务场景定制。中间就是我们操作的画板,上面是一些小工具,核心就是用到上面的 toJSON 可以将我们绘制的节点路径样式转为JSON,然后右边就是一些属性,当我们点击网格、点击节点、点击线在右侧都会有不同的属性,当然这些属性也都是自己加上的,还有很多属性都可以自定义加上,包括样式属性,节点的边框粗细、颜色,填充色,连接线的样式、动画、路由方式、连接方式、还有节点连接桩等等都是可以设置的

下面介绍下AntV\X6使用 

这里只能说是说一些大致的思路方向,核心代码,很多细节不可能都能说到,尤其是右边的具体的属性,我也会将完整代码上传一下

这里是demo完整代码(同样的功能,分别用、Vue3实现):

1. 安装 

  1. # npm
  2. $ npm install @antv/x6 --save
  3. $ npm install insert-css
  4. # yarn
  5. $ yarn add @antv/x6
  6. $ yarn add insert-css

2. 安装完成之后,使用 import 或 require 进行引用

import {Graph, Addon, FunctionExt, Shape} from '@antv/x6'

3. 布局 

布局其实蛮简单的了,就正常布局即可 

index.vue 

  1. <template>
  2. <div class="flow">
  3. <div class="content">
  4. <!--左侧工具栏-->
  5. <div id="stencil" />
  6. <div class="panel">
  7. <!--流程图工具栏-->
  8. <div class="toolbar">
  9. <!-- <tool-bar v-if="isReady" /> -->
  10. </div>
  11. <!--流程图画板-->
  12. <div id="container" />
  13. </div>
  14. <!--右侧工具栏-->
  15. <div class="config">
  16. <!-- <config-panel v-if="isReady" /> -->
  17. </div>
  18. </div>
  19. </div>
  20. </template>

index.css 

  1. .flow {
  2. width: 100vw;
  3. height: 100vh;
  4. }
  5. .content {
  6. width: 100%;
  7. height: 100%;
  8. display: flex;
  9. }
  10. #stencil {
  11. width: 290px;
  12. height: 100%;
  13. position: relative;
  14. border-right: 1px solid rgba(0, 0, 0, 0.08);
  15. box-sizing: border-box;
  16. }
  17. .panel {
  18. width: calc(100% - 580px);
  19. height: 100%;
  20. }
  21. .panel .toolbar {
  22. width: 100%;
  23. height: 38px;
  24. display: flex;
  25. align-items: center;
  26. background-color: #f7f9fb;
  27. border-bottom: 1px solid rgba(0, 0, 0, 0.08);
  28. }
  29. .panel #container {
  30. width: 100%;
  31. height: calc(100% - 38px);
  32. }
  33. .config {
  34. width: 290px;
  35. height: 100%;
  36. padding: 0 10px;
  37. border-left: 1px solid rgba(0, 0, 0, 0.08);
  38. box-sizing: border-box;
  39. }

4. 初始化流程图、画板 

graph/index.js  

  1. this.graph = new Graph({
  2. background: {
  3. color: '#a5a5a5' // 设置画布背景颜色
  4. },
  5. container: dom, //画板的dom容器
  6. width: width, //画板的宽度
  7. height: height, //画板的高度
  8. autoResize: true,
  9. grid: {
  10. size: 10,
  11. visible: true,
  12. type: 'doubleMesh',
  13. args: [
  14. {
  15. color: '#cccccc',
  16. thickness: 1,
  17. },
  18. {
  19. color: '#5F95FF',
  20. thickness: 1,
  21. factor: 4,
  22. },
  23. ],
  24. },
  25. scroller: {
  26. enabled: false,
  27. pageVisible: false,
  28. pageBreak: false,
  29. pannable: false,
  30. },
  31. // 开启画布缩放
  32. mousewheel: {
  33. enabled: true,
  34. modifiers: ['ctrl', 'meta'],
  35. minScale: 0.5,
  36. maxScale: 2,
  37. },
  38. connecting: {
  39. anchor: 'center',
  40. connectionPoint: 'anchor',
  41. allowBlank: true,
  42. highlight: true,
  43. snap: true, // 是否自动吸附
  44. allowMulti: true, // 是否允许在相同的起始节点和终止之间创建多条边
  45. allowNode: false, // 是否允许边链接到节点(非节点上的链接桩)
  46. allowBlank: false, // 是否允许连接到空白点
  47. allowLoop: false, // 是否允许创建循环连线,即边的起始节点和终止节点为同一节点,
  48. allowEdge: false, // 是否允许边链接到另一个边
  49. highlight: true, // 拖动边时,是否高亮显示所有可用的连接桩或节点
  50. connectionPoint: "anchor", // 指定连接点
  51. anchor: "center", // 指定被连接的节点的锚点
  52. createEdge() {
  53. // X6 的 Shape 命名空间中内置 Edge、DoubleEdge、ShadowEdge 三种边
  54. return new Shape.DoubleEdge({
  55. attrs: {
  56. // line: {
  57. // // stroke: '#5F95FF',
  58. // // strokeWidth: 4,
  59. // // targetMarker: {
  60. // // name: 'classic',
  61. // // size: 8,
  62. // // },
  63. // stroke: '#1890ff',
  64. // strokeDasharray: 5,
  65. // targetMarker: null,//block classic diamond cross async path circle circlePlus ellipse
  66. // style: {
  67. // animation: 'ant-line 30s infinite linear',
  68. // },
  69. // },
  70. line: {
  71. strokeWidth: 8,
  72. stroke: '#e54033',
  73. strokeDasharray: 5,
  74. style: {
  75. animation: 'ant-line 30s infinite linear',
  76. },
  77. targetMarker: null, // 去掉箭头
  78. },
  79. outline: {
  80. stroke: '#73d13d',
  81. strokeWidth: 15,
  82. }
  83. },
  84. router: {
  85. name: 'metro',
  86. }
  87. })
  88. },
  89. validateConnection({
  90. sourceView,
  91. targetView,
  92. sourceMagnet,
  93. targetMagnet,
  94. }) {
  95. if (sourceView === targetView) {
  96. return false
  97. }
  98. if (!sourceMagnet) {
  99. return false
  100. }
  101. if (!targetMagnet) {
  102. return false
  103. }
  104. return true
  105. },
  106. },
  107. highlighting: {
  108. magnetAvailable: {
  109. name: 'stroke',
  110. args: {
  111. padding: 4,
  112. attrs: {
  113. strokeWidth: 4,
  114. stroke: 'rgba(223,234,255)',
  115. },
  116. },
  117. },
  118. },
  119. // 开启拖拽平移(防止冲突,按下修饰键并点击鼠标才能触发画布拖拽)
  120. panning: {
  121. enabled: true,
  122. modifiers: 'shift',
  123. },
  124. resizing: true,
  125. rotating: true,
  126. selecting: {
  127. enabled: true,
  128. multiple: true,
  129. rubberband: true,
  130. movable: true,
  131. showNodeSelectionBox: true,
  132. },
  133. snapline: true,
  134. history: true,
  135. clipboard: {
  136. enabled: true,
  137. },
  138. keyboard: {
  139. enabled: true,
  140. },
  141. embedding: {
  142. enabled: true,
  143. findParent({ node }) {
  144. const bbox = node.getBBox()
  145. return this.getNodes().filter((node) => {
  146. // 只有 data.parent 为 true 的节点才是父节点
  147. const data = node.getData()
  148. if (data && data.parent) {
  149. const targetBBox = node.getBBox()
  150. return bbox.isIntersectWithRect(targetBBox)
  151. }
  152. return false
  153. })
  154. },
  155. },
  156. })

5. 初始化左侧根节点 

graph/index.js  

  1. this.stencil = new Addon.Stencil({
  2. target: this.graph, // 刚才初始化流程图画板的实例
  3. stencilGraphWidth: 280,
  4. search: { rect: true },
  5. collapsable: true,
  6. groups: [
  7. {
  8. name: 'basic',
  9. title: '基础节点',
  10. graphHeight: 180,
  11. },
  12. {
  13. name: 'custom-image',
  14. title: '系统设计图',
  15. graphHeight: 600
  16. },
  17. // {
  18. // name: 'combination',
  19. // title: '组合节点',
  20. // layoutOptions: {
  21. // columns: 1,
  22. // marginX: 60,
  23. // },
  24. // graphHeight: 260,
  25. // },
  26. // {
  27. // name: 'group',
  28. // title: '节点组',
  29. // graphHeight: 100,
  30. // layoutOptions: {
  31. // columns: 1,
  32. // marginX: 60,
  33. // },
  34. // },
  35. ],
  36. })
  37. const stencilContainer = document.querySelector('#stencil') // 左侧的节点容器
  38. stencilContainer?.appendChild(this.stencil.container) // 追加进去

6. 初始化具体每个根节点下不同类型节点 

 ① 连接桩配置

ports.js 

  1. export const basicPorts = {
  2. groups: {
  3. top: {
  4. position: 'top',
  5. attrs: {
  6. circle: {
  7. r: 4,
  8. magnet: true,
  9. stroke: '#5F95FF',
  10. strokeWidth: 1,
  11. fill: '#fff',
  12. style: {
  13. visibility: 'hidden',
  14. },
  15. },
  16. },
  17. },
  18. right: {
  19. position: 'right',
  20. attrs: {
  21. circle: {
  22. r: 4,
  23. magnet: true,
  24. stroke: '#5F95FF',
  25. strokeWidth: 1,
  26. fill: '#fff',
  27. style: {
  28. visibility: 'hidden',
  29. },
  30. },
  31. },
  32. },
  33. bottom: {
  34. position: 'bottom',
  35. attrs: {
  36. circle: {
  37. r: 4,
  38. magnet: true,
  39. stroke: '#5F95FF',
  40. strokeWidth: 1,
  41. fill: '#fff',
  42. style: {
  43. visibility: 'hidden',
  44. },
  45. },
  46. },
  47. },
  48. left: {
  49. position: 'left',
  50. attrs: {
  51. circle: {
  52. r: 4,
  53. magnet: true,
  54. stroke: '#5F95FF',
  55. strokeWidth: 1,
  56. fill: '#fff',
  57. style: {
  58. visibility: 'hidden',
  59. },
  60. },
  61. },
  62. },
  63. },
  64. items: [
  65. {
  66. group: 'top',
  67. },
  68. {
  69. group: 'right',
  70. },
  71. {
  72. group: 'bottom',
  73. },
  74. {
  75. group: 'left',
  76. }
  77. ]
  78. }
  79. export const customPorts = {
  80. groups: {
  81. top: {
  82. position: {
  83. name: 'absolute',
  84. }
  85. },
  86. right: {
  87. position: {
  88. name: 'absolute',
  89. }
  90. },
  91. bottom: {
  92. position: {
  93. name: 'absolute',
  94. }
  95. },
  96. left: {
  97. position: {
  98. name: 'absolute',
  99. }
  100. }
  101. },
  102. items: [
  103. {
  104. id: 'port1',
  105. group: 'top',
  106. // 通过 args 指定绝对位置
  107. args: {
  108. x: 25,
  109. y: -5,
  110. },
  111. attrs: {
  112. circle: {
  113. r: 4,
  114. magnet: true,
  115. stroke: '#5F95FF',
  116. strokeWidth: 1,
  117. fill: '#fff',
  118. style: {
  119. visibility: 'hidden',
  120. },
  121. },
  122. },
  123. },
  124. {
  125. id: 'port2',
  126. group: 'right',
  127. args: {
  128. x: 55,
  129. y: 25,
  130. },
  131. attrs: {
  132. circle: {
  133. r: 4,
  134. magnet: true,
  135. stroke: '#5F95FF',
  136. strokeWidth: 1,
  137. fill: '#fff',
  138. style: {
  139. visibility: 'hidden',
  140. }
  141. },
  142. }
  143. },
  144. {
  145. id: 'port3',
  146. group: 'bottom',
  147. args: {
  148. x: 25,
  149. y: 55,
  150. },
  151. attrs: {
  152. circle: {
  153. r: 4,
  154. magnet: true,
  155. stroke: '#5F95FF',
  156. strokeWidth: 1,
  157. fill: '#fff',
  158. style: {
  159. visibility: 'hidden',
  160. }
  161. }
  162. }
  163. },
  164. {
  165. id: 'port4',
  166. group: 'left',
  167. args: {
  168. x: -5,
  169. y: 25,
  170. },
  171. attrs: {
  172. circle: {
  173. r: 4,
  174. magnet: true,
  175. stroke: '#5F95FF',
  176. strokeWidth: 1,
  177. fill: '#fff',
  178. style: {
  179. visibility: 'hidden',
  180. }
  181. }
  182. }
  183. }
  184. ]
  185. }

② 注册节点 

 先注册节点,一会儿再createNode,最后再load

shape.js 

  1. import { Graph, Dom, Node } from '@antv/x6'
  2. import { basicPorts, customPorts } from './ports' // 连接桩配置
  3. // 基础节点
  4. export const FlowChartRect = Graph.registerNode('flow-chart-rect', {
  5. inherit: 'rect',
  6. width: 80,
  7. height: 42,
  8. attrs: {
  9. body: {
  10. stroke: '#5F95FF',
  11. strokeWidth: 1,
  12. fill: '#ffffff',
  13. },
  14. fo: {
  15. refWidth: '100%',
  16. refHeight: '100%',
  17. },
  18. foBody: {
  19. xmlns: Dom.ns.xhtml,
  20. style: {
  21. width: '100%',
  22. height: '100%',
  23. display: 'flex',
  24. justifyContent: 'center',
  25. alignItems: 'center',
  26. },
  27. },
  28. 'edit-text': {
  29. contenteditable: 'true',
  30. class: 'x6-edit-text',
  31. style: {
  32. width: '100%',
  33. textAlign: 'center',
  34. fontSize: 12,
  35. color: 'rgba(0,0,0,0.85)',
  36. },
  37. },
  38. text: {
  39. fontSize: 12,
  40. fill: '#080808',
  41. },
  42. },
  43. markup: [
  44. {
  45. tagName: 'rect',
  46. selector: 'body',
  47. },
  48. {
  49. tagName: 'text',
  50. selector: 'text',
  51. },
  52. {
  53. tagName: 'foreignObject',
  54. selector: 'fo',
  55. children: [
  56. {
  57. ns: Dom.ns.xhtml,
  58. tagName: 'body',
  59. selector: 'foBody',
  60. children: [
  61. {
  62. tagName: 'div',
  63. selector: 'edit-text',
  64. },
  65. ],
  66. },
  67. ],
  68. },
  69. ],
  70. ports: { ...basicPorts },
  71. })
  72. // 组合节点
  73. export const FlowChartImageRect = Graph.registerNode('flow-chart-image-rect', {
  74. inherit: 'rect',
  75. width: 200,
  76. height: 60,
  77. attrs: {
  78. body: {
  79. stroke: '#5F95FF',
  80. strokeWidth: 1,
  81. fill: 'rgba(95,149,255,0.05)',
  82. },
  83. image: {
  84. 'xlink:href':
  85. 'https://gw.alipayobjects.com/zos/antfincdn/FLrTNDvlna/antv.png',
  86. width: 16,
  87. height: 16,
  88. x: 12,
  89. y: 12,
  90. },
  91. title: {
  92. text: 'Node',
  93. refX: 40,
  94. refY: 14,
  95. fill: 'rgba(0,0,0,0.85)',
  96. fontSize: 12,
  97. 'text-anchor': 'start',
  98. },
  99. text: {
  100. text: 'this is content text',
  101. refX: 40,
  102. refY: 38,
  103. fontSize: 12,
  104. fill: 'rgba(0,0,0,0.6)',
  105. 'text-anchor': 'start',
  106. },
  107. },
  108. markup: [
  109. {
  110. tagName: 'rect',
  111. selector: 'body',
  112. },
  113. {
  114. tagName: 'image',
  115. selector: 'image',
  116. },
  117. {
  118. tagName: 'text',
  119. selector: 'title',
  120. },
  121. {
  122. tagName: 'text',
  123. selector: 'text',
  124. },
  125. ],
  126. ports: { ...basicPorts },
  127. })
  128. export const FlowChartTitleRect = Graph.registerNode('flow-chart-title-rect', {
  129. inherit: 'rect',
  130. width: 200,
  131. height: 68,
  132. attrs: {
  133. body: {
  134. stroke: '#5F95FF',
  135. strokeWidth: 1,
  136. fill: 'rgba(95,149,255,0.05)',
  137. },
  138. head: {
  139. refWidth: '100%',
  140. stroke: 'transparent',
  141. height: 28,
  142. fill: 'rgb(95,149,255)',
  143. },
  144. image: {
  145. 'xlink:href':
  146. 'https://gw.alipayobjects.com/zos/antfincdn/FLrTNDvlna/antv.png',
  147. height: 16,
  148. x: 6,
  149. y: 6,
  150. },
  151. title: {
  152. text: 'Node',
  153. refX: 30,
  154. refY: 9,
  155. fill: '#ffffff',
  156. fontSize: 12,
  157. 'text-anchor': 'start',
  158. },
  159. text: {
  160. text: 'this is content text',
  161. refX: 8,
  162. refY: 45,
  163. fontSize: 12,
  164. fill: 'rgba(0,0,0,0.6)',
  165. 'text-anchor': 'start',
  166. },
  167. },
  168. markup: [
  169. {
  170. tagName: 'rect',
  171. selector: 'body',
  172. },
  173. {
  174. tagName: 'rect',
  175. selector: 'head',
  176. },
  177. {
  178. tagName: 'image',
  179. selector: 'image',
  180. },
  181. {
  182. tagName: 'text',
  183. selector: 'title',
  184. },
  185. {
  186. tagName: 'text',
  187. selector: 'text',
  188. },
  189. ],
  190. ports: { ...basicPorts },
  191. })
  192. export const FlowChartAnimateText = Graph.registerNode('flow-chart-animate-text', {
  193. inherit: 'rect',
  194. width: 200,
  195. height: 60,
  196. attrs: {
  197. body: {
  198. stroke: '#5F95FF',
  199. strokeWidth: 1,
  200. fill: 'rgba(95,149,255,0.05)',
  201. },
  202. text1: {
  203. class: 'animate-text1',
  204. text: 'AntV X6',
  205. fontSize: 32,
  206. },
  207. text2: {
  208. class: 'animate-text2',
  209. text: 'AntV X6',
  210. fontSize: 32,
  211. },
  212. },
  213. markup: [
  214. {
  215. tagName: 'rect',
  216. selector: 'body',
  217. },
  218. {
  219. tagName: 'text',
  220. selector: 'text1',
  221. },
  222. {
  223. tagName: 'text',
  224. selector: 'text2',
  225. },
  226. ],
  227. })
  228. // 自定义 系统设计图
  229. export const FlowChartImageRectCustom = Graph.registerNode('flow-chart-image-rect-custom', {
  230. inherit: 'rect',
  231. width: 80,
  232. height: 80,
  233. markup: [
  234. {
  235. tagName: 'rect',
  236. selector: 'body',
  237. },
  238. {
  239. tagName: 'image',
  240. },
  241. {
  242. tagName: 'text',
  243. selector: 'label',
  244. }
  245. ],
  246. attrs: {
  247. body: {
  248. // 节点线的颜色
  249. stroke: 'transparent',
  250. // 背景填充色
  251. fill: 'transparent',
  252. },
  253. // 自定义图片
  254. image: {
  255. width: 60,
  256. height: 60,
  257. refX: 0,
  258. refY: 0,
  259. },
  260. label: {
  261. refX: 3,
  262. refY: 2,
  263. textAnchor: 'left',
  264. textVerticalAnchor: 'top',
  265. fontSize: 12,
  266. fill: 'black',
  267. },
  268. 'edit-text': {
  269. contenteditable: 'true',
  270. class: 'x6-edit-text',
  271. style: {
  272. width: '100%',
  273. textAlign: 'center',
  274. fontSize: 12,
  275. color: 'rgba(0,0,0,0.85)'
  276. },
  277. },
  278. text: {
  279. fontSize: 12,
  280. fill: '#080808',
  281. },
  282. },
  283. ports: { ...customPorts },
  284. })
  285. // 节点组
  286. export class NodeGroup extends Node {
  287. collapsed = true
  288. postprocess() {
  289. this.toggleCollapse(true)
  290. }
  291. isCollapsed() {
  292. return this.collapsed
  293. }
  294. toggleCollapse(collapsed) {
  295. const target = collapsed == null ? !this.collapsed : collapsed
  296. if (target) {
  297. this.attr('buttonSign', { d: 'M 1 5 9 5 M 5 1 5 9' })
  298. this.resize(200, 40)
  299. } else {
  300. this.attr('buttonSign', { d: 'M 2 5 8 5' })
  301. this.resize(240, 240)
  302. }
  303. this.collapsed = target
  304. }
  305. }
  306. NodeGroup.config({
  307. shape: 'rect',
  308. markup: [
  309. {
  310. tagName: 'rect',
  311. selector: 'body',
  312. },
  313. {
  314. tagName: 'image',
  315. selector: 'image',
  316. },
  317. {
  318. tagName: 'text',
  319. selector: 'text',
  320. },
  321. {
  322. tagName: 'g',
  323. selector: 'buttonGroup',
  324. children: [
  325. {
  326. tagName: 'rect',
  327. selector: 'button',
  328. attrs: {
  329. 'pointer-events': 'visiblePainted',
  330. },
  331. },
  332. {
  333. tagName: 'path',
  334. selector: 'buttonSign',
  335. attrs: {
  336. fill: 'none',
  337. 'pointer-events': 'none',
  338. },
  339. },
  340. ],
  341. },
  342. ],
  343. attrs: {
  344. body: {
  345. refWidth: '100%',
  346. refHeight: '100%',
  347. strokeWidth: 1,
  348. fill: 'rgba(95,149,255,0.05)',
  349. stroke: '#5F95FF',
  350. },
  351. image: {
  352. 'xlink:href':
  353. 'https://gw.alipayobjects.com/mdn/rms_0b51a4/afts/img/A*X4e0TrDsEiIAAAAAAAAAAAAAARQnAQ',
  354. width: 16,
  355. height: 16,
  356. x: 8,
  357. y: 12,
  358. },
  359. text: {
  360. fontSize: 12,
  361. fill: 'rgba(0,0,0,0.85)',
  362. refX: 30,
  363. refY: 15,
  364. },
  365. buttonGroup: {
  366. refX: '100%',
  367. refX2: -25,
  368. refY: 13,
  369. },
  370. button: {
  371. height: 14,
  372. width: 16,
  373. rx: 2,
  374. ry: 2,
  375. fill: '#f5f5f5',
  376. stroke: '#ccc',
  377. cursor: 'pointer',
  378. event: 'node:collapse',
  379. },
  380. buttonSign: {
  381. refX: 3,
  382. refY: 2,
  383. stroke: '#808080',
  384. },
  385. },
  386. })
  387. Graph.registerNode('groupNode', NodeGroup)

③ createNode、load 

graph/index.js  

  1. const { graph } = this
  2. // 基础节点
  3. const r1 = graph.createNode({
  4. shape: 'flow-chart-rect',
  5. attrs: {
  6. body: {
  7. rx: 24,
  8. ry: 24,
  9. },
  10. text: {
  11. text: '起始节点',
  12. },
  13. },
  14. })
  15. const r2 = graph.createNode({
  16. shape: 'flow-chart-rect',
  17. attrs: {
  18. text: {
  19. text: '流程节点',
  20. },
  21. },
  22. })
  23. const r3 = graph.createNode({
  24. shape: 'flow-chart-rect',
  25. width: 52,
  26. height: 52,
  27. angle: 45,
  28. attrs: {
  29. 'edit-text': {
  30. style: {
  31. transform: 'rotate(-45deg)',
  32. },
  33. },
  34. text: {
  35. text: '判断节点',
  36. transform: 'rotate(-45deg)',
  37. },
  38. },
  39. ports: {
  40. groups: {
  41. top: {
  42. position: {
  43. name: 'top',
  44. args: {
  45. dx: -26,
  46. },
  47. },
  48. },
  49. right: {
  50. position: {
  51. name: 'right',
  52. args: {
  53. dy: -26,
  54. },
  55. },
  56. },
  57. bottom: {
  58. position: {
  59. name: 'bottom',
  60. args: {
  61. dx: 26,
  62. },
  63. },
  64. },
  65. left: {
  66. position: {
  67. name: 'left',
  68. args: {
  69. dy: 26,
  70. },
  71. },
  72. },
  73. },
  74. },
  75. })
  76. const r4 = graph.createNode({
  77. shape: 'flow-chart-rect',
  78. width: 70,
  79. height: 70,
  80. attrs: {
  81. body: {
  82. rx: 35,
  83. ry: 35,
  84. },
  85. text: {
  86. text: '链接节点',
  87. },
  88. },
  89. })
  90. // 组合节点
  91. const c1 = graph.createNode({
  92. shape: 'flow-chart-image-rect',
  93. })
  94. const c2 = graph.createNode({
  95. shape: 'flow-chart-title-rect',
  96. })
  97. const c3 = graph.createNode({
  98. shape: 'flow-chart-animate-text',
  99. })
  100. // 节点组
  101. const g1 = graph.createNode({
  102. shape: 'groupNode',
  103. attrs: {
  104. text: {
  105. text: 'Group Name',
  106. },
  107. },
  108. data: {
  109. parent: true,
  110. },
  111. })
  112. // 系统设计图
  113. const imgs = [
  114. {
  115. image: require('../../../assets/ldb.png')
  116. },
  117. {
  118. image: require('../../../assets/冷冻泵.png')
  119. },
  120. {
  121. image: require('../../../assets/冷却泵.png')
  122. },
  123. {
  124. image: require('../../../assets/wft1.png')
  125. },
  126. {
  127. image: require('../../../assets/wft2.png')
  128. },
  129. {
  130. image: require('../../../assets/wft3.png')
  131. },
  132. {
  133. image: require('../../../assets/wft4.png')
  134. },
  135. {
  136. image: require('../../../assets/wft5.png')
  137. }
  138. ]
  139. const imgNodes = imgs.map(item => {
  140. return graph.createNode({
  141. // shape: 'flow-chart-image-rect-custom',
  142. // attrs: {
  143. // image: {
  144. // 'xlink:href': item.image,
  145. // }
  146. // }
  147. shape: 'image', //可选值:Rect Circle Ellipse Polygon Polyline Path Image HTML TextBlock BorderedImage EmbeddedImage InscribedImage Cylinder
  148. imageUrl: item.image,
  149. attrs: {
  150. image: {
  151. // fill: 'yellow',
  152. },
  153. },
  154. width: 52,
  155. height: 52,
  156. ports: { ...customPorts }
  157. })
  158. })
  159. this.stencil.load([r1, r2, r3, r4], 'basic')
  160. this.stencil.load(imgNodes, 'custom-image')
  161. // this.stencil.load([c1, c2, c3], 'combination')
  162. // this.stencil.load([g1], 'group')

7. 根据json数据渲染 

graph/index.js  

this.graph.fromJSON(jsonData)

8. 鼠标的一些事件,连接桩的显示时机等 

graph/index.js 

  1. // 连接桩显示时机
  2. showPorts(ports, show) {
  3. for (let i = 0, len = ports.length; i < len; i = i + 1) {
  4. ports[i].style.visibility = show ? 'visible' : 'hidden'
  5. }
  6. }
  7. initEvent() {
  8. const { graph } = this
  9. const container = document.getElementById('container')
  10. graph.on('node:contextmenu', ({ cell, view }) => {
  11. console.log(view.container)
  12. const oldText = cell.attr('text/text')
  13. cell.attr('text/style/display', 'none')
  14. const elem = view.container.querySelector('.x6-edit-text')
  15. if (elem) {
  16. elem.innerText = oldText
  17. elem.focus()
  18. }
  19. const onBlur = () => {
  20. cell.attr('text/text', elem.innerText)
  21. }
  22. if(elem){
  23. elem.addEventListener('blur', () => {
  24. onBlur()
  25. elem.removeEventListener('blur', onBlur)
  26. })
  27. }
  28. })
  29. // 鼠标移入 显示连接桩
  30. graph.on('node:mouseenter', FunctionExt.debounce(() => {
  31. const ports = container.querySelectorAll('.x6-port-body')
  32. this.showPorts(ports, true)
  33. }), 500,)
  34. // 鼠标移出 隐藏连接桩
  35. graph.on('node:mouseleave', () => {
  36. const ports = container.querySelectorAll('.x6-port-body')
  37. this.showPorts(ports, false)
  38. })
  39. graph.on('node:collapse', ({ node, e }) => {
  40. e.stopPropagation()
  41. node.toggleCollapse()
  42. const collapsed = node.isCollapsed()
  43. const cells = node.getDescendants()
  44. cells.forEach(n => {
  45. if (collapsed) {
  46. n.hide()
  47. } else {
  48. n.show()
  49. }
  50. })
  51. })
  52. // backspace
  53. graph.bindKey('delete', () => {
  54. const cells = graph.getSelectedCells()
  55. if (cells.length) {
  56. graph.removeCells(cells)
  57. }
  58. })
  59. }

9. 最后再给大家上个导出的JSON数据 

  1. export default {
  2. "cells": [
  3. {
  4. "position": {
  5. "x": 420,
  6. "y": 160
  7. },
  8. "size": {
  9. "width": 80,
  10. "height": 42
  11. },
  12. "attrs": {
  13. "text": {
  14. "text": "起始节点"
  15. },
  16. "body": {
  17. "rx": 24,
  18. "ry": 24
  19. }
  20. },
  21. "visible": true,
  22. "shape": "flow-chart-rect",
  23. "ports": {
  24. "groups": {
  25. "top": {
  26. "position": "top",
  27. "attrs": {
  28. "circle": {
  29. "r": 3,
  30. "magnet": true,
  31. "stroke": "#5F95FF",
  32. "strokeWidth": 1,
  33. "fill": "#fff",
  34. "style": {
  35. "visibility": "hidden"
  36. }
  37. }
  38. }
  39. },
  40. "right": {
  41. "position": "right",
  42. "attrs": {
  43. "circle": {
  44. "r": 3,
  45. "magnet": true,
  46. "stroke": "#5F95FF",
  47. "strokeWidth": 1,
  48. "fill": "#fff",
  49. "style": {
  50. "visibility": "hidden"
  51. }
  52. }
  53. }
  54. },
  55. "bottom": {
  56. "position": "bottom",
  57. "attrs": {
  58. "circle": {
  59. "r": 3,
  60. "magnet": true,
  61. "stroke": "#5F95FF",
  62. "strokeWidth": 1,
  63. "fill": "#fff",
  64. "style": {
  65. "visibility": "hidden"
  66. }
  67. }
  68. }
  69. },
  70. "left": {
  71. "position": "left",
  72. "attrs": {
  73. "circle": {
  74. "r": 3,
  75. "magnet": true,
  76. "stroke": "#5F95FF",
  77. "strokeWidth": 1,
  78. "fill": "#fff",
  79. "style": {
  80. "visibility": "hidden"
  81. }
  82. }
  83. }
  84. }
  85. },
  86. "items": [
  87. {
  88. "group": "top",
  89. "id": "45726225-0a03-409e-8475-07da4b8533c5"
  90. },
  91. {
  92. "group": "right",
  93. "id": "06111939-bf01-48d9-9f54-6465d9d831c6"
  94. },
  95. {
  96. "group": "bottom",
  97. "id": "6541f8dc-e48b-4b8c-a105-2ab3a47f1f21"
  98. },
  99. {
  100. "group": "left",
  101. "id": "54781206-573f-4982-a21e-5fac1e0e8a60"
  102. }
  103. ]
  104. },
  105. "id": "8650a303-3568-4ff2-9fac-2fd3ae7e6f2a",
  106. "zIndex": 1
  107. },
  108. {
  109. "position": {
  110. "x": 420,
  111. "y": 250
  112. },
  113. "size": {
  114. "width": 80,
  115. "height": 42
  116. },
  117. "attrs": {
  118. "text": {
  119. "text": "流程节点"
  120. }
  121. },
  122. "visible": true,
  123. "shape": "flow-chart-rect",
  124. "ports": {
  125. "groups": {
  126. "top": {
  127. "position": "top",
  128. "attrs": {
  129. "circle": {
  130. "r": 3,
  131. "magnet": true,
  132. "stroke": "#5F95FF",
  133. "strokeWidth": 1,
  134. "fill": "#fff",
  135. "style": {
  136. "visibility": "hidden"
  137. }
  138. }
  139. }
  140. },
  141. "right": {
  142. "position": "right",
  143. "attrs": {
  144. "circle": {
  145. "r": 3,
  146. "magnet": true,
  147. "stroke": "#5F95FF",
  148. "strokeWidth": 1,
  149. "fill": "#fff",
  150. "style": {
  151. "visibility": "hidden"
  152. }
  153. }
  154. }
  155. },
  156. "bottom": {
  157. "position": "bottom",
  158. "attrs": {
  159. "circle": {
  160. "r": 3,
  161. "magnet": true,
  162. "stroke": "#5F95FF",
  163. "strokeWidth": 1,
  164. "fill": "#fff",
  165. "style": {
  166. "visibility": "hidden"
  167. }
  168. }
  169. }
  170. },
  171. "left": {
  172. "position": "left",
  173. "attrs": {
  174. "circle": {
  175. "r": 3,
  176. "magnet": true,
  177. "stroke": "#5F95FF",
  178. "strokeWidth": 1,
  179. "fill": "#fff",
  180. "style": {
  181. "visibility": "hidden"
  182. }
  183. }
  184. }
  185. }
  186. },
  187. "items": [
  188. {
  189. "group": "top",
  190. "id": "d1346f43-969a-4201-af5d-d09b7ef79980"
  191. },
  192. {
  193. "group": "right",
  194. "id": "d561926a-3a24-449a-abb1-0c20bc89947e"
  195. },
  196. {
  197. "group": "bottom",
  198. "id": "0cbde5df-ef35-410e-b6c3-a6b1f5561e3f"
  199. },
  200. {
  201. "group": "left",
  202. "id": "2fceb955-f7af-41ac-ac02-5a2ea514544e"
  203. }
  204. ]
  205. },
  206. "id": "7b6fd715-83e6-4053-8c2b-346e6a857bf3",
  207. "zIndex": 2
  208. },
  209. {
  210. "shape": "edge",
  211. "attrs": {
  212. "line": {
  213. "stroke": "#5F95FF",
  214. "strokeWidth": 1,
  215. "targetMarker": {
  216. "name": "classic",
  217. "size": 8
  218. },
  219. "strokeDasharray": 0
  220. }
  221. },
  222. "id": "00f3c401-8bad-46b9-b692-232aa011d4c5",
  223. "router": {
  224. "name": "manhattan"
  225. },
  226. "zIndex": 3,
  227. "source": {
  228. "cell": "8650a303-3568-4ff2-9fac-2fd3ae7e6f2a",
  229. "port": "6541f8dc-e48b-4b8c-a105-2ab3a47f1f21"
  230. },
  231. "target": {
  232. "cell": "7b6fd715-83e6-4053-8c2b-346e6a857bf3",
  233. "port": "d1346f43-969a-4201-af5d-d09b7ef79980"
  234. }
  235. },
  236. {
  237. "position": {
  238. "x": 425,
  239. "y": 371
  240. },
  241. "size": {
  242. "width": 70,
  243. "height": 70
  244. },
  245. "attrs": {
  246. "text": {
  247. "text": "链接节点"
  248. },
  249. "body": {
  250. "rx": 35,
  251. "ry": 35
  252. }
  253. },
  254. "visible": true,
  255. "shape": "flow-chart-rect",
  256. "ports": {
  257. "groups": {
  258. "top": {
  259. "position": "top",
  260. "attrs": {
  261. "circle": {
  262. "r": 3,
  263. "magnet": true,
  264. "stroke": "#5F95FF",
  265. "strokeWidth": 1,
  266. "fill": "#fff",
  267. "style": {
  268. "visibility": "hidden"
  269. }
  270. }
  271. }
  272. },
  273. "right": {
  274. "position": "right",
  275. "attrs": {
  276. "circle": {
  277. "r": 3,
  278. "magnet": true,
  279. "stroke": "#5F95FF",
  280. "strokeWidth": 1,
  281. "fill": "#fff",
  282. "style": {
  283. "visibility": "hidden"
  284. }
  285. }
  286. }
  287. },
  288. "bottom": {
  289. "position": "bottom",
  290. "attrs": {
  291. "circle": {
  292. "r": 3,
  293. "magnet": true,
  294. "stroke": "#5F95FF",
  295. "strokeWidth": 1,
  296. "fill": "#fff",
  297. "style": {
  298. "visibility": "hidden"
  299. }
  300. }
  301. }
  302. },
  303. "left": {
  304. "position": "left",
  305. "attrs": {
  306. "circle": {
  307. "r": 3,
  308. "magnet": true,
  309. "stroke": "#5F95FF",
  310. "strokeWidth": 1,
  311. "fill": "#fff",
  312. "style": {
  313. "visibility": "hidden"
  314. }
  315. }
  316. }
  317. }
  318. },
  319. "items": [
  320. {
  321. "group": "top",
  322. "id": "089ce61a-4b17-4ed8-9c3f-5b905f484425"
  323. },
  324. {
  325. "group": "right",
  326. "id": "fd4b8c95-d1eb-41ea-b3e1-15135814b292"
  327. },
  328. {
  329. "group": "bottom",
  330. "id": "9bb8ec19-b1e2-432d-8735-b008da064948"
  331. },
  332. {
  333. "group": "left",
  334. "id": "fbf8759a-1059-47bb-b556-f0a4477e48d3"
  335. }
  336. ]
  337. },
  338. "id": "762cbe4d-fd2b-4cb2-95bb-fae3cb9ef7fc",
  339. "zIndex": 4
  340. },
  341. {
  342. "angle": 45,
  343. "position": {
  344. "x": 310,
  345. "y": 380
  346. },
  347. "size": {
  348. "width": 52,
  349. "height": 52
  350. },
  351. "attrs": {
  352. "text": {
  353. "text": "判断节点",
  354. "transform": "rotate(-45deg)"
  355. },
  356. "edit-text": {
  357. "style": {
  358. "transform": "rotate(-45deg)"
  359. }
  360. }
  361. },
  362. "visible": true,
  363. "shape": "flow-chart-rect",
  364. "ports": {
  365. "groups": {
  366. "top": {
  367. "position": {
  368. "name": "top",
  369. "args": {
  370. "dx": -26
  371. }
  372. },
  373. "attrs": {
  374. "circle": {
  375. "r": 3,
  376. "magnet": true,
  377. "stroke": "#5F95FF",
  378. "strokeWidth": 1,
  379. "fill": "#fff",
  380. "style": {
  381. "visibility": "hidden"
  382. }
  383. }
  384. }
  385. },
  386. "right": {
  387. "position": {
  388. "name": "right",
  389. "args": {
  390. "dy": -26
  391. }
  392. },
  393. "attrs": {
  394. "circle": {
  395. "r": 3,
  396. "magnet": true,
  397. "stroke": "#5F95FF",
  398. "strokeWidth": 1,
  399. "fill": "#fff",
  400. "style": {
  401. "visibility": "hidden"
  402. }
  403. }
  404. }
  405. },
  406. "bottom": {
  407. "position": {
  408. "name": "bottom",
  409. "args": {
  410. "dx": 26
  411. }
  412. },
  413. "attrs": {
  414. "circle": {
  415. "r": 3,
  416. "magnet": true,
  417. "stroke": "#5F95FF",
  418. "strokeWidth": 1,
  419. "fill": "#fff",
  420. "style": {
  421. "visibility": "hidden"
  422. }
  423. }
  424. }
  425. },
  426. "left": {
  427. "position": {
  428. "name": "left",
  429. "args": {
  430. "dy": 26
  431. }
  432. },
  433. "attrs": {
  434. "circle": {
  435. "r": 3,
  436. "magnet": true,
  437. "stroke": "#5F95FF",
  438. "strokeWidth": 1,
  439. "fill": "#fff",
  440. "style": {
  441. "visibility": "hidden"
  442. }
  443. }
  444. }
  445. }
  446. },
  447. "items": [
  448. {
  449. "group": "top",
  450. "id": "c133349e-4a4a-4d7d-9b38-26c36b3e68c5"
  451. },
  452. {
  453. "group": "right",
  454. "id": "af190d15-b0f1-4f92-85bc-e0c4df83e2a7"
  455. },
  456. {
  457. "group": "bottom",
  458. "id": "c51a4f3b-759b-47ed-9d80-fa4f6c114e64"
  459. },
  460. {
  461. "group": "left",
  462. "id": "c241a7e4-12d3-4dde-9694-0f0e5f7b9a91"
  463. }
  464. ]
  465. },
  466. "id": "ef3865af-8a91-4164-8466-3f6b4315070f",
  467. "zIndex": 5
  468. },
  469. {
  470. "shape": "edge",
  471. "attrs": {
  472. "line": {
  473. "stroke": "#5F95FF",
  474. "strokeWidth": 1,
  475. "targetMarker": {
  476. "name": "classic",
  477. "size": 8
  478. }
  479. }
  480. },
  481. "id": "9031a1ee-8deb-4b1e-90e6-96d40d3a8515",
  482. "router": {
  483. "name": "manhattan"
  484. },
  485. "zIndex": 7,
  486. "source": {
  487. "cell": "ef3865af-8a91-4164-8466-3f6b4315070f",
  488. "port": "af190d15-b0f1-4f92-85bc-e0c4df83e2a7"
  489. },
  490. "target": {
  491. "cell": "762cbe4d-fd2b-4cb2-95bb-fae3cb9ef7fc",
  492. "port": "fbf8759a-1059-47bb-b556-f0a4477e48d3"
  493. }
  494. },
  495. {
  496. "angle": 45,
  497. "position": {
  498. "x": 566,
  499. "y": 380
  500. },
  501. "size": {
  502. "width": 52,
  503. "height": 52
  504. },
  505. "attrs": {
  506. "text": {
  507. "text": "判断节点",
  508. "transform": "rotate(-45deg)"
  509. },
  510. "edit-text": {
  511. "style": {
  512. "transform": "rotate(-45deg)"
  513. }
  514. }
  515. },
  516. "visible": true,
  517. "shape": "flow-chart-rect",
  518. "ports": {
  519. "groups": {
  520. "top": {
  521. "position": {
  522. "name": "top",
  523. "args": {
  524. "dx": -26
  525. }
  526. },
  527. "attrs": {
  528. "circle": {
  529. "r": 3,
  530. "magnet": true,
  531. "stroke": "#5F95FF",
  532. "strokeWidth": 1,
  533. "fill": "#fff",
  534. "style": {
  535. "visibility": "hidden"
  536. }
  537. }
  538. }
  539. },
  540. "right": {
  541. "position": {
  542. "name": "right",
  543. "args": {
  544. "dy": -26
  545. }
  546. },
  547. "attrs": {
  548. "circle": {
  549. "r": 3,
  550. "magnet": true,
  551. "stroke": "#5F95FF",
  552. "strokeWidth": 1,
  553. "fill": "#fff",
  554. "style": {
  555. "visibility": "hidden"
  556. }
  557. }
  558. }
  559. },
  560. "bottom": {
  561. "position": {
  562. "name": "bottom",
  563. "args": {
  564. "dx": 26
  565. }
  566. },
  567. "attrs": {
  568. "circle": {
  569. "r": 3,
  570. "magnet": true,
  571. "stroke": "#5F95FF",
  572. "strokeWidth": 1,
  573. "fill": "#fff",
  574. "style": {
  575. "visibility": "hidden"
  576. }
  577. }
  578. }
  579. },
  580. "left": {
  581. "position": {
  582. "name": "left",
  583. "args": {
  584. "dy": 26
  585. }
  586. },
  587. "attrs": {
  588. "circle": {
  589. "r": 3,
  590. "magnet": true,
  591. "stroke": "#5F95FF",
  592. "strokeWidth": 1,
  593. "fill": "#fff",
  594. "style": {
  595. "visibility": "hidden"
  596. }
  597. }
  598. }
  599. }
  600. },
  601. "items": [
  602. {
  603. "group": "top",
  604. "id": "c133349e-4a4a-4d7d-9b38-26c36b3e68c5"
  605. },
  606. {
  607. "group": "right",
  608. "id": "af190d15-b0f1-4f92-85bc-e0c4df83e2a7"
  609. },
  610. {
  611. "group": "bottom",
  612. "id": "c51a4f3b-759b-47ed-9d80-fa4f6c114e64"
  613. },
  614. {
  615. "group": "left",
  616. "id": "c241a7e4-12d3-4dde-9694-0f0e5f7b9a91"
  617. }
  618. ]
  619. },
  620. "id": "9be960d0-fb75-49b1-8131-abc05b5991bd",
  621. "zIndex": 9
  622. },
  623. {
  624. "shape": "edge",
  625. "attrs": {
  626. "line": {
  627. "stroke": "#5F95FF",
  628. "strokeWidth": 1,
  629. "targetMarker": {
  630. "name": "classic",
  631. "size": 8
  632. }
  633. }
  634. },
  635. "id": "8af8b072-dfb9-458a-b15e-dd5d4c1863bd",
  636. "router": {
  637. "name": "manhattan"
  638. },
  639. "zIndex": 10,
  640. "source": {
  641. "cell": "7b6fd715-83e6-4053-8c2b-346e6a857bf3",
  642. "port": "d561926a-3a24-449a-abb1-0c20bc89947e"
  643. },
  644. "target": {
  645. "cell": "9be960d0-fb75-49b1-8131-abc05b5991bd",
  646. "port": "c133349e-4a4a-4d7d-9b38-26c36b3e68c5"
  647. }
  648. },
  649. {
  650. "shape": "edge",
  651. "attrs": {
  652. "line": {
  653. "stroke": "#5F95FF",
  654. "strokeWidth": 1,
  655. "targetMarker": {
  656. "name": "classic",
  657. "size": 8
  658. }
  659. }
  660. },
  661. "id": "58874c23-da0b-46e6-9124-5154e8570bfd",
  662. "router": {
  663. "name": "manhattan"
  664. },
  665. "zIndex": 11,
  666. "source": {
  667. "cell": "9be960d0-fb75-49b1-8131-abc05b5991bd",
  668. "port": "c241a7e4-12d3-4dde-9694-0f0e5f7b9a91"
  669. },
  670. "target": {
  671. "cell": "762cbe4d-fd2b-4cb2-95bb-fae3cb9ef7fc",
  672. "port": "fd4b8c95-d1eb-41ea-b3e1-15135814b292"
  673. }
  674. },
  675. {
  676. "shape": "edge",
  677. "attrs": {
  678. "line": {
  679. "stroke": "#5F95FF",
  680. "strokeWidth": 1,
  681. "targetMarker": {
  682. "name": "classic",
  683. "size": 8
  684. }
  685. }
  686. },
  687. "id": "13d78262-f889-4e6f-9980-29f9e2d87c8f",
  688. "router": {
  689. "name": "manhattan"
  690. },
  691. "zIndex": 12,
  692. "source": {
  693. "cell": "7b6fd715-83e6-4053-8c2b-346e6a857bf3",
  694. "port": "2fceb955-f7af-41ac-ac02-5a2ea514544e"
  695. },
  696. "target": {
  697. "cell": "ef3865af-8a91-4164-8466-3f6b4315070f",
  698. "port": "c133349e-4a4a-4d7d-9b38-26c36b3e68c5"
  699. }
  700. },
  701. {
  702. "shape": "edge",
  703. "attrs": {
  704. "line": {
  705. "stroke": "#5F95FF",
  706. "strokeWidth": 1,
  707. "targetMarker": {
  708. "name": "classic",
  709. "size": 8
  710. }
  711. }
  712. },
  713. "id": "9c7b7539-2f82-478d-a592-60dfceede791",
  714. "router": {
  715. "name": "manhattan"
  716. },
  717. "zIndex": 13,
  718. "source": {
  719. "cell": "7b6fd715-83e6-4053-8c2b-346e6a857bf3",
  720. "port": "0cbde5df-ef35-410e-b6c3-a6b1f5561e3f"
  721. },
  722. "target": {
  723. "cell": "762cbe4d-fd2b-4cb2-95bb-fae3cb9ef7fc",
  724. "port": "089ce61a-4b17-4ed8-9c3f-5b905f484425"
  725. }
  726. },
  727. {
  728. "position": {
  729. "x": 420,
  730. "y": 497
  731. },
  732. "size": {
  733. "width": 80,
  734. "height": 42
  735. },
  736. "attrs": {
  737. "text": {
  738. "text": "流程节点"
  739. }
  740. },
  741. "visible": true,
  742. "shape": "flow-chart-rect",
  743. "ports": {
  744. "groups": {
  745. "top": {
  746. "position": "top",
  747. "attrs": {
  748. "circle": {
  749. "r": 3,
  750. "magnet": true,
  751. "stroke": "#5F95FF",
  752. "strokeWidth": 1,
  753. "fill": "#fff",
  754. "style": {
  755. "visibility": "hidden"
  756. }
  757. }
  758. }
  759. },
  760. "right": {
  761. "position": "right",
  762. "attrs": {
  763. "circle": {
  764. "r": 3,
  765. "magnet": true,
  766. "stroke": "#5F95FF",
  767. "strokeWidth": 1,
  768. "fill": "#fff",
  769. "style": {
  770. "visibility": "hidden"
  771. }
  772. }
  773. }
  774. },
  775. "bottom": {
  776. "position": "bottom",
  777. "attrs": {
  778. "circle": {
  779. "r": 3,
  780. "magnet": true,
  781. "stroke": "#5F95FF",
  782. "strokeWidth": 1,
  783. "fill": "#fff",
  784. "style": {
  785. "visibility": "hidden"
  786. }
  787. }
  788. }
  789. },
  790. "left": {
  791. "position": "left",
  792. "attrs": {
  793. "circle": {
  794. "r": 3,
  795. "magnet": true,
  796. "stroke": "#5F95FF",
  797. "strokeWidth": 1,
  798. "fill": "#fff",
  799. "style": {
  800. "visibility": "hidden"
  801. }
  802. }
  803. }
  804. }
  805. },
  806. "items": [
  807. {
  808. "group": "top",
  809. "id": "d1346f43-969a-4201-af5d-d09b7ef79980"
  810. },
  811. {
  812. "group": "right",
  813. "id": "d561926a-3a24-449a-abb1-0c20bc89947e"
  814. },
  815. {
  816. "group": "bottom",
  817. "id": "0cbde5df-ef35-410e-b6c3-a6b1f5561e3f"
  818. },
  819. {
  820. "group": "left",
  821. "id": "2fceb955-f7af-41ac-ac02-5a2ea514544e"
  822. }
  823. ]
  824. },
  825. "id": "c7b4dfb0-2cc1-4ce1-839d-22b13bdf86e5",
  826. "zIndex": 14
  827. },
  828. {
  829. "shape": "edge",
  830. "attrs": {
  831. "line": {
  832. "stroke": "#5F95FF",
  833. "strokeWidth": 1,
  834. "targetMarker": {
  835. "name": "classic",
  836. "size": 8
  837. }
  838. }
  839. },
  840. "id": "9dfeb591-70e1-4b52-a463-3078a6fde579",
  841. "router": {
  842. "name": "manhattan"
  843. },
  844. "zIndex": 15,
  845. "source": {
  846. "cell": "762cbe4d-fd2b-4cb2-95bb-fae3cb9ef7fc",
  847. "port": "9bb8ec19-b1e2-432d-8735-b008da064948"
  848. },
  849. "target": {
  850. "cell": "c7b4dfb0-2cc1-4ce1-839d-22b13bdf86e5",
  851. "port": "d1346f43-969a-4201-af5d-d09b7ef79980"
  852. }
  853. },
  854. {
  855. "position": {
  856. "x": 420,
  857. "y": 616
  858. },
  859. "size": {
  860. "width": 80,
  861. "height": 42
  862. },
  863. "attrs": {
  864. "text": {
  865. "text": "结束节点"
  866. },
  867. "body": {
  868. "rx": 24,
  869. "ry": 24
  870. }
  871. },
  872. "visible": true,
  873. "shape": "flow-chart-rect",
  874. "ports": {
  875. "groups": {
  876. "top": {
  877. "position": "top",
  878. "attrs": {
  879. "circle": {
  880. "r": 3,
  881. "magnet": true,
  882. "stroke": "#5F95FF",
  883. "strokeWidth": 1,
  884. "fill": "#fff",
  885. "style": {
  886. "visibility": "hidden"
  887. }
  888. }
  889. }
  890. },
  891. "right": {
  892. "position": "right",
  893. "attrs": {
  894. "circle": {
  895. "r": 3,
  896. "magnet": true,
  897. "stroke": "#5F95FF",
  898. "strokeWidth": 1,
  899. "fill": "#fff",
  900. "style": {
  901. "visibility": "hidden"
  902. }
  903. }
  904. }
  905. },
  906. "bottom": {
  907. "position": "bottom",
  908. "attrs": {
  909. "circle": {
  910. "r": 3,
  911. "magnet": true,
  912. "stroke": "#5F95FF",
  913. "strokeWidth": 1,
  914. "fill": "#fff",
  915. "style": {
  916. "visibility": "hidden"
  917. }
  918. }
  919. }
  920. },
  921. "left": {
  922. "position": "left",
  923. "attrs": {
  924. "circle": {
  925. "r": 3,
  926. "magnet": true,
  927. "stroke": "#5F95FF",
  928. "strokeWidth": 1,
  929. "fill": "#fff",
  930. "style": {
  931. "visibility": "hidden"
  932. }
  933. }
  934. }
  935. }
  936. },
  937. "items": [
  938. {
  939. "group": "top",
  940. "id": "45726225-0a03-409e-8475-07da4b8533c5"
  941. },
  942. {
  943. "group": "right",
  944. "id": "06111939-bf01-48d9-9f54-6465d9d831c6"
  945. },
  946. {
  947. "group": "bottom",
  948. "id": "6541f8dc-e48b-4b8c-a105-2ab3a47f1f21"
  949. },
  950. {
  951. "group": "left",
  952. "id": "54781206-573f-4982-a21e-5fac1e0e8a60"
  953. }
  954. ]
  955. },
  956. "id": "5ac48b64-d507-4006-954b-f8fbf8016ad2",
  957. "zIndex": 16
  958. },
  959. {
  960. "shape": "edge",
  961. "attrs": {
  962. "line": {
  963. "stroke": "#5F95FF",
  964. "strokeWidth": 1,
  965. "targetMarker": {
  966. "name": "classic",
  967. "size": 8
  968. }
  969. }
  970. },
  971. "id": "f847f055-9073-4c8e-92c5-8124597d1e7e",
  972. "router": {
  973. "name": "manhattan"
  974. },
  975. "zIndex": 17,
  976. "source": {
  977. "cell": "c7b4dfb0-2cc1-4ce1-839d-22b13bdf86e5",
  978. "port": "0cbde5df-ef35-410e-b6c3-a6b1f5561e3f"
  979. },
  980. "target": {
  981. "cell": "5ac48b64-d507-4006-954b-f8fbf8016ad2",
  982. "port": "45726225-0a03-409e-8475-07da4b8533c5"
  983. }
  984. }
  985. ]
  986. }

以上都是核心代码了,希望帮到大家! 

网站建设定制开发 软件系统开发定制 定制软件开发 软件开发定制 定制app开发 app开发定制 app开发定制公司 电商商城定制开发 定制小程序开发 定制开发小程序 客户管理系统开发定制 定制网站 定制开发 crm开发定制 开发公司 小程序开发定制 定制软件 收款定制开发 企业网站定制开发 定制化开发 android系统定制开发 定制小程序开发费用 定制设计 专注app软件定制开发 软件开发定制定制 知名网站建设定制 软件定制开发供应商 应用系统定制开发 软件系统定制开发 企业管理系统定制开发 系统定制开发