@ -1,150 +0,0 @@ |
|||||
<template> |
|
||||
<div class="control-container"> |
|
||||
<!-- 功能按钮 --> |
|
||||
<ul> |
|
||||
<li |
|
||||
v-for="(item, key) in titleLists" |
|
||||
:key="key" |
|
||||
:title="item.text" |
|
||||
@mouseenter.prevent="onEnter(key)" |
|
||||
@mouseleave.prevent="focusIndex = -1" |
|
||||
> |
|
||||
<a-button |
|
||||
:disabled="item.disabled" |
|
||||
:style="{ cursor: item.disabled === false ? 'pointer' : 'not-allowed' }" |
|
||||
@click="onControl(item, key)" |
|
||||
> |
|
||||
<span :class="'iconfont ' + item.icon"></span> |
|
||||
<p>{{ item.text }}</p> |
|
||||
</a-button> |
|
||||
</li> |
|
||||
</ul> |
|
||||
</div> |
|
||||
</template> |
|
||||
|
|
||||
<script lang="ts"> |
|
||||
import { defineComponent, ref, unref, onMounted } from 'vue'; |
|
||||
|
|
||||
export default defineComponent({ |
|
||||
name: 'Control', |
|
||||
props: { |
|
||||
lf: Object || String, |
|
||||
catTurboData: Boolean, |
|
||||
}, |
|
||||
emits: ['catData'], |
|
||||
setup(props, { emit }) { |
|
||||
let focusIndex = ref(-1); |
|
||||
let titleLists = ref([ |
|
||||
{ |
|
||||
icon: 'icon-zoom-out-hs', |
|
||||
text: '缩小', |
|
||||
disabled: false, |
|
||||
}, |
|
||||
{ |
|
||||
icon: 'icon-enlarge-hs', |
|
||||
text: '放大', |
|
||||
disabled: false, |
|
||||
}, |
|
||||
{ |
|
||||
icon: 'icon-full-screen-hs', |
|
||||
text: '适应', |
|
||||
disabled: false, |
|
||||
}, |
|
||||
{ |
|
||||
icon: 'icon-previous-hs', |
|
||||
text: '上一步', |
|
||||
disabled: true, |
|
||||
}, |
|
||||
{ |
|
||||
icon: 'icon-next-step-hs', |
|
||||
text: '下一步', |
|
||||
disabled: true, |
|
||||
}, |
|
||||
{ |
|
||||
icon: 'icon-download-hs', |
|
||||
text: '下载图片', |
|
||||
disabled: false, |
|
||||
}, |
|
||||
{ |
|
||||
icon: 'icon-watch-hs', |
|
||||
text: '查看数据', |
|
||||
disabled: false, |
|
||||
}, |
|
||||
]); |
|
||||
|
|
||||
const onControl = (item, key) => { |
|
||||
['zoom', 'zoom', 'resetZoom', 'undo', 'redo', 'getSnapshot'].forEach((v, i) => { |
|
||||
let domControl = props.lf; |
|
||||
if (key === 1) { |
|
||||
domControl.zoom(true); |
|
||||
} |
|
||||
if (key === 6) { |
|
||||
emit('catData'); |
|
||||
} |
|
||||
if (key === i) { |
|
||||
domControl[v](); |
|
||||
} |
|
||||
}); |
|
||||
}; |
|
||||
|
|
||||
const onEnter = (key) => { |
|
||||
focusIndex.value = key; |
|
||||
}; |
|
||||
|
|
||||
onMounted(() => { |
|
||||
props.lf.on('history:change', ({ data: { undoAble, redoAble } }) => { |
|
||||
unref(titleLists)[3].disabled = !undoAble; |
|
||||
unref(titleLists)[4].disabled = !redoAble; |
|
||||
}); |
|
||||
}); |
|
||||
|
|
||||
return { |
|
||||
focusIndex, |
|
||||
titleLists, |
|
||||
onControl, |
|
||||
onEnter, |
|
||||
}; |
|
||||
}, |
|
||||
}); |
|
||||
</script> |
|
||||
|
|
||||
<style scoped> |
|
||||
@import './assets/iconfont/iconfont.css'; |
|
||||
|
|
||||
.control-container { |
|
||||
position: absolute; |
|
||||
right: 20px; |
|
||||
background: hsla(0, 0%, 100%, 0.8); |
|
||||
box-shadow: 0 1px 4px rgb(0 0 0 / 30%); |
|
||||
} |
|
||||
|
|
||||
.iconfont { |
|
||||
font-size: 25px; |
|
||||
} |
|
||||
|
|
||||
.control-container p { |
|
||||
margin: 0; |
|
||||
font-size: 12px; |
|
||||
} |
|
||||
|
|
||||
.control-container ul { |
|
||||
display: flex; |
|
||||
justify-content: space-around; |
|
||||
align-items: center; |
|
||||
margin: 2px; |
|
||||
} |
|
||||
|
|
||||
.control-container ul li { |
|
||||
width: 60px; |
|
||||
text-align: center; |
|
||||
} |
|
||||
|
|
||||
.control-container ul li button { |
|
||||
width: 100%; |
|
||||
height: 60px; |
|
||||
padding: 0; |
|
||||
background-color: transparent; |
|
||||
border: none; |
|
||||
outline: none; |
|
||||
} |
|
||||
</style> |
|
||||
@ -0,0 +1,156 @@ |
|||||
|
<template> |
||||
|
<div :class="`${prefixCls}-toolbar`" class="flex items-center px-2 py-1"> |
||||
|
<template v-for="(item, index) in toolbarItemList" :key="item.type || index"> |
||||
|
<Tooltip placement="bottom" v-bind="item.disabled ? { visible: false } : {}"> |
||||
|
<template #title>{{ item.tooltip }}</template> |
||||
|
<span :class="`${prefixCls}-toolbar__icon`" v-if="item.icon" @click="onControl(item)"> |
||||
|
<Icon |
||||
|
:icon="item.icon" |
||||
|
:class="item.disabled ? 'cursor-not-allowed disabeld' : 'cursor-pointer'" |
||||
|
/> |
||||
|
</span> |
||||
|
</Tooltip> |
||||
|
<Divider v-if="item.separate" type="vertical" /> |
||||
|
</template> |
||||
|
</div> |
||||
|
</template> |
||||
|
<script lang="ts"> |
||||
|
import type { ToolbarConfig } from './types'; |
||||
|
|
||||
|
import { defineComponent, ref, onUnmounted, unref, nextTick, watchEffect } from 'vue'; |
||||
|
import { Divider, Tooltip } from 'ant-design-vue'; |
||||
|
import { Icon } from '/@/components/Icon'; |
||||
|
|
||||
|
import { useFlowChartContext } from './useFlowContext'; |
||||
|
import { ToolbarTypeEnum } from './enum'; |
||||
|
|
||||
|
export default defineComponent({ |
||||
|
name: 'FlowChartToolbar', |
||||
|
components: { Icon, Divider, Tooltip }, |
||||
|
props: { |
||||
|
prefixCls: String, |
||||
|
}, |
||||
|
setup(_, { emit }) { |
||||
|
const toolbarItemList = ref<ToolbarConfig[]>([ |
||||
|
{ |
||||
|
type: ToolbarTypeEnum.ZOOM_IN, |
||||
|
icon: 'codicon:zoom-out', |
||||
|
tooltip: '缩小', |
||||
|
}, |
||||
|
{ |
||||
|
type: ToolbarTypeEnum.ZOOM_OUT, |
||||
|
icon: 'codicon:zoom-in', |
||||
|
tooltip: '放大', |
||||
|
}, |
||||
|
{ |
||||
|
type: ToolbarTypeEnum.RESET_ZOOM, |
||||
|
icon: 'codicon:screen-normal', |
||||
|
tooltip: '重置比例', |
||||
|
}, |
||||
|
{ separate: true }, |
||||
|
{ |
||||
|
type: ToolbarTypeEnum.UNDO, |
||||
|
icon: 'ion:arrow-undo-outline', |
||||
|
tooltip: '后退', |
||||
|
disabled: true, |
||||
|
}, |
||||
|
{ |
||||
|
type: ToolbarTypeEnum.REDO, |
||||
|
icon: 'ion:arrow-redo-outline', |
||||
|
tooltip: '前进', |
||||
|
disabled: true, |
||||
|
}, |
||||
|
{ separate: true }, |
||||
|
{ |
||||
|
type: ToolbarTypeEnum.SNAPSHOT, |
||||
|
icon: 'ion:download-outline', |
||||
|
tooltip: '下载', |
||||
|
}, |
||||
|
{ |
||||
|
type: ToolbarTypeEnum.VIEW_DATA, |
||||
|
icon: 'carbon:document-view', |
||||
|
tooltip: '查看数据', |
||||
|
}, |
||||
|
]); |
||||
|
|
||||
|
const { logicFlow } = useFlowChartContext(); |
||||
|
|
||||
|
function onHistoryChange({ data: { undoAble, redoAble } }) { |
||||
|
const itemsList = unref(toolbarItemList); |
||||
|
const undoIndex = itemsList.findIndex((item) => item.type === ToolbarTypeEnum.UNDO); |
||||
|
const redoIndex = itemsList.findIndex((item) => item.type === ToolbarTypeEnum.REDO); |
||||
|
if (undoIndex !== -1) { |
||||
|
unref(toolbarItemList)[undoIndex].disabled = !undoAble; |
||||
|
} |
||||
|
if (redoIndex !== -1) { |
||||
|
unref(toolbarItemList)[redoIndex].disabled = !redoAble; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
const onControl = (item) => { |
||||
|
const lf = unref(logicFlow); |
||||
|
if (!lf) { |
||||
|
return; |
||||
|
} |
||||
|
switch (item.type) { |
||||
|
case ToolbarTypeEnum.ZOOM_IN: |
||||
|
lf.zoom(); |
||||
|
break; |
||||
|
case ToolbarTypeEnum.ZOOM_OUT: |
||||
|
lf.zoom(true); |
||||
|
break; |
||||
|
case ToolbarTypeEnum.RESET_ZOOM: |
||||
|
lf.resetZoom(); |
||||
|
break; |
||||
|
case ToolbarTypeEnum.UNDO: |
||||
|
lf.undo(); |
||||
|
break; |
||||
|
case ToolbarTypeEnum.REDO: |
||||
|
lf.redo(); |
||||
|
break; |
||||
|
case ToolbarTypeEnum.SNAPSHOT: |
||||
|
lf.getSnapshot(); |
||||
|
break; |
||||
|
case ToolbarTypeEnum.VIEW_DATA: |
||||
|
emit('catData'); |
||||
|
break; |
||||
|
} |
||||
|
}; |
||||
|
|
||||
|
watchEffect(async () => { |
||||
|
if (unref(logicFlow)) { |
||||
|
await nextTick(); |
||||
|
unref(logicFlow)?.on('history:change', onHistoryChange); |
||||
|
} |
||||
|
}); |
||||
|
|
||||
|
onUnmounted(() => { |
||||
|
unref(logicFlow)?.off('history:change', onHistoryChange); |
||||
|
}); |
||||
|
return { toolbarItemList, onControl }; |
||||
|
}, |
||||
|
}); |
||||
|
</script> |
||||
|
<style lang="less" scoped> |
||||
|
@prefix-cls: ~'@{namespace}-flow-chart-toolbar'; |
||||
|
|
||||
|
.@{prefix-cls} { |
||||
|
height: 36px; |
||||
|
background: @content-background; |
||||
|
border-bottom: 1px solid @border-color-base; |
||||
|
|
||||
|
.disabeld { |
||||
|
color: @disabled-color; |
||||
|
} |
||||
|
|
||||
|
&__icon { |
||||
|
display: inline-block; |
||||
|
padding: 2px 4px; |
||||
|
margin-right: 10px; |
||||
|
|
||||
|
&:hover { |
||||
|
color: @primary-color; |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
</style> |
||||
@ -1,145 +0,0 @@ |
|||||
<template> |
|
||||
<!-- 左侧bpmn元素选择器 --> |
|
||||
<div class="node-panel"> |
|
||||
<div |
|
||||
class="node-item" |
|
||||
v-for="item in nodeList" |
|
||||
:key="item.text" |
|
||||
@mousedown="nodeDragNode(item)" |
|
||||
> |
|
||||
<div class="node-item-icon" :class="item.class"> |
|
||||
<div v-if="item.type === 'user' || item.type === 'time'" class="shape"></div> |
|
||||
</div> |
|
||||
<span class="node-label">{{ item.text }}</span> |
|
||||
</div> |
|
||||
</div> |
|
||||
</template> |
|
||||
|
|
||||
<script lang="ts"> |
|
||||
import { defineComponent, ref, unref } from 'vue'; |
|
||||
export default defineComponent({ |
|
||||
name: 'NodePanel', |
|
||||
props: { |
|
||||
lf: Object, |
|
||||
nodeList: Array, |
|
||||
}, |
|
||||
setup(props) { |
|
||||
let node = ref({ |
|
||||
type: 'rect', |
|
||||
property: { |
|
||||
a: 'efrwe', |
|
||||
b: 'wewe', |
|
||||
}, |
|
||||
}); |
|
||||
let properties = ref({ |
|
||||
a: 'efrwe', |
|
||||
b: 'wewe', |
|
||||
}); |
|
||||
|
|
||||
const nodeDragNode = (item) => { |
|
||||
props.lf.dnd.startDrag({ |
|
||||
type: item.type, |
|
||||
properties: unref(properties), |
|
||||
}); |
|
||||
}; |
|
||||
|
|
||||
return { |
|
||||
node, |
|
||||
properties, |
|
||||
nodeDragNode, |
|
||||
}; |
|
||||
}, |
|
||||
}); |
|
||||
</script> |
|
||||
|
|
||||
<style scoped> |
|
||||
.node-panel { |
|
||||
position: absolute; |
|
||||
top: 100px; |
|
||||
left: 50px; |
|
||||
z-index: 101; |
|
||||
width: 70px; |
|
||||
padding: 20px 10px; |
|
||||
text-align: center; |
|
||||
background-color: white; |
|
||||
border-radius: 6px; |
|
||||
box-shadow: 0 0 10px 1px rgb(228, 224, 219); |
|
||||
} |
|
||||
|
|
||||
.node-item { |
|
||||
margin-bottom: 20px; |
|
||||
} |
|
||||
|
|
||||
.node-item-icon { |
|
||||
display: flex; |
|
||||
height: 30px; |
|
||||
background-size: cover; |
|
||||
flex-wrap: wrap; |
|
||||
justify-content: center; |
|
||||
} |
|
||||
|
|
||||
.node-label { |
|
||||
margin-top: 5px; |
|
||||
font-size: 12px; |
|
||||
user-select: none; |
|
||||
} |
|
||||
|
|
||||
.node-start { |
|
||||
background: url('./background/start.png') no-repeat; |
|
||||
background-size: cover; |
|
||||
} |
|
||||
|
|
||||
.node-rect { |
|
||||
border: 1px solid black; |
|
||||
} |
|
||||
|
|
||||
.node-user { |
|
||||
background: url('./background/user.png') no-repeat; |
|
||||
background-size: cover; |
|
||||
} |
|
||||
|
|
||||
.node-time { |
|
||||
background: url('./background/time.png') no-repeat; |
|
||||
background-size: cover; |
|
||||
} |
|
||||
|
|
||||
.node-push { |
|
||||
background: url('./background/push.png') no-repeat; |
|
||||
background-size: cover; |
|
||||
} |
|
||||
|
|
||||
.node-download { |
|
||||
background: url('./background/download.png') no-repeat; |
|
||||
background-size: cover; |
|
||||
} |
|
||||
|
|
||||
.node-click { |
|
||||
background: url('./background/click.png') no-repeat; |
|
||||
background-size: cover; |
|
||||
} |
|
||||
|
|
||||
.node-end { |
|
||||
background: url('./background/end.png') no-repeat; |
|
||||
background-size: cover; |
|
||||
} |
|
||||
|
|
||||
.bpmn-start { |
|
||||
cursor: grab; |
|
||||
background: url('./assets/background/bpmn-start.png') center center no-repeat; |
|
||||
} |
|
||||
|
|
||||
.bpmn-end { |
|
||||
cursor: grab; |
|
||||
background: url('./assets/background/bpmn-end.png') center center no-repeat; |
|
||||
} |
|
||||
|
|
||||
.bpmn-user { |
|
||||
cursor: grab; |
|
||||
background: url('./assets/background/bpmn-user.png') center center no-repeat; |
|
||||
} |
|
||||
|
|
||||
.bpmn-exclusiveGateway { |
|
||||
cursor: grab; |
|
||||
background: url('./assets/background/bpmn-exclusiveGateway.png') center center no-repeat; |
|
||||
} |
|
||||
</style> |
|
||||
|
Before Width: | Height: | Size: 921 B |
|
Before Width: | Height: | Size: 830 B |
|
Before Width: | Height: | Size: 697 B |
|
Before Width: | Height: | Size: 754 B |
|
Before Width: | Height: | Size: 2.6 KiB |
|
Before Width: | Height: | Size: 2.0 KiB |
|
Before Width: | Height: | Size: 4.8 KiB |
|
Before Width: | Height: | Size: 2.2 KiB |
|
Before Width: | Height: | Size: 3.9 KiB |
|
Before Width: | Height: | Size: 2.9 KiB |
|
Before Width: | Height: | Size: 40 KiB |
@ -1,48 +0,0 @@ |
|||||
@font-face { |
|
||||
font-family: 'iconfont'; |
|
||||
src: url('iconfont.eot?t=1618544337340'); /* IE9 */ |
|
||||
src: url('iconfont.eot?t=1618544337340#iefix') format('embedded-opentype'), |
|
||||
/* IE6-IE8 */ |
|
||||
url('data:application/x-font-woff2;charset=utf-8;base64,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') |
|
||||
format('woff2'), |
|
||||
url('iconfont.woff?t=1618544337340') format('woff'), |
|
||||
url('iconfont.ttf?t=1618544337340') format('truetype'), |
|
||||
/* chrome, firefox, opera, Safari, Android, iOS 4.2+ */ |
|
||||
url('iconfont.svg?t=1618544337340#iconfont') format('svg'); /* iOS 4.1- */ |
|
||||
} |
|
||||
|
|
||||
.iconfont { |
|
||||
font-family: 'iconfont' !important; |
|
||||
font-size: 16px; |
|
||||
font-style: normal; |
|
||||
-webkit-font-smoothing: antialiased; |
|
||||
-moz-osx-font-smoothing: grayscale; |
|
||||
} |
|
||||
|
|
||||
.icon-full-screen-hs:before { |
|
||||
content: '\e656'; |
|
||||
} |
|
||||
|
|
||||
.icon-watch-hs:before { |
|
||||
content: '\e766'; |
|
||||
} |
|
||||
|
|
||||
.icon-download-hs:before { |
|
||||
content: '\e6af'; |
|
||||
} |
|
||||
|
|
||||
.icon-enlarge-hs:before { |
|
||||
content: '\e765'; |
|
||||
} |
|
||||
|
|
||||
.icon-previous-hs:before { |
|
||||
content: '\e84c'; |
|
||||
} |
|
||||
|
|
||||
.icon-zoom-out-hs:before { |
|
||||
content: '\e744'; |
|
||||
} |
|
||||
|
|
||||
.icon-next-step-hs:before { |
|
||||
content: '\e84b'; |
|
||||
} |
|
||||
@ -1,58 +0,0 @@ |
|||||
{ |
|
||||
"id": "2491438", |
|
||||
"name": "liu'c'tu", |
|
||||
"font_family": "iconfont", |
|
||||
"css_prefix_text": "icon-", |
|
||||
"description": "", |
|
||||
"glyphs": [ |
|
||||
{ |
|
||||
"icon_id": "755619", |
|
||||
"name": "自适应图标", |
|
||||
"font_class": "full-screen-hs", |
|
||||
"unicode": "e656", |
|
||||
"unicode_decimal": 58966 |
|
||||
}, |
|
||||
{ |
|
||||
"icon_id": "14445801", |
|
||||
"name": "查看", |
|
||||
"font_class": "watch-hs", |
|
||||
"unicode": "e766", |
|
||||
"unicode_decimal": 59238 |
|
||||
}, |
|
||||
{ |
|
||||
"icon_id": "9712640", |
|
||||
"name": "下载", |
|
||||
"font_class": "download-hs", |
|
||||
"unicode": "e6af", |
|
||||
"unicode_decimal": 59055 |
|
||||
}, |
|
||||
{ |
|
||||
"icon_id": "1029099", |
|
||||
"name": "放大", |
|
||||
"font_class": "enlarge-hs", |
|
||||
"unicode": "e765", |
|
||||
"unicode_decimal": 59237 |
|
||||
}, |
|
||||
{ |
|
||||
"icon_id": "20017362", |
|
||||
"name": "上一步", |
|
||||
"font_class": "previous-hs", |
|
||||
"unicode": "e84c", |
|
||||
"unicode_decimal": 59468 |
|
||||
}, |
|
||||
{ |
|
||||
"icon_id": "1010015", |
|
||||
"name": "缩小", |
|
||||
"font_class": "zoom-out-hs", |
|
||||
"unicode": "e744", |
|
||||
"unicode_decimal": 59204 |
|
||||
}, |
|
||||
{ |
|
||||
"icon_id": "20017363", |
|
||||
"name": "下一步", |
|
||||
"font_class": "next-step-hs", |
|
||||
"unicode": "e84b", |
|
||||
"unicode_decimal": 59467 |
|
||||
} |
|
||||
] |
|
||||
} |
|
||||
|
Before Width: | Height: | Size: 9.4 KiB |
@ -0,0 +1,11 @@ |
|||||
|
export enum ToolbarTypeEnum { |
||||
|
ZOOM_IN = 'zoomIn', |
||||
|
ZOOM_OUT = 'zoomOut', |
||||
|
RESET_ZOOM = 'resetZoom', |
||||
|
|
||||
|
UNDO = 'undo', |
||||
|
REDO = 'redo', |
||||
|
|
||||
|
SNAPSHOT = 'snapshot', |
||||
|
VIEW_DATA = 'viewData', |
||||
|
} |
||||
@ -0,0 +1,124 @@ |
|||||
|
<template> |
||||
|
<div class="h-full" :class="prefixCls"> |
||||
|
<FlowChartToolbar :prefixCls="prefixCls" v-if="toolbar" /> |
||||
|
<div ref="lfElRef" class="h-full"></div> |
||||
|
</div> |
||||
|
</template> |
||||
|
<script lang="ts"> |
||||
|
import type { Definition } from '@logicflow/core'; |
||||
|
|
||||
|
import { defineComponent, ref, onMounted, unref, nextTick, computed, watch } from 'vue'; |
||||
|
|
||||
|
import FlowChartToolbar from './FlowChartToolbar.vue'; |
||||
|
import LogicFlow from '@logicflow/core'; |
||||
|
import { Snapshot, BpmnElement, Menu, DndPanel } from '@logicflow/extension'; |
||||
|
|
||||
|
import { useDesign } from '/@/hooks/web/useDesign'; |
||||
|
import { createFlowChartContext } from './useFlowContext'; |
||||
|
|
||||
|
import { toLogicFlowData } from './adpterForTurbo'; |
||||
|
|
||||
|
import '@logicflow/core/dist/style/index.css'; |
||||
|
import '@logicflow/extension/lib/style/index.css'; |
||||
|
export default defineComponent({ |
||||
|
name: 'FlowChart', |
||||
|
components: { FlowChartToolbar }, |
||||
|
props: { |
||||
|
flowOptions: { |
||||
|
type: Object as PropType<Definition>, |
||||
|
default: () => {}, |
||||
|
}, |
||||
|
|
||||
|
data: { |
||||
|
type: Object as PropType<any>, |
||||
|
default: () => {}, |
||||
|
}, |
||||
|
|
||||
|
toolbar: { |
||||
|
type: Boolean, |
||||
|
default: true, |
||||
|
}, |
||||
|
}, |
||||
|
setup(props) { |
||||
|
const lfElRef = ref<ElRef>(null); |
||||
|
|
||||
|
const lfInstance = ref<Nullable<LogicFlow>>(null); |
||||
|
|
||||
|
const { prefixCls } = useDesign('flow-chart'); |
||||
|
createFlowChartContext({ |
||||
|
logicFlow: (lfInstance as unknown) as LogicFlow, |
||||
|
}); |
||||
|
|
||||
|
const getFlowOptions = computed(() => { |
||||
|
const { flowOptions } = props; |
||||
|
|
||||
|
const defaultOptions: Partial<Definition> = { |
||||
|
grid: true, |
||||
|
background: { |
||||
|
color: '#f7f9ff', |
||||
|
}, |
||||
|
keyboard: { |
||||
|
enabled: true, |
||||
|
}, |
||||
|
...flowOptions, |
||||
|
}; |
||||
|
return defaultOptions as Definition; |
||||
|
}); |
||||
|
|
||||
|
watch( |
||||
|
() => props.data, |
||||
|
() => { |
||||
|
onRender(); |
||||
|
} |
||||
|
); |
||||
|
|
||||
|
watch( |
||||
|
() => props.flowOptions, |
||||
|
(options) => { |
||||
|
unref(lfInstance)?.updateEditConfig(options); |
||||
|
} |
||||
|
); |
||||
|
|
||||
|
// init logicFlow |
||||
|
async function init() { |
||||
|
await nextTick(); |
||||
|
|
||||
|
const lfEl = unref(lfElRef); |
||||
|
if (!lfEl) { |
||||
|
return; |
||||
|
} |
||||
|
|
||||
|
// Canvas configuration |
||||
|
LogicFlow.use(Snapshot); |
||||
|
// Use the bpmn plug-in to introduce bpmn elements, which can be used after conversion in turbo |
||||
|
LogicFlow.use(BpmnElement); |
||||
|
// Start the right-click menu |
||||
|
LogicFlow.use(Menu); |
||||
|
LogicFlow.use(DndPanel); |
||||
|
lfInstance.value = new LogicFlow({ |
||||
|
...unref(getFlowOptions), |
||||
|
container: lfEl, |
||||
|
}); |
||||
|
unref(lfInstance)?.setDefaultEdgeType('line'); |
||||
|
onRender(); |
||||
|
} |
||||
|
|
||||
|
async function onRender() { |
||||
|
await nextTick(); |
||||
|
const lf = unref(lfInstance); |
||||
|
if (!lf) { |
||||
|
return; |
||||
|
} |
||||
|
const lFData = toLogicFlowData(props.data); |
||||
|
lf.render(lFData); |
||||
|
} |
||||
|
|
||||
|
onMounted(init); |
||||
|
|
||||
|
return { |
||||
|
prefixCls, |
||||
|
lfElRef, |
||||
|
}; |
||||
|
}, |
||||
|
}); |
||||
|
</script> |
||||
@ -0,0 +1,14 @@ |
|||||
|
import { NodeConfig } from '@logicflow/core'; |
||||
|
import { ToolbarTypeEnum } from './enum'; |
||||
|
|
||||
|
export interface NodeItem extends NodeConfig { |
||||
|
icon: string; |
||||
|
} |
||||
|
|
||||
|
export interface ToolbarConfig { |
||||
|
type?: string | ToolbarTypeEnum; |
||||
|
tooltip?: string | boolean; |
||||
|
icon?: string; |
||||
|
disabled?: boolean; |
||||
|
separate?: boolean; |
||||
|
} |
||||
@ -0,0 +1,17 @@ |
|||||
|
import type LogicFlow from '@logicflow/core'; |
||||
|
|
||||
|
import { provide, inject } from 'vue'; |
||||
|
|
||||
|
const key = Symbol('flow-chart'); |
||||
|
|
||||
|
type Instance = { |
||||
|
logicFlow: LogicFlow; |
||||
|
}; |
||||
|
|
||||
|
export function createFlowChartContext(instance: Instance) { |
||||
|
provide(key, instance); |
||||
|
} |
||||
|
|
||||
|
export function useFlowChartContext(): Instance { |
||||
|
return inject(key) as Instance; |
||||
|
} |
||||
@ -0,0 +1,4 @@ |
|||||
|
export default { |
||||
|
name: 'Graphics editor', |
||||
|
flowChart: 'FlowChart', |
||||
|
}; |
||||
@ -0,0 +1,4 @@ |
|||||
|
export default { |
||||
|
name: '图形编辑器', |
||||
|
flowChart: '流程图', |
||||
|
}; |
||||
@ -0,0 +1,18 @@ |
|||||
|
import type { MenuModule } from '/@/router/types'; |
||||
|
import { t } from '/@/hooks/web/useI18n'; |
||||
|
|
||||
|
const menu: MenuModule = { |
||||
|
orderNo: 5000, |
||||
|
menu: { |
||||
|
name: t('routes.demo.flow.name'), |
||||
|
path: '/flow', |
||||
|
|
||||
|
children: [ |
||||
|
{ |
||||
|
path: 'flowChart', |
||||
|
name: t('routes.demo.flow.flowChart'), |
||||
|
}, |
||||
|
], |
||||
|
}, |
||||
|
}; |
||||
|
export default menu; |
||||
@ -0,0 +1,27 @@ |
|||||
|
import type { AppRouteModule } from '/@/router/types'; |
||||
|
|
||||
|
import { LAYOUT } from '/@/router/constant'; |
||||
|
import { t } from '/@/hooks/web/useI18n'; |
||||
|
|
||||
|
const charts: AppRouteModule = { |
||||
|
path: '/flow', |
||||
|
name: 'FlowDemo', |
||||
|
component: LAYOUT, |
||||
|
redirect: '/flow/flowChart', |
||||
|
meta: { |
||||
|
icon: 'tabler:chart-dots', |
||||
|
title: t('routes.demo.flow.name'), |
||||
|
}, |
||||
|
children: [ |
||||
|
{ |
||||
|
path: 'flowChart', |
||||
|
name: 'flowChartDemo', |
||||
|
component: () => import('/@/views/demo/comp/flow-chart/index.vue'), |
||||
|
meta: { |
||||
|
title: t('routes.demo.flow.flowChart'), |
||||
|
}, |
||||
|
}, |
||||
|
], |
||||
|
}; |
||||
|
|
||||
|
export default charts; |
||||