实用的 Vue.Draggable 拖拽插件 - zptime/blog GitHub Wiki
Vue.Draggable 是一款基于 Sortable.js 实现的 vue 拖拽插件。支持移动设备拖拽,可以在不同列表间拖拽,支持 vue 2 过渡动画兼容,总之是一款非常优秀的 vue 拖拽组件。
Vue.Draggable
拖拽组件的特点是:换位置时,相当于是从当前位置移除,插入到另一个地方,插入地方的数据都往后移动一位。
Github 地址: https://github.com/SortableJS/Vue.Draggable
中文文档:https://www.itxst.com/vue-draggable/tutorial.html
npm i -S vuedraggable
// 引入
import draggable from "vuedraggable";
// 组件注册
export default {
components: {
draggable,
},
};
// 使用
<draggable></draggable>;
export const mockData = {
fruitColumns: {
columns: [
{ name: "苹果", key: "apple", checked: true },
{ name: "梨子", key: "pear", checked: true },
{ name: "桃子", key: "peach", checked: false },
{ name: "西瓜", key: "watermelon", checked: true },
{ name: "榴莲", key: "durian", checked: false },
{ name: "百香果", key: "passion fruit", checked: true },
{ name: "哈密瓜", key: "Cantaloupe", checked: true },
{ name: "香蕉", key: "banana", checked: true },
{ name: "菠萝", key: "Pineapple", checked: true },
],
},
foodColumns: {
columns: [
{ name: "粥", key: "pineapple", checked: true },
{ name: "米饭", key: "rice", checked: true },
{ name: "饺子", key: "dumpling", checked: true },
{ name: "面条", key: "noodle", checked: false },
{ name: "豆浆", key: "soy milk", checked: true },
{ name: "面包", key: "bread", checked: false },
],
},
};
数据源是一个列表,同时展示的是多个拖拽组件。
- 通过
group
来区分组,可以在相同的组之间互相拖拽。本次实践的是不同组的拖拽组件,不能互相拖拽。 -
:draggable=".card"
定义了哪些元素是可以被拖动的
<template>
<div class="m-list">
<div
class="m-list-item"
v-for="(item, index) in dragList"
:key="item.type"
:id="item.type"
>
<template v-if="item && item.data && item.data.length">
<div class="m-list-item-header">
<div class="title">{{ item.title }}</div>
</div>
<draggable
v-model="item.data"
class="m-list-item-content"
ghostClass="card-ghost"
chosenClass="card-chosen"
dragClass="card-drag"
draggable=".card"
:group="item.type"
animation="300"
>
<transition-group>
<div
v-for="data in item.data"
:key="data.key"
:class="[
'card',
{
'card-active': item.checkedKeys.includes(data.key),
},
]"
@click.stop.prevent="handleDragClick(data, item, index)"
>
{{ data.name }}
</div>
</transition-group>
</draggable>
</template>
</div>
</div>
</template>
- 将模拟数据转为拖拽组件需要的数据:
queryColumns()
- 点击事件,进行选中和未选中切换,对应的状态也会更改,默认是白色,选中时淡蓝色:
handleDragClick()
<script>
import * as R from "ramda";
import draggable from "vuedraggable";
// 拖拽列表
const dragList = [
{ type: "fruit", key: "fruitColumns", title: "水果", data: [] },
{ type: "food", key: "foodColumns", title: "食物", data: [] },
];
export default {
data() {
return {
dragList,
};
},
components: {
draggable,
},
mounted() {
this.queryColumns();
},
methods: {
queryColumns() {
this.dragList = R.pipe(
R.toPairs,
R.map(([key, props]) => {
let result = R.mergeDeepRight(
R.find(R.propEq("key", key))(this.dragList),
{
key,
...props,
}
);
// 对应填充
result.data = R.pathOr([], ["columns"], result);
result.checkedKeys = R.map(
(o) => o.key,
R.filter((r) => r.checked, R.pathOr([], ["data"], result))
);
return result;
})
)(mockData);
},
handleDragClick(data, item, index) {
if (!R.includes(data.key, item.checkedKeys)) {
item.checkedKeys = R.append(data.key, item.checkedKeys);
} else {
item.checkedKeys = R.filter((o) => o !== data.key, item.checkedKeys);
}
// 此处要注意一下,更改的方式
R.adjust(index, () => changeCheckStatus(item), this.dragList);
},
},
};
</script>
-
ghostClass="card-ghost"
:设置拖动元素的占位符样式,需要加!important 才能生效(粉色背景,放大) -
chosenClass="card-chosen"
:被选中目标的样式,需要加!important 才能生效(淡蓝色背景) -
dragClass="card-drag"
:拖动元素的样式,需要加!important 才能生效(蓝色背景)
<style lang="scss" scoped>
.m-list {
width: 500px;
&-item {
margin-bottom: 32px;
cursor: move;
&:last-of-type {
margin-bottom: 16px;
}
&-header {
display: flex;
margin-bottom: 20px;
align-items: center;
justify-content: space-between;
line-height: 20px;
.title {
font-size: 16px;
font-weight: bold;
}
}
&-content {
display: flex;
flex-wrap: wrap;
> span {
flex: 1;
display: flex;
flex-wrap: wrap;
}
/* 被拖拽对象的样式 */
.card {
display: flex;
align-items: center;
justify-content: center;
width: 156px;
height: 40px;
border: 1px solid #ccc;
border-radius: 4px;
margin-right: 8px;
margin-bottom: 8px;
// 动画实现
transition: transform 0.3s;
&-active {
color: #1890ff;
border-color: #a8d2ee;
background-color: #e6f7ff;
cursor: move;
}
/* 被选中样式:必须在ghost样式之前 */
&-chosen {
color: #fff;
}
/* 拖动元素样式 */
&-drag {
background-color: #1890ff !important;
color: #fff;
border: none;
}
/* 拖动元素占位符样式 */
&-ghost {
background-color: pink !important;
color: #fff;
transform: scale(1.08);
}
}
.card + .card {
margin-bottom: 8px;
}
}
}
}
</style>