vue拖拽排序效果
朝阳39 人气:0效果预览
组件 drag.vue
<template> <TransitionGroup name="group-list" tag="ul"> <li 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" /> </li> </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> .list-item { list-style: none; position: relative; margin-bottom: 10px; border-radius: 4px; padding: 4px; background-color: #fff; cursor: move; } .list-item.is-dragover::before { content: ""; position: absolute; bottom: -4px; left: 0; width: 100%; height: 4px; background-color: #0c6bc9; } .list-item.is-dragover::after { content: ""; position: absolute; bottom: -8px; left: -6px; border: 3px solid #0c6bc9; border-radius: 50%; width: 6px; height: 6px; background-color: #fff; } .group-list-move { transition: transform 0.8s; } </style>
使用范例
index.vue
<template> <div class="dragBox"> <Drag style="width: 200px" :list="list1" :config="config1"> <template v-slot="{ item }"> <div class="item"> {{ item.name }} </div> </template> </Drag> <Drag style="width: 200px" :list="list2" :config="config2"> <template v-slot="{ item }"> <div class="item"> {{ item.name }} </div> </template> </Drag> </div> </template> <script> import Drag from "./drag.vue"; export default { components: { Drag, }, data() { return { list1: new Array(10).fill(0).map((_, i) => ({ name: `列表1 - ${i + 1}`, draggable: true, })), 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 { border: 1px solid #ccc; width: 200px; height: 30px; text-align: center; } </style>
参数说明
list: 渲染列表
config: {
name: '', // 跨列表关联名,跨列表拖拽时必传
push: true, // 当前列表是否支持从其他列表push元素进来
pull: true, // 将当前列表的某个元素拖拽并添加到其他列表里,该元素是否从当前列表移除
exchange: true, // 当前列表元素之间是否支持交换位置
}
加载全部内容