项目中需要使用到拖拽,这里使用vue3-dnd来满足需求

这里项目使用的vue3(使用js而非ts)

插件官网地址:Vue3 DnD

安装

npm install vue3-dnd react-dnd-html5-backend

然后在app.vue里面添加代码

<template>
    <DndProvider :backend="HTML5Backend">
        <router-view/>
    </DndProvider>
</template>

 通过DndProvider组件为项目提供拖拽功能

 概念

项目item和类型:被拖拽的对象我们称呼为某种类型的项目,类型的作用是让放置目标过滤可以放置的项目

监视器monitor:存放项目的拖动状态,例如项目是否可以放置、项目移动了多少像素等

拖拽的元素drag:可以拖拽的元素,一般用:ref="drag"设置元素可以拖拽(drag是需要调用插件方法设置的ref函数,下面代码进行示例)

放置的元素drop:可以将拖拽的元素放置在此,一般用:ref="drop"设置元素可以放置(drop是需要调用插件方法设置的ref函数,下面代码进行示例)

思路

需求是一个固定大小的元素上有多个可以拖动的元素,拖动后需要保存这些元素的位置

这里先定义一个组件dragBox.vue 每个组件就是一个可以拖动的元素,然后将组件注册全局

// 可拖动的元素类型
export const dragItemType = {
    TEXT: 'text',
    TABLE: 'table'
}

注意:    <div v-if="isDragging" :ref="drag" /> 这个div是必须的。否则只能拖动一次。

<template>
    <div v-if="isDragging" :ref="drag" />
    <div :ref="drag" class="box" v-else :style="{left:left+'px',top:top+'px',height:height+'px',width:width+'px',fontSize:fontSize+'px'}">
        {{ title }}
        <slot></slot>
    </div>
</template>

<script setup>
import {useDrag} from 'vue3-dnd'
import {defineProps} from 'vue'
import {dragItemType} from "@/util/enum";
import { toRefs } from '@vueuse/core'

const props = defineProps({
    id: String,//唯一标识
    left: Number,//左边距
    top: Number,//上边距
    height:Number,//元素高度
    width: Number,//元素宽度
    fontSize:Number,//字体大小
    title: String//文本
})

const [collect, drag] = useDrag(() => ({
    type: dragItemType.TEXT,
    item: {id: props.id, left: props.left, top: props.top},
    collect: monitor => ({
        isDragging: monitor.isDragging
    })
}))
const { isDragging } = toRefs(collect)
</script>

<style scoped>
.box {
    position: absolute;
    background-color: white;
    border: 1px dashed gray;
    cursor: move;
    padding:0;
}
</style>

在页面上,我们就可以使用该拖动组件了

<template>
<div>
    <div class="container" :ref="drop" style="height:300px;width:300px;">
         <drag-box v-for="value in details" :key='value.field' :id="value.field" :top="value.top"
                                              :fontSize="value.fontSize"
                                              :left="value.left" :height="value.height"
                                              :width="value.width" :title="value.title">
         </drag-box>
    </div>
</div>
</template>

<script setup>
import {ref, reactive, onMounted, watch, inject, computed, toRaw} from 'vue';
import {useDrop} from 'vue3-dnd'
import {dragItemType} from "@/util/enum";

//定义拖拽元素
const details=ref([])
details.value.push({
    field:'test1',
    title:'测试字段1',
    fontSize:'9',
    height:'20',
    width:'100',
    top:'10',
    left:'10',
})
details.value.push({
    field:'test2',
    title:'测试字段2',
    fontSize:'9',
    height:'30',
    width:'150',
    top:'50',
    left:'50',
})
//移动元素(通过修改子组件的top和left来移动)
const moveBox = (id, top, left) => {
    let box = details.value.find(x => x.field === id)
    Object.assign(box, {top, left})
}
//定义放置元素,
const [, drop] = useDrop(() => ({
    accept: dragItemType.TEXT,
    drop(item, monitor) {
        const delta = monitor.getDifferenceFromInitialOffset()//获取位移距离
        const left = Math.round(item.left + delta.x)//计算移动后的top和left
        const top = Math.round(item.top + delta.y)
        moveBox(item.id, top, left)
        return undefined
    }
}))
</script>

<style scoped>
.container {
    border: 1px solid grey;
    margin: 10px;
    position: relative;
}
</style>

补充

更多composition API:https://hcg1023.github.io/vue3-dnd/guide/composition/use-drag.html

更多示例:示例 | Vue3 DnD 

本文只提供其中一种简单的应用。

Logo

基于 Vue 的企业级 UI 组件库和中后台系统解决方案,为数万开发者服务。

更多推荐