所有分类
  • 所有分类
  • Html5资源
  • React资源
  • Vue资源
  • Php资源
  • ‌小程序资源
  • Python资源

Vue 3 拖拽组件 VueDraggable 完整使用指南 – 支持移动端触摸操作

概述

vue.draggable.next 是一个专为 Vue 3 设计的拖拽组件,基于 Sortable.js 构建。它提供了强大的拖拽功能,支持列表排序、跨列表拖拽、触摸设备支持等特性。

特性

  • 🚀 Vue 3 支持:完全兼容 Vue 3 Composition API 和 Options API
  • 📱 移动端友好:支持触摸设备操作
  • 🎨 CSS 依赖:不依赖任何 CSS 框架
  • 📦 TypeScript 支持:内置 TypeScript 类型定义
  • 轻量级:压缩后约 7KB
  • 🔧 功能丰富:支持所有 Sortable.js 选项

安装

# npm
npm install vuedraggable@next

# yarn
yarn add vuedraggable@next

# pnpm
pnpm add vuedraggable@next

常用示例

拖拽排序

<template>
  <div class="min-h-screen bg-gradient-to-br from-gray-50 to-blue-50 p-8">
    <div class="max-w-2xl mx-auto">
      <h1 class="text-3xl font-bold text-gray-800 text-center mb-8">
        可拖拽列表
      </h1>

      <div class="bg-white rounded-xl shadow-lg p-6">
        <draggable
          v-model="list"
          handle=".drag-handle"
          :animation="200"
          item-key="id"
          class="space-y-3"
        >
          <template #item="{ element }">
            <div
              class="item-with-handle bg-gradient-to-r from-white to-gray-50 border border-gray-200 rounded-lg p-4 shadow-sm hover:shadow-md transition-all duration-200"
            >
              <span
                class="drag-handle text-gray-400 hover:text-gray-600 transition-colors duration-200 cursor-grab active:cursor-grabbing select-none mr-4 text-lg font-bold"
              >
                ⋮⋮
              </span>
              <span class="item-content text-gray-700 font-medium flex-1">{{
                element.name
              }}</span>
            </div>
          </template>
        </draggable>
      </div>
    </div>
  </div>
</template>

<script setup>
import { ref } from "vue";
import draggable from "vuedraggable";

const list = ref([
  { id: 1, name: "项目 1" },
  { id: 2, name: "项目 2" },
]);
</script>

<style scoped>
/* 保留原有的拖拽手柄样式,确保拖拽功能正常 */
.drag-handle {
  cursor: grab;
  user-select: none;
}

.drag-handle:active {
  cursor: grabbing;
}
</style>

条件移动

<script setup>
import { ref } from "vue";
import draggable from "vuedraggable";

const list = ref([
  { id: 1, name: "可移动项目", locked: false },
  { id: 2, name: "锁定项目", locked: true },
  { id: 3, name: "另一个可移动项目", locked: false },
]);

// 阻止移动锁定的项目
const checkMove = (event) => {
  // 不允许移动锁定的项目
  if (event.draggedContext.element.locked) {
    return false;
  }

  // 不允许拖拽到锁定的项目上
  if (event.relatedContext.element?.locked) {
    return false;
  }

  return true;
};
</script>

<template>
  <div class="min-h-screen bg-gradient-to-br from-gray-50 to-blue-50 p-8">
    <div class="max-w-2xl mx-auto">
      <h1 class="text-3xl font-bold text-gray-800 text-center mb-8">
        条件移动列表
      </h1>

      <div class="bg-white rounded-xl shadow-lg p-6">
        <div class="mb-4 p-3 bg-blue-50 border border-blue-200 rounded-lg">
          <p class="text-sm text-blue-700">
            <span class="font-medium">提示:</span
            >锁定的项目(🔒)无法移动,可移动项目可以自由拖拽排序
          </p>
        </div>

        <draggable
          v-model="list"
          :move="checkMove"
          item-key="id"
          class="space-y-3"
        >
          <template #item="{ element }">
            <div
              :class="[
                'move-item p-4 rounded-lg border transition-all duration-200',
                element.locked
                  ? 'bg-gray-100 border-gray-300 text-gray-500 cursor-not-allowed opacity-60'
                  : 'bg-gradient-to-r from-white to-blue-50 border-blue-200 text-gray-700 hover:shadow-md hover:scale-[1.02] cursor-move',
              ]"
            >
              <div class="flex items-center justify-between">
                <span class="font-medium">{{ element.name }}</span>
                <div class="flex items-center space-x-2">
                  <span v-if="element.locked" class="text-lg">🔒</span>
                  <span v-else class="text-sm text-gray-400">⋮⋮</span>
                </div>
              </div>
            </div>
          </template>
        </draggable>
      </div>
    </div>
  </div>
</template>

<style scoped>
/* 保留原有的锁定项目样式,确保功能正常 */
.move-item.locked {
  opacity: 0.6;
  cursor: not-allowed;
}
</style>

多列表间拖拽

