节点拖拽功能正常
This commit is contained in:
@@ -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_
|
||||
|
@@ -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>
|
||||
|
Reference in New Issue
Block a user