vue3 实现组件拖拽小案例
一.实现效果
将不同组件拖拽至展示区展示
-
拖拽前 -
拖拽后,取消后还原(没有动态演示真抱歉)
二.实现过程
页面基本样式
<template>
<div class="drag-view">
<!-- 拖拽页面基本布局 -->
<div class="drag-left">
预览区
<drag-cpns :names="names"></drag-cpns>
</div>
<div class="drag-middle">
展示区
<show-cpn></show-cpn>
</div>
<div class="drag-right"></div>
</div>
</template>
<script>
import DragCpns from "../components/dragCpns.vue";
import ShowCpn from "../components/showCpn.vue";
export default {
components: { DragCpns, ShowCpn },
name: "dragView",
setup() {
let names = ["组件1", "组件2", "组件3", "组件4"];//组件名称
return {
names,
};
},
};
</script>
<style scoped>
.drag-view {
width: 100%;
height: 800px;
display: flex;
}
.drag-left {
height: 100%;
flex: 3;
background-color: rgb(224, 83, 83);
padding-left: 10px;
}
.drag-middle {
height: 100%;
flex: 5;
background-color: rgb(251, 255, 0);
padding-left: 10px;
}
.drag-right {
height: 100%;
flex: 3;
background-color: rgb(14, 218, 233);
padding-left: 10px;
}
</style>
拖拽效果实现
-
实现组件可拖拽 为使得页面元素可拖拽,为其添加 draggable=“true” 属性即可(H5) <div v-for="(item, index) in cpnNames" :key="index">
<!-- 用于循环生成所有组件列表 -->
<div
class="drag-cpn"
draggable="true"
>
<p>{{ item }}</p>
</div>
</div>
<script>
let dragStart = function (event) {
console.log(event);
};
let dragEnd = function (event) {
console.log(event);
};
</script>
-
拖拽相关事件
-
拖放相关事件:ondrop 用于绑定在拖拽元素放置区域(即展示部分) <div :class="style" @drop="drop">
<p>{{ text }}</p>
</div>
但需要使用event.preventDefault()阻止某一DOM元素对 dragover 的默认行为,才能使 drop 事件正确执行。在vue中可简写为@dragover.prevent,即: <div :class="style" @drop="drop" @dragover.prevent>
<p>{{ text }}</p>
</div>
<script>
let drop = function (event) {
console.log("drop", event);
};
</script>
控制台输出该事件发现,并不能像前面拖拽事件通过event得到拖拽事件源,若想在脱拽中传递参数至放置组件,需要用到event.dataTransfer.setData()。 但是在函数中传递参数后会丢失event参数,此时可以通过特殊变量$event实现参数绑定。 <div v-for="(item, index) in cpnNames" :key="index">
<!-- 用于循环生成所有组件列表 -->
<div
class="drag-cpn"
draggable="true"
@dragstart="dragStart($event, item)"
@dragend="dragEnd($event, item)"
>
<p>{{ item }}</p>
</div>
</div>
<script>
let dragStart = function (event, item) {
event.dataTransfer.setData("drag-info", item);//这里的参数只能为string类型
};
let dragEnd = function (event, item) {
event.dataTransfer.clearData();//拖拽事件结束,清除消息
};
</script>
拖放组件得到参数: <script>
let drop = function (event) {
console.log("drop", event);
console.log(event.dataTransfer.getData("drag-info"));
};
</script>
三.具体实现思路
? 通过dragstart将拖拽组件的内容以及类名字符串(因为只能传递字符串参数),将展示区的内容字符串和样式替换。
? 具体代码:
<template>
<div class="drag-view">
<!-- 拖拽页面基本布局 -->
<div class="drag-left">
预览区
<drag-cpns :names="names"></drag-cpns>
</div>
<div class="drag-middle">
展示区
<show-cpn></show-cpn>
</div>
<div class="drag-right"></div>
</div>
</template>
<script>
import DragCpns from "../components/dragCpns.vue";
import ShowCpn from "../components/showCpn.vue";
export default {
components: { DragCpns, ShowCpn },
name: "dragView",
setup() {
let names = ["组件1", "组件2", "组件3", "组件4"];
return {
names,
};
},
};
</script>
<style scoped>
.drag-view {
width: 100%;
height: 800px;
display: flex;
}
.drag-left {
height: 100%;
flex: 3;
background-color: rgb(224, 83, 83);
padding-left: 10px;
}
.drag-middle {
height: 100%;
flex: 5;
background-color: rgb(251, 255, 0);
padding-left: 10px;
}
.drag-right {
height: 100%;
flex: 3;
background-color: rgb(14, 218, 233);
padding-left: 10px;
}
</style>
DragCpns.vue:
<template>
<div>
<div v-for="(item, index) in cpnNames" :key="index">
<!-- 用于循环生成所有组件列表 -->
<div
class="drag-cpn"
draggable="true"
@dragstart="dragStart($event, item)"
@dragend="dragEnd($event, item)"
>
<p>{{ item }}</p>
</div>
</div>
</div>
</template>
<script>
import { computed } from "vue";
export default {
name: "",
props: {
names: {
type: Array,
},
},
setup(props) {
let cpnNames = computed(() => {
return props.names;
}).value;
let dragStart = function (event, item) {
console.log(event);
console.log(item);
event.dataTransfer.setData("drag-info", item);
};
let dragEnd = function (event, item) {
console.log(event);
console.log(item);
event.dataTransfer.clearData();
};
for (let item in cpnNames) {
console.log(cpnNames[item]);
}
return {
cpnNames,
dragStart,
dragEnd,
};
},
};
</script>
<style scoped>
.drag-cpn {
width: 300px;
height: 100px;
background-color: #fff;
margin-top: 30px;
margin-left: 10px;
text-align: center;
cursor: move;
user-select: none;
}
.drag-cpn p {
line-height: 10px;
padding-top: 40px;
}
</style>
ShowCpn.vue:
<template>
<div>
<div :class="style" @drop="drop" @dragover.prevent>
<p>{{ text }}</p>
</div>
<div v-show="isShow" class="detale-cpn">
<a href="javascript:;" @click="detaleDrag">取消</a>
</div>
</div>
</template>
<script>
import { ref } from "vue";
export default {
name: "",
setup() {
let style = ref("show-cpn");
let text = ref("展示组件");
let isShow = ref(false);
let drop = function (event) {
console.log("drop", event);
//此处返回的是drop事件源,也就是说不通过额外的信息传递是无法找到源本拖拽的组件
console.log(event.dataTransfer.getData("drag-info"));
text.value = event.dataTransfer.getData("drag-info");
style.value = "drag-cpn";
isShow.value = true;
};
let detaleDrag = function () {
text.value = "展示组件";
style.value = "show-cpn";
isShow.value = false;
};
return {
drop,
text,
style,
isShow,
detaleDrag,
};
},
};
</script>
<style scoped>
.show-cpn {
width: 525px;
height: 120px;
background-color: #fff;
margin-top: 30px;
margin-left: 10px;
text-align: center;
user-select: none;
border: rgb(0, 0, 0) 0.8px dashed;
opacity: 0.7;
}
.show-cpn p {
line-height: 10px;
padding-top: 40px;
}
/* 替换后样式 , 最好抽离在通用css样式中*/
.drag-cpn {
width: 300px;
height: 100px;
background-color: #fff;
margin: 0 auto;
text-align: center;
cursor: move;
user-select: none;
}
.drag-cpn p {
line-height: 10px;
padding-top: 40px;
}
.detale-cpn {
position: relative;
}
.detale-cpn a {
position: absolute;
right: 50px;
top: -50px;
}
</style>
四.参考
1.(1) Vue 拖拽实现及问题备忘 - SegmentFault 思否
2.低代码开发_哔哩哔哩_bilibili
|