项目需求做一个和element中穿梭框一样的效果,但是要求多家一个功能,就是右边的数据要实现拖动排序的效果;没办法,不能用element现成的组件,只能自己封装;代码
<template>
<div class="customItems">
<div class="customList">
<p class="title"><el-checkbox v-model="leftAll" @change="checkAll($event,left,'left')">左边列表</el-checkbox></p>
<div class="checkboxList">
<el-checkbox class="checkbox" @change="check('left')" v-model="item.checked" v-for="item in left" :key="item.id">{{item.name}}</el-checkbox>
</div>
</div>
<div class="center">
<el-button class="btn" size="mini" :disabled="!toRightFlag" @click="toRight" icon="el-icon-d-arrow-right"></el-button>
<el-button class="btn" size="mini" :disabled="!toLeftFlag" @click="toLeft" icon="el-icon-d-arrow-left"></el-button>
</div>
<div class="checkedList">
<p class="title"><el-checkbox v-model="rightAll" @change="checkAll($event,right,'right')">已选择</el-checkbox><el-button class="sort" type="text" v-show="right.length>1" @click="sort">{{sortFlag?'保存':'排序'}}</el-button></p>
<div class="checkboxList">
<el-checkbox class="checkbox" @change="check('right')" v-model="item.checked" v-for="item in right" v-show="!sortFlag" :key="item.id">{{item.name}}</el-checkbox>
<p :class="{checkbox:true, dragBox:true,active:active===item.id}" :style="{top:top+'px'}" @mousedown="moveStart($event,item.id,index)" v-for="item,index in right" v-show="sortFlag">{{item.name}}</p>
</div>
</div>
</div>
</template>
<script>
export default {
data(){
return {
left:[],
right:[],
leftAll:false,
rightAll:false,
toLeftFlag:false,
toRightFlag:false,
sortFlag:false,
active:"",
x:0,
y:0,
l:0,
top:0
}
},
computed : {},
methods : {
//数据初始化
dataInit(list){
for(let i=0,len=this.left.length; i<len; i++){
if(list.indexOf(this.left[i].id)!==-1){
this.right.push(JSON.parse(JSON.stringify(this.left[i])));
this.left.splice(i,1);
i--;
len--;
}
}
},
//两边的全选事件
checkAll(val,arr,key){
arr.forEach(e=>{
e.checked=val;
});
this[key]=JSON.parse(JSON.stringify(this[key]));
this.check(key)
},
//单选事件
check(key){
for(let i=0,len=this[key].length; i<len; i++){
if(this[key][i].checked){
key==="left"?this.toRightFlag=true:this.toLeftFlag=true;
return;
}
}
key==="left"?this.toRightFlag=false:this.toLeftFlag=false;
},
//移动选中项到右边
toRight(){
for(let i=0,len=this.left.length; i<len; i++){
if(this.left[i].checked){
this.left[i].checked=false;
this.right.push(JSON.parse(JSON.stringify(this.left[i])));
this.left.splice(i,1);
i--;
len=this.left.length
}
}
this.toRightFlag=this.leftAll=false;
this.change();
},
//移动选中项到左边
toLeft(){
for(let i=0,len=this.right.length; i<len; i++){
if(this.right[i].checked){
this.right[i].checked=false;
this.left.push(JSON.parse(JSON.stringify(this.right[i])));
this.right.splice(i,1);
i--;
len=this.right.length
}
}
this.toLeftFlag=this.rightAll=false;
this.change();
},
//右边数据发生变化时,处理数据格式,同时出发change事件;里面的逻辑可以自己编辑
change(){
let res=[];
this.right.forEach(e=>{
res.push(e.id)
});
this.$emit("change",res)
},
//排序按钮和保存按钮点击事件
sort(){
this.sortFlag=!this.sortFlag;
if(!this.sortFlag){
this.change();
}
},
//开始拖动
moveStart(e,id,index){
this.x=e.clientX;
this.y=e.clientY;
this.index=index;
this.active=id;
document.addEventListener("mousemove",this.moveing,false);
document.addEventListener("mouseup",this.moveEnd,false);
},
//拖动中
moveing(e){
this.l=e.clientX-this.x;
this.top=e.clientY-this.y;
if(this.top>0){//向下
if(this.index<this.right.length-1){
if(this.top>=15){
this.right[this.index+1]=this.right.splice(this.index,1,this.right[this.index+1])[0];
this.index+=1;
this.y+=30;
this.top-=30;
}
}
}else {//向上
if(this.index>0){
if(this.top<=-15){
this.right[this.index-1]=this.right.splice(this.index,1,this.right[this.index-1])[0];
this.index-=1;
this.y-=30;
this.top +=30;
}
}
}
},
//拖动结束
moveEnd(e){
document.removeEventListener("mousemove",this.moveing);//移除鼠标移动事件
document.removeEventListener("mouseup",this.moveEnd);//移除事件
this.l=this.top=0;
this.active="";
this.index=0;
}
},
mounted(){
this.left=this.$attrs.data
}
}
</script>
<style scoped>
.customItems{
display:flex;
}
.customList,.checkedList{
border: 1px solid #ebeef5;
border-radius: 4px;
overflow: hidden;
background: #fff;
display: inline-block;
vertical-align: middle;
width: 240px;
max-height: 100%;
box-sizing: border-box;
position: relative;
}
.customList{
}
.title{
height: 40px;
line-height: 40px;
background: #f5f7fa;
margin: 0;
padding-left: 15px;
border-bottom: 1px solid #ebeef5;
box-sizing: border-box;
color: #000;
}
.checkboxList{
margin: 0;
padding: 6px 0;
list-style: none;
height: 246px;
overflow: auto;
box-sizing: border-box;
}
.checkbox{
height: 30px;
line-height: 30px;
padding-left: 15px;
display: block;
margin:0;
}
.sort{
float:right;
margin-right:30px;
}
.dragBox{
cursor:pointer;
-webkit-user-select:none;
-moz-user-select:none;
-ms-user-select:none;
user-select:none;
}
.dragBox.active{
position:relative;
background-color:rgba(0,0,0,.3);
}
.center{
width:80px;
display:flex;
flex-direction:column;
justify-content:center;
align-items:center;
}
.center .btn{
padding:0;
margin:10px;
width:30px;
height:30px;
}
</style>
组件引用
<Transfer ref="transfer " :data="data" @change="change"></Transfer>
data是供选择的数组数据;
change是右边的数据发生变化是的回调事件;方便修改表单中绑定的值,返回参数为右边的数据
如果有初始数据,需要调用组件的dataInit方法进行数据初始化
this.$refs.transfer.dataInit(list)//list传入初始数据
基本能实现需求穿梭框加排序的需求