首页 > 其他分享 >使用vue-super-flow的使用进行工作流的梳理

使用vue-super-flow的使用进行工作流的梳理

时间:2023-07-06 09:44:56浏览次数:39  
标签:node vue type flow label meta link conf super

1.安装依赖

npm install vue-super-flow

2.在页面中引用

<template>
<super-flow></super-flow>
</template>

<script >
import SuperFlow from 'vue-super-flow'
import 'vue-super-flow/lib/index.css'
export default {
components: {SuperFlow}
}
</script>

3.使用

<template>
<v-container class="loan-work-queue" grid-list-xl fluid>
<v-row style="height: 771px;">
<v-col cols="9">
<div class="super-flow-demo1">
<div class="node-container">
<span
class="node-item"
v-for="(item, index) in nodeItemList"
:key="index"
@mousedown="evt => nodeItemMouseDown(evt, item.value)">
{{ item.label }}
</span>
</div>
<div
class="flow-container"
ref="flowContainer"
@click="flowNodeClick">
<super-flow
@toJSON="toJSON"
ref="superFlow"
:graph-menu="graphMenu"
:node-menu="nodeMenu"
:link-menu="linkMenu"
:link-base-style="linkBaseStyle"
:link-style="linkStyle"
:link-desc="linkDesc"
:node-list="nodeList"
:link-list="linkList">
<template v-slot:node="{meta}">
<div
@mouseup="nodeMouseUp"
@click="nodeClick"
:class="meta.type? `flow-node-${meta.type}`: ''"
class="flow-node ellipsis">
<div class="node-content" :title="meta.name">{{ meta.name }}</div>
</div>
</template>
</super-flow>
<v-btn
text
@click="saveFlow"
color="primary"
class="saveIcon"
>SAVE
</v-btn>
</div>
</div>
</v-col>
<v-col cols="3">
<div>
<el-form
class="link-base-style-form"
ref="linkBaseStyle"
label-width="100px"
@submit.native.prevent
:model="linkBaseStyle">
<h4 class="style-title">Link style customization</h4>
<v-row>
<v-col cols="12">
<el-form-item label="color">
<el-color-picker
v-model="linkBaseStyle.color">
</el-color-picker>
</el-form-item>
</v-col>
<v-col cols="12">
<el-form-item label="hover">
<el-color-picker
v-model="linkBaseStyle.hover">
</el-color-picker>
</el-form-item>
</v-col>
<v-col cols="12">
<el-form-item label="textColor">
<el-color-picker
v-model="linkBaseStyle.textColor">
</el-color-picker>
</el-form-item>
</v-col>
<v-col cols="12">
<el-form-item label="textHover">
<el-color-picker
v-model="linkBaseStyle.textHover">
</el-color-picker>
</el-form-item>
</v-col>
<v-col cols="12">
<el-form-item label="font">
<el-select
size="medium"
v-model="linkBaseStyle.font">
<el-option
v-for="item in fontList"
:key="item"
:label="item"
:value="item">
</el-option>
</el-select>
</el-form-item>
</v-col>
<v-col cols="12">
<el-form-item label="background">
<el-color-picker
v-model="linkBaseStyle.background">
</el-color-picker>
</el-form-item>
</v-col>
<v-col cols="12">
<el-form-item label="dotted">
<el-switch
v-model="linkBaseStyle.dotted"
active-color="#13ce66"
inactive-color="#ff4949">
</el-switch>
</el-form-item>
</v-col>
<v-col cols="12">
<el-form-item label="lineDash">
<el-select
size="medium"
style="width: 80px"
v-model="linkBaseStyle.lineDash[0]">
<el-option
v-for="item in [1,2,3,4,5,6,7,8]"
:key="item"
:label="item"
:value="item">
</el-option>
</el-select>
<el-select
size="medium"
style="width: 80px"
v-model="linkBaseStyle.lineDash[1]">
<el-option
v-for="item in [1,2,3,4,5,6,7,8]"
:key="item"
:label="item"
:value="item">
</el-option>
</el-select>
</el-form-item>
</v-col>
</v-row>
</el-form>
</div>
</v-col>
</v-row>

