安装
# 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 事件下获取。