本文详解如何在Element Plus的el-table中结合SortableJS实现行拖拽排序。针对异步数据加载导致的UI与数据不一致问题,通过添加row-key属性解决。同时深入探讨二次确认排序时的取消逻辑,指出仅return false无法回滚UI,需利用nextTick重新初始化Sortable实例以恢复交互功能。
基于Element Plus的el-table组件构建表格,并集成SortableJS库以支持行拖拽排序功能。
安装依赖包 npm install -D sortablejs
采用Element Plus官方提供的表格示例代码作为基础结构。
<template>
<el-table :data="tableData" style="width: 100%">
<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>
</template>
<script lang="ts" setup>
const tableData = [
{
date: '2016-05-03',
name: 'Tom',
address: 'No. 189, Grove St, Los Angeles',
},
{
date: '2016-05-02',
name: 'Tom',
address: 'No. 189, Grove St, Los Angeles',
},
{
date: '2016-05-04',
name: 'Tom',
address: 'No. 189, Grove St, Los Angeles',
},
{
date: '2016-05-01',
name: 'Tom',
address: 'No. 189, Grove St, Los Angeles',
},
]
</script>引入SortableJS插件模块;
import Sortable from 'sortablejs'
接着定义用于初始化排序功能的函数实例。
const elTableRef = ref();
const setSort = () => {
const el = elTableRef?.value?.$el.querySelector('tbody')
new Sortable(el, {
animation: 180,
delay: 0,
onEnd: (e) => {
const targetRow = tableData.value.splice(e.oldIndex, 1)
tableData.value.splice(e.newIndex, 0, targetRow[0]);
},
})
}随后在 onMounted 生命周期钩子中执行setSort函数以绑定拖拽事件。
以下是实现基础排序功能的完整案例代码。
<template>
<el-table ref="elTableRef" :data="tableData" style="width: 100%">
<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>
</template>
<script lang="ts" setup>
import Sortable from 'sortablejs'
import { onMounted, ref } from 'vue';
const tableData = [
{
date: '2016-05-01',
name: 'Tom',
address: 'No. 189, Grove St, Los Angeles',
},
{
date: '2016-05-02',
name: 'Tom',
address: 'No. 189, Grove St, Los Angeles',
},
{
date: '2016-05-03',
name: 'Tom',
address: 'No. 189, Grove St, Los Angeles',
},
{
date: '2016-05-04',
name: 'Tom',
address: 'No. 189, Grove St, Los Angeles',
},
]
const elTableRef = ref();
const setSort = () => {
const el = elTableRef?.value?.$el.querySelector('tbody')
new Sortable(el, {
animation: 180,
delay: 0,
onEnd: (e) => {
const targetRow = tableData.splice(e.oldIndex, 1)
tableData.splice(e.newIndex, 0, targetRow[0]);
},
})
}
onMounted(() => {
setSort();
})
</script>当数据异步加载时,可能出现排序显示混乱的情况。
在实际业务场景中,tableData通常通过异步接口获取,此处模拟异步请求后调用排序逻辑的过程。
- 初始化时将tableData数据定义为空数组
const tableData = ref<any[]>([]) - 在setTimeout定时器回调中为tableData赋值模拟异步数据返回。
完整代码如下所示:
<template>
<el-table ref="elTableRef" :data="tableData" style="width: 100%">
<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>
</template>
<script lang="ts" setup>
import Sortable from 'sortablejs'
import { onMounted, ref } from 'vue';
const tableData = ref<any[]>([])
const elTableRef = ref();
const setSort = () => {
const el = elTableRef?.value?.$el.querySelector('tbody')
new Sortable(el, {
animation: 180,
delay: 0,
onEnd: (e) => {
const targetRow = tableData.value.splice(e.oldIndex, 1)
tableData.value.splice(e.newIndex, 0, targetRow[0]);
},
})
}
onMounted(() => {
setSort();
setTimeout(() => {
tableData.value = [
{
date: '2016-05-01',
name: 'Tom',
address: 'No. 189, Grove St, Los Angeles',
},
{
date: '2016-05-02',
name: 'Tom',
address: 'No. 189, Grove St, Los Angeles',
},
{
date: '2016-05-03',
name: 'Tom',
address: 'No. 189, Grove St, Los Angeles',
},
{
date: '2016-05-04',
name: 'Tom',
address: 'No. 189, Grove St, Los Angeles',
},
]
}, 200)
})
</script>?错误现象:此时实现的排序功能存在异常,若将第一行拖拽至第三行下方,会导致第一行和第二行同时移动至第三行下方。
随后在onEnd钩子中检查 tableData.value 数据,发现数据顺序正常(1位于3之下,2保持原位),但页面渲染中1和2均错误地显示在3之下。
解决方案:在el-table组件属性中添加 row-key="date" 唯一索引属性;
实现Sortable排序的二次确认逻辑。
业务开发中常需对排序操作进行二次确认。
- 若用户确认,则执行排序操作;
- 若用户取消,则撤销排序状态;
根据SortableJS官方文档,可在onMove、onUpdate或onEnd回调函数中处理相关逻辑。
若使用 onMove 回调,弹窗触发时机过早,不符合业务交互习惯。
使用onEnd 回调时,排序动作已实际完成。
最终选择在onUpdate回调中弹出二次确认窗口。
// 列表内元素顺序更新的时候触发
onUpdate: function (/**Event*/evt) {
ElMessageBox.confirm(
'是否拖拽到此位置?',
'提示',
{
confirmButtonText: '确定',
cancelButtonText: '取消',
type: 'warning',
}
)
.then(() => {
ElMessage({
type: 'success',
message: '排序成功',
})
return true;
})
.catch(() => {
ElMessage({
type: 'info',
message: '取消排序',
})
return false;
})
},点击取消按钮时,代码中执行了return false;但页面排序状态并未回滚。此时查看tableData.value的数据,发现数据顺序已被取消。
这让人联想到之前的异步数据Bug,当时数据正确但页面显示异常,通过row-key解决。如今再次出现UI与数据不一致,原因何在?
最初尝试使用vue的forceUpdate强制更新页面,但未生效。随后搜索到给el-table添加key属性的方式强制刷新::key="forceUpdate"。
完整示例代码如下:
<template>
<el-table ref="elTableRef" row-key="date" :key="forceUpdate" :data="tableData" style="width: 100%">
<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>
</template>
<script lang="ts" setup>
import Sortable from 'sortablejs'
import { onMounted, ref } from 'vue';
import { ElMessage, ElMessageBox } from 'element-plus'
const tableData = ref<any[]>([])
const elTableRef = ref();
const forceUpdate = ref(0);
const setSort = () => {
const el = elTableRef?.value?.$el.querySelector('tbody')
new Sortable(el, {
animation: 180,
delay: 0,
// 列表内元素顺序更新的时候触发
onUpdate: function (/**Event*/evt) {
ElMessageBox.confirm(
'是否拖拽到此位置?',
'提示',
{
confirmButtonText: '确定',
cancelButtonText: '取消',
type: 'warning',
}
)
.then(() => {
ElMessage({
type: 'success',
message: '排序成功',
})
return true;
})
.catch(() => {
ElMessage({
type: 'info',
message: '取消排序',
})
forceUpdate.value = Date.now();
return false;
})
},
})
}
onMounted(() => {
setTimeout(() => {
tableData.value = [
{
date: '2016-05-01',
name: 'Tom',
address: 'No. 189, Grove St, Los Angeles',
},
{
date: '2016-05-02',
name: 'Tom',
address: 'No. 189, Grove St, Los Angeles',
},
{
date: '2016-05-03',
name: 'Tom',
address: 'No. 189, Grove St, Los Angeles',
},
{
date: '2016-05-04',
name: 'Tom',
address: 'No. 189, Grove St, Los Angeles',
},
]
}, 200)
setSort();
})
</script>?存在问题:此时表格仅能拖动一次,点击取消排序后,整个表格的拖拽功能失效。
解决办法:在nextTick中重新调用 setSort 方法以重新绑定Sortable实例;
<template>
<el-table ref="elTableRef" row-key="date" :key="forceUpdate" :data="tableData" style="width: 100%">
<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>
</template>
<script lang="ts" setup>
import Sortable from 'sortablejs'
import { onMounted, ref, nextTick } from 'vue';
import { ElMessage, ElMessageBox } from 'element-plus'
const tableData = ref<any[]>([])
const elTableRef = ref();
const forceUpdate = ref(0);
const setSort = () => {
const el = elTableRef?.value?.$el.querySelector('tbody')
new Sortable(el, {
animation: 180,
delay: 0,
// 列表内元素顺序更新的时候触发
onUpdate: function (/**Event*/evt) {
ElMessageBox.confirm(
'是否拖拽到此位置?',
'提示',
{
confirmButtonText: '确定',
cancelButtonText: '取消',
type: 'warning',
}
)
.then(() => {
ElMessage({
type: 'success',
message: '排序成功',
})
return true;
// 排序成功,可以调用下保存接口,将数据保存下来,然后更新下tableData数据
})
.catch(() => {
ElMessage({
type: 'info',
message: '取消排序',
})
forceUpdate.value = Date.now();
// 重新调用下排序
nextTick(() => {
setSort();
})
})
},
})
}
onMounted(() => {
setTimeout(() => {
tableData.value = [
{
date: '2016-05-01',
name: 'Tom',
address: 'No. 189, Grove St, Los Angeles',
},
{
date: '2016-05-02',
name: 'Tom',
address: 'No. 189, Grove St, Los Angeles',
},
{
date: '2016-05-03',
name: 'Tom',
address: 'No. 189, Grove St, Los Angeles',
},
{
date: '2016-05-04',
name: 'Tom',
address: 'No. 189, Grove St, Los Angeles',
},
]
}, 200)
setSort();
})
</script>以上即为Element Plus结合SortableJS实现表格行拖拽效果的详细内容,更多关于Element Plus实现表格行拖拽的资料请关注本站其它相关文章!