<el-dialog
:title="drawerConf.title"
:visible.sync="drawerConf.visible"
:close-on-click-modal="false"
width="500px">
<el-form
@keyup.native.enter="settingSubmit"
@submit.native.prevent
v-show="drawerConf.type === drawerType.node"
ref="nodeSetting"
:model="nodeSetting">
<el-form-item
label="node name"
prop="name">
<el-input
v-model="nodeSetting.name"
placeholder="Please enter the node name"
maxlength="30">
</el-input>
</el-form-item>
<el-form-item
label="node description"
prop="desc">
<el-input
v-model="nodeSetting.desc"
placeholder="Please enter a node description"
maxlength="30">
</el-input>
</el-form-item>
</el-form>
<el-form
@keyup.native.enter="settingSubmit"
@submit.native.prevent
v-show="drawerConf.type === drawerType.link"
ref="linkSetting"
:model="linkSetting">
<el-form-item
label="link description"
prop="desc">
<el-input
v-model="linkSetting.desc"
placeholder="Please enter a link description">
</el-input>
</el-form-item>
</el-form>
<span
slot="footer"
class="dialog-footer">
<el-button
@click="drawerConf.cancel">
CANCEL
</el-button>
<el-button
type="primary"
@click="settingSubmit">
OK
</el-button>
</span>
</el-dialog>
</v-container>
</template>

<script>
import SuperFlow from 'vue-super-flow'
import 'vue-super-flow/lib/index.css'

const drawerType = {
node: 0,
link: 1
}

