List drag and drop sorting in Vue3

The main content of this paper is divided into three parts. First, I wrote the function of dragging and sorting the list by handwriting in Vue3 using HTML5the attribute. draggableNext, it records how to drag and sort table components in combination with Element Plusthe component library . The last part is the use of drag and drop components.sortable.jsel-tablevuedraggable

This article is based on Vite, Vue3, Element Plustechnology stack

draggable implements drag and drop sorting

properties and events

draggableAttributes are new draggable attributes in HTML5.

In HTML, except for images, links and selected text which are draggable by default, other elements are not draggable by default. If you want other elements to be draggable, you first need to set draggablethe property to true.

<p draggable="true"> 可拖拽</p>
复制代码

After the setting is complete, some events need to be combined to complete the drag and drop:

  • Events for dragging elements
event trigger timing
dragstart Executed once when dragging starts
drag Triggered multiple times after dragging starts
dragend Triggered 1 time after dragging
  • Events that can drop targets
event trigger timing
dragenter Executed once when dragging an element into a droppable target
dragover Triggered multiple times when dragging an element into a droppable target (once every 100 milliseconds)
drop When the dragged element is released into the dropable target (this event will only take effect if dragover is set)

placeable target

dragenter or  dragoverevent can be used to indicate valid drop targets, where the dragged element might be dropped.

Setting allow to be placed also requires blocking  dragenter and  dragover default handling of events.

<div ondragenter="event.preventDefault()">
复制代码

Make an example to understand 可放置目标:

DataTransfer object

DataTransfer The object is used to save the data during the dragging process, set the dragging type, etc.  The object can be obtained on the properties eventof  all drag events  .dataTransferDataTransfer

  • dataTransfer.dropEffect设置拖拽的操作类型。值必须是nonecopylink 或 move
function dragover(e) {
    e.preventDefault()
    e.dataTransfer.dropEffect = 'move'
}
复制代码
  • 传递数据 (不建议使用这种方法,可能有兼容问题)只能在drop事件中接收(测试的时候在其他拖拽事件中获取不到)
function dragstart(e, index) {
	e.stopPropagation()
	e.dataTransfer.dropEffect = 'move'
        // 传数据
	e.dataTransfer.setData('text/plain', '111111111')
}
function drop(e) {
        // 接收数据
	console.log(e.dataTransfer.getData('text/plain'));
}
复制代码

Vue3 中拖拽排序

根据上面描述的属性和事件在 Vue3 编写一个拖拽排序的列表大概分下面几个步骤:

  1. 创建一个列表,遍历渲染到页面
  2. 列表项添加 draggable="true"
  3. 列表项添加事件 dragstart dragenter dragend dragover
  4. dragenter 事件中,需要传入列表项的下标,实时进行元素的排序。排序的核心逻辑也是在 dragenter
  5. 代码执行的逻辑是:列表项拖拽到可放置目标时,将该拖拽的元素从原位置删除,再将拖拽的元素插入到当前可放置目标的位置

代码如下:

<template>
    <div>
            <div
                class="item"
                v-for="(item, i) in drag.list"
                :key="item.id"
                draggable="true"
                @dragstart="dragstart($event, i)"
                @dragenter="dragenter($event, i)"
                @dragend="dragend"
                @dragover="dragover"
               >
                {{ item.name }}
            </div>
    </div>
</template>
<script setup>
import { reactive } from 'vue'
const drag = reactive({
	list: [
		{ name: 'a', id: 1 },
		{ name: 'b', id: 2 },
		{ name: 'c', id: 3 },
		{ name: 'd', id: 4 },
		{ name: 'e', id: 5 },
	]
})

let dragIndex = 0

function dragstart(e, index) {
	e.stopPropagation()
	dragIndex = index
	setTimeout(() => {
		e.target.classList.add('moveing')
	},0)
}
function dragenter(e, index) {
	e.preventDefault()
	// 拖拽到原位置时不触发
	if (dragIndex !== index) {
		const source = drag.list[dragIndex];
		drag.list.splice(dragIndex, 1);
		drag.list.splice(index, 0, source);

		// 更新节点位置
		dragIndex = index
	}
}
function dragover(e) {
	e.preventDefault()
	e.dataTransfer.dropEffect = 'move'
}
function dragend(e) {
	e.target.classList.remove('moveing')
}
</script>

<style lang="scss" scoped>
.item {
	width: 200px;
	height: 40px;
	line-height: 40px;
	// background-color: #f5f6f8;
	background-color: skyblue;
	text-align: center;
	margin: 10px;
	color: #fff;
	font-size: 18px;
}

.container {
  position: relative;
  padding: 0;
}

.moveing {
	opacity: 0;
}
</style>
复制代码

此时是这样的效果:

动画.gif

这样就在 Vue3 中实现了拖拽排序。还可以利用 Vue 的<TransitionGroup> 内置组件,添加动画效果,让元素的过渡不会很生硬。

添加代码:

