vue实现组件拖拽

效果预览

QQ录屏20231016140810

组件 drag.vue



<template>
  <TransitionGroup name="group-list" tag="div">
    <div
      v-for="(item, index) in list"
      :key="item.name"
      :draggable="item.draggable"
      :class="[
        'list-item',
        {
          'is-dragover':
            index === dropIndex && item.draggable && config.exchange,
        },
      ]"
      @dragstart="onDragstart($event, index)"
      @dragenter="onDragenter(index)"
      @dragover.prevent="onDragover(index)"
      @dragleave="onDragleave"
      @dragend="onDragend"
      @drop="onDrop"
    >
      <slot :item="item" />
    </div>
  </TransitionGroup>
</template>
  
  <script>
export default {
  name: "Draggable",
  props: {
    list: {
      type: Array,
      default: () => [],
    },
    config: {
      type: Object,
      default: () => ({
        name: "",
        push: true,
        pull: true,
        exchange: true,
      }),
    },
  },

  data() {
    return {
      dragIndex: null,
      dropIndex: null,
    };
  },

  computed: {
    isPush() {
      const { dropIndex, dragIndex } = this;
      return dropIndex !== null && dragIndex === null;
    },

    isExchange() {
      const { dropIndex, dragIndex } = this;
      return dragIndex !== null && dropIndex !== null;
    },

    pushCbName() {
      const {
        config: { name },
      } = this;
      return `${name}-push-callback`;
    },
  },

  methods: {
    onDragstart(e, i) {
      const {
        list,
        config: { name },
        transferData,
      } = this;

      this.dragIndex = i;

      if (name) {
        transferData({ e, key: name, type: "set", data: list[i] });
      } else {
        throw new Error("缺少配置关联名name");
      }

      this.$emit("drag-start", i);
    },

    onDragenter(i) {
      this.dropIndex = i;
      this.$emit("drag-enter", i);
    },

    onDragover(i) {
      const { dragIndex, dropIndex } = this;
      if (i === dragIndex || i === dropIndex) return;
      this.dropIndex = i;
      this.$emit("drag-over", i);
    },

    onDragleave() {
      this.dropIndex = null;
    },

    onDrop(e) {
      const {
        list,
        dropIndex,
        dragIndex,
        config: { name, push: enablePush, exchange },
        isPush,
        isExchange,
        pushCbName,
        storage,
        resetIndex,
        transferData,
      } = this;

      if (dropIndex === dragIndex || !exchange) return;

      if (isPush) {
        if (!enablePush) {
          resetIndex();
          return;
        }

        if (name) {
          list.splice(
            dropIndex,
            0,
            transferData({ e, key: name, type: "get" })
          );

          storage("set", pushCbName, true);
        } else {
          resetIndex();
          throw new Error("缺少配置关联属性name");
        }
        resetIndex();
        return;
      }

      if (isExchange) {
        const drapItem = list[dragIndex];
        const dropItem = list[dropIndex];
        list.splice(dropIndex, 1, drapItem);
        list.splice(dragIndex, 1, dropItem);
      }

      resetIndex();
    },

    onDragend() {
      const {
        list,
        dragIndex,
        config: { pull: enablePull },
        pushCbName,
        storage,
        resetIndex,
      } = this;

      if (enablePull) {
        const isPushSuccess = storage("get", pushCbName);

        if (isPushSuccess) {
          list.splice(dragIndex, 1);
          storage("remove", pushCbName);
        }
      }
      resetIndex();
      this.$emit("drag-end");
    },

    storage(type, key, value) {
      return {
        get() {
          return JSON.parse(localStorage.getItem(key));
        },
        set() {
          localStorage.setItem(key, JSON.stringify(value));
        },
        remove() {
          localStorage.removeItem(key);
        },
      }[type]();
    },

    resetIndex() {
      this.dropIndex = null;
      this.dragIndex = null;
    },

    transferData({ e, key, type, data } = {}) {
      if (type === "get") {
        return JSON.parse(e.dataTransfer.getData(`${key}-drag-key`));
      }

      if (type === "set") {
        e.dataTransfer.setData(`${key}-drag-key`, JSON.stringify(data));
      }
    },
  },
};
</script>
  
  <style  scoped>
.TransitionGroup {
  display: flex;
}
.list-item {
  list-style: none;
  position: relative;
  margin: 0 10px 10px 0;
  border-radius: 4px;
  display: flex;
  background-color: #fff;
  cursor: move;
}

.group-list-move {
  transition: transform 0.5s;
}
</style>
  

页面引用

index.vue

 

<template>
  <div class="dragBox">
              <Drag
                style="display: flex; flex-wrap: wrap"
                :list="list1"
                :config="config1"
              >
                <template v-slot="{ item }">
                  <div class="item">
                    <i
                      class="el-icon-s-custom"
                      v-if="item.index % 2 == 0"
                      style="
                        font-size: 25px;
                        color: rgb(255, 255, 255);
                        background: pink;
                        width: 40px;
                        height: 40px;
                        border-radius: 50%;
                        line-height: 40px;
                        margin-right: 15px;
                      "
                    ></i>
                    <i
                      v-else
                      class="el-icon-s-custom"
                      style="
                        font-size: 25px;
                        color: rgb(255, 255, 255);
                        background: #6095f9;
                        width: 40px;
                        height: 40px;
                        border-radius: 50%;
                        line-height: 40px;
                        margin-right: 15px;
                      "
                    ></i
                    >{{ item.name }}
                  </div>
                </template>
              </Drag>
            </div>
</template>

<script>
import Drag from "./drag.vue";

export default {
  components: {
    Drag,
  },

  data() {
    return {
        //list: 渲染列表
        //config: {
          // name: '', // 跨列表关联名,跨列表拖拽时必传
          // push: true, // 当前列表是否支持从其他列表push元素进来
          // pull: true, // 将当前列表的某个元素拖拽并添加到其他列表里,该元素是否从当前列表移除
          // exchange: true, // 当前列表元素之间是否支持交换位置
        //}

      list1: [
        { name: "张", draggable: true, index: 1 },
        { name: "王", draggable: true, index: 2 },
        { name: "李", draggable: true, index: 3 },
        { name: "赵", draggable: true, index: 4 },
      ],

      config1: {
        name: "test",
        push: true,
        pull: true,
        exchange: true,
      },

      list2: new Array(10).fill(0).map((_, i) => ({
        name: `列表2 - ${i + 1}`,
        draggable: true,
      })),

      config2: {
        name: "test",
        push: true,
        pull: true,
        exchange: true,
      },
    };
  },
};
</script>

<style  scoped>
.dragBox {
  display: flex;
  justify-content: center;
}
.item {
  width: 180px;
  height: 60px;
  display: flex;
  align-items: center;
  justify-content: center;
  background: #f9fafc;
  line-height: 60px;
  text-align: center;
}
</style>


 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值