export default {
components: {
SuperFlow
},
data () {
return {
nodeList: [
{
id: 1,
coordinate: [400, 120],
width: 120,
height: 40,
meta: {
label: 'start',
name: 'start',
type: 'start'
}
},
{
id: 2,
coordinate: [360, 235],
width: 200,
height: 40,
meta: {
label: 'process',
name: 'process',
type: 'process'
}
},
{
id: 3,
coordinate: [400, 360],
width: 120,
height: 40,
meta: {
label: 'end',
name: 'end',
type: 'end'
}
}
],
linkList: [
{
id: 4,
startAt: [60, 40],
startId: 1,
endAt: [100, 0],
endId: 2,
meta: null
},
{
id: 5,
startAt: [100, 40],
startId: 2,
endAt: [60, 0],
endId: 3,
meta: null
}
],
drawerType,
drawerConf: {
title: '',
visible: false,
type: null,
info: null,
open: (type, info) => {
const conf = this.drawerConf
conf.visible = true
conf.type = type
conf.info = info
if (conf.type === drawerType.node) {
conf.title = 'NODE'
if (this.$refs.nodeSetting) this.$refs.nodeSetting.resetFields()
this.$set(this.nodeSetting, 'name', info.meta.name)
this.$set(this.nodeSetting, 'desc', info.meta.desc)
} else {
conf.title = 'LINK'
if (this.$refs.linkSetting) this.$refs.linkSetting.resetFields()
this.$set(this.linkSetting, 'desc', info.meta ? info.meta.desc : '')
}
},
cancel: () => {
this.drawerConf.visible = false
if (this.drawerConf.type === drawerType.node) {
this.$refs.nodeSetting.clearValidate()
} else {
this.$refs.linkSetting.clearValidate()
}
}
},
linkSetting: {
desc: ''
},
nodeSetting: {
name: '',
desc: ''
},

dragConf: {
isDown: false,
isMove: false,
offsetTop: 0,
offsetLeft: 0,
clientX: 0,
clientY: 0,
ele: null,
info: null
},
nodeItemList: [
{
label: 'start',
value: () => ({
width: 120,
height: 40,
meta: {
label: 'start',
name: 'start',
type: 'start'
}
})
},
{
label: 'process',
value: () => ({
width: 200,
height: 40,
meta: {
label: 'process',
name: 'process',
type: 'process'
}
})
},
{
label: 'if',
value: () => ({
width: 168,
height: 168,
meta: {
label: 'if',
name: 'if',
type: 'if'
}
})
},
{
label: 'end',
value: () => ({
width: 120,
height: 40,
meta: {
label: 'end',
name: 'end',
type: 'end'
}
})
}
],
graphMenu: [
[
{
// 选项 label
label: 'start',
// 选项是否禁用
disable (graph) {
return !!graph.nodeList.find(node => node.meta.label === '1')
},
// 选项选中后回调函数
selected (graph, coordinate) {
graph.addNode({
width: 120,
height: 40,
coordinate,
meta: {
label: 'start',
name: 'start',
type: 'start'
}
})
}
},
{
label: 'process',
selected (graph, coordinate) {
graph.addNode({
width: 200,
height: 40,
coordinate,
meta: {
label: 'process',
name: 'process',
type: 'process'
}
})
}
},
{
label: 'if',
selected (graph, coordinate) {
graph.addNode({
width: 168,
height: 168,
coordinate,
meta: {
label: 'if',
name: 'if',
type: 'if'
}
})
}
}
],
[
{
label: 'end',
selected (graph, coordinate) {
graph.addNode({
width: 120,
height: 40,
coordinate,
meta: {
label: 'end',
name: 'end',
type: 'end'
}
})
}
}
],
[
{
label: 'select all',
selected: graph => {
graph.selectAll()
}
}
]
],
nodeMenu: [
[
{
label: 'delete',
selected: node => {
node.remove()
}
},
{
label: 'edit',
selected: node => {
this.drawerConf.open(drawerType.node, node)
}
}
]
],
linkMenu: [
[
{
label: 'delete',
selected: link => {
link.remove()
}
},
{
label: 'edit',
selected: link => {
this.drawerConf.open(drawerType.link, link)
}
}
]
],

linkBaseStyle: {
color: '#666666', // line 颜色
hover: '#FF0000', // line hover 的颜色
textColor: '#666666', // line 描述文字颜色
textHover: '#FF0000', // line 描述文字 hover 颜色
font: '14px Arial', // line 描述文字 字体设置 参考 canvas font
dotted: false, // 是否是虚线
lineDash: [4, 4], // 虚线时生效,虚线长度和间隔长度
background: 'rgba(255,255,255,0.6)' // 描述文字背景色
},
fontList: [
'14px Arial',
'italic small-caps bold 12px arial'
]
}
},
mounted () {
document.addEventListener('mousemove', this.docMousemove)
document.addEventListener('mouseup', this.docMouseup)
this.$once('hook:beforeDestroy', () => {
document.removeEventListener('mousemove', this.docMousemove)
document.removeEventListener('mouseup', this.docMouseup)
})
},
methods: {
flowNodeClick () {
// console.log(this.$refs.superFlow.graph)
// const data = this.$refs.superFlow.toJSON()
// console.log(data)
},
toJSON (data) {
// console.log(data)
},
saveFlow () {
this.nodeList = this.$refs.superFlow.toJSON().nodeList
this.linkList = this.$refs.superFlow.toJSON().linkList
},
linkStyle (link) {
if (link.meta && link.meta.desc === '1') {
return {
color: 'red',
hover: '#FF00FF',
dotted: true
}
} else {
return {}
}
},
linkDesc (link) {
return link.meta ? link.meta.desc : ''
},
settingSubmit () {
const conf = this.drawerConf
if (this.drawerConf.type === drawerType.node) {
if (!conf.info.meta) conf.info.meta = {}
Object.keys(this.nodeSetting).forEach(key => {
this.$set(conf.info.meta, key, this.nodeSetting[key])
})
this.$refs.nodeSetting.resetFields()
} else {
if (!conf.info.meta) conf.info.meta = {}
Object.keys(this.linkSetting).forEach(key => {
this.$set(conf.info.meta, key, this.linkSetting[key])
})
this.$refs.linkSetting.resetFields()
}
conf.visible = false
},
nodeMouseUp (evt) {
evt.preventDefault()
},
nodeClick () {
// console.log(arguments)
},
docMousemove ({ clientX, clientY }) {
const conf = this.dragConf
if (conf.isMove) {
conf.ele.style.top = clientY - conf.offsetTop + 'px'
conf.ele.style.left = clientX - conf.offsetLeft + 'px'
} else if (conf.isDown) {
// 鼠标移动量大于 5 时 移动状态生效
conf.isMove = Math.abs(clientX - conf.clientX) > 5 || Math.abs(clientY - conf.clientY) > 5
}
},
docMouseup ({ clientX, clientY }) {
const conf = this.dragConf
conf.isDown = false

if (conf.isMove) {
const {
top,
right,
bottom,
left
} = this.$refs.flowContainer.getBoundingClientRect()

// 判断鼠标是否进入 flow container
if (
clientX > left && clientX < right && clientY > top && clientY < bottom
) {
// 获取拖动元素左上角相对 super flow 区域原点坐标
const coordinate = this.$refs.superFlow.getMouseCoordinate(
clientX - conf.offsetLeft,
clientY - conf.offsetTop
)
// 添加节点
this.$refs.superFlow.addNode({
coordinate,
...conf.info
})
}
conf.isMove = false
}
if (conf.ele) {
conf.ele.remove()
conf.ele = null
}
},
nodeItemMouseDown (evt, infoFun) {
const {
clientX,
clientY,
currentTarget
} = evt

const {
top,
left
} = evt.currentTarget.getBoundingClientRect()

const conf = this.dragConf
const ele = currentTarget.cloneNode(true)

Object.assign(this.dragConf, {
offsetLeft: clientX - left,
offsetTop: clientY - top,
clientX: clientX,
clientY: clientY,
info: infoFun(),
ele,
isDown: true
})

ele.style.position = 'fixed'
ele.style.margin = '0'
ele.style.top = clientY - conf.offsetTop + 'px'
ele.style.left = clientX - conf.offsetLeft + 'px'

this.$el.appendChild(this.dragConf.ele)
}
}
}
</script>

