今天封装(CV即用)了vue3常用的vuedraggable拖拽标签组件+复选功能,介绍了特性、属性、函数配置、事件、插槽、使用六个方面,让开发变得更加高效。
效果CV即用
1. 安装引入vuedraggable(官网)官网地址
npm install -S vuedraggable@next //安装最新版
import draggable from "vuedraggable"; // 页面引入
2. 特性
- 支持触摸设备
- 支持拖拽和选择文本
- 支持智能滚动
- 支持不同列表之间的拖拽
- 不以jQuery为基础
- 和视图模型同步刷新
- 支持撤销操作
- 当需要完全控制时,可以抛出所有变化
- 可以和现有的UI组件兼容
3. 属性
如果下面的属性说明未能完全看明白,请访问非vue版 sortable.js里面有更详细的例子。
标题 | 说明 |
---|---|
value | Array,非必须,默认为null。用于实现拖拽的list,通常和内部v-for循环的数组为同一数组。不是直接使用,而是通过v-model引入。<v-model=“myArray”> |
list | Array,非必须,默认为null。就是value的替代品。和v-model不能共用,从表现上没有不同 |
element | String,默认div。就是draggable标签在渲染后展现出来的标签类型,也是包含拖动列表和插槽的外部标签,可以用来兼容UI组件。 |
group | :group= “name”,相同的组之间可以相互拖拽 或者 { name: “…”, pull: [true, false, ‘clone’, array , function], put: [true, false, array , function] } |
sort | :sort= “true”,是否开启内部排序,如果设置为false,它所在组无法排序,在其他组可以拖动排序 |
delay | :delay= “0”, 鼠标按下后多久可以拖拽 |
touchStartThreshold | 鼠标移动多少px才能拖动元素 |
disabled | :disabled= “true”,是否启用拖拽组件 |
animation | 拖动时的动画效果,还是很酷的,数字类型。如设置animation=1000表示1秒过渡动画效果 |
handle | :handle=“.mover” 只有当鼠标移动到css为mover类的元素上才能拖动 |
filter | :filter=“.unmover” 设置了unmover样式的元素不允许拖动 |
draggable | :draggable=“.item” 哪些元素是可以被拖动的 |
ghostClass | :ghostClass=“ghostClass” 设置拖动元素的占位符类名,你的自定义样式可能需要加!important能生效,并把forceFallback属性设置成true |
chosenClass | :ghostClass=“hostClass” 被选中目标的样式,你的自定义样式可能需要加!important才能生效,并把forceFallback属性设置成true |
dragClass | :dragClass="dragClass"拖动元素的样式,你的自定义样式可能需要加!important才能生效,并把forceFallback属性设置成true |
dataIdAttr | dataIdAttr: ‘data-id’ |
forceFallback | 默认false,忽略HTML5的拖拽行为,因为h5里有个属性也是可以拖动,你要自定义ghostClass chosenClass dragClass样式时,建议forceFallback设置为true |
fallbackClass | 默认false,克隆的DOM元素的类名 |
allbackOnBody | 默认false,克隆的元素添加到文档的body中 |
fallbackTolerance | 拖拽之前应该移动的px |
scroll | 默认true,有滚动区域是否允许拖拽 |
scrollFn | 滚动回调函数 |
scrollSensitivity | 距离滚动区域多远时,滚动滚动条 |
scrollSpeed | 滚动速度 |
4. 函数配置
函数
-
setData: 设置值时的回调函数
-
onChoose: 选择单元时的回调函数
-
onStart: 开始拖动时的回调函数
-
onEnd: 拖动结束时的回调函数
-
onAdd: 添加单元时的回调函数
-
onUpdate: 排序发生变化时的回调函数
-
onRemove: 单元被移动到另一个列表时的回调函数
-
onFilter: 尝试选择一个被filter过滤的单元的回调函数
-
onMove: 移动单元时的回调函数
-
onClone: clone时的回调函数
函数对象的属性
-
to: 移动到的列表的容器
-
from:来源列表容器
-
item: 被移动的单元
-
clone: 副本的单元
-
oldIndex:移动前的序号
-
newIndex:移动后的序号
5. 事件
种类
- start
- add
- remove
- update
- end
- choose
- sort
- filter
- clone
属性
-
add: 包含被添加到列表的元素
-
newIndex: 添加后的新索引
-
element: 被添加的元素
-
removed: 从列表中移除的元素
-
oldIndex: 移除前的索引
-
element: 被移除的元素
-
moved:内部移动的
-
newIndex: 改变后的索引
-
oldIndex: 改变前的索引
-
element: 被移动的元素
6. 插槽
提供一个footer插槽,在排序列表之下。永远位于最下方。
<draggable v-model="myArray" :options="{draggable:'.item'}">
<div v-for="element in myArray" :key="element.id" class="item">
{{element.name}}
</div>
<button slot="footer" @click="addPeople">Add</button>
</draggable>
7. 封装示例(CV即用)
组件完整代码
<template>
<div class="about">
<el-dialog
class="notice-dialog1"
:model-value="dragDialog"
:before-close="confirm"
title="配置列表展示字段"
width="50%"
:append-to-body="true"
:close-on-press-escape="false"
:close-on-click-modal="false"
:show-close="false"
>
<template #header="{ close, titleId, titleClass }">
<div class="my-header">
<h4 :id="titleId" :class="titleClass">
<span>配置列表展示字段 </span>
<span class="spanTip"> (用户可拖动排序)</span>
</h4>
<el-icon @click="close" class="el-icon--left">
<CircleClose />
</el-icon>
</div>
</template>
<draggable
:list="dragArr"
ghost-class="ghost"
:force-fallback="true"
chosen-class="chosenClass"
animation="300"
@start="onDragStart"
@end="onDragEnd"
:sort="true"
handle=".drag"
item-key="id"
>
<template #item="{ element, index }">
<div :class="element.state == 1 ? 'item1 item' : 'item'">
<i
><el-checkbox
@change="checkoutChange(element, index)"
v-model="element.state"
size="large"
:label="element.name"
:true-label="1"
:false-label="0"
/>
</i>
<i>{{ index + 1 }}</i>
<i class="drag">拖动</i>
</div>
</template>
</draggable>
<template #footer>
<span class="dialog-footer">
<el-button
class="dialogCancelBtn"
@click="confirm"
style="min-width: 80px"
>取 消</el-button
>
<el-button
class="dialogOkBtn"
type="primary"
@click="submitDrag"
style="min-width: 80px"
>
确 定
</el-button>
</span>
</template>
</el-dialog>
</div>
</template>
<script setup>
import draggable from "vuedraggable";
const props = defineProps({
dragDialog: {
type: Boolean,
required: true,
},
dragArr: {
type: Array,
},
});
const emit = defineEmits([
"update:dragDialog",
"submitDrag",
"checkoutChange",
"onDragStart",
"onDragEnd",
]);
// 关闭
const confirm = () => {
emit("update:dragDialog", false);
};
// 提交
const submitDrag = () => {
emit("submitDrag");
};
// 复选框勾选
const checkoutChange = (element, index) => {
emit("checkoutChange", element, index);
};
// 拖动前
const onDragStart = () => {
emit("onDragStart");
};
// 拖动后
const onDragEnd = () => {
emit("onDragEnd");
};
</script>
<style lang="scss">
.notice-dialog1 {
padding: 0 12px;
border-radius: 15px;
.my-header {
position: relative;
h4 {
text-align: center;
height: 30px;
}
.el-icon {
position: absolute;
top: 2px;
right: -10px;
cursor: pointer;
}
.spanTip {
font-size: 14px;
color: #999;
}
}
.item {
padding: 6px 10px;
width: 46%;
display: inline-flex;
justify-content: space-between;
margin: 0 0.5%;
background-color: #f6f8fa;
border-radius: 5px;
line-height: 42px;
i:nth-child(1) {
width: 70%;
height: 42px;
line-height: 46px;
}
i:nth-child(2) {
width: 12%;
height: 16px;
line-height: 16px;
margin: auto 0;
text-align: center;
text-decoration: underline;
border-left: 1px solid #ccc;
border-right: 1px solid #ccc;
}
i:nth-child(3) {
width: 8%;
height: 42px;
line-height: 42px;
text-decoration: underline;
padding-left: 20px;
img {
width: 15px;
height: 20px;
margin-top: 10px;
}
}
.el-checkbox.el-checkbox--large {
height: 7%;
}
.el-checkbox__input.is-checked .el-checkbox__inner {
background-color: #114ed9;
border-color: #114ed9;
}
.el-checkbox__input.is-checked + .el-checkbox__label {
color: #114ed9;
}
}
.item1 {
background-color: #ffffff;
box-shadow: 0 0 10px #0000001a;
border-left: solid 2px #114ed9;
width: 45.8%;
line-height: 40px;
}
.drag {
cursor: move;
}
.item + .item {
margin-top: 20px;
}
.ghost {
border: solid 1px rgb(19, 41, 239) !important;
}
.chosenClass {
background-color: #eee;
opacity: 1;
border: solid 1px red;
}
}
.notice-dialog1 .el-dialog__header {
border-bottom: 1px solid #f6f6f6;
margin-right: 0;
}
.notice-dialog1 .el-dialog__body {
height: 300px;
overflow: auto;
}
.notice-dialog1 .el-dialog__body::-webkit-scrollbar {
display: none;
}
.notice-dialog1 .el-dialog__footer {
border-top: 1px solid #f6f6f6;
text-align: center;
padding-top: 20px;
}
</style>
组件使用
<template>
<div>
<el-button @click="onClick">弹框</el-button>
<DragDialog
v-model:dragDialog="dialogDrag"
:dragArr="myArray"
@submitDrag="submitDrag"
@checkoutChange="checkoutChange"
@onDragStart="onDragStart"
@onDragEnd="onDragEnd"
></DragDialog>
</div>
</template>
<script setup>
const dialogDrag = ref(false);
const myArray = ref([
{
name: "张三",
state: "0",
id: 1,
},
{
name: "李四",
state: "0",
id: 2,
},
{
name: "王五",
state: "0",
id: 3,
},
{
name: "赵六",
state: "0",
id: 4,
},
{
name: "AK、dadada",
state: "0",
id: 5,
},
]);
const onClick = () => {
dialogDrag.value = true;
};
// 提交
const submitDrag = () => {};
// 拖动前
const onDragStart = () => {};
// 拖动后
const onDragEnd = () => {};
// 复选框
const checkoutChange = (element, index) => {};
</script>
结语
本篇文章到此就结束了,欢迎在评论区交流。
🔥如果此文对你有帮助的话,欢迎💗关注、👍点赞、⭐收藏、✍️评论, 支持一下博主~