You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

无需第三方库实现Vue多列跨列及列内可排序拖拽的方案咨询

原生实现JIRA风格可拖拽看板的实现方案(Vue优先)

基础逻辑框架

完全基于HTML5原生Drag & Drop API实现,无任何第三方依赖,核心拆为状态管理、拖拽事件响应、位置计算更新三个模块即可。

Vue 3 实现步骤

  • 定义数据结构
    采用「列-任务」两层嵌套响应式结构,示例定义:
    import { ref } from 'vue'
    // 看板列配置
    const boardColumns = ref([
      { id: 'todo', name: '待处理', tasks: [
        { id: 'task_001', content: '完成首页接口联调', priority: 1 },
        { id: 'task_002', content: '修复用户登录异常问题', priority: 2 }
      ]},
      { id: 'in_progress', name: '进行中', tasks: [] },
      { id: 'done', name: '已完成', tasks: [] }
    ])
    // 拖拽临时状态
    const draggingTask = ref(null)
    const fromColumnId = ref('')
    const hoverTarget = ref({ columnId: '', taskId: null, position: 'before' }) // position可选before/after
    
  • 绑定拖拽核心事件
    给任务卡片设置draggable="true"属性,绑定对应事件:
    1. dragstart事件:触发时记录当前拖拽任务对象、起始列ID,给拖拽元素添加半透明样式做状态反馈
    2. dragend事件:触发时清空所有拖拽临时状态,还原元素样式
      给所有任务卡片、列的任务容器绑定dragover事件,阻止默认行为,同时计算悬停位置:
    const handleDragOver = (e, targetColumnId, targetTaskId = null) => {
      e.preventDefault()
      // 计算当前拖拽点相对于目标任务的位置,判断插入到上方还是下方
      if (targetTaskId) {
        const targetRect = e.currentTarget.getBoundingClientRect()
        const midY = targetRect.top + targetRect.height / 2
        hoverTarget.value = {
          columnId: targetColumnId,
          taskId: targetTaskId,
          position: e.clientY < midY ? 'before' : 'after'
        }
      } else {
        // 悬停在空列容器中,直接插入到列末尾
        hoverTarget.value = { columnId: targetColumnId, taskId: null, position: 'after' }
      }
    }
    
  • 实现drop事件的更新逻辑
    给任务卡片、列的任务容器绑定drop事件,触发时根据拖拽状态更新数据:
    const handleDrop = (e) => {
      e.preventDefault()
      // 从原列删除拖拽任务
      const fromColumn = boardColumns.value.find(col => col.id === fromColumnId.value)
      const taskIndex = fromColumn.tasks.findIndex(t => t.id === draggingTask.value.id)
      fromColumn.tasks.splice(taskIndex, 1)
      // 插入到目标位置
      const targetColumn = boardColumns.value.find(col => col.id === hoverTarget.value.columnId)
      if (hoverTarget.value.taskId === null) {
        // 空列直接插入末尾
        targetColumn.tasks.push(draggingTask.value)
      } else {
        const targetIndex = targetColumn.tasks.findIndex(t => t.id === hoverTarget.value.taskId)
        const insertIndex = hoverTarget.value.position === 'before' ? targetIndex : targetIndex + 1
        targetColumn.tasks.splice(insertIndex, 0, draggingTask.value)
      }
    }
    
  • 交互优化
    • 拖拽过程中给被拖拽元素添加opacity: 0.5、cursor: grab的样式
    • 根据hoverTarget的位置在对应点位显示插入提示线,明确释放后的插入位置
    • 可扩展添加拖动时列容器自动滚动、拖拽范围限制等自定义逻辑

其他技术栈通用实现思路

核心逻辑完全通用,仅需将状态管理部分替换为对应框架的响应式实现即可,原生JS实现可直接操作DOM节点的增删完成位置更新。


内容的提问来源于stack exchange,提问作者Nata Vacheishvili

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.09.25 06:36:07