|
@@ -4,11 +4,33 @@
|
|
|
class="h-60px shrink-0 border-b border-b-solid border-gray-200 flex items-center justify-between px-12px"
|
|
class="h-60px shrink-0 border-b border-b-solid border-gray-200 flex items-center justify-between px-12px"
|
|
|
>
|
|
>
|
|
|
<div class="left flex items-center gap-4">
|
|
<div class="left flex items-center gap-4">
|
|
|
- <el-breadcrumb separator="/">
|
|
|
|
|
|
|
+ <el-breadcrumb separator="/" class="flex items-center">
|
|
|
<el-breadcrumb-item>Workspace</el-breadcrumb-item>
|
|
<el-breadcrumb-item>Workspace</el-breadcrumb-item>
|
|
|
- <el-breadcrumb-item>workflow_1</el-breadcrumb-item>
|
|
|
|
|
|
|
+ <el-breadcrumb-item>
|
|
|
|
|
+ <Input ref="inputRef" v-model="workflow.name" variant="borderless" />
|
|
|
|
|
+ </el-breadcrumb-item>
|
|
|
</el-breadcrumb>
|
|
</el-breadcrumb>
|
|
|
- <IconButton icon="iconoir:plus" type="primary" link>标签</IconButton>
|
|
|
|
|
|
|
+ <div class="flex gap-2" v-show="!showTagInput" @click="showTagInput = true">
|
|
|
|
|
+ <el-tag type="info" v-for="tag in workflow.tags" :key="tag" :disable-transitions="false">
|
|
|
|
|
+ {{ tag }}
|
|
|
|
|
+ </el-tag>
|
|
|
|
|
+ </div>
|
|
|
|
|
+ <el-input-tag
|
|
|
|
|
+ v-show="showTagInput"
|
|
|
|
|
+ v-model="workflow.tags"
|
|
|
|
|
+ placeholder="按回车键添加标签"
|
|
|
|
|
+ aria-label="按回车键添加标签"
|
|
|
|
|
+ :max-tags="5"
|
|
|
|
|
+ @blur="showTagInput = false"
|
|
|
|
|
+ />
|
|
|
|
|
+ <IconButton
|
|
|
|
|
+ v-if="!workflow.tags?.length && !showTagInput"
|
|
|
|
|
+ icon="iconoir:plus"
|
|
|
|
|
+ type="primary"
|
|
|
|
|
+ link
|
|
|
|
|
+ @click="showTagInput = true"
|
|
|
|
|
+ >标签</IconButton
|
|
|
|
|
+ >
|
|
|
</div>
|
|
</div>
|
|
|
<div class="right flex items-center gap-2">
|
|
<div class="right flex items-center gap-2">
|
|
|
<el-button type="default" size="small">发布</el-button>
|
|
<el-button type="default" size="small">发布</el-button>
|
|
@@ -18,8 +40,8 @@
|
|
|
<template #dropdown>
|
|
<template #dropdown>
|
|
|
<el-dropdown-item>描述</el-dropdown-item>
|
|
<el-dropdown-item>描述</el-dropdown-item>
|
|
|
<el-dropdown-item>复用</el-dropdown-item>
|
|
<el-dropdown-item>复用</el-dropdown-item>
|
|
|
- <el-dropdown-item>重命名</el-dropdown-item>
|
|
|
|
|
- <el-dropdown-item divided>删除</el-dropdown-item>
|
|
|
|
|
|
|
+ <el-dropdown-item @click="handleRename">重命名</el-dropdown-item>
|
|
|
|
|
+ <el-dropdown-item divided @click="handleDelete">删除</el-dropdown-item>
|
|
|
</template>
|
|
</template>
|
|
|
</el-dropdown>
|
|
</el-dropdown>
|
|
|
</div>
|
|
</div>
|
|
@@ -40,7 +62,7 @@
|
|
|
<RunWorkflow v-model:visible="runVisible" />
|
|
<RunWorkflow v-model:visible="runVisible" />
|
|
|
<Setter
|
|
<Setter
|
|
|
:id="nodeID"
|
|
:id="nodeID"
|
|
|
- :workflow="workflow"
|
|
|
|
|
|
|
+ :workflow="workflow!"
|
|
|
@update:node:data="hangleUpdateNodeData"
|
|
@update:node:data="hangleUpdateNodeData"
|
|
|
v-model:visible="setterVisible"
|
|
v-model:visible="setterVisible"
|
|
|
/>
|
|
/>
|
|
@@ -54,18 +76,20 @@
|
|
|
</template>
|
|
</template>
|
|
|
|
|
|
|
|
<script setup lang="ts">
|
|
<script setup lang="ts">
|
|
|
-import { ref, inject, type CSSProperties, onBeforeUnmount } from 'vue'
|
|
|
|
|
|
|
+import { ref, inject, type CSSProperties, onBeforeUnmount, watch } from 'vue'
|
|
|
import { startNode, endNode, httpNode, conditionNode, databaseNode, codeNode } from '@repo/nodes'
|
|
import { startNode, endNode, httpNode, conditionNode, databaseNode, codeNode } from '@repo/nodes'
|
|
|
import { Workflow, type IWorkflow, type XYPosition, type Connection } from '@repo/workflow'
|
|
import { Workflow, type IWorkflow, type XYPosition, type Connection } from '@repo/workflow'
|
|
|
import { v4 as uuid } from 'uuid'
|
|
import { v4 as uuid } from 'uuid'
|
|
|
|
|
+import { useRoute, useRouter } from 'vue-router'
|
|
|
|
|
|
|
|
import Setter from '@/components/setter/index.vue'
|
|
import Setter from '@/components/setter/index.vue'
|
|
|
import RunWorkflow from '@/components/RunWorkflow/index.vue'
|
|
import RunWorkflow from '@/components/RunWorkflow/index.vue'
|
|
|
import EditorFooter from '@/features/editorFooter/index.vue'
|
|
import EditorFooter from '@/features/editorFooter/index.vue'
|
|
|
|
|
|
|
|
-import { IconButton } from '@repo/ui'
|
|
|
|
|
|
|
+import { IconButton, Input } from '@repo/ui'
|
|
|
|
|
|
|
|
import type { SourceType } from '@repo/nodes'
|
|
import type { SourceType } from '@repo/nodes'
|
|
|
|
|
+import { dayjs, ElMessageBox } from 'element-plus'
|
|
|
|
|
|
|
|
const layout = inject<{ setMainStyle: (style: CSSProperties) => void }>('layout')
|
|
const layout = inject<{ setMainStyle: (style: CSSProperties) => void }>('layout')
|
|
|
|
|
|
|
@@ -74,13 +98,36 @@ layout?.setMainStyle({
|
|
|
})
|
|
})
|
|
|
|
|
|
|
|
const footerHeight = ref(32)
|
|
const footerHeight = ref(32)
|
|
|
|
|
+const route = useRoute()
|
|
|
|
|
+const router = useRouter()
|
|
|
|
|
+const id = (route.params?.id as string) || uuid()
|
|
|
|
|
+const projectMap = JSON.parse(localStorage.getItem(`workflow-map`) || '{}') as Record<
|
|
|
|
|
+ string,
|
|
|
|
|
+ IWorkflow
|
|
|
|
|
+>
|
|
|
|
|
+const showTagInput = ref(false)
|
|
|
|
|
|
|
|
-const workflow = ref<IWorkflow>({
|
|
|
|
|
- id: uuid(),
|
|
|
|
|
- nodes: [startNode, endNode],
|
|
|
|
|
- edges: []
|
|
|
|
|
-})
|
|
|
|
|
|
|
+const workflow = ref<IWorkflow>(
|
|
|
|
|
+ projectMap?.[id]
|
|
|
|
|
+ ? projectMap[id]
|
|
|
|
|
+ : {
|
|
|
|
|
+ id,
|
|
|
|
|
+ name: 'workflow_1',
|
|
|
|
|
+ created: dayjs().format('MM 月 DD 日'),
|
|
|
|
|
+ nodes: [startNode, endNode],
|
|
|
|
|
+ edges: []
|
|
|
|
|
+ }
|
|
|
|
|
+)
|
|
|
|
|
+const inputRef = ref<InstanceType<typeof Input>>()
|
|
|
|
|
|
|
|
|
|
+watch(
|
|
|
|
|
+ () => workflow.value,
|
|
|
|
|
+ (workflow) => {
|
|
|
|
|
+ projectMap[id] = workflow
|
|
|
|
|
+ localStorage.setItem(`workflow-map`, JSON.stringify(projectMap))
|
|
|
|
|
+ },
|
|
|
|
|
+ { deep: true }
|
|
|
|
|
+)
|
|
|
/**
|
|
/**
|
|
|
* Editor
|
|
* Editor
|
|
|
*/
|
|
*/
|
|
@@ -96,13 +143,12 @@ const setterVisible = ref(false)
|
|
|
const runVisible = ref(false)
|
|
const runVisible = ref(false)
|
|
|
const handleRunWorkflow = () => {
|
|
const handleRunWorkflow = () => {
|
|
|
runVisible.value = true
|
|
runVisible.value = true
|
|
|
- console.log('run workflow')
|
|
|
|
|
}
|
|
}
|
|
|
const handleNodeCreate = (value: SourceType | string) => {
|
|
const handleNodeCreate = (value: SourceType | string) => {
|
|
|
const id = uuid()
|
|
const id = uuid()
|
|
|
if (typeof value === 'string') {
|
|
if (typeof value === 'string') {
|
|
|
if (value === 'stickyNote') {
|
|
if (value === 'stickyNote') {
|
|
|
- workflow.value.nodes.push({
|
|
|
|
|
|
|
+ workflow.value?.nodes.push({
|
|
|
id,
|
|
id,
|
|
|
type: 'canvas-node',
|
|
type: 'canvas-node',
|
|
|
zIndex: -1,
|
|
zIndex: -1,
|
|
@@ -135,7 +181,7 @@ const handleNodeCreate = (value: SourceType | string) => {
|
|
|
|
|
|
|
|
// 如果存在对应节点则添加
|
|
// 如果存在对应节点则添加
|
|
|
if (nodeToAdd) {
|
|
if (nodeToAdd) {
|
|
|
- workflow.value.nodes.push({
|
|
|
|
|
|
|
+ workflow.value?.nodes.push({
|
|
|
...nodeToAdd,
|
|
...nodeToAdd,
|
|
|
data: {
|
|
data: {
|
|
|
...nodeToAdd.data,
|
|
...nodeToAdd.data,
|
|
@@ -145,7 +191,7 @@ const handleNodeCreate = (value: SourceType | string) => {
|
|
|
id: uuid()
|
|
id: uuid()
|
|
|
})
|
|
})
|
|
|
}
|
|
}
|
|
|
- console.log(workflow.value.nodes, 'workflow.nodes')
|
|
|
|
|
|
|
+ console.log(workflow.value?.nodes, 'workflow.nodes')
|
|
|
}
|
|
}
|
|
|
const handleNodeClick = (id: string, position: XYPosition) => {
|
|
const handleNodeClick = (id: string, position: XYPosition) => {
|
|
|
nodeID.value = id
|
|
nodeID.value = id
|
|
@@ -156,14 +202,37 @@ const handleDrop = (position: XYPosition, event: DragEvent) => {
|
|
|
console.log('drag and drop at', position, event)
|
|
console.log('drag and drop at', position, event)
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
+/**
|
|
|
|
|
+ * 修改工作流名称
|
|
|
|
|
+ */
|
|
|
|
|
+const handleRename = () => {
|
|
|
|
|
+ inputRef.value?.focus()
|
|
|
|
|
+ inputRef.value?.select()
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+/**
|
|
|
|
|
+ * 删除工作流
|
|
|
|
|
+ */
|
|
|
|
|
+const handleDelete = () => {
|
|
|
|
|
+ ElMessageBox.confirm('确定要删除吗?', '提示', {
|
|
|
|
|
+ confirmButtonText: '确定',
|
|
|
|
|
+ cancelButtonText: '取消',
|
|
|
|
|
+ type: 'warning'
|
|
|
|
|
+ }).then(() => {
|
|
|
|
|
+ console.log('删除成功')
|
|
|
|
|
+ localStorage.removeItem(`project_${id}`)
|
|
|
|
|
+ router.push('/')
|
|
|
|
|
+ })
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
/**
|
|
/**
|
|
|
* 创建连线
|
|
* 创建连线
|
|
|
*/
|
|
*/
|
|
|
const onCreateConnection = (connection: Connection) => {
|
|
const onCreateConnection = (connection: Connection) => {
|
|
|
const { source, target } = connection
|
|
const { source, target } = connection
|
|
|
|
|
|
|
|
- if (!workflow.value.edges.some((edge) => edge.source === source && edge.target === target)) {
|
|
|
|
|
- workflow.value.edges.push({
|
|
|
|
|
|
|
+ if (!workflow.value?.edges.some((edge) => edge.source === source && edge.target === target)) {
|
|
|
|
|
+ workflow.value?.edges.push({
|
|
|
id: `edge-${source}-${target}`,
|
|
id: `edge-${source}-${target}`,
|
|
|
source,
|
|
source,
|
|
|
target,
|
|
target,
|
|
@@ -178,7 +247,7 @@ const onCreateConnection = (connection: Connection) => {
|
|
|
*/
|
|
*/
|
|
|
const handleUpdateNodesPosition = (events: { id: string; position: XYPosition }[]) => {
|
|
const handleUpdateNodesPosition = (events: { id: string; position: XYPosition }[]) => {
|
|
|
events?.forEach(({ id, position }) => {
|
|
events?.forEach(({ id, position }) => {
|
|
|
- const node = workflow.value.nodes.find((node) => node.id === id)
|
|
|
|
|
|
|
+ const node = workflow.value?.nodes.find((node) => node.id === id)
|
|
|
if (node) {
|
|
if (node) {
|
|
|
node.position = position
|
|
node.position = position
|
|
|
}
|
|
}
|
|
@@ -189,7 +258,7 @@ const handleUpdateNodesPosition = (events: { id: string; position: XYPosition }[
|
|
|
* 修改节点数据
|
|
* 修改节点数据
|
|
|
*/
|
|
*/
|
|
|
const hangleUpdateNodeData = (id: string, data: any) => {
|
|
const hangleUpdateNodeData = (id: string, data: any) => {
|
|
|
- const node = workflow.value.nodes.find((node) => node.id === id)
|
|
|
|
|
|
|
+ const node = workflow.value?.nodes.find((node) => node.id === id)
|
|
|
if (node) {
|
|
if (node) {
|
|
|
node.data = {
|
|
node.data = {
|
|
|
...node.data,
|
|
...node.data,
|
|
@@ -203,7 +272,7 @@ const hangleUpdateNodeData = (id: string, data: any) => {
|
|
|
* 修改节点属性
|
|
* 修改节点属性
|
|
|
*/
|
|
*/
|
|
|
const handleUpdateNodeProps = (id: string, attrs: Record<string, unknown>) => {
|
|
const handleUpdateNodeProps = (id: string, attrs: Record<string, unknown>) => {
|
|
|
- const node = workflow.value.nodes.find((node) => node.id === id)
|
|
|
|
|
|
|
+ const node = workflow.value?.nodes.find((node) => node.id === id)
|
|
|
if (node) {
|
|
if (node) {
|
|
|
if (node.data?.renderType === 'stickyNote') {
|
|
if (node.data?.renderType === 'stickyNote') {
|
|
|
Object.assign(node.data, attrs)
|
|
Object.assign(node.data, attrs)
|