<template>
  <div class="min-h-screen bg-gradient-to-br from-blue-50 to-indigo-100 p-8">
    <div class="max-w-6xl mx-auto">
      <h1 class="text-3xl font-bold text-gray-800 text-center mb-8">
        任务管理看板
      </h1>

      <div class="grid grid-cols-1 md:grid-cols-2 gap-8">
        <!-- 待办列表 -->
        <div class="bg-white rounded-xl shadow-lg p-6">
          <div class="flex items-center mb-6">
            <div class="w-3 h-3 bg-yellow-400 rounded-full mr-3"></div>
            <h3 class="text-xl font-semibold text-gray-700">待办任务</h3>
            <span
              class="ml-auto bg-yellow-100 text-yellow-800 text-sm font-medium px-3 py-1 rounded-full"
            >
              {{ todoList.length }}
            </span>
          </div>

          <draggable
            v-model="todoList"
            group="tasks"
            class="min-h-[200px] space-y-3"
            :animation="150"
            item-key="id"
          >
            <template #item="{ element }">
              <div
                class="bg-gradient-to-r from-yellow-50 to-orange-50 border border-yellow-200 rounded-lg p-4 shadow-sm hover:shadow-md transition-all duration-200 cursor-move hover:scale-[1.02]"
              >
                <div class="flex items-center">
                  <div class="w-2 h-2 bg-yellow-400 rounded-full mr-3"></div>
                  <span class="text-gray-700 font-medium">{{
                    element.text
                  }}</span>
                </div>
              </div>
            </template>
          </draggable>
        </div>

        <!-- 已完成列表 -->
        <div class="bg-white rounded-xl shadow-lg p-6">
          <div class="flex items-center mb-6">
            <div class="w-3 h-3 bg-green-400 rounded-full mr-3"></div>
            <h3 class="text-xl font-semibold text-gray-700">已完成</h3>
            <span
              class="ml-auto bg-green-100 text-green-800 text-sm font-medium px-3 py-1 rounded-full"
            >
              {{ doneList.length }}
            </span>
          </div>

          <draggable
            v-model="doneList"
            group="tasks"
            class="min-h-[200px] space-y-3"
            :animation="150"
            item-key="id"
          >
            <template #item="{ element }">
              <div
                class="bg-gradient-to-r from-green-50 to-emerald-50 border border-green-200 rounded-lg p-4 shadow-sm hover:shadow-md transition-all duration-200 cursor-move hover:scale-[1.02] opacity-90"
              >
                <div class="flex items-center">
                  <div class="w-2 h-2 bg-green-400 rounded-full mr-3"></div>
                  <span class="text-gray-600 font-medium line-through">{{
                    element.text
                  }}</span>
                  <svg
                    class="w-4 h-4 text-green-500 ml-auto"
                    fill="currentColor"
                    viewBox="0 0 20 20"
                  >
                    <path
                      fill-rule="evenodd"
                      d="M16.707 5.293a1 1 0 010 1.414l-8 8a1 1 0 01-1.414 0l-4-4a1 1 0 011.414-1.414L8 12.586l7.293-7.293a1 1 0 011.414 0z"
                      clip-rule="evenodd"
                    ></path>
                  </svg>
                </div>
              </div>
            </template>
          </draggable>
        </div>
      </div>
    </div>
  </div>
</template>

<script setup>
import { ref } from "vue";
import draggable from "vuedraggable";

const todoList = ref([
  { id: 1, text: "学习 Vue 3" },
  { id: 2, text: "构建应用" },
]);

const doneList = ref([{ id: 3, text: "阅读文档" }]);
</script>

API 参考

Props

属性类型默认值描述
modelValueArray[]与拖拽同步的数组(配合 v-model 使用)
listArray[]直接修改的数组(modelValue 的替代方案)
itemKeyString/Functionundefined用于跟踪项目的键(推荐使用以提高性能)
tagString‘div’根元素的 HTML 标签
componentStringnull用作根元素的 Vue 组件名
componentDataObjectnull传递给组件的 props/attrs
cloneFunction(item) => item拖拽时克隆项目的函数
moveFunctionnull控制移动操作的函数
groupString/ObjectundefinedSortable 组选项
sortBooleantrue启用列表内排序
disabledBooleanfalse禁用拖拽
animationNumber0动画速度(毫秒)
ghostClassString幽灵元素的 CSS 类
chosenClassString选中元素的 CSS 类
dragClassString拖拽元素的 CSS 类

Events

事件描述载荷
@change列表变化时触发{ added?, removed?, moved? }
@start开始拖拽SortableEvent
@end结束拖拽SortableEvent
@add从其他列表添加项目SortableEvent
@remove移除项目到其他列表SortableEvent
@update项目顺序改变SortableEvent
@sort列表的任何变化SortableEvent
@choose选择项目SortableEvent
@unchoose取消选择项目SortableEvent

故障排除

常见问题

  1. 项目无法拖拽:检查 disabled 属性是否为 false,项目是否有唯一键
  2. 性能问题:使用 item-key 属性获得更好的跟踪性能
  3. 触摸不工作:确保 touch-action CSS 没有阻止触摸事件
  4. 过渡闪烁:使用 tag="transition-group" 配合正确的过渡类

调试模式

<draggable
  v-model="list"
  @start="console.log('开始拖拽', $event)"
  @end="console.log('结束拖拽', $event)"
  @change="console.log('列表变化', $event)"
>
  <!-- 项目 -->
</draggable>

移动端支持

组件开箱即用支持移动设备。为了获得更好的移动端体验:

.drag-item {
  /* 拖拽时防止文本选择 */
  user-select: none;
  -webkit-user-select: none;

  /* 更好的触摸目标 */
  min-height: 44px;

  /* 平滑反馈 */
  transition: transform 0.2s ease;
}

.drag-item:active {
  transform: scale(1.02);
}

总结

Vue.Draggable 是一个功能强大的拖拽组件,支持多种拖拽场景。通过简单的配置,你可以轻松实现拖拽排序、拖拽组等功能。同时,Vue.Draggable 还提供了丰富的 API 和事件,方便你进行自定义操作。如果你需要实现复杂的拖拽功能,Vue.Draggable 是一个非常好的选择。

原文链接:https://code.ifrontend.net/archives/1297,转载请注明出处。
0

评论0

显示验证码
没有账号?注册  忘记密码?