feat(Workflow): add modules

This commit is contained in:
jxxghp
2025-02-23 13:16:01 +08:00
parent 24a8125621
commit 7a025bcd38
7 changed files with 230 additions and 0 deletions

View File

@@ -26,7 +26,12 @@
"@fullcalendar/timegrid": "^6.1.15",
"@fullcalendar/vue3": "^6.1.15",
"@iconify/utils": "^2.2.1",
"@vue-flow/background": "^1.3.2",
"@vue-flow/controls": "^1.1.2",
"@vue-flow/core": "^1.42.1",
"@vue-flow/minimap": "^1.5.2",
"@vue-flow/node-resizer": "^1.4.0",
"@vue-flow/node-toolbar": "^1.1.0",
"@vue-js-cron/vuetify": "^5.0.9",
"@vueuse/core": "^12.4.0",
"@vueuse/math": "^12.4.0",

120
src/@core/utils/workflow.ts Normal file
View File

@@ -0,0 +1,120 @@
import { useVueFlow } from '@vue-flow/core'
import { ref, watch } from 'vue'
let id = 0
/**
* @returns {string} - A unique id.
*/
function getId() {
return `dndnode_${id++}`
}
/**
* In a real world scenario you'd want to avoid creating refs in a global scope like this as they might not be cleaned up properly.
* @type {{draggedType: Ref<string|null>, isDragOver: Ref<boolean>, isDragging: Ref<boolean>}}
*/
const state = {
/**
* The type of the node being dragged.
*/
draggedType: ref(null),
isDragOver: ref(false),
isDragging: ref(false),
}
export default function useDragAndDrop() {
const { draggedType, isDragOver, isDragging } = state
const { addNodes, screenToFlowCoordinate, onNodesInitialized, updateNode } = useVueFlow()
watch(isDragging, dragging => {
document.body.style.userSelect = dragging ? 'none' : ''
})
function onDragStart(event: any, type: any) {
if (event.dataTransfer) {
event.dataTransfer.setData('application/vueflow', type)
event.dataTransfer.effectAllowed = 'move'
}
draggedType.value = type
isDragging.value = true
document.addEventListener('drop', onDragEnd)
}
/**
* Handles the drag over event.
*
* @param {DragEvent} event
*/
function onDragOver(event: any) {
event.preventDefault()
if (draggedType.value) {
isDragOver.value = true
if (event.dataTransfer) {
event.dataTransfer.dropEffect = 'move'
}
}
}
function onDragLeave() {
isDragOver.value = false
}
function onDragEnd() {
isDragging.value = false
isDragOver.value = false
draggedType.value = null
document.removeEventListener('drop', onDragEnd)
}
/**
* Handles the drop event.
*
* @param {DragEvent} event
*/
function onDrop(event: any) {
const position = screenToFlowCoordinate({
x: event.clientX,
y: event.clientY,
})
const nodeId = getId()
const newNode = {
id: nodeId,
type: draggedType.value || undefined,
position,
data: { label: nodeId },
}
/**
* Align node position after drop, so it's centered to the mouse
*
* We can hook into events even in a callback, and we can remove the event listener after it's been called.
*/
const { off } = onNodesInitialized(() => {
updateNode(nodeId, node => ({
position: { x: node.position.x - node.dimensions.width / 2, y: node.position.y - node.dimensions.height / 2 },
}))
off()
})
addNodes(newNode)
}
return {
draggedType,
isDragOver,
isDragging,
onDragStart,
onDragLeave,
onDragOver,
onDrop,
}
}

View File

@@ -0,0 +1,39 @@
<script lang="ts" setup>
import { ref } from 'vue'
import { VueFlow, useVueFlow } from '@vue-flow/core'
import Sidebar from '../workflow/Sidebar.vue'
import DropzoneBackground from '../workflow/DropzoneBackground.vue'
import useDragAndDrop from '@core/utils/workflow'
const { onConnect, addEdges } = useVueFlow()
const { onDragOver, onDrop, onDragLeave, isDragOver } = useDragAndDrop()
const nodes = ref([])
onConnect(addEdges)
</script>
<template>
<div class="dnd-flow" @drop="onDrop">
<VueFlow :nodes="nodes" @dragover="onDragOver" @dragleave="onDragLeave">
<DropzoneBackground
:style="{
backgroundColor: isDragOver ? '#e7f3ff' : 'transparent',
transition: 'background-color 0.2s ease',
}"
>
<p v-if="isDragOver">Drop here</p>
</DropzoneBackground>
</VueFlow>
<Sidebar />
</div>
</template>
<style>
@import '@vue-flow/core/dist/style.css';
@import '@vue-flow/core/dist/theme-default.css';
@import '@vue-flow/controls/dist/style.css';
@import '@vue-flow/minimap/dist/style.css';
@import '@vue-flow/node-resizer/dist/style.css';
</style>

