目录
- 1. 安装所需依赖
- 2. 组件设计实现
- 3. 组件使用示例
在前端开发中,拖拽排序是一种提升用户体验非常好的方式,常见的场景有单列表拖拽排序,多列表拖拽交换排序,比如以下这种效果:
下面将以这种效果为例,设计一个组件。
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> |