<style lang="scss" scoped>
.loan-work-queue {
width: 100%;
height: calc(100vh - 166px);
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%);
margin: 10px;
padding: 0;
background: #fff;
overflow: hidden;
}
.style-title {
margin-bottom: 20px;
}

.ellipsis {
white-space : nowrap;
text-overflow : ellipsis;
overflow : hidden;
word-wrap : break-word;
}
.link-base-style-form {
.el-form-item {
margin-bottom : 12px;
}
padding-top : 30px;
// border-bottom : 1px solid #DCDCDC;
}
.super-flow-demo1 {
margin-top : 20px;
width : 100%;
height : 732px;
background-color : #f5f5f5;

> .node-container {
width : 200px;
float : left;
height : 100%;
text-align : center;
background-color : #FFFFFF;
}
> .flow-container {
width : calc(100% - 200px);
float : left;
height : 100%;
overflow : hidden;
position: relative;
}
.saveIcon {
position: absolute;
right: 10px;
bottom: 10px;
}
.super-flow__node {
.flow-node {
box-sizing : border-box;
width : 100%;
height : 100%;
line-height : 40px;
padding : 0 6px;
font-size : 16px;
color: #fff;
font-weight: bold;
.node-content {
text-align: center;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
padding-left: 3%;
}
}
}
/*开始节点样式*/
.ellipsis.flow-node-start {
// background: #55ABFC;
background: #6bc76a;
border-radius: 10px;
border: 1px solid #b4b4b4;
}
/*流程节点样式*/
.ellipsis.flow-node-process {
position: relative;
// background: #30B95C;
background: #b6c6e7;
border: 1px solid #b4b4b4;
}
/*条件节点样式*/
.ellipsis.flow-node-if {
width: 120px;
height: 120px;
position: relative;
top: 24px;
left: 24px;
// background: #BC1D16;
background: #b6e3e7;
border: 1px solid #b4b4b4;
transform: rotateZ(45deg); //倾斜
.node-content {
position: absolute;
top: 50%;
left: 20px;
width: 100%;
transform: rotateZ(-45deg) translateY(-75%);
}
}
/*结束节点样式*/
.ellipsis.flow-node-end {
// background: #000;
background: #299999;
border-radius: 10px;
border: 1px solid #b4b4b4;
}
}
.node-item {
font-size : 14px;
display : inline-block;
height : 30px;
width : 120px;
margin-top : 20px;
background-color : #FFFFFF;
line-height : 30px;
box-shadow : 1px 1px 4px rgba(0, 0, 0, 0.3);
cursor : pointer;
user-select : none;
text-align : center;
z-index : 6;
&:hover {
box-shadow : 1px 1px 8px rgba(0, 0, 0, 0.4);
}
}
</style>
<style>
.link-base-style-form .el-form-item__label {
text-align: left;
margin-left: 20px;
}
.link-base-style-form {
border: none;
}
.super-flow-demo1 .super-flow__node {
border: none;
background: none;
box-shadow: none;
}
</style>

 

