概述
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
| 属性 | 类型 | 默认值 | 描述 | 
|---|---|---|---|
| modelValue | Array | [] | 与拖拽同步的数组(配合 v-model 使用) | 
| list | Array | [] | 直接修改的数组(modelValue 的替代方案) | 
| itemKey | String/Function | undefined | 用于跟踪项目的键(推荐使用以提高性能) | 
| tag | String | ‘div’ | 根元素的 HTML 标签 | 
| component | String | null | 用作根元素的 Vue 组件名 | 
| componentData | Object | null | 传递给组件的 props/attrs | 
| clone | Function | (item) => item | 拖拽时克隆项目的函数 | 
| move | Function | null | 控制移动操作的函数 | 
| group | String/Object | undefined | Sortable 组选项 | 
| sort | Boolean | true | 启用列表内排序 | 
| disabled | Boolean | false | 禁用拖拽 | 
| animation | Number | 0 | 动画速度(毫秒) | 
| ghostClass | String | ” | 幽灵元素的 CSS 类 | 
| chosenClass | String | ” | 选中元素的 CSS 类 | 
| dragClass | String | ” | 拖拽元素的 CSS 类 | 
Events
| 事件 | 描述 | 载荷 | 
|---|---|---|
| @change | 列表变化时触发 | { added?, removed?, moved? } | 
| @start | 开始拖拽 | SortableEvent | 
| @end | 结束拖拽 | SortableEvent | 
| @add | 从其他列表添加项目 | SortableEvent | 
| @remove | 移除项目到其他列表 | SortableEvent | 
| @update | 项目顺序改变 | SortableEvent | 
| @sort | 列表的任何变化 | SortableEvent | 
| @choose | 选择项目 | SortableEvent | 
| @unchoose | 取消选择项目 | SortableEvent | 
故障排除
常见问题
- 项目无法拖拽:检查 disabled属性是否为 false,项目是否有唯一键
- 性能问题:使用 item-key属性获得更好的跟踪性能
- 触摸不工作:确保 touch-action CSS 没有阻止触摸事件
- 过渡闪烁:使用 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