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();
        }
      }
    });
  }
相关推荐
weixin_45690427几秒前
Vue.jsmain.js/request.js/user.js/store/index.js Vuex状态管理项目核心模块深度解析
前端·javascript·vue.js
伍哥的传说4 分钟前
Vue 3.6 Alien Signals:让响应式性能飞跃式提升
前端·javascript·vue.js·vue性能优化·alien-signals·细粒度更新·vue 3.6新特性
永日456708 分钟前
学习日记-HTML-day51-9.9
前端·学习·html
狗头大军之江苏分军25 分钟前
iPhone 17 vs iPhone 17 Pro:到底差在哪?买前别被忽悠了
前端
小林coding25 分钟前
再也不怕面试了!程序员 AI 面试练习神器终于上线了
前端·后端·面试
文心快码BaiduComate37 分钟前
WAVE SUMMIT深度学习开发者大会2025举行 文心大模型X1.1发布
前端·后端·程序员
babytiger38 分钟前
python 通过selenium调用chrome浏览器
前端·chrome
passer98144 分钟前
基于webpack的场景解决
前端·webpack
华科云商xiao徐1 小时前
Java并发编程常见“坑”与填坑指南
javascript·数据库·爬虫