diff --git a/packages/frontend/src/components/FileManager.vue b/packages/frontend/src/components/FileManager.vue index 764a992..3a77661 100644 --- a/packages/frontend/src/components/FileManager.vue +++ b/packages/frontend/src/components/FileManager.vue @@ -400,60 +400,68 @@ const SCROLL_SPEED = 10; // px per interval,基础滚动速度 const handleDragOver = (event: DragEvent) => { event.preventDefault(); const isExternalFileDrag = event.dataTransfer?.types.includes('Files') ?? false; - const isInternalDrag = !!draggedItem.value; + const isInternalDrag = !!draggedItem.value; // Check if an internal item is being dragged - // --- Determine Drop Effect --- let effect: 'copy' | 'move' | 'none' = 'none'; let currentTargetFilename: string | null = null; + let highlightContainer = false; // Flag to control container highlighting const targetElement = event.target as HTMLElement; - const targetRow = targetElement.closest('tr.file-row'); // Find closest row (folder or file) - // Safely access dataset only if targetRow is an HTMLElement + const targetRow = targetElement.closest('tr.file-row'); const targetFilename = (targetRow instanceof HTMLElement) ? targetRow.dataset.filename : undefined; const targetIsFolder = targetRow?.classList.contains('folder-row'); - if (props.wsDeps.isConnected.value && isExternalFileDrag) { - // External Drag (Upload) - if (targetIsFolder && targetFilename && targetFilename !== '..') { - effect = 'copy'; // Allow dropping into subfolders - currentTargetFilename = targetFilename; - } else if (!targetRow) { - effect = 'copy'; // Allow dropping into the main container area (current path) - currentTargetFilename = null; // No specific target row - } else { - effect = 'none'; // Don't allow dropping external files onto file rows or '..' - currentTargetFilename = null; - } - isDraggingOver.value = (effect === 'copy'); // Set general drag-over state if allowed + if (props.wsDeps.isConnected.value) { + if (isExternalFileDrag) { + // External Drag (Upload) + effect = 'copy'; // Always allow copy for external files + highlightContainer = true; // Highlight the container - } else if (isInternalDrag && draggedItem.value) { - // Internal Drag (Move) - if (targetIsFolder && targetFilename && targetFilename !== draggedItem.value.filename) { - // Allow dropping onto any folder row (including '..') except itself - effect = 'move'; - currentTargetFilename = targetFilename; - } else { - effect = 'none'; - currentTargetFilename = null; - } - isDraggingOver.value = false; // Don't use general drag-over for internal moves + // Determine the specific target folder for potential drop and row highlighting + if (targetIsFolder && targetFilename && targetFilename !== '..') { + currentTargetFilename = targetFilename; // Target is a subfolder row + } else { + currentTargetFilename = null; // Target is the current directory (or invalid row) + } + } else if (isInternalDrag && draggedItem.value) { + // Internal Drag (Move) + highlightContainer = false; // Do not highlight the container for internal moves + + if (targetIsFolder && targetFilename && targetFilename !== draggedItem.value.filename) { + // Allow dropping onto any folder row (including '..') except itself + effect = 'move'; + currentTargetFilename = targetFilename; // Target is the specific folder row + } else { + // Invalid target for internal move + effect = 'none'; + currentTargetFilename = null; + } + } else { + // Other drag types + effect = 'none'; + currentTargetFilename = null; + highlightContainer = false; + } } else { - // Other drag types or not connected + // Not connected effect = 'none'; currentTargetFilename = null; - isDraggingOver.value = false; + highlightContainer = false; } + // --- Apply Drop Effect and Target Highlighting --- if (event.dataTransfer) { event.dataTransfer.dropEffect = effect; } + isDraggingOver.value = highlightContainer; // Control container highlight based on flag dragOverTarget.value = currentTargetFilename; // Set specific row target for highlighting // --- 处理自动滚动 --- const container = fileListContainerRef.value; - if (container && (isExternalFileDrag || draggedItem.value)) { // 仅在有效拖拽时处理滚动 + // 仅在有效拖拽 (外部文件或内部文件) 且效果不是 'none' 时处理滚动 + if (container && (isExternalFileDrag || isInternalDrag) && effect !== 'none') { const rect = container.getBoundingClientRect(); const mouseY = event.clientY - rect.top; // 鼠标在容器内的 Y 坐标 @@ -489,7 +497,7 @@ const handleDragOver = (event: DragEvent) => { } } } else { - // 如果拖拽无效或容器不存在,确保停止滚动 + // 如果拖拽无效、效果为 'none' 或容器不存在,确保停止滚动 if (scrollIntervalId.value !== null) { clearInterval(scrollIntervalId.value); scrollIntervalId.value = null; @@ -615,14 +623,30 @@ const handleDragLeaveRow = (targetItem: FileListItem) => { const handleDropOnRow = (targetItem: FileListItem, event: DragEvent) => { event.preventDefault(); - event.stopPropagation(); // 阻止事件冒泡到父容器的 drop 处理 + // 检查是否是外部文件拖拽 + const files = event.dataTransfer?.files; + if (files && files.length > 0) { + // 如果是外部文件拖拽,不阻止冒泡,让父容器的 handleDrop 处理上传 + console.log(`[FileManager ${props.sessionId}] External file drop detected on row, letting parent handle.`); + // 不需要清除 draggedItem.value,因为外部拖拽时它应该为 null + // dragOverTarget.value = null; // 清除悬停状态 (父容器 handleDrop 会处理) + return; + } + + // --- 以下是处理内部文件移动的逻辑 --- + event.stopPropagation(); // 仅在处理内部移动时阻止冒泡 const sourceItem = draggedItem.value; dragOverTarget.value = null; // 清除悬停状态 - // 验证拖放操作的有效性 (与之前相同) + // 验证内部拖放操作的有效性 + // 注意:这里的 !sourceItem 检查现在只会在非外部文件拖拽时发生, + // 如果 sourceItem 仍然是 null,说明不是有效的内部拖拽。 if (!sourceItem || sourceItem.filename === '..' || (targetItem.filename !== '..' && !targetItem.attrs.isDirectory) || sourceItem.filename === targetItem.filename) { - console.log(`[FileManager ${props.sessionId}] Drop on row ignored: Invalid target or source. Source: ${sourceItem?.filename}, Target: ${targetItem.filename}`); - draggedItem.value = null; + console.log(`[FileManager ${props.sessionId}] Internal drop on row ignored: Invalid target or source. Source: ${sourceItem?.filename}, Target: ${targetItem.filename}`); + // 如果 sourceItem 存在但无效,才需要清除 + if (sourceItem) { + draggedItem.value = null; + } return; } @@ -1682,3 +1706,4 @@ td:nth-child(5) { /* Modified */ +