一、html原生方法:
eg:表格-两列直接交换顺序:
<template>
<el-table :data="tableData" style="width: 100%">
<el-table-column
v-for="(item, index) in columns"
:key="item.prop"
:prop="item.prop"
:label="item.label"
>
<template #header="{ column }">
<span
style="cursor: move"
draggable="true"
@dragstart="handleDragStart(item)"
@drop="handleDrop(item)"
@dragover.prevent
>{{ column.label }}</span
>
</template>
</el-table-column>
</el-table>
</template>
<script setup lang="ts">
import { ref, nextTick } from 'vue';
interface TableColumn {
prop: string;
label: string;
}
const tableData = ref([
{ id: '1', name: '测试', location: '中国' },
{ id: '2', name: '张三', location: '英国' },
{ id: '3', name: '李四', location: '日本' },
{ id: '4', name: '王五', location: '韩国' },
{ id: '5', name: '小八', location: '厕所' },
]);
const columns = ref<TableColumn[]>([
{ label: '主键', prop: 'id' },
{ label: '名称', prop: 'name' },
{ label: '国家', prop: 'location' },
]);
const startIndex = ref();
const handleDragStart = (column) => {
if (columns.value.indexOf(column) !== startIndex.value) {
startIndex.value = columns.value.indexOf(column);
}
};
const handleDrop = (column) => {
if (startIndex.value !== null) {
const temp = columns.value[columns.value.indexOf(column)];
columns.value[columns.value.indexOf(column)] = columns.value[startIndex.value];
columns.value[startIndex.value] = temp;
startIndex.value = null;
}
};
</script>
如果只是想把拖动的列放入另一列前面或后面,而不是交换顺序(比如不相邻的两列交换位置),则把handleDrop方法这样写就行
if (startIndex.value !== null) {
console.log('edn', columns.value.indexOf(column));
columns.value.splice(
columns.value.indexOf(column),
0,
columns.value.splice(startIndex.value, 1)[0],
);
startIndex.value = null;
}
同时支持拖拽交换、插入另一个元素左侧或右侧
<template>
<el-table :data="tableData" style="width: 100%">
<el-table-column
v-for="(item, index) in columns"
:key="item.prop"
:prop="item.prop"
:label="item.label"
>
<template #header="{ column }">
<div style="display: flex; align-items: center">
<span draggable="true" @dragover.prevent @drop="handleDropLeft(index)"
><el-icon class="hover_point"><ArrowLeftBold /></el-icon
></span>
<span
style="cursor: move"
draggable="true"
@dragstart="handleDragStart(index)"
@drop="handleDrop(index)"
@dragover.prevent
>{{ column.label }}</span
><span draggable="true" @dragover.prevent @drop="handleDropRight(index)"
><el-icon class="hover_point"><ArrowRightBold /></el-icon></span
></div>
</template>
</el-table-column>
</el-table>
</template>
<script setup lang="ts">
import { ref, nextTick } from 'vue';
interface TableColumn {
prop: string;
label: string;
}
const tableData = ref([
{ id: '1', name: '测试', location: '中国' },
{ id: '2', name: '张三', location: '英国' },
{ id: '3', name: '李四', location: '日本' },
{ id: '4', name: '王五', location: '韩国' },
{ id: '5', name: '小八', location: '厕所' },
]);
const columns = ref<TableColumn[]>([
{ label: '主键', prop: 'id' },
{ label: '名称', prop: 'name' },
{ label: '国家', prop: 'location' },
]);
const startIndex = ref();
const handleDragStart = (index) => {
if (index !== startIndex.value) {
startIndex.value = index;
}
};
const handleDrop = (index) => {
if (startIndex.value !== null) {
const temp = columns.value[index];
columns.value[index] = columns.value[startIndex.value];
columns.value[startIndex.value] = temp;
startIndex.value = null;
}
};
function handleDropLeft(index) {
if (startIndex.value !== null) {
columns.value.splice(index - 1, 0, columns.value.splice(startIndex.value, 1)[0]);
startIndex.value = null;
}
}
function handleDropRight(index) {
if (startIndex.value !== null) {
columns.value.splice(index, 0, columns.value.splice(startIndex.value, 1)[0]);
startIndex.value = null;
}
}
</script>
二、使用sortablejs(推荐)
pnpm install sortablejs --save || npm install sortablejs --save
代码示例:
<template>
<!-- 页面表格 -->
<el-table :data="tableData" border align="left" height="400">
<el-table-column
style="cursor: move"
v-for="(item, index) in changeCol"
:key="`index`"
:prop="changeCol[index].prop"
:label="item.label"
>
<template #default="{ row, column, $index }" style=''>
<div style="display: flex; align-items: center;">
<el-icon :size='20' class="handle" style='cursor: move;' v-show="column.property === 'date'" ><Rank /></el-icon>
<span style="margin-left: 10px">{{ row[column.property] }}</span>
</div>
</template>
</el-table-column>
</el-table>
<!-- 弹框表格 -->
</template>
<script setup lang="ts">
import { onMounted, ref } from 'vue';
import Sortable from 'sortablejs';
const changeCol = ref([
{
label: '日期',
prop: 'date',
state: 'California',
},
{
label: '姓名',
prop: 'name',
state: 'California',
},
{
label: '地址',
prop: 'address',
state: 'California',
},
]);
const tableData = ref([
{
date: '2023-04-28',
name: 'aaa',
address: '长沙市雨花区',
done: false,
},
{
date: '2023-04-29',
name: 'bbb',
address: '长沙市岳麓区',
done: false,
},
{
date: '2023-04-30',
name: 'ccc',
address: '长沙市天心区',
done: false,
},
{
date: '2023-05-01',
name: 'ddd',
address: '长沙市芙蓉区',
done: false,
},
{
date: '2023-05-01',
name: 'ddd',
address: '长沙市芙蓉区',
done: false,
},
{
date: '2023-05-01',
name: 'ddd',
address: '长沙市芙蓉区',
done: false,
},
{
date: '2023-05-01',
name: 'ddd',
address: '长沙市芙蓉区',
done: false,
},
{
date: '2023-05-01',
name: 'ddd',
address: '长沙市芙蓉区',
done: false,
},
{
date: '2023-05-01',
name: 'ddd',
address: '长沙市芙蓉区',
done: false,
},
{
date: '2023-05-01',
name: 'ddd',
address: '长沙市芙蓉区',
done: false,
},
{
date: '2023-05-01',
name: 'ddd',
address: '长沙市芙蓉区',
done: false,
},
]);
const dropCol = ref([
{
label: '日期',
prop: 'date',
},
{
label: '姓名',
prop: 'name',
},
{
label: '地址',
prop: 'address',
},
]);
// 行拖拽
const rowDrop = () => {
const tbody = document.querySelector('tbody');
Sortable.create(tbody, {
group: {
name: 'words',
pull: true,
put: true,
},
handle:'.handle',//设置可拖拽的
animation: 300, //动画参数,
onEnd({ newIndex, oldIndex }) {
const currRow = tableData.value.splice(oldIndex, 1)[0];
tableData.value.splice(newIndex, 0, currRow);
},
});
};
// 列拖拽
const columnDrop = () => {
const wrapperTr = document.querySelector('tr');
Sortable.create(wrapperTr, {
animation: 180,
delay: 0,
onEnd: (evt) => {
setTimeout(() => {
const oldItem = dropCol.value[evt.oldIndex];
dropCol.value.splice(evt.oldIndex, 1);
dropCol.value.splice(evt.newIndex, 0, oldItem);
}, 5);
},
});
};
// 弹框显示隐藏
// 初始化
onMounted(() => {
rowDrop();
columnDrop();
});
</script>
常用配置
//一个网页存在多个分组时设置,组名相同的组之间元素可以相互拖拽
group: "name",
//2种group写法选一种就可以了
group: {
name: 'name',
pull: 'clone', //克隆元素
},
//是否允许元素内部排序,如果为false当有多个排序组时,多个组之间可以拖拽,本身不能拖拽(默认true)
sort: true,
//是否禁用拖拽和排序
disabled: false,
//动画效果持续时间(不设置或0都没有过渡效果)
animation: 150,
//点击指定class类的元素才能拖拽(比如点击元素内的图标才能拖拽元素,可以给图标设置my-handle class)
//class可以定义在元素本身上,也可以定义在子元素上
handle: ".my-handle",
// class为ignore的元素不能拖动
filter: ".ignore",
//含有item 类的元素可以被拖拽(class只能定义在元素本身上)
draggable: ".item",
//指定获取拖动后排序的属性
dataIdAttr: 'data-id',
//给停靠位置添加的class(可以给这个class定义样式)
ghostClass: "ghost",
//选中元素添加的类(包括悬浮的元素和停靠位置的元素)
chosenClass: "chosen",
//拖拽对象移动时添加的类
dragClass: "drag",
//禁用html5原生拖拽
forceFallback: false,
...
//克隆事件
onClone: function (evt) {
//被克隆的对象(被移到另外地方的那个元素)
var origEl = evt.item;
//克隆后的对象(还是在原来位置的元素)
var cloneEl = evt.clone;
cloneEl.innerHTML = "clone出的元素";
},
...
三、vuedraggable(推荐)
vuedeaggable是基于sortable.js二次封封装的一个js库, 二者本质上没啥区别, 一个针对于vue去使用的, 一个可以在其他地方更广泛的去使用,两个都是非常优秀的
1、安装
npm i -S vuedraggable
2、.vue 文件引入
import draggable from "vuedraggable";
components: { draggable },
3、简单的使用
template
<draggable
v-model="draggableList"
animation="300"
:options="{
sort: true
}"
dragClass="pointer"
>
<div v-for="(item, index) in draggableList" :key="index">
<div class="item">
<i
v-if="draggableTitle === '保存排序'"
class="el-icon-rank mr15 pointer"
></i>
{{ item.name }}
</div>
</div>
</draggable>
data
draggableTitle: "修改排序",
draggableList: [
{ id: 1, name: "11" },
{ id: 2, name: "22" },
{ id: 3, name: "33" },
{ id: 4, name: "44" },
{ id: 5, name: "55" }
],
methods
draggableClick() {
if (this.draggableTitle === "修改排序") {
this.draggableTitle = "保存排序";
} else {
this.$message.success("保存成功");
this.draggableTitle = "修改排序";
}
},
总结
如果是 element、 ant 组件里的表格 拖拽排序 建议用Sortable, 其他简单的列表排序用vuedraggable
vuedraggable 使用是一个标签 包含你所要拖拽的内容list, 而element ant 这种ui框架,对 表格进行了封装,
vuedraggable无法准确的去包含到表格行(及tbody里的tr td)数据, 而Sortable是直接绑定一个dom,可以根据dom去绑定
如果这两种方法都无法实现想要的效果那就再使用html原生方法吧