实现效果:
HTML5为所有HTML元素规定了一个draggable属性,draggable=true,因此可用以下事件触发实现。
绑定在目标元素上的事件(起始)
事件 | |
---|---|
dragstart | 拖拽开始 |
drag | 拖拽移动事件 |
dragend | 结束拖拽 |
绑定在目标区域上的事件(放置)
事件 | |
---|---|
dragenter | 进入目标区域时触发(一瞬间的触发) |
dragover | 在目标区域中时触发(始终触发状态) |
dragleave | (不松开鼠标的情况下)离开目标区域时触发(可重复触发) |
drop | 在目标区域中松开鼠标时触发,并放下被拖拽的元素 |
代码如下:
1、新建组件文件夹drop
2、drop.vue,,样式可根据自身需求调整
<template>
<div style="margin-top: 100px;">
<div class="drag-container">
<div v-for="(item,index) in source" :key="index">
<div class="text">{{list_name[item]}}</div>
<items
:list="list[item]"
:target="item"
@itemDragstart="dragstart" @itemDragends="dragend">
<template slot-scope="scope">
<p>{{scope.data.name}}</p>
</template>
</items>
</div>
</div>
<div class="drop-container">
<div class="item" v-for="(item,index) in target" :key="index">
<p>{{option_name[item]}}</p>
<items
:option="option[item].data"
:target="item"
@itemDragstart="dragstart"
@itemDragover="dragover"
@itemDrop="drop"
@itemDragends="dragend"
>
<template slot-scope="scope">
<div class="list">
<span>{{scope.data.name}}</span>
</div>
</template>
<p class="tips" v-if="option[item].data.length==0" slot="show" style="color:#969696">拖入放置</p>
</items>
</div>
</div>
</div>
</template>
<script>
import items from "./items.vue"
export default {
name: "drop",
components: {
items,
},
data(){
return {
list:{
A_area:[
{
id:1,
name:'A_id1'
},{
id:2,
name:'A_id2'
},],
B_area:[
{
id:1,
name:'B_id1'
},{
id:2,
name:'B_id2'
},{
id:3,
name:'B_id2'
}],
},
list_name:{
A_area:"A区域",
B_area:'B区域',
},
option:{//拖入 type:single只能拖入一个,multiple可拖入多个
a_data:{data:[],type:'single'},
b_data:{data:[],type:'multiple'},
},
option_name:{
a_data:"A放置区域--只能放一个(A只能放在A中)",
b_data:'B放置区域--可放多个',
},
not_drag:[{//能否拖动控制参数
dragFrom:'A_area',//从哪儿开始
dragEnd:'b_data',//到哪儿结束
}],
dragItem:{},//拖动的字段
dragFrom:'',//拖动从哪开始
dragIndex:'',//拖动的字段在数组中的索引
dropIndex:'',//放入地方的字段的索引
dragEnd:'',//拖动到哪儿结束
target:[],//存放可拖入的区域
source:[],//需拖入的区域
}
},
mounted(){
for(var key in this.option){
this.target.push(key);
}
for(var key in this.list){
this.source.push(key);
}
},
methods:{
//拖拽开始
dragstart(param){
this.dragItem = param.dragItem;
this.dragFrom = param.dragFrom;
if(!isNaN(param.index)){
this.dragIndex = param.index;
}
this.dropIndex = this.indexFn(param.event.target);
},
//进入拖拽区域后多次触发
dragover(param){
var target = param.event.target;
this.dragEnd = param.dragEnd;//获取拖入到哪儿
//是否显示效果框
let array = []
this.option[this.dragEnd].data.forEach((item,index)=>{
array.push(item);
})
//判断能否拖动
try {
this.not_drag.forEach((item,index)=>{
if(this.dragFrom == item.dragFrom && this.dragEnd ==item.dragEnd ){
foreach.break=new Error("StopIteration");
}
})
} catch (e) {
if(e.message==="foreach is not defined") {
return;
}else throw e;
}
param.event.preventDefault();
var nodeName = target.className;
if(nodeName=='drop-area'){
this.dropIndex = -1;
}
},
//松开鼠标完成拖拽后触发
drop(){
//删除
if(this.dragFrom != 'A_area' && this.dragFrom != 'B_area' ){
this.option[this.dragFrom].data.splice(this.dragIndex,1);
}
//交换位置
if(this.dragFrom == this.dragEnd && this.dropIndex>-1){
var targetArr = this.option[this.dragEnd].data;
targetArr.splice(this.dropIndex,0,this.dragItem);
return;
}
//添加
let array = []
this.option[this.dragEnd].data.forEach((item,index)=>{ //去重
array.push(item);
})
if (array.indexOf(this.dragItem) === -1) {
if(this.option[this.dragEnd].type=='single'){//判断拖入单个还是多个
this.option[this.dragEnd].data = Object.assign([],this.option[this.dragEnd].data)
this.option[this.dragEnd].data[0] =this.dragItem
}else{
this.option[this.dragEnd].data.push(this.dragItem);
}
}
},
//拖拽结束后触发,不管是否拖拽成功
dragend(){
this.dragFrom = '';
this.dragEnd = '';
this.dragItem = '';
},
deleteItem(el,index){ //按del删除
this.option[el].data.splice(index,1);
},
//判断拖动字段的索引,获取当前子元素是其父元素下子元素的排位
indexFn(el) {
var index = 0;
if (!el || !el.parentNode) {
return -1;
}
while (el && (el = el.nextElementSibling)) {
index++;
}
return index;
},
}
}
</script>
<style>
*{
box-sizing: border-box;
list-style: none;
padding: 0;
margin: 0;
-moz-user-select: none;
-webkit-user-select: none;
-ms-user-select: none;
user-select: none;
}
.drag-container{
width: 200px;
height: 500px;
float: left;
border: 1px solid #ccc;
}
.drag-container .text{
text-align: left;
height: 30px;
line-height: 30px;
font-weight: 600;
}
.drag-container p{
height: 40px;
line-height: 40px;
text-align: center;
cursor: pointer;
}
.drop-container{
margin-left: 80px;
float: left;
height: 500px;
width: 600px;
border: 1px solid #ccc;
}
.drop-container .item{
width: 600px;
height: auto;
border-bottom: 1px solid #ccc;
}
.drop-container .item p{
height: 35px;
line-height: 35px;
text-align: left;
}
.drop-area{
width: 600px;
/* background-color: #4c72ff; */
padding: 10px;
display: flex;
flex-flow: wrap;
box-sizing: border-box;
position: relative;
overflow: hidden;
}
.drop-area p{
display: inline-block;
margin: 0;
}
.drop-area .list{
min-width: 50px;
height: 40px;
line-height: 40px;
margin: 5px;
border: 1px solid #ccc;
border-radius: 4px;
padding: 0 10px;
box-sizing: border-box;
position: relative;
/* z-index: 99; */
padding: 0 25px;
background: #fff;
}
.drop-area .list i{
position: absolute;
top: 2px;
right: 2px;
list-style: none;
}
div[name="dragged"] {
background-color: rgba(165,147,224,0.3);
min-width: 50px;
height: 40px;
line-height: 40px;
border-radius: 4px;
padding: 0 10px;
box-sizing: border-box;
color: rgba(165,147,224,0);
z-index: 1;
}
</style>
3、items.vue
<template>
<div>
<div>
<p v-for="(item,i) in list"
:key="i"
draggable="true"
@dragstart="dragstart($event,item,i)"
@dragend="dragend($event,item,i)"
>
<slot :data="item"></slot>
</p>
</div>
<div>
<div class="drop-area"
@drop="drop($event)"
@dragover="dragover($event)">
<div v-for="(item,i) in option"
style="position: relative;z-index: 99;"
:key="i"
@dragover.prevent
draggable="true"
tabindex="0"
@dragstart="dragstart($event,item,i)"
@dragend="dragend($event,item,i)"
>
<slot :data="item"></slot>
</div>
<slot name="show" :dragEnd="dragEnd"></slot>
</div>
</div>
</div>
</template>
<script>
export default {
name: "items",
props: ['target','param','list','option'],//target已选字段的类型,param效果框样式参数
data(){
return {
dragItem:{},//拖动的字段
dragFrom:'',//拖动从哪开始
dragEnd:'',//拖动到哪儿结束
}
},
methods:{
dragstart(event,item,index){
if(this.BrowserType().name == "FF"){
// 兼容火狐浏览器,火狐拖拽必须携带数据
console.log(event.dataTransfer.setData("imgInfo", event.target.id));
}
event.dataTransfer.effectAllowed="move";
this.dragItem = item;
this.dragFrom = this.target;
var param = {
dragFrom : this.target,
dragItem : item,
index:index,
event:event
}
this.$emit('itemDragstart',param)
},
//进入拖拽区域后多次触发
dragover(event){
this.dragEnd = this.target;
event.dataTransfer.dropEffect = 'move';//修改鼠标效果
var param = {
dragEnd:this.dragEnd,
event: event
}
this.$emit('itemDragover',param)
},
//松开鼠标完成拖拽后触发
drop(event){
this.$emit('itemDrop')
},
//拖拽结束后触发,不管是否拖拽成功
dragend(event){
this.$emit('itemDragends')
},
//浏览器判断
BrowserType(){
var userAgent = navigator.userAgent; //取得浏览器的userAgent字符串
var isOpera = userAgent.indexOf("Opera") > -1; //判断是否Opera浏览器
var isIE = userAgent.indexOf("compatible") > -1 && userAgent.indexOf("MSIE") > -1 && !isOpera; //判断是否IE浏览器
var isEdge = userAgent.indexOf("Windows NT 6.1; Trident/7.0;") > -1 && !isIE; //判断是否IE的Edge浏览器
var isFF = userAgent.indexOf("Firefox") > -1; //判断是否Firefox浏览器
var isSafari = userAgent.indexOf("Safari") > -1 && userAgent.indexOf("Chrome") == -1; //判断是否Safari浏览器
var isChrome = userAgent.indexOf("Chrome") > -1 && userAgent.indexOf("Safari") > -1; //判断Chrome浏览器
if (isIE){
var reIE = new RegExp("MSIE (\\d+\\.\\d+);");
reIE.test(userAgent);
var fIEVersion = parseFloat(RegExp["$1"]);
return {name:"IE",num: fIEVersion};
}//isIE end
if (isFF) { return {name:"FF",num: "FF"};}
if (isOpera) { return {name:"Opera",num: "Opera"};}
if (isSafari) { return {name:"Safari",num: "Safari"};}
if (isChrome) { return {name:"Chrome",num: "Chrome"};}
if (isEdge) { return {name:"Edge",num: "Edge"};}
}
}
}
</script>