参考 https://blog.csdn.net/qq_37899622/article/details/120187689

标签:node,vue,type,flow,label,meta,link,conf,super
From: https://www.cnblogs.com/luzanzan/p/17531236.html

相关文章

  • 前端Vue自定义顶部导航栏navBar 导航栏搜索框searchBar 导航栏右侧菜单按钮button
    前端Vue自定义顶部导航栏navBar导航栏搜索框searchBar导航栏右侧菜单按钮button,下载完整代码请访问uni-app插件市场地址:https://ext.dcloud.net.cn/plugin?id=13342效果图如下:cc-headerSearch使用方法<!--icon:右侧菜单图标@searchClick:搜索点击 @rigIconClick:右......
  • 前端Vue自定义带历史记录的搜索框组件searchBar 支持搜索输入框清空 搜索历史存储记录
    前端Vue自定义带历史记录的搜索框组件searchBar支持搜索输入框清空搜索历史存储记录清除,下载完整代码请访问uni-app插件市场地址:https://ext.dcloud.net.cn/plugin?id=13343效果图如下:cc-hisSearchBar使用方法//不同的业务功能历史记录设置不同存储keyconstkStora......
  • gitflow为什么要单独检出一个release分支?在develop分支上测试不行吗
    在Gitflow工作流中,将release分支从develop分支中单独检出的主要原因是为了在发布之前进行稳定性和功能测试,以确保发布版本的质量。以下是几个原因说明为什么要单独检出release分支进行测试:隔离开发和测试环境:通过将测试从开发环境(develop分支)隔离出来,可以避免测试中......
  • vuex的使用
    下载安装npmivuex搭建vuex环境1.创建文件:src/store/index.js//引入Vue核心库importVuefrom'vue'//引入VueximportVuexfrom'vuex'//应用Vuex插件Vue.use(Vuex)//准备actions对象——响应组件中用户的动作constactions={}//准备mutations对象——修改state......
  • vue(二)vue组件
    单文件组件在其他框架都鼓励把模板、逻辑和样式的代码区分成不同文件的时候,Vue却反其道行之。使用单文件组件,Vue把模板、相关脚本和CSS一起整合放在.vue结尾的一个单文件中。这些文件最终会通过JS打包工具(例如Webpack)处理,这意味着你可以使用构建时工具。你可以使用比如......
  • vue(一)vue项目结构
    安装Vuenpminstall-g@vue/clivue--version创建vue项目vuecreatevue-demo运行项目cdvue-demonpmrunserve安装vue高亮插件:vscode安装volar项目结构Vue的默认包管理器为yarn。。如果你此后需要使用不同的包管理器,则可以在运行vuecreate时传入参数--packageMa......
  • Vue 3.0 reactive/effect
    reactive.js:import{isObject}from"../utils";import{track,trigger}from"./effect";exportfunctionreactive(target){//判断target类型//如果是基本类型,则不将其转化成响应式if(!isObject(target))returntarget;constproxy......
  • 基于vue-cli 5 和webpack 5实现微前端
    有这么一个需求,项目里有很多业务模块,它们都有引用一些公共组件,每个业务模块打包后都是一个独立的应用,当公共组件修改时,单独打包公共组件,其他应用能够不需要重新构建,就能直接使用最新的公共组件,要怎么实现?一开始我想到的是使用网络资源,就是把公共组件打包后的js文件放到服务器,其他......
  • 向AI请教能否用图片生成vue代码
    Canfigmageneratevuecodebasedonascreenshotcapturedfromanandroidapp?Wed,Jul5,2023,3:49pmavatarNo,FigmadoesnotnativelygenerateVuecodebasedonascreenshotcapturedfromanAndroidapp.Figmaisprimarilyadesignandprototyping......
  • 10:vue3 组件注册方式(全局注册和局部注册)
    组件注册方式一个Vue组件在使用前需要先被“注册”,这样Vue才能在渲染模板时找到其对应的实现。组件注册有两种方式:全局注册和局部注册。全局注册将09节课程的Header组件进行全局注册演练在main.js中添加Header.vue组件的注册1import{createApp}from'vue'2impo......