fix: 改进拖拽排序实现,整个item可拖拽并添加详细调试日志

This commit is contained in:
Your Name 2026-01-16 13:33:26 +08:00
parent 417281083f
commit a3943f22a7

View File

@ -71,18 +71,19 @@
:key="item.id"
class="task-type-item"
:class="{ 'dragging': dragIndex === index, 'drag-over': dragOverIndex === index }"
draggable="true"
@dragstart="handleDragStart($event, index)"
@dragend="handleDragEnd"
@dragover.prevent="handleDragOver($event, index)"
@dragleave="handleDragLeave"
@drop="handleDrop($event, index)"
@drop.prevent="handleDrop($event, index)"
>
<div class="task-type-row">
<!-- 拖拽手柄 -->
<div
class="drag-handle"
title="拖拽调整顺序"
draggable="true"
@dragstart="handleDragStart($event, index)"
@dragend="handleDragEnd"
@mousedown.prevent
>
<Icon name="menu" size="sm" />
</div>
@ -556,7 +557,7 @@
</template>
<script setup lang="ts">
import { ref, reactive, computed, onMounted, onUnmounted, watch } from 'vue'
import { ref, reactive, computed, onMounted, onUnmounted, watch, nextTick } from 'vue'
import { useRouter, useRoute } from 'vue-router'
import { message } from 'ant-design-vue'
import type { UploadFile, UploadProps } from 'ant-design-vue'
@ -953,13 +954,31 @@ const removeTaskType = (index: number) => {
// 拖拽开始
const handleDragStart = (e: DragEvent, index: number) => {
const target = e.target as HTMLElement
// 如果点击的是可交互元素(input、select、button等),阻止拖拽
if (target.tagName === 'INPUT' ||
target.tagName === 'SELECT' ||
target.tagName === 'BUTTON' ||
target.closest('input, select, button, .ant-cascader, .ant-input-number, .ant-select')) {
e.preventDefault()
return false
}
// 只有从拖拽手柄或空白区域开始拖拽才允许
if (!target.closest('.drag-handle') && !target.closest('.task-type-item')) {
e.preventDefault()
return false
}
dragIndex.value = index
if (e.dataTransfer) {
e.dataTransfer.effectAllowed = 'move'
e.dataTransfer.setData('text/plain', String(index))
e.dataTransfer.setData('application/json', JSON.stringify({ index }))
// 设置拖拽图像为整个 item
const target = e.target as HTMLElement
const itemElement = target.closest('.task-type-item') as HTMLElement
if (itemElement) {
// 创建一个临时元素作为拖拽图像
@ -967,11 +986,18 @@ const handleDragStart = (e: DragEvent, index: number) => {
dragImage.style.opacity = '0.8'
dragImage.style.position = 'absolute'
dragImage.style.top = '-1000px'
dragImage.style.width = itemElement.offsetWidth + 'px'
document.body.appendChild(dragImage)
e.dataTransfer.setDragImage(dragImage, e.offsetX, e.offsetY)
setTimeout(() => document.body.removeChild(dragImage), 0)
setTimeout(() => {
if (document.body.contains(dragImage)) {
document.body.removeChild(dragImage)
}
}, 0)
}
}
return true
}
// 拖拽结束
@ -1006,30 +1032,49 @@ const handleDrop = (e: DragEvent, targetIndex: number) => {
if (data) {
sourceIndex = parseInt(data, 10)
} else {
console.warn('无法获取拖拽源索引')
handleDragEnd()
return
}
}
if (sourceIndex === null || sourceIndex === targetIndex || sourceIndex < 0 || targetIndex < 0) {
if (sourceIndex === null || isNaN(sourceIndex) || sourceIndex < 0 || targetIndex < 0) {
console.warn('无效的拖拽索引:', { sourceIndex, targetIndex })
handleDragEnd()
return
}
console.log('拖拽排序:', sourceIndex, '->', targetIndex)
if (sourceIndex === targetIndex) {
console.log('拖拽到相同位置,无需移动')
handleDragEnd()
return
}
// 交换位置 - 使用 splice 原地修改数组以确保响应式更新
console.log('开始拖拽排序:', {
sourceIndex,
targetIndex,
before: formData.taskTypes.map((t, i) => ({ index: i, id: t.id, taskTypeId: t.taskTypeId }))
})
// 创建新数组并交换位置
const newTaskTypes = [...formData.taskTypes]
const [removed] = newTaskTypes.splice(sourceIndex, 1)
newTaskTypes.splice(targetIndex, 0, removed)
// 直接替换整个数组以触发 Vue 的响应式更新
console.log('拖拽后数组:', newTaskTypes.map((t, i) => ({ index: i, id: t.id, taskTypeId: t.taskTypeId })))
// 直接替换整个数组(reactive 对象会自动响应)
formData.taskTypes = newTaskTypes
// 等待 DOM 更新后再检查
nextTick(() => {
console.log('DOM更新后数组:', formData.taskTypes.map((t, i) => ({ index: i, id: t.id, taskTypeId: t.taskTypeId })))
})
// 重新计算积分(pipelineGroups 是 computed,会自动更新)
calculateScore()
message.success('任务顺序已调整')
message.success(`任务顺序已调整: 第 ${sourceIndex + 1} 项移动到第 ${targetIndex + 1} 项`)
handleDragEnd()
}