Vue.Draggable使用nested-with-vmodel进行拖拽

Vue.Draggable使用nested-with-vmodel进行拖拽

1. 介绍

‌draggable‌是一个基于Sortable.js的Vue组件,用于实现拖拽功能。它支持触摸设备、拖拽和选择文本、智能滚动、不同列表之间的拖拽等功能,并且与Vue的视图模型同步刷新,兼容Vue2的过渡动画,支持撤销操作,并且可以与现有的UI组件兼容‌。

2. 官网地址

nested-with-vmodel拖拽

3. 安装

bash 复制代码
npm install vuedraggable

或者

bash 复制代码
pnpm install vuedraggable

4. NestedVmodel.vue组件

html 复制代码
<script lang="ts">
import { defineComponent, PropType } from "vue";
import draggable from "vuedraggable";

export default defineComponent({
  name: "NestedVmodel",
  components: {
    draggable
  },
  props: {
    list: {
      type: Array as PropType<
        Array<{ id: number; name: string; elements: Array<any> }>
      >,
      required: true
    }
  },
  emits: ["update:modelValue"],
  setup(props, { emit }) {
    const dragOptions = {
      animation: 200,
      group: "description",
      disabled: false,
      ghostClass: "ghost"
    };

    const emitter = (value: any) => {
      emit("update:modelValue", value);
    };

    const updateChildList = (parentId: number, updatedList: any) => {
      const updatedElements = props.list.map(el => {
        if (el.id === parentId) {
          return { ...el, elements: updatedList };
        }
        return el;
      });
      emitter(updatedElements);
    };

    const getUpdatedList = () => {
      return props.list;
    };

    return {
      dragOptions,
      emitter,
      updateChildList,
      getUpdatedList
    };
  }
});
</script>
<template>
  <draggable
    v-bind="dragOptions"
    tag="div"
    class="item-container"
    :list="list"
    item-key="id"
    @update:modelValue="emitter"
  >
    <template #item="{ element }">
      <div :key="element.id" class="item-group">
        <div class="item">{{ element.name }}</div>
        <nested-vmodel
          class="item-sub"
          :list="element.elements"
          @update:modelValue="updateChildList(element.id, $event)"
        />
      </div>
    </template>
  </draggable>
</template>

<style scoped lang="scss">
.item-container {
  box-sizing: border-box;
  max-width: 100%;
  padding: 0.75rem;
  margin: 0 auto;
  background-color: #f8f9fa;
  border-bottom-right-radius: 8px;
  border-bottom-left-radius: 8px;
  box-shadow: 0 4px 8px rgb(0 0 0 / 10%);
}

.item-group {
  margin-bottom: 1rem;
}

.item {
  padding: 0.5rem;
  cursor: pointer;
  background-color: #fff;
  border: 1px solid #ddd;
  border-radius: 6px;
  box-shadow: 0 2px 4px rgb(0 0 0 / 5%);
  transition: box-shadow 0.2s ease;
}

.item:hover {
  box-shadow: 0 4px 8px rgb(0 0 0 / 10%);
}

.item-sub {
  border-left: 2px dashed #bbb;
}

.ghost {
  background-color: #e9ecef;
  opacity: 0.6;
}
</style>

5. 如何使用(示例代码)

html 复制代码
<script lang="ts" setup>
import { ref, onMounted } from "vue";
import nestedVmodel from "@/components/ReTools/NestedVmodel.vue";

// 拖拽列表数据
const dragList = ref([]);
const nestedTestRef = ref(null);

// 获取数据逻辑
const getPromptGroup = async () => {
  // 获取数据的逻辑
  dragList.value = [
    {
      id: 4,
      name: "Lord Farquad",
      elements: []
    },
    {
      id: 1,
      name: "Shrek",
      elements: [
        {
          id: 5,
          name: "Prince Charming",
          elements: [
            {
              id: 3,
              name: "Donkey",
              elements: [
                {
                  id: 2,
                  name: "Fiona",
                  elements: []
                }
              ]
            }
          ]
        }
      ]
    }
  ];
};

const handleUpdate = newList => {
  dragList.value = newList;
};

const getList = () => {
  if (nestedTestRef.value) {
    return nestedTestRef.value.getUpdatedList();
  }
};

// 在组件挂载时加载数据
onMounted(async () => {
  if (dragList.value.length === 0) {
    await getPromptGroup();
  }
});
// 将 getList 方法暴露给父组件
defineExpose({
  getList
});
</script>

<template>
  <nested-vmodel
    ref="nestedTestRef"
    :list="dragList"
    @update:modelValue="handleUpdate"
  />
</template>

6. 页面效果(随意拖拽)

7. 弹窗打开获取数据

html 复制代码
function openDraggableDialog(row?: ApiProps) {
    addDialog({
      title: "###",
      props: {
        
      },
      width: "40%",
      draggable: true,
      fullscreenIcon: true,
      closeOnClickModal: false,
      contentRenderer: () => h(draggable, { ref: draggableRef }),
      beforeSure: done => {
        if (draggableRef.value) {
          const updatedList = draggableRef.value.getList();
        }
      }
    });
  }
相关推荐
小万7422 小时前
God‘s Eye View 深度解析:15 路公开信号 + CesiumJS 的浏览器级 OSINT 控制台,60 帧渲染的“真实间谍卫星模拟器”
前端
IT星宿2 小时前
浏览器标签页全是一个图标:我用名字哈希生成 favicon,让每个项目都有身份标识
前端
OpsEye2 小时前
大模型账单对账难:不同厂商 Token 统计口径差异怎么处理
javascript·ai编程
全栈项目管理程序猿2 小时前
ArcGIS JS 基础教程(32):体元系列 - VoxelSlice 体元切片
前端·javascript
拖孩4 小时前
AI 帮我写的小程序「厨菜记」上线满月了,挣了两百块!!!
前端·后端·微信小程序
用户360055579004 小时前
Kestrel_VLLM - 13 · 为什么排除 MoE FFN Tiling 与 Tensor Core
前端
流烟默5 小时前
SSE 实战:在 SpringBoot + Vue 项目中零依赖实现轻量级实时消息推送
vue.js·spring boot·后端·sse
Hilaku5 小时前
AI 流式输出都用 SSE,什么时候必须换 WebSocket?
前端·javascript·程序员
奇牙coding5 小时前
Codex CLI 升级后多文件编辑静默失效:config.yaml 枚举变更排查与修复指南
java·前端·javascript·ai
小羊没烦恼!5 小时前
Ajax与JSON的一些总结
android·java·前端·数据库·mysql·算法·okhttp