View File

@@ -0,0 +1,13 @@
<script lang="ts" setup>
import { Background } from '@vue-flow/background'
</script>
<template>
<div class="dropzone-background">
<Background :size="2" :gap="20" pattern-color="#BDBDBD" />
<div class="overlay">
<slot />
</div>
</div>
</template>

View File

@@ -0,0 +1,21 @@
<script lang="ts" setup>
import useDragAndDrop from '@core/utils/workflow'
const { onDragStart } = useDragAndDrop()
</script>
<template>
<aside>
<div class="description">You can drag these nodes to the pane.</div>
<div class="nodes">
<div class="vue-flow__node-input" :draggable="true" @dragstart="onDragStart($event, 'input')">Input Node</div>
<div class="vue-flow__node-default" :draggable="true" @dragstart="onDragStart($event, 'default')">
Default Node
</div>
<div class="vue-flow__node-output" :draggable="true" @dragstart="onDragStart($event, 'output')">Output Node</div>
</div>
</aside>
</template>

View File

@@ -2,6 +2,7 @@
import api from '@/api'
import { Workflow } from '@/api/types'
import { useDisplay } from 'vuetify'
import WorkflowEditDialog from '@/components/dialog/WorkflowEditDialog.vue'
// APP
const display = useDisplay()

View File

@@ -2082,6 +2082,16 @@
path-browserify "^1.0.1"
vscode-uri "^3.0.8"
"@vue-flow/background@^1.3.2":
version "1.3.2"
resolved "https://registry.yarnpkg.com/@vue-flow/background/-/background-1.3.2.tgz#0c90cd05e5d60da017bbaf5a1c3eb6af7ed9b778"
integrity sha512-eJPhDcLj1wEo45bBoqTXw1uhl0yK2RaQGnEINqvvBsAFKh/camHJd5NPmOdS1w+M9lggc9igUewxaEd3iCQX2w==
"@vue-flow/controls@^1.1.2":
version "1.1.2"
resolved "https://registry.yarnpkg.com/@vue-flow/controls/-/controls-1.1.2.tgz#d71899ed793f741400d043efdd49765a3a94c75f"
integrity sha512-6dtl/JnwDBNau5h3pDBdOCK6tdxiVAOL3cyruRL61gItwq5E97Hmjmj2BIIqX2p7gU1ENg3z80Z4zlu58fGlsg==
"@vue-flow/core@^1.42.1":
version "1.42.1"
resolved "https://registry.yarnpkg.com/@vue-flow/core/-/core-1.42.1.tgz#d4274c7452766f05e642317af197be85d4c6188e"
@@ -2092,6 +2102,27 @@
d3-selection "^3.0.0"
d3-zoom "^3.0.0"
"@vue-flow/minimap@^1.5.2":
version "1.5.2"
resolved "https://registry.yarnpkg.com/@vue-flow/minimap/-/minimap-1.5.2.tgz#8d036800c52ec77bfe1586c74d51c66e017f5ef6"
integrity sha512-XNSpWwwXfCWqJilc2eCW+3ry3r9vhF8HmUw5wrAsUTHiss4R9k5uZLABo7c3T3VdcVRJ8pTfUJ9vjpzb8H+FKg==
dependencies:
d3-selection "^3.0.0"
d3-zoom "^3.0.0"
"@vue-flow/node-resizer@^1.4.0":
version "1.4.0"
resolved "https://registry.yarnpkg.com/@vue-flow/node-resizer/-/node-resizer-1.4.0.tgz#a1603d4fff73c6282096d34e57243de6e719b151"
integrity sha512-S52MRcSpd6asza8Cl0bKM2sHGrbq7vBydKHDuPdoTD+cvjNX6XF4LSiPZOuzExePI6b+O6dg2EZ1378oOLGFpA==
dependencies:
d3-drag "^3.0.0"
d3-selection "^3.0.0"
"@vue-flow/node-toolbar@^1.1.0":
version "1.1.0"
resolved "https://registry.yarnpkg.com/@vue-flow/node-toolbar/-/node-toolbar-1.1.0.tgz#d28c554789be533ca2bd9ef5cbc342e6016332ea"
integrity sha512-6RVDHgY+x8m1cXPaEkqPa/RMR90AC1hPHYBK/QVh8k6lJnFPgwJ9PSiYoC4amsUiDK0mF0Py+PlztLJY1ty+4A==
"@vue-js-cron/core@5.4.1":
version "5.4.1"
resolved "https://registry.yarnpkg.com/@vue-js-cron/core/-/core-5.4.1.tgz#5c9b4a3b65f215f5f6767b56f2bc7e7d87e32834"