节点初步完成
This commit is contained in:
@@ -53,10 +53,10 @@
|
||||
<div class="flex-1 flex">
|
||||
<!-- 左侧工作流面板 -->
|
||||
<div
|
||||
class="bg-white shadow-lg transition-all duration-300 overflow-hidden"
|
||||
class="bg-white shadow-lg transition-all duration-300 overflow-hidden flex flex-col"
|
||||
:class="{ 'w-0': !showLeftPanel, 'w-280px': showLeftPanel }"
|
||||
>
|
||||
<div class="p-4 border-b border-gray-200 flex justify-between items-center">
|
||||
<div class="p-4 border-b border-gray-200 flex justify-between items-center flex-shrink-0">
|
||||
<h2 class="text-lg font-semibold text-gray-800">
|
||||
任务流
|
||||
</h2>
|
||||
@@ -69,20 +69,27 @@
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<div class="p-4 h-[calc(100vh-120px)] overflow-y-auto">
|
||||
<div class="flex-1 overflow-y-auto p-4">
|
||||
<draggable
|
||||
v-model="workflowNodes"
|
||||
:animation="200"
|
||||
class="space-y-2 min-h-full"
|
||||
item-key="id"
|
||||
handle=".drag-handle"
|
||||
@change="onWorkflowChange"
|
||||
>
|
||||
<template #item="{ element, index }">
|
||||
<div
|
||||
class="p-3 border rounded-lg cursor-move hover:shadow-md transition-shadow"
|
||||
class="p-3 border rounded-lg hover:shadow-md transition-shadow drag-handle"
|
||||
:style="{ backgroundColor: element.backgroundColor, color: element.textColor }"
|
||||
>
|
||||
<div class="flex items-center justify-between">
|
||||
<div class="flex items-center space-x-2">
|
||||
<div class="flex items-center space-x-2 cursor-move flex-1">
|
||||
<div class="flex flex-col space-y-0.5 opacity-60">
|
||||
<div class="w-1 h-1 bg-current rounded-full" />
|
||||
<div class="w-1 h-1 bg-current rounded-full" />
|
||||
<div class="w-1 h-1 bg-current rounded-full" />
|
||||
</div>
|
||||
<component
|
||||
:is="getIconComponent(element.icon)"
|
||||
class="w-4 h-4"
|
||||
@@ -93,13 +100,13 @@
|
||||
<div class="flex items-center space-x-1">
|
||||
<button
|
||||
v-if="element.config?.editable"
|
||||
class="p-1 rounded hover:bg-white/20"
|
||||
class="p-1 rounded hover:bg-white/20 cursor-pointer"
|
||||
@click.stop="editNodeConfig(element)"
|
||||
>
|
||||
<Settings class="w-3 h-3" />
|
||||
</button>
|
||||
<button
|
||||
class="p-1 rounded hover:bg-white/20"
|
||||
class="p-1 rounded hover:bg-white/20 cursor-pointer"
|
||||
@click.stop="removeNode(index)"
|
||||
>
|
||||
<X class="w-3 h-3" />
|
||||
@@ -107,7 +114,7 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div v-if="element.config?.duration" class="mt-2 text-xs opacity-80">
|
||||
<div v-if="element.config?.duration" class="mt-2 text-xs opacity-80 ml-6">
|
||||
延时: {{ element.config.duration }}ms
|
||||
</div>
|
||||
</div>
|
||||
@@ -115,12 +122,15 @@
|
||||
</draggable>
|
||||
|
||||
<div
|
||||
v-if="workflowNodes.length === 0" class="text-center py-8 transition-colors"
|
||||
:class="dragOverWorkflow ? 'text-blue-600' : 'text-gray-400'"
|
||||
v-if="workflowNodes.length === 0"
|
||||
class="text-center py-8 text-gray-400 flex flex-col items-center justify-center min-h-[200px]"
|
||||
>
|
||||
<Move class="w-8 h-8 mx-auto mb-2" />
|
||||
<p class="text-sm">
|
||||
{{ dragOverWorkflow ? '松开鼠标添加节点' : '从右侧拖拽节点到此处' }}
|
||||
从右侧点击节点添加到此处
|
||||
</p>
|
||||
<p class="text-xs mt-1 opacity-75">
|
||||
添加后可拖拽排序
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
@@ -136,38 +146,40 @@
|
||||
</button>
|
||||
|
||||
<!-- 右侧节点库 -->
|
||||
<div class="flex-1 p-6 bg-gray-50">
|
||||
<div class="mb-6">
|
||||
<div class="flex-1 flex flex-col bg-gray-50 min-h-0">
|
||||
<div class="p-6 pb-4 bg-gray-50 flex-shrink-0">
|
||||
<h1 class="text-2xl font-bold text-gray-900">
|
||||
节点库
|
||||
</h1>
|
||||
<p class="text-gray-600 mt-1">
|
||||
拖拽节点到左侧创建任务流
|
||||
点击节点添加到左侧任务流
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<!-- 节点库 - 改为点击按钮 -->
|
||||
<div class="space-y-6">
|
||||
<div v-for="(nodes, category) in categorizedNodes" :key="category">
|
||||
<h3 class="text-lg font-semibold text-gray-800 mb-3">
|
||||
{{ category }}
|
||||
</h3>
|
||||
<div class="grid grid-cols-4 gap-4">
|
||||
<button
|
||||
v-for="element in nodes"
|
||||
:key="element.id"
|
||||
class="p-4 border rounded-lg cursor-pointer hover:shadow-lg transition-all hover:scale-105 text-center"
|
||||
:style="{ backgroundColor: element.backgroundColor, color: element.textColor }"
|
||||
@click="addNodeToWorkflow(element)"
|
||||
>
|
||||
<component
|
||||
:is="getIconComponent(element.icon)"
|
||||
class="w-8 h-8 mx-auto mb-2"
|
||||
/>
|
||||
<p class="text-sm font-medium">
|
||||
{{ element.name }}
|
||||
</p>
|
||||
</button>
|
||||
<!-- 节点库 - 独立滚动区域 -->
|
||||
<div class="flex-1 overflow-y-auto px-6 pb-6 min-h-0">
|
||||
<div class="space-y-6">
|
||||
<div v-for="(nodes, category) in categorizedNodes" :key="category">
|
||||
<h3 class="text-lg font-semibold text-gray-800 mb-3">
|
||||
{{ category }}
|
||||
</h3>
|
||||
<div class="grid grid-cols-4 gap-4">
|
||||
<button
|
||||
v-for="element in nodes"
|
||||
:key="element.id"
|
||||
class="p-4 border rounded-lg cursor-pointer hover:shadow-lg transition-all hover:scale-105 text-center"
|
||||
:style="{ backgroundColor: element.backgroundColor, color: element.textColor }"
|
||||
@click="addNodeToWorkflow(element)"
|
||||
>
|
||||
<component
|
||||
:is="getIconComponent(element.icon)"
|
||||
class="w-8 h-8 mx-auto mb-2"
|
||||
/>
|
||||
<p class="text-sm font-medium">
|
||||
{{ element.name }}
|
||||
</p>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -222,7 +234,7 @@
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import type { FlowNode } from "~/components/flow-nodes";
|
||||
import type { FlowNode } from "~/components/flow-nodes/nodes";
|
||||
import {
|
||||
ArrowLeft,
|
||||
ChevronLeft,
|
||||
@@ -250,14 +262,12 @@
|
||||
import { computed, ref } from "vue";
|
||||
import { useRouter } from "vue-router";
|
||||
import draggable from "vuedraggable";
|
||||
import { getNodesByCategory, nodeDefinitions } from "~/components/flow-nodes";
|
||||
import { getNodesByCategory, nodeDefinitions } from "~/components/flow-nodes/nodes";
|
||||
|
||||
const router = useRouter();
|
||||
const workflowNodes = ref<FlowNode[]>([]);
|
||||
const showLeftPanel = ref(true);
|
||||
const editingNode = ref<FlowNode | null>(null);
|
||||
const isDragging = ref(false);
|
||||
const dragOverWorkflow = ref(false);
|
||||
|
||||
const categorizedNodes = computed(() => getNodesByCategory());
|
||||
|
||||
@@ -282,7 +292,7 @@
|
||||
const cloneNode = (original: FlowNode): FlowNode => {
|
||||
// 生成更健壮的唯一ID
|
||||
const timestamp = Date.now();
|
||||
const randomString = Math.random().toString(36).substr(2, 9);
|
||||
const randomString = Math.random().toString(36).substring(2, 11);
|
||||
const uniqueId = `${original.id}-${timestamp}-${randomString}`;
|
||||
|
||||
return {
|
||||
@@ -335,30 +345,6 @@
|
||||
console.log("当前工作流节点:", workflowNodes.value);
|
||||
};
|
||||
|
||||
// 拖拽事件处理器用于视觉反馈
|
||||
const onDragStart = (event: any) => {
|
||||
isDragging.value = true;
|
||||
console.log("开始拖拽", event);
|
||||
};
|
||||
|
||||
const onDragEnd = (event: any) => {
|
||||
isDragging.value = false;
|
||||
dragOverWorkflow.value = false;
|
||||
console.log("拖拽结束", event);
|
||||
};
|
||||
|
||||
const onDragEnter = (event: any) => {
|
||||
if (isDragging.value) {
|
||||
dragOverWorkflow.value = true;
|
||||
console.log("进入工作流区域", event);
|
||||
}
|
||||
};
|
||||
|
||||
const onDragLeave = (event: any) => {
|
||||
dragOverWorkflow.value = false;
|
||||
console.log("离开工作流区域", event);
|
||||
};
|
||||
|
||||
const editNodeConfig = (node: FlowNode) => {
|
||||
editingNode.value = { ...node };
|
||||
};
|
||||
@@ -463,4 +449,40 @@
|
||||
.w-280px {
|
||||
width: 280px;
|
||||
}
|
||||
|
||||
/* 拖拽时的样式 */
|
||||
.sortable-ghost {
|
||||
opacity: 0.5;
|
||||
}
|
||||
|
||||
.sortable-chosen {
|
||||
transform: scale(1.02);
|
||||
}
|
||||
|
||||
.sortable-drag {
|
||||
transform: rotate(2deg);
|
||||
}
|
||||
|
||||
/* 确保滚动容器独立 */
|
||||
.overflow-y-auto {
|
||||
scrollbar-width: thin;
|
||||
scrollbar-color: rgba(156, 163, 175, 0.5) transparent;
|
||||
}
|
||||
|
||||
.overflow-y-auto::-webkit-scrollbar {
|
||||
width: 6px;
|
||||
}
|
||||
|
||||
.overflow-y-auto::-webkit-scrollbar-track {
|
||||
background: transparent;
|
||||
}
|
||||
|
||||
.overflow-y-auto::-webkit-scrollbar-thumb {
|
||||
background-color: rgba(156, 163, 175, 0.5);
|
||||
border-radius: 3px;
|
||||
}
|
||||
|
||||
.overflow-y-auto::-webkit-scrollbar-thumb:hover {
|
||||
background-color: rgba(156, 163, 175, 0.7);
|
||||
}
|
||||
</style>
|
||||
|
5
app/layouts/flow.vue
Normal file
5
app/layouts/flow.vue
Normal file
@@ -0,0 +1,5 @@
|
||||
<template>
|
||||
<div>
|
||||
<slot />
|
||||
</div>
|
||||
</template>
|
@@ -3,5 +3,9 @@
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
// 简单的页面包装器,使用WorkflowEditor组件
|
||||
import WorkflowEditor from "~/components/flow-nodes/WorkflowEditor.vue";
|
||||
|
||||
definePageMeta({
|
||||
layout: "flow"
|
||||
});
|
||||
</script>
|
||||
|
Reference in New Issue
Block a user