1、引入依赖
npm install sortablejs --save

2、table表格设置
1、添加属性 ref="multipleTable" row-key="id" @row-drag-end="handleDragEnd"
2、添加列
<el-table-column width="50" align="center">
<template >
<i class="el-icon-rank drag-handle"></i>
</template>
</el-table-column>
<el-table v-loading="loading" :data="tableData"
:highlight-selection-row="true" :key="componentKey"
ref="multipleTable" row-key="id" @row-drag-end="handleDragEnd" >
<el-table-column width="50" align="center">
<template >
<i class="el-icon-rank drag-handle"></i>
</template>
</el-table-column>
<el-table-column type="selection" width="55" align="center" />
<el-table-column label="序号" type="index" align="center" min-width="5%" />
</el-table>
显示效果如下

3、添加事件
1、必须是 mounted ,不可以使用created
2、解决无法二次拖拽、拖拽后表格数据无法重新渲染问题
补充: :
1、key="componentKey"
2、// 重新初始化拖拽功能
this.$nextTick(() => {
this.initDragAndDrop();
});
3、 this.$set(this, 'materialList', newData);
<script>
import Sortable from "sortablejs";
export default {
name: "demo",
data() {
return {
tableData: [], // 确保初始化为空数组
sortable: null,
componentKey: 1000,
}
},
created() {
this.getList();
},
mounted() {
this.initDragAndDrop();
},
methods: {
initDragAndDrop() {
// 获取表格的tbody元素
const tbody = this.$refs.multipleTable.$el.querySelector('.el-table__body-wrapper tbody');
// 初始化Sortable
this.sortable = new Sortable(tbody, {
handle: '.drag-handle', // 指定拖拽手柄
animation: 150, // 动画时间
ghostClass: 'sortable-ghost', // 拖拽时的占位元素样式
onEnd: (event) => {
// 拖拽结束时触发
this.handleDragEnd(event);
}
});
console.log(this.sortable);
},
// 处理拖拽结束事件
handleDragEnd(evt) {
// 如果位置没有变化,不做处理
if (evt.oldIndex === evt.newIndex) {
return;
}
// 获取拖拽的行数据
const draggingRow = this.materialList[evt.oldIndex];
// 从原数组中移除并插入到新位置
var newData = JSON.parse(JSON.stringify(this.materialList));;
newData.splice(evt.newIndex, 0, newData.splice(evt.oldIndex, 1)[0]);
console.log(newData)
const nextRow = newData[evt.newIndex + 1];
// 通知父组件数据已更新
this.$set(this, 'materialList', newData);
// 强制刷新组件以确保视图更新
this.componentKey += 1;
// 发送拖拽信息
this.$emit('drag-end', {
id: draggingRow.materialDeviceId, // 拖拽行的ID
originalIndex: evt.oldIndex, // 原始位置(0开始)
originalPosition: evt.oldIndex + 1, // 原始位置(1开始)
newIndex: evt.newIndex, // 新位置(0开始)
newPosition: evt.newIndex + 1, // 新位置(1开始)
moved: evt.oldIndex !== evt.newIndex // 是否发生了移动
});
// TODO + -
console.log(`拖拽完成 - ID: ${draggingRow.materialDeviceId}, 从第${evt.oldIndex + 1}行移到第${evt.newIndex + 1}行`);
console.log('下一个位置:' , nextRow.materialDeviceId , nextRow.orderNum , nextRow.name)
// 重新初始化拖拽功能
this.$nextTick(() => {
this.initDragAndDrop();
});
},
/** 查询素材分配列表 */
getList() {
listDevice(this.queryParams).then(response => {
// 确保响应数据不为null
this.tableData= response.rows ? response.rows : [];
this.total = response.total ? response.total : 0;
}).catch(error => {
// 添加错误处理,防止数据为null
console.error('获取数据失败:', error);
this.tableData= [];
this.total = 0;
});
},
}
}
</script>
4、添加样式
<style scoped>
.el-icon-rank{
font-size: x-large;
}
/* 拖拽相关样式 */
.cursor-move {
cursor: move;
color: #909399;
transition: color 0.2s;
}
.cursor-move:hover {
color: #409EFF;
}
/* 拖拽时的占位符样式 */
::v-deep .sortable-ghost {
opacity: 0.5;
background-color: #f5f7fa;
}
/* 选中行样式 */
::v-deep .sortable-chosen {
background-color: #e6f7ff !important;
}
/* 正在拖拽的行样式 */
::v-deep .dragging-row {
background-color: #fffbe6 !important;
}
</style>
5、拖拽效果

