1 <template>
2 <v-container class="workflow-container" grid-list-xl fluid>
3 <div class="super-flow-demo">
4 <div class="node-container">
5 <div
6 class="node-item"
7 v-for="(item, index) in nodeItemList"
8 :key="index"
9 @mousedown="evt => nodeItemMouseDown(evt, item.value)">
10 {{ item.label }}
11 </div>
12 </div>
13 <div
14 class="flow-container"
15 ref="flowContainer">
16 <super-flow
17 ref="superFlow"
18 :graph-menu="graphMenu"
19 :node-menu="nodeMenu"
20 :link-menu="linkMenu"
21 :link-desc="linkDesc"
22 :node-list="nodeList"
23 :link-list="linkList">
24 <template v-slot:node="{meta}">
25 <div
26 :class="meta.type? `flow-node-${meta.type}`: ''"
27 class="flow-node ellipsis">
28 <div class="node-content" :title="meta.name">{{ meta.name }}</div>
29 </div>
30 </template>
31 </super-flow>
32 <v-btn
33 @click="saveFlow"
34 color="primary"
35 class="saveIcon"
36 >SAVE
37 </v-btn>
38 </div>
39 </div>
40
41 <el-dialog
42 :title="drawerConf.title"
43 :visible.sync="drawerConf.visible"
44 :close-on-click-modal="false"
45 width="500px">
46 <el-form
47 @keyup.native.enter="settingSubmit"
48 @submit.native.prevent
49 v-show="drawerConf.type === drawerType.node"
50 ref="nodeSetting"
51 :model="nodeSetting">
52 <el-form-item
53 label="node name"
54 prop="name">
55 <el-input
56 v-model="nodeSetting.name"
57 placeholder="Please enter the node name"
58 maxlength="30">
59 </el-input>
60 </el-form-item>
61 <el-form-item
62 label="node description"
63 prop="desc">
64 <el-input
65 v-model="nodeSetting.desc"
66 placeholder="Please enter a node description"
67 maxlength="30">
68 </el-input>
69 </el-form-item>
70 </el-form>
71 <el-form
72 @keyup.native.enter="settingSubmit"
73 @submit.native.prevent
74 v-show="drawerConf.type === drawerType.link"
75 ref="linkSetting"
76 :model="linkSetting">
77 <el-form-item
78 label="link description"
79 prop="desc">
80 <el-input
81 v-model="linkSetting.desc"
82 placeholder="Please enter a link description">
83 </el-input>
84 </el-form-item>
85 </el-form>
86 <span
87 slot="footer"
88 class="dialog-footer">
89 <el-button @click="drawerConf.cancel">
90 CANCEL
91 </el-button>
92 <el-button type="primary" @click="settingSubmit">
93 OK
94 </el-button>
95 </span>
96 </el-dialog>
97 </v-container>
98 </template>
99
100 <script>
101 import SuperFlow from 'vue-super-flow'
102 import 'vue-super-flow/lib/index.css'
103
104 const drawerType = {
105 node: 0,
106 link: 1
107 }
108
109 export default {
110 components: {
111 SuperFlow
112 },
113 data () {
114 return {
115 drawerType,
116 nodeList: [
117 {
118 id: "N1",
119 coordinate: [771, 32],
120 width: 120,
121 height: 40,
122 meta: {
123 label: 'start',
124 name: 'start',
125 type: 'start'
126 }
127 },
128 {
129 id: "N2",
130 coordinate: [731, 137],
131 width: 200,
132 height: 40,
133 meta: {
134 desc: '1',
135 label: 'process',
136 name: 'process11111',
137 type: 'process'
138 }
139 },
140 {
141 id: "N3",
142 coordinate: [747, 237],
143 width: 168,
144 height: 168,
145 meta: {
146 desc: '?',
147 label: 'if',
148 name: 'if?????',
149 type: 'if'
150 }
151 },
152 {
153 id: "N4",
154 coordinate: [731, 505],
155 width: 200,
156 height: 40,
157 meta: {
158 desc: '2',
159 label: 'process',
160 name: 'process22222',
161 type: 'process'
162 }
163 },
164 {
165 id: "N5",
166 coordinate: [1088, 300],
167 width: 200,
168 height: 40,
169 meta: {
170 desc: '3',
171 label: 'process',
172 name: 'process33333',
173 type: 'process'
174 }
175 },
176 {
177 id: "N6",
178 coordinate: [771, 597],
179 width: 120,
180 height: 40,
181 meta: {
182 label: 'end',
183 name: 'end',
184 type: 'end'
185 }
186 }
187 ],
188 linkList: [
189 {
190 id: "L1",
191 startAt: [60, 40],
192 startId: "N1",
193 endAt: [100, 0],
194 endId: "N2",
195 meta: null
196 },
197 {
198 id: "L2",
199 startAt: [100, 40],
200 startId: "N2",
201 endAt: [84, 0],
202 endId: "N3",
203 meta: null
204 },
205 {
206 id: "L3",
207 startAt: [100, 40],
208 startId: "N4",
209 endAt: [60, 0],
210 endId: "N6",
211 meta: null
212 },
213 {
214 id: "L4",
215 startAt: [84, 168],
216 startId: "N3",
217 endAt: [100, 0],
218 endId: "N4",
219 meta: {
220 desc: 'YES'
221 }
222 },
223 {
224 id: "L5",
225 startAt: [168, 84],
226 startId: "N3",
227 endAt: [0, 20],
228 endId: "N5",
229 meta: {
230 desc: 'NO'
231 }
232 },
233 {
234 id: "L6",
235 startAt: [100, 0],
236 startId: "N5",
237 endAt: [200, 20],
238 endId: "N2",
239 meta: null
240 }
241 ],
242 drawerConf: {
243 title: '',
244 visible: false,
245 type: null,
246 info: null,
247 open: (type, info) => {
248 const conf = this.drawerConf
249 conf.visible = true
250 conf.type = type
251 conf.info = info
252 if (conf.type === drawerType.node) {
253 conf.title = 'NODE'
254 if (this.$refs.nodeSetting) this.$refs.nodeSetting.resetFields()
255 this.$set(this.nodeSetting, 'name', info.meta.name)
256 this.$set(this.nodeSetting, 'desc', info.meta.desc)
257 } else {
258 conf.title = 'LINK'
259 if (this.$refs.linkSetting) this.$refs.linkSetting.resetFields()
260 this.$set(this.linkSetting, 'desc', info.meta ? info.meta.desc : '')
261 }
262 },
263 cancel: () => {
264 this.drawerConf.visible = false
265 if (this.drawerConf.type === drawerType.node) {
266 this.$refs.nodeSetting.clearValidate()
267 } else {
268 this.$refs.linkSetting.clearValidate()
269 }
270 }
271 },
272 linkSetting: {
273 desc: ''
274 },
275 nodeSetting: {
276 name: '',
277 desc: ''
278 },
279 nodeItemList: [
280 {
281 label: 'start',
282 value: () => ({
283 width: 120,
284 height: 40,
285 meta: {
286 label: 'start',
287 name: 'start',
288 type: 'start'
289 }
290 })
291 },
292 {
293 label: 'process',
294 value: () => ({
295 width: 200,
296 height: 40,
297 meta: {
298 label: 'process',
299 name: 'process',
300 type: 'process'
301 }
302 })
303 },
304 {
305 label: 'if',
306 value: () => ({
307 width: 168,
308 height: 168,
309 meta: {
310 label: 'if',
311 name: 'if',
312 type: 'if'
313 }
314 })
315 },
316 {
317 label: 'end',
318 value: () => ({
319 width: 120,
320 height: 40,
321 meta: {
322 label: 'end',
323 name: 'end',
324 type: 'end'
325 }
326 })
327 }
328 ],
329 graphMenu: [
330 [
331 {
332 // 选项 label
333 label: 'start',
334 // 选项是否禁用
335 disable (graph) {
336 return !!graph.nodeList.find(node => node.meta.label === 'start')
337 },
338 // 选项选中后回调函数
339 selected (graph, coordinate) {
340 graph.addNode({
341 width: 120,
342 height: 40,
343 coordinate,
344 meta: {
345 label: 'start',
346 name: 'start',
347 type: 'start'
348 }
349 })
350 }
351 },
352 {
353 label: 'process',
354 selected (graph, coordinate) {
355 graph.addNode({
356 width: 200,
357 height: 40,
358 coordinate,
359 meta: {
360 label: 'process',
361 name: 'process',
362 type: 'process'
363 }
364 })
365 }
366 },
367 {
368 label: 'if',
369 selected (graph, coordinate) {
370 graph.addNode({
371 width: 168,
372 height: 168,
373 coordinate,
374 meta: {
375 label: 'if',
376 name: 'if',
377 type: 'if'
378 }
379 })
380 }
381 }
382 ],
383 [
384 {
385 label: 'end',
386 selected (graph, coordinate) {
387 graph.addNode({
388 width: 120,
389 height: 40,
390 coordinate,
391 meta: {
392 label: 'end',
393 name: 'end',
394 type: 'end'
395 }
396 })
397 }
398 }
399 ],
400 [
401 {
402 label: 'select all',
403 selected: graph => {
404 graph.selectAll()
405 }
406 }
407 ]
408 ],
409 nodeMenu: [
410 [
411 {
412 label: 'delete',
413 selected: node => {
414 node.remove()
415 }
416 },
417 {
418 label: 'edit',
419 selected: node => {
420 this.drawerConf.open(drawerType.node, node)
421 }
422 }
423 ]
424 ],
425 linkMenu: [
426 [
427 {
428 label: 'delete',
429 selected: link => {
430 link.remove()
431 }
432 },
433 {
434 label: 'edit',
435 selected: link => {
436 this.drawerConf.open(drawerType.link, link)
437 }
438 }
439 ]
440 ],
441 dragConf: {
442 isDown: false,
443 isMove: false,
444 offsetTop: 0,
445 offsetLeft: 0,
446 clientX: 0,
447 clientY: 0,
448 ele: null,
449 info: null
450 }
451 }
452 },
453 mounted () {
454 document.addEventListener('mousemove', this.docMousemove)
455 document.addEventListener('mouseup', this.docMouseup)
456 this.$once('hook:beforeDestroy', () => {
457 document.removeEventListener('mousemove', this.docMousemove)
458 document.removeEventListener('mouseup', this.docMouseup)
459 })
460 },
461 methods: {
462 saveFlow () {
463 this.nodeList = this.$refs.superFlow.toJSON().nodeList
464 this.linkList = this.$refs.superFlow.toJSON().linkList
465 console.log(this.nodeList)
466 console.log(this.linkList)
467 },
468 linkDesc (link) {
469 return link.meta ? link.meta.desc : ''
470 },
471 settingSubmit () {
472 const conf = this.drawerConf
473 if (this.drawerConf.type === drawerType.node) {
474 if (!conf.info.meta) conf.info.meta = {}
475 Object.keys(this.nodeSetting).forEach(key => {
476 this.$set(conf.info.meta, key, this.nodeSetting[key])
477 })
478 this.$refs.nodeSetting.resetFields()
479 } else {
480 if (!conf.info.meta) conf.info.meta = {}
481 Object.keys(this.linkSetting).forEach(key => {
482 this.$set(conf.info.meta, key, this.linkSetting[key])
483 })
484 this.$refs.linkSetting.resetFields()
485 }
486 conf.visible = false
487 },
488 docMousemove ({ clientX, clientY }) {
489 const conf = this.dragConf
490 if (conf.isMove) {
491 conf.ele.style.top = clientY - conf.offsetTop + 'px'
492 conf.ele.style.left = clientX - conf.offsetLeft + 'px'
493 } else if (conf.isDown) {
494 // 鼠标移动量大于 5 时 移动状态生效
495 conf.isMove = Math.abs(clientX - conf.clientX) > 5 || Math.abs(clientY - conf.clientY) > 5
496 }
497 },
498 docMouseup ({ clientX, clientY }) {
499 const conf = this.dragConf
500 conf.isDown = false
501
502 if (conf.isMove) {
503 const {
504 top,
505 right,
506 bottom,
507 left
508 } = this.$refs.flowContainer.getBoundingClientRect()
509
510 // 判断鼠标是否进入 flow container
511 if (
512 clientX > left && clientX < right && clientY > top && clientY < bottom
513 ) {
514 // 获取拖动元素左上角相对 super flow 区域原点坐标
515 const coordinate = this.$refs.superFlow.getMouseCoordinate(
516 clientX - conf.offsetLeft,
517 clientY - conf.offsetTop
518 )
519 // 添加节点
520 this.$refs.superFlow.addNode({
521 coordinate,
522 ...conf.info
523 })
524 }
525 conf.isMove = false
526 }
527 if (conf.ele) {
528 conf.ele.remove()
529 conf.ele = null
530 }
531 },
532 nodeItemMouseDown (evt, infoFun) {
533 const {
534 clientX,
535 clientY,
536 currentTarget
537 } = evt
538
539 const {
540 top,
541 left
542 } = evt.currentTarget.getBoundingClientRect()
543
544 const conf = this.dragConf
545 const ele = currentTarget.cloneNode(true)
546
547 Object.assign(this.dragConf, {
548 offsetLeft: clientX - left,
549 offsetTop: clientY - top,
550 clientX: clientX,
551 clientY: clientY,
552 info: infoFun(),
553 ele,
554 isDown: true
555 })
556
557 ele.style.position = 'fixed'
558 ele.style.margin = '0'
559 ele.style.top = clientY - conf.offsetTop + 'px'
560 ele.style.left = clientX - conf.offsetLeft + 'px'
561
562 this.$el.appendChild(this.dragConf.ele)
563 }
564 }
565 }
566 </script>
567
568 <style lang="scss" scoped>
569 .workflow-container {
570 width: calc(100vw - 80px);
571 height: calc(100vh - 128px);
572 box-shadow: 0px 3px 1px -2px rgb(0 0 0 / 20%), 0px 2px 2px 0px rgb(0 0 0 / 14%), 0px 1px 5px 0px rgb(0 0 0 / 12%);
573 margin: 32px;
574 padding: 0;
575 background: #fff;
576 overflow: hidden;
577 }
578 .ellipsis {
579 white-space : nowrap;
580 text-overflow : ellipsis;
581 overflow : hidden;
582 word-wrap : break-word;
583 }
584 .super-flow-demo {
585 position: relative;
586 margin: 20px;
587 background: #f5f5f5;
588 height: calc(100vh - 168px);
589
590 .node-container {
591 width: 100%;
592 height: 50px;
593 background-color: #FFFFFF;
594
595 .node-item {
596 display: inline-block;
597 font-size: 14px;
598 height: 30px;
599 width: 120px;
600 margin: 0 20px 0 0;
601 background: #FFFFFF;
602 line-height: 30px;
603 box-shadow: 1px 1px 4px rgba(0, 0, 0, 0.3);
604 cursor: pointer;
605 user-select: none; // 防止鼠标左键拖动选中页面的文字
606 text-align: center;
607 &:hover {
608 box-shadow : 1px 1px 8px rgba(0, 0, 0, 0.4);
609 }
610 }
611 }
612 .flow-container {
613 width: 100%;
614 height: calc(100% - 50px);
615
616 .super-flow {
617 overflow: auto;
618 }
619 }
620 .saveIcon {
621 position: absolute;
622 right: 0px;
623 top: 0px;
624 }
625 .super-flow__node {
626 .flow-node {
627 box-sizing: border-box;
628 width: 100%;
629 height: 100%;
630 line-height: 40px;
631 padding: 0 6px;
632 font-size: 16px;
633 color: #fff;
634 font-weight: bold;
635 .node-content {
636 text-align: center;
637 overflow: hidden;
638 text-overflow: ellipsis;
639 white-space: nowrap;
640 }
641 }
642 }
643 /*开始节点样式*/
644 .ellipsis.flow-node-start {
645 background: #55ABFC;
646 border-radius: 10px;
647 border: 1px solid #b4b4b4;
648 }
649 /*流程节点样式*/
650 .ellipsis.flow-node-process {
651 position: relative;
652 background: #30B95C;
653 border: 1px solid #b4b4b4;
654 }
655 /*条件节点样式*/
656 .ellipsis.flow-node-if {
657 width: 120px;
658 height: 120px;
659 position: relative;
660 top: 24px;
661 left: 24px;
662 background: #BC1D16;
663 border: 1px solid #b4b4b4;
664 transform: rotateZ(45deg); //倾斜
665 .node-content {
666 position: absolute;
667 top: 50%;
668 left: 20px;
669 width: 100%;
670 transform: rotateZ(-45deg) translateY(-75%);
671 }
672 }
673 /*结束节点样式*/
674 .ellipsis.flow-node-end {
675 background: #000;
676 border-radius: 10px;
677 border: 1px solid #b4b4b4;
678 }
679 }
680 </style>
681 <style>
682 .super-flow-demo .super-flow__node {
683 border: none;
684 background: none;
685 box-shadow: none;
686 }
687 </style>

