vue3 vuedraggable实现拖拽组件+复选功能(组件封装使用)

发布于:2024-04-05 ⋅ 阅读:(72) ⋅ 点赞:(0)

今天封装(CV即用)了vue3常用的vuedraggable拖拽标签组件+复选功能,介绍了特性、属性、函数配置、事件、插槽、使用六个方面,让开发变得更加高效。

效果CV即用

Snipaste_2024-02-23_10-48-54.png

1. 安装引入vuedraggable(官网)


npm install -S vuedraggable@next //安装最新版
import draggable from "vuedraggable"; // 页面引入

2. 特性

  • 支持触摸设备
  • 支持拖拽和选择文本
  • 支持智能滚动
  • 支持不同列表之间的拖拽
  • 不以jQuery为基础
  • 和视图模型同步刷新
  • 支持撤销操作
  • 当需要完全控制时,可以抛出所有变化
  • 可以和现有的UI组件兼容

3. 属性

如果下面的属性说明未能完全看明白,请访问非vue版 里面有更详细的例子。

标题 说明
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>

结语

本篇文章到此就结束了,欢迎在评论区交流。

🔥如果此文对你有帮助的话,欢迎💗关注、👍点赞、⭐收藏✍️评论,  支持一下博主~