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();
        }
      }
    });
  }
相关推荐
Qrun1 小时前
Windows11安装nvm管理node多版本
前端·vscode·react.js·ajax·npm·html5
中国lanwp1 小时前
全局 npm config 与多环境配置
前端·npm·node.js
JELEE.2 小时前
Django登录注册完整代码(图片、邮箱验证、加密)
前端·javascript·后端·python·django·bootstrap·jquery
TeleostNaCl4 小时前
解决 Chrome 无法访问网页但无痕模式下可以访问该网页 的问题
前端·网络·chrome·windows·经验分享
前端大卫5 小时前
为什么 React 中的 key 不能用索引?
前端
你的人类朋友5 小时前
【Node】手动归还主线程控制权:解决 Node.js 阻塞的一个思路
前端·后端·node.js
小李小李不讲道理7 小时前
「Ant Design 组件库探索」五:Tabs组件
前端·react.js·ant design
毕设十刻7 小时前
基于Vue的学分预警系统98k51(程序 + 源码 + 数据库 + 调试部署 + 开发环境配置),配套论文文档字数达万字以上,文末可获取,系统界面展示置于文末
前端·数据库·vue.js
mapbar_front8 小时前
在职场生存中如何做个不好惹的人
前端
牧杉-惊蛰8 小时前
纯flex布局来写瀑布流
前端·javascript·css