vue super flow 多种形状的更多相关文章

  1. echarts设置图标图例legend多种形状

    legend: {   icon: "circle",   //  字段控制形状  类型包括 circle,rect,line,roundRect,triangle,diamond ...

  2. canvas 多种形状绘图方法

    function canvasUploadImg(image,imageName,imgType,callbackfn){ var img_width = image.width; var img_h ...

  3. Vue 源码学习(1)

    概述 我在闲暇时间学习了一下 Vue 的源码,有一些心得,现在把它们分享给大家. 这个分享只是 Vue源码系列 的第一篇,主要讲述了如下内容: 寻找入口文件 在打包的过程中 Vue 发生了什么变化 在 ...

  4. vue 快速入门 系列 —— 实例方法(或 property)和静态方法

    其他章节请看: vue 快速入门 系列 实例方法(或 property)和静态方法 在 Vue(自身) 项目结构 一文中,我们研究了 vue 项目自身构建过程,也知晓了 import Vue from ...

  5. Vue.js——60分钟快速入门

    Vue.js介绍 Vue.js是当下很火的一个JavaScript MVVM库,它是以数据驱动和组件化的思想构建的.相比于Angular.js,Vue.js提供了更加简洁.更易于理解的API,使得我们 ...

  6. Vue.js——60分钟快速入门

    Vue.js介绍 Vue.js是当下很火的一个JavaScript MVVM库,它是以数据驱动和组件化的思想构建的.相比于Angular.js,Vue.js提供了更加简洁.更易于理解的API,使得我们 ...

  7. Android项目实战(九):CustomShapeImageView 自定义形状的ImageView

    一个两年前出来的第三方类库,具有不限于圆形ImageView的多种形状ImageView,项目开发必备 github下载地址:https://github.com/MostafaGazar/Custo ...

  8. Vue.js快速入门

    Vue.js介绍 Vue.js是当下很火的一个JavaScript MVVM库,它是以数据驱动和组件化的思想构建的.相比于Angular.js,Vue.js提供了更加简洁.更易于理解的API,使得我们 ...

  9. Vue.js介绍

    http://www.cnblogs.com/keepfool/p/5619070.html Vue.js介绍 Vue.js是当下很火的一个JavaScript MVVM库,它是以数据驱动和组件化的思 ...

  10. vue.js之个人总结

    1.MVVM模式 MVVM模式(Model-View-ViewModel)的运作如下图: 1)上图解析:ViewModel是Vue.js的核心,它是一个Vue实例.Vue实例是作用于某一个HTML元素 ...

随机推荐

  1. 简单使用wireshark

    wireshark抓包工具 拓扑图: 拓扑图解释:终端用户使用wireshark抓包工具监听无线网卡,监听时,终端访问互联网,可实时监听网络抓包 操作步骤: 一,打开wireshark抓包工具,监听网 ...

  2. mysql转DM的日期函数转换

    背景: 项目要从mysql转换为DM数据库,发现很多日期函数在DM是不能用的. 所以大概总结下有哪些,以及转换思路. 正文: INTERVAL 表示日期间隔. 看做拼接符. DATE_ADD 表示日期 ...

  3. 开源 IM 系统 tinode 部署教程| WSL 环境

    背景 我们的需求是在本地部署一套 IM 系统,选择 tinode.为便于后端启动,我们采用 WSL 环境,配合 docker 安装数据库,来启动 IM 应用. 解决 WSL 启动前端和后台服务 cmd ...

  4. pyinstaller打包后运行提示“No module named 'PyQt5' "

    记录遇到的一个很傻的问题 脚本用到了PyQt5模块,安装成功脚本可以正常运行,但是使用pyinstaller 打包后,运行 exe文件提示 No module named 'PyQt5' 原因: 因为 ...

  5. 在linux 下如何快速创建环境

    首先先在你的主目录下创建一个文件 touch venv python创建环境 python3 -m venv venv 创建虚拟环境 (提示:当你有错找不到的时候可以重新创建一个环境) source ...

  6. 真的,Web安全入门看这个就够了!

    一.HTTP协议 1.HTTP 什么是HTTP? 超文本传输协议,HTTP是基于B/S架构进行通信的,而HTTP的服务器端实现程序有httpd.nginx等,其客户端的实现程序主要是Web浏览器,例如 ...

  7. Docker之Redis保姆级别安装

    Docker之Redis保姆级别安装: 如果觉得样式不好:跳转即可 http://www.lifengying.site/(md文件复制过来有些样式会不一样) 学英语网站项目:自己先保证Redis.N ...

  8. 2022-05-31内部群每日三题-清辉PMP

    1.由于项目执行期间的范围变更,项目经理确定供应商必须对一个已在使用的产品模块进行更改.项目经理首先做什么? A.准备一份变更请求,以更新供应商的合同条款 B.检查采购管理计划和合同条款 C.将该信息 ...

  9. 从个人角度谈为什么要使用git多分支?

    今天烦死了,公司的项目没有使用多分支管理,造成给某客户的打包的代码竟然需要截取gitlab节点重新打包. 在我上家公司开发Android项目的时候,使用了非常鲜明的多分支管理,当时作为项目运维,对gi ...

  10. 如何实现chrome谷歌浏览器多开(独立环境 独立cookie)、改任务栏图标

    多开谷歌浏览器: 由于各种各样的原因,你可能需要在一个电脑登录某个平台,比如一个电脑登录3个公众号,或者3个知乎等等. 最简单的方案是,直接安装3个不同的浏览器,比如一个谷歌浏览器,一个火狐浏览器,一 ...