Vue.Draggable使用nested-with-vmodel进行拖拽
1. 介绍
draggable是一个基于Sortable.js的Vue组件,用于实现拖拽功能。它支持触摸设备、拖拽和选择文本、智能滚动、不同列表之间的拖拽等功能,并且与Vue的视图模型同步刷新,兼容Vue2的过渡动画,支持撤销操作,并且可以与现有的UI组件兼容。
2. 官网地址
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();
}
}
});
}