节点拖拽功能正常

This commit is contained in:
2025-08-22 14:09:10 +08:00
parent dbf190597b
commit 6351c038b5
2 changed files with 126 additions and 38 deletions

View File

@@ -48,3 +48,30 @@
- Maintain proper layout and responsive behavior
- Fix overflow handling for both panels
- _Requirements: 4.1, 4.2_
- [x] 8. Fix drag and drop sorting functionality
- Configure vuedraggable with proper options for smooth sorting
- Add force-fallback option to ensure consistent behavior
- Improve drag handle visibility and interaction
- Fix drag event handling for proper reordering
- _Requirements: 1.2, 1.3, 2.2_
- [x] 9. Remove left panel collapse functionality
- Remove collapse/expand button and related functionality
- Simplify left panel layout to fixed width
- Clean up unused state variables and imports
- _Requirements: 4.1_
- [x] 10. Implement desktop application UX
- Disable text selection throughout the application
- Prevent web-like drag selection behavior
- Maintain text selection only for input fields
- Add proper cursor states for drag operations
- _Requirements: 4.1, 4.2, 4.3_
- [x] 11. Fix button interaction conflicts with drag handle
- Separate drag handle area from button interaction area
- Ensure delete and edit buttons are clickable and not blocked by drag area
- Add proper z-index and pointer-events handling
- Improve visual feedback for buttons and drag handle
- _Requirements: 1.3, 2.2, 4.3_

View File