<template>
<div>
	<TransitionGroup name="list" tag="div" class="container">
		<div
			class="item"
			v-for="(item, i) in drag.list"
			:key="item.id"
			draggable="true"
			@dragstart="dragstart($event, i)"
			@dragenter="dragenter($event, i)"
			@dragend="dragend"
			@dragover="dragover"
		>
			{{ item.name }}
		</div>
	</TransitionGroup>
</div>
</template>

<style lang="scss" scoped>
    .list-move, /* 对移动中的元素应用的过渡 */
    .list-enter-active,
    .list-leave-active {
        transition: all 0.2s ease;
    }
</style>
复制代码

效果如下:

222.gif

sortable.js

接下来在 Element Plus 组件库中使用 sortable.js 进行表格排序。

Element Plus 是基于 Vue3 的常用的开源组件库

安装 sortable.js

npm i sortablejs -S
复制代码

使用 el-table 组件,写一个表格:

<template>
<div>
        <el-table :data="tableData" id="dragTable" border style="width: 800px;">
                <el-table-column prop="date" label="Date" width="180" />
                <el-table-column prop="name" label="Name" width="180" />
                <el-table-column prop="address" label="Address" />
        </el-table>
</div>
</template>

<script setup>
const tableData = [
	{
		date: '2016-05-03',
		name: 'Tom',
		address: 'No. 189, Grove St, Los Angeles',
	},
	{
		date: '2016-05-02',
		name: 'Cilly',
		address: 'No. 189, Grove St, Los Angeles',
	},
	{
		date: '2016-05-04',
		name: 'Linda',
		address: 'No. 189, Grove St, Los Angeles',
	},
	{
		date: '2016-05-01',
		name: 'John',
		address: 'No. 189, Grove St, Los Angeles',
	},
]
</script>
复制代码

现在就有了一个表格:

捕获.PNG

然后导入 sortable.js

<script setup>
import Sortable from 'sortablejs'
import { onMounted } from 'vue'

function setSort() {
    const el = document.querySelector('#dragTable table tbody')
    new Sortable(el, {
    sort: true,
		ghostClass: 'sortable-ghost',
		onEnd: (e) => {
			const targetRow = tableData.splice(e.oldIndex, 1)[0]
			tableData.splice(e.newIndex, 0, targetRow)
			console.log(tableData)
		},
	})
}
onMounted(() => {
	setSort()
})

const tableData = [
 // ...
]
</script>
复制代码

onMounted 中,也就是组件挂载完成之后,实例化 Sortable(),传入要进行拖拽排序的节点 el 和其它一些配置参数。现在可以进行拖拽了。

333.gif

有些时候,可能需要按住拖动图标才可以进行拖动,需要添加 handle 配置,并指定对应的样式名。

    <el-table :data="tableData" id="dragTable" border style="width: 600px; margin: 20px">
      <!-- ...... 省略代码 -->
      <el-table-column label="操作" width="100">
        <template #default>
          <div class="handle-drag">
            <el-icon>
              <Sort />
            </el-icon>
          </div>
        </template>
      </el-table-column>
    </el-table>
复制代码

上面代码将表格添加了一个操作列,并将操作列的图标设置一个样式类。下面的配置表示只有包含.handle-drag 样式的元素才可以被拖动。其他位置不能被拖动。

  new Sortable(el, {
    // ...
    handle: '.handle-drag',
    // ...
  })
复制代码

444.gif

查看其他配置项

vuedraggable

vue.draggable.next 是 Vue3 的拖拽组件,是基于 Sortable.js 实现的。可以用于拖拽列表、菜单、工作台、选项卡等常见的场景。

安装:

npm i -S vuedraggable@next
复制代码

使用:

<script setup>
import draggable from 'vuedraggable'
import { reactive } from 'vue'

const state = reactive({
	list1: [1, 2, 3, 4],
	list2: ['a', 'b', 'c', 'd'],
})

function onStart() {}

function onEnd() {
	console.log(state)
}

</script>
复制代码

先导入 draggable并定义一些基础数据。

<template>
	<div style="margin-left: 30px;">
		<draggable
			:list="state.list1"
			:force-fallback="true"
			chosen-class="chosen"
			animation="300"
			@start="onStart"
			@end="onEnd"
		>
			<template #item="{ element }">
				<div class="item">
					{{ element }}
				</div>
			</template>
		</draggable>
	</div>
</template>
复制代码

其中 @start@end 为拖拽开始和结束时的事件。chosen-class 为拖拽时的样式。

555.gif

为组件设置相同的 group 属性,可以实现在不同的块之间拖拽。

		<draggable group="group" :list="state.list1" >
			<template #item="{ element }">
				<div class="item bck1">
					{{ element }}
				</div>
			</template>
		</draggable>

		<draggable group="group" :list="state.list2" >
			<template #item="{ element }">
				<div class="item bck2">
					{{ element }}
				</div>
			</template>
		</draggable>
复制代码

666.gif

相关地址

Guess you like

Origin juejin.im/post/7231519213897875512