vue3 + el-table + Sortablejs 实现拖拽排序

459 阅读1分钟

安装第三方拖拽库

npm install sortablejs --save

效果预览

e-table-拖拽排序1.gif

代码实现

完整代码地址:gitee - 拖拽排序

<template>
	<div class="drap-table-sort">
        <!-- 一定要给 el-table 绑定唯一的 row-key, 否则行拖拽时页面上显示的顺序会有问题 -->
		<el-table
			:data="tableData"
			stripe
			style="width: 100%"
            row-key="id">
			<el-table-column
				label=""
				width="50"
				class-name="drap-handle-cell">
				<template #default="scoped">
					<span class="drap-handle">三</span>
				</template>
			</el-table-column>
			<el-table-column
				v-for="(item, index) in colums"
				:prop="item.prop"
				:label="item.label"
				width="180">
			</el-table-column>
		</el-table>
	</div>
</template>

<script lang="ts" setup>
import Sortable from 'sortablejs';

import { onMounted, onUnmounted, ref } from 'vue';
const colums = ref([
	{
		label: 'date',
		prop: 'date',
	},
	{
		label: 'name',
		prop: 'name',
	},
	{
		label: 'address',
		prop: 'address',
	},
]);
const tableData = ref([
	{
        id: '0',
		date: '2016-05-03',
		name: 'Tom',
		address: 'No. 189, Grove St, Los Angeles',
	},
	{
        id: '1',
		date: '2016-05-02',
		name: 'Tom',
		address: 'No. 189, Grove St, Los Angeles',
	},
	{
        id: '2',
		date: '2016-05-04',
		name: 'Tom',
		address: 'No. 189, Grove St, Los Angeles',
	},
	{
        id: '3',
		date: '2016-05-01',
		name: 'Tom',
		address: 'No. 189, Grove St, Los Angeles',
	},
]);

// 处理 拖拽逻辑
const handleRowDrag = () => {
	const tableBody = document.querySelector('.el-table__body-wrapper tbody') as HTMLElement;
	Sortable.create(tableBody, {
		animation: 150,
		handle: '.drap-handle-cell',
		direction: 'vertical',
		scroll: true,
		scrollSensitivity: 50,
		forceFallback: true,
		onStart: (event) => {
			// scroll 设为 true 会导致拖拽时选中其他文本,
			// 所以拖拽时禁用选中,拖拽完后恢复文本选择
			document.body.style.userSelect = 'none';
		},
		onEnd: (event: Sortable.SortableEvent) => {
			// 恢复文本选择
			document.body.style.userSelect = '';

			const moveItem = tableData.value.splice(event.oldIndex!, 1)[0];
			tableData.value.splice(event.newIndex!, 0, moveItem);
		},
	});
};

const handleColDrag = () => {
	const tableHeader = document.querySelector('.el-table__header-wrapper tr') as HTMLElement;
	Sortable.create(tableHeader, {
		animation: 150,
		direction: 'horizontal',
		onEnd: (event: Sortable.SortableEvent) => {
            // 因为 table 中第一列是 拖动按钮,所以 table 的 item 比 colums 多一项
            // index 是以 table 的列数为准的,所以需要 -1  
            const oldIndex = event.oldIndex! - 1
            const newIndex = event.newIndex! - 1
            const moveCol = colums.value.splice(oldIndex, 1)[0];
			colums.value.splice(newIndex, 0, moveCol);
		},
	});
};

const originDrop = document.body.ondrop;
onMounted(() => {
	// 据说 火狐浏览器 拖拽时会跳转到新的页面,所以这里先禁用默认事件
	document.body.ondrop = function (event) {
		event.preventDefault();
		event.stopPropagation();
	};

	handleRowDrag();
	handleColDrag();
});
onUnmounted(() => {
	document.body.ondrop = originDrop;
});
</script>

<style lang="scss" scoped>
.drap-table-sort {
	:deep(.el-table) {
		.drap-handle-cell {
			cursor: pointer;
		}
	}
}
</style>