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();
        }
      }
    });
  }
相关推荐
书山有路_邓17 分钟前
vscode 如何鼠标双击时选择带有-的
前端
张三风啊2 小时前
vue config 接口地址配置
前端·javascript·vue.js
多情码农无情键2 小时前
浏览器漫谈HTML--2.2从表单标签看vue的响应式系统 理论+实战
前端·javascript·html
北极糊的狐2 小时前
vue页面成绩案例(for渲染表格/删除/添加/统计总分/平均分/不及格显红色/输入内容去首尾空格trim/输入内容转数字number)
前端·javascript·vue.js
边洛洛3 小时前
路由传参、搜索、多选框勾选、新增/编辑表单复用
前端·javascript·vue.js
breakthrough_014 小时前
创建一个简单的 Nuxt.js 应用
开发语言·javascript·ecmascript
OEC小胖胖5 小时前
Vue 3 中 onUnload 和 onPageScroll 使用详解
前端·javascript·vue.js·前端框架·web
秋田君5 小时前
uniapp中使用Mescroll实现下拉刷新与上拉加载项目实战
javascript·uni-app
川石教育6 小时前
Vue前端开发-slot传参
前端·vue.js·前端框架·前端开发·slot组件