在前端开发中,拖拽排序是一种提升用户体验非常好的方式,常见的场景有单列表拖拽排序,多列表拖拽交换排序,比如以下这种效果:
下面将以这种效果为例,设计一个组件。
1. 安装所需依赖
npm install vuedraggable --save
本例中目前所用的版本为:2.20.0
2. 组件设计实现
<template>
<div class="dnd-list">
<div class="dnd-list-aside" :style="{width:width1 }" >
<h3>{{ list1Title }}</h3>
<draggable :list="list1" group="article" class="drag-area" :set-data="setData">
<div :key="element.id" v-for="element in list1" class="list-complete-item">
<div class="list-complete-item-handle1">
{{ element.id }} {{ element.title }} [{{ element.author }}]
</div>
<div style="position:absolute;right:0px">
<span style="float:right;margin-top:-20px;margin-right:5px;" @click="deleteItem(element)">
<i style="color: #ff4949" class="el-icon-delete" />
</span>
</div>
</div>
</draggable>
</div>
<div class="dnd-list-aside" :style="{width:width2}">
<h3>{{ list2Title }}</h3>
<draggable :list="list2" group="article" class="drag-area">
<div :key="element.id" v-for="element in list2" class="list-complete-item">
<div class="list-complete-item-handle2" @click="pushItem(element)">
{{ element.id }} {{ element.title }} [{{ element.author }}]
</div>
</div>
</draggable>
</div>
</div>
</template>
<script>
import draggable from "vuedraggable";
export default {
name: "DndList",
components: { draggable },
props: {
list1: {
type: Array,
default() {
return [];
},
},
list2: {
type: Array,
default() {
return [];
},
},
list1Title: {
type: String,
default: "list1",
},
list2Title: {
type: String,
default: "list2",
},
width1: {
type: String,
default: "48%",
},
width2: {
type: String,
default: "48%",
},
},
methods: {
// 是否在列表一
isNotInList1(v) {
return this.list1.every((k) => v.id !== k.id);
},
// 是否在列表二
isNotInList2(v) {
return this.list2.every((k) => v.id !== k.id);
},
// 删除列表项
deleteItem(element) {
for (const item of this.list1) {
if (item.id === element.id) {
const index = this.list1.indexOf(item);
this.list1.splice(index, 1);
break;
}
}
if (this.isNotInList2(element)) {
this.list2.unshift(element);
}
},
// 点击切换列表项
pushItem(element) {
for (const item of this.list2) {
if (item.id === element.id) {
const index = this.list2.indexOf(item);
this.list2.splice(index, 1);
break;
}
}
if (this.isNotInList1(element)) {
this.list1.push(element);
}
},
// 拖拽交换时
setData(dataTransfer) {
// 解决火狐问题
// 详见 : https://github.com/RubaXa/Sortable/issues/1012
dataTransfer.setData("Text", "");
},
},
};
</script>
<style lang="scss" scoped>
.dnd-list {
background: #fff;
padding-bottom: 40px;
&:after {
content: "";
display: table;
clear: both;
}
.dnd-list-aside {
float:left;
padding-bottom: 30px;
&:first-of-type {
margin-right: 2%;
}
.drag-area{
margin-top: 15px;
min-height: 50px;
padding-bottom: 30px;
}
}
}
.list-complete-item {
cursor: pointer;
position: relative;
font-size: 14px;
padding: 5px 12px;
margin-top: 4px;
border: 1px solid #bfcbd9;
transition: all 1s;
}
.list-complete-item-handle1 {
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
margin-right: 50px;
}
.list-complete-item-handle2 {
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
margin-right: 20px;
}
.list-complete-item.sortable-chosen {
background: #4ab7bd;
}
.list-complete-item.sortable-ghost {
background: #30b08f;
}
.list-complete-enter,.list-complete-leave-active {
opacity: 0;
}
</style>
3. 组件使用示例
<template>
<div class="box">
<DndList :list1="list1" :list2="list2" :list1Title="list1Title" :list2Title="list2Title"></DndList>
</div>
</template>
<script>
import DndList from "@/components/DndList";
export default {
components:{
DndList:DndList
},
data() {
return {
list1:[
{id:1,title:"《西游记》",author:"吴承恩"},
{id:2,title:"《红楼梦》",author:"曹雪芹"},
{id:3,title:"《水浒传》",author:"施耐庵"},
{id:4,title:"《三国演义》",author:"罗贯中"},
{id:5,title:"《名人传》",author:"罗曼罗兰"},
{id:6,title:"《钢铁是怎样炼成的》",author:"奥斯特洛夫斯基"},
],
list2:[
{id:7,title:"《鲁宾逊漂流记》",author:"笛福"},
{id:8,title:"《格列佛游记》",author:"约翰斯威夫特"},
{id:9,title:"《繁星春水》",author:"冰心"},
],
list1Title:"我的图书收藏",
list2Title:"已读完的图书"
};
}
}
</script>
<style scoped>
.box{
width:600px;
margin:20px;
padding:10px;
background:#fff;
}
</style>