vue2/vue3 拖拽组件draggable封装

安装

# npm
npm install vuedraggable --save # vue2
npm install vuedraggable@next --save # vue3

# yarn
yarn add vuedraggable # vue2
yarn add vuedraggable@next # vue3

使用

1. 在 Vue 项目中导入 vuedraggable 组件并注册。

import draggable from 'vuedraggable'

export default {
  components: {
    draggable
  }
}

2. 然后在模板中使用它。

vue2 写法

<draggable v-model="list">
  <div v-for="item in list" :key="item.id">{{item}}</div>
</draggable>
data: {
  list: ['Item 1', 'Item 2', 'Item 3']
}

vue3 写法

<draggable v-model="list">
  <template #item="{ element }">
    <!-- 这里只能有一个div,存在注释掉的div也会报错 -->
    <div>
      <!-- div里面可以随意写 -->
      {{element}}
    </div>
  </template>
</draggable>
const state = reactive({
  list: ['Item 1', 'Item 2', 'Item 3']
})

封装组件

代码

group

相同的组之间可以相互拖拽。
ghostClass

设置拖动元素时的样式。

更多属性查看:sortable.js group属性多组之间拖动 - itxst.com

父组件

<template>
  <div class="menuBox">
    <div class="notUsed" ref="allMenus">
      <div class="title">未使用</div>
      <Drag :list="allList" divName="notUsedMenus" @getAllListValue="getAllListValue" />
    </div>
    <div class="selectMenu" ref="selectMenu">
      <div class="title">已使用</div>
      <Drag :list="usedList" divName="leftMenu" @getLeftListValue="getLeftListValue" />
    </div>
  </div>
</template>

<script lang="ts">
import { toRefs, reactive, onMounted, defineComponent, watch } from "vue";
import Drag from "./drag.vue";
export default defineComponent({
  name: "menuBar",
  components: {
    Drag
  },
  setup(props, {emit}) {
    const state = reactive({
      allList: [], // 未使用数据
      usedList: [], // 已使用数据
      initialList: ["工具1", "工具2", "工具3", "工具4"]
    });

    // 获取最终排序
    const submit = () => {
      console.log('allList', state.allList)
      console.log('usedList', state.usedList)
    }

    const getAllListValue = (e) => {
      state.allList = e
    }

    const getLeftListValue = (e) => {
      state.usedList = e
    }

    onMounted(() => {
      state.allList = state.initialList
    })

    return {
      submit,
      getAllListValue,
      getLeftListValue,
      ...toRefs(state),
    };
  },
});
</script>

<style scoped lang="scss">
.menuBox {
  height: 100%;
  display: flex;
  flex-direction: column;
  justify-content: space-between;
  .title {
    color: #20a5d6;
    font-size: 18px;
    margin-bottom: 8px;
  }
  .notUsed {
    height: 50%;
  }
  .selectMenu {
    height: 50%;
  }
}
</style>

子组件(封装的 draggable 组件)

<template>
  <div class="drag">
    <!-- 使用class类名进行区分不同的draggable,相当于一个标识 -->
    <draggable
      tag="div"
      :class="divName"
      v-model="allList"
      animation="300"
      ghostClass="ghost"
      :group="groupRight"
      @change="onChange($event, allList, divName)"
    >
      <template #item="{ element }">
        <div class="toolList">
          <span>{{ element }}</span>
        </div>
      </template>
    </draggable>
  </div>
</template>

<script lang="ts">
import { toRefs, reactive, defineComponent, watch } from "vue";
import draggable from "vuedraggable";
export default defineComponent({
  name: "drag",
  components: {
    draggable,
  },
  props: {
    list: {
      type: Array,
      default: [],
    },
    divName: {
      type: String,
      default: "",
    },
  },
  setup(props, {emit}) {
    const state = reactive({
      allList: [],
      groupRight: {
        name: "useList",
        pull: true, // 拖出
        put: true, // 拖入
      },
    });

    // 拖拽改变事件
    const onChange = (e, list, divName) => {
      // list 获取到拖拽后的值
      if(divName && list) {
        if(divName === 'notUsedMenus') {
          emit('getAllListValue', list)
        } else if(divName === 'leftMenu') {
          emit('getLeftListValue', list)
        }
      }
    }

    watch(
      () => props.list,
      (val) => {
        state.allList = val
      }
    )

    return {
      onChange,
      ...toRefs(state),
    };
  },
});
</script>

<style scoped lang="scss">
.drag {
  height: 80%;
}
.notUsedMenus, .leftMenu {
  width: 100%;
  height: 100%;
  border: 1px solid #fff;
  color: #fff;
  overflow: auto;
  display: flex;
  flex-wrap: wrap;
  padding: 6px 0;
  .toolList {
    width: 58px;
    text-align: center;
    display: flex;
    flex-direction: column;
    position: relative;
    font-size: 16px;
    cursor: pointer;
  }
}
.ghost {
  background: none;
}
</style>

注意:当有两个 draggable 嵌套时,拖拽里层的内容到外层,可能只会触发外层的 change 事件,无法触发里层的 change 事件,这时候如果需要获取里层的值发生改变,可以在外层的 change 事件下获取。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值