@@ -1,5 +1,5 @@
<template>
<div class="h-screen flex flex-col bg-gray-50">
<div class="h-screen flex flex-col bg-gray-50 select-none">
<!-- 顶部工具栏 -->
<div class="bg-white border-b border-gray-200 px-4 py-3">
<div class="flex items-center space-x-4">
@@ -52,11 +52,10 @@
<!-- 主内容区域 -->
<div class="flex-1 flex">
<!-- 左侧工作流面板 -->
<div
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 flex-shrink-0">
<div class="bg-white shadow-lg overflow-hidden flex flex-col w-280px">
<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>
@@ -76,19 +75,28 @@
class="space-y-2 min-h-full"
item-key="id"
handle=".drag-handle"
ghost-class="sortable-ghost"
chosen-class="sortable-chosen"
drag-class="sortable-drag"
:force-fallback="true"
@change="onWorkflowChange"
>
<template #item="{ element, index }">
<div
class="p-3 border rounded-lg hover:shadow-md transition-shadow drag-handle"
:style="{ backgroundColor: element.backgroundColor, color: element.textColor }"
class="p-3 border rounded-lg hover:shadow-md transition-shadow"
:style="{
backgroundColor: element.backgroundColor,
color: element.textColor
}"
>
<div class="flex items-center justify-between">
<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 class="flex items-center space-x-2 flex-1 drag-handle cursor-move">
<div
class="flex flex-col space-y-0.5 opacity-60 hover:opacity-100 transition-opacity"
>
<div class="w-1.5 h-1.5 bg-current rounded-full" />
<div class="w-1.5 h-1.5 bg-current rounded-full" />
<div class="w-1.5 h-1.5 bg-current rounded-full" />
</div>
<component
:is="getIconComponent(element.icon)"
@@ -97,16 +105,16 @@
<span class="text-sm font-medium">{{ element.name }}</span>
</div>
<div class="flex items-center space-x-1">
<div class="flex items-center space-x-1 flex-shrink-0">
<button
v-if="element.config?.editable"
class="p-1 rounded hover:bg-white/20 cursor-pointer"
class="p-1 rounded hover:bg-white/20 cursor-pointer z-10 relative"
@click.stop="editNodeConfig(element)"
>
<Settings class="w-3 h-3" />
</button>
<button
class="p-1 rounded hover:bg-white/20 cursor-pointer"
class="p-1 rounded hover:bg-white/20 cursor-pointer z-10 relative"
@click.stop="removeNode(index)"
>
<X class="w-3 h-3" />
@@ -114,7 +122,10 @@
</div>
</div>
<div v-if="element.config?.duration" class="mt-2 text-xs opacity-80 ml-6">
<div
v-if="element.config?.duration"
class="mt-2 text-xs opacity-80 ml-6"
>
延时: {{ element.config.duration }}ms
</div>
</div>
@@ -136,15 +147,6 @@
</div>
</div>
<!-- 左侧面板折叠按钮 -->
<button
class="absolute left-0 top-1/2 transform -translate-y-1/2 bg-white border border-gray-200 rounded-r-md px-1 py-4 shadow-lg hover:bg-gray-50"
@click="showLeftPanel = !showLeftPanel"
>
<ChevronLeft v-if="showLeftPanel" class="w-4 h-4" />
<ChevronRight v-else class="w-4 h-4" />
</button>
<!-- 右侧节点库 -->
<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">
@@ -168,7 +170,10 @@
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 }"
:style="{
backgroundColor: element.backgroundColor,
color: element.textColor
}"
@click="addNodeToWorkflow(element)"
>
<component
@@ -192,10 +197,7 @@
class="fixed inset-0 bg-black/50 flex items-center justify-center z-50"
@click="cancelEdit"
>
<div
class="bg-white rounded-lg p-6 w-80 max-w-sm"
@click.stop
>
<div class="bg-white rounded-lg p-6 w-80 max-w-sm" @click.stop>
<h3 class="text-lg font-semibold mb-4">
编辑配置
</h3>
@@ -237,8 +239,6 @@
import type { FlowNode } from "~/components/flow-nodes/nodes";
import {
ArrowLeft,
ChevronLeft,
ChevronRight,
Clock,
Computer,
Download,
@@ -262,11 +262,14 @@
import { computed, ref } from "vue";
import { useRouter } from "vue-router";
import draggable from "vuedraggable";
import { getNodesByCategory, nodeDefinitions } from "~/components/flow-nodes/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 categorizedNodes = computed(() => getNodesByCategory());
@@ -355,7 +358,9 @@
const saveEdit = () => {
if (editingNode.value) {
const index = workflowNodes.value.findIndex((n) => n.id === editingNode.value!.id);
const index = workflowNodes.value.findIndex(
(n) => n.id === editingNode.value!.id
);
if (index !== -1) {
workflowNodes.value[index] = { ...editingNode.value };
}
@@ -400,7 +405,9 @@
}))
};
const blob = new Blob([JSON.stringify(taskData, null, 2)], { type: "application/json" });
const blob = new Blob([JSON.stringify(taskData, null, 2)], {
type: "application/json"
});
const url = URL.createObjectURL(blob);
const a = document.createElement("a");
a.href = url;
@@ -450,17 +457,54 @@
width: 280px;
}
/* 禁用文本选择,提供桌面应用体验 */
* {
user-select: none;
-webkit-user-select: none;
-moz-user-select: none;
-ms-user-select: none;
}
/* 输入框仍然允许选择 */
input,
textarea {
user-select: text;
-webkit-user-select: text;
-moz-user-select: text;
-ms-user-select: text;
}
/* 拖拽时的样式 */
.sortable-ghost {
opacity: 0.5;
opacity: 0.3;
background: rgba(59, 130, 246, 0.1) !important;
border: 2px dashed rgba(59, 130, 246, 0.5) !important;
}
.sortable-chosen {
transform: scale(1.02);
box-shadow: 0 8px 25px rgba(0, 0, 0, 0.15);
}
.sortable-drag {
transform: rotate(2deg);
opacity: 0.8;
}
/* 拖拽手柄样式 */
.drag-handle {
cursor: grab;
border-radius: 6px;
padding: 4px;
margin: -4px;
}
.drag-handle:hover {
background-color: rgba(255, 255, 255, 0.1);
}
.drag-handle:active {
cursor: grabbing;
}
/* 确保滚动容器独立 */
@@ -485,4 +529,21 @@
.overflow-y-auto::-webkit-scrollbar-thumb:hover {
background-color: rgba(156, 163, 175, 0.7);
}
/* 防止拖拽时出现默认的拖拽图像 */
.drag-handle * {
pointer-events: none;
}
/* 确保按钮可以正常点击 */
button {
pointer-events: auto !important;
z-index: 10;
}
/* 按钮悬停效果 */
button:hover {
transform: scale(1.1);
transition: transform 0.1s ease;
}
</style>