实现Element UI中的el-table表格组件的行和列的拖拽排序
使用
Vue3 + Element Plus UI + sortablejs
安装sortablejs
sh
pnpm install sortablejs
行拖拽
基本实现
效果
html
<script setup>
import { onMounted, ref } from "vue";
import Sortable from "sortablejs";
const tableData = ref([
{
id: 1,
date: "2016-05-02",
name: "王小虎111",
age: 21,
address: "上海市普陀区金沙江路 1518 弄",
},
{
id: 2,
date: "2016-05-04",
name: "王小虎222",
age: 22,
address: "上海市普陀区金沙江路 1517 弄",
},
{
id: 3,
date: "2016-05-01",
name: "王小虎333",
age: 23,
address: "上海市普陀区金沙江路 1519 弄",
},
{
id: 4,
date: "2016-05-03",
name: "王小虎444",
age: 24,
address: "上海市普陀区金沙江路 1516 弄",
},
{
id: 5,
date: "2016-05-08",
name: "王小虎555",
age: 25,
address: "上海市普陀区金沙江路 1518 弄",
},
]);
onMounted(() => {
rowDrop();
});
/**
* 行拖拽排序
*/
function rowDrop() {
// 要侦听拖拽响应的DOM对象
const tbody = document.querySelector(".el-table__body-wrapper tbody");
const data = tableData.value;
Sortable.create(tbody, {
// handle: ".el-icon-rank",
ghostClass: "target-row-class",
// 结束拖拽后的回调函数
onEnd({ newIndex, oldIndex }) {
// 将oldIndex位置的数据删除并取出,oldIndex后面位置的数据会依次前移一位
const currentRow = data.splice(oldIndex, 1)[0];
// 将被删除元素插入到新位置(currRow表示上面的被删除数据)
data.splice(newIndex, 0, currentRow);
},
});
}
</script>
<template>
<el-table
ref="table"
:data="tableData"
style="width: 100%"
border
row-key="id"
>
<el-table-column prop="date" label="日期" />
<el-table-column prop="name" label="姓名" />
<el-table-column prop="age" label="年龄" />
<el-table-column prop="address" label="地址" />
<!--<el-table-column align="center" width="55">-->
<!-- <i class="el-icon-rank" />-->
<!--</el-table-column>-->
</el-table>
</template>
<style scoped lang="less">
.el-table {
::v-deep {
.target-row-class {
background: #f0f9eb;
}
}
}
</style>
禁止某几行拖拽
效果
html
<script setup>
import { onMounted, ref } from "vue";
import Sortable from "sortablejs";
const tableData = ref([
{
id: 1,
date: "2016-05-02",
name: "王小虎111",
age: 21,
address: "上海市普陀区金沙江路 1518 弄",
},
{
id: 2,
date: "2016-05-04",
name: "王小虎222",
age: 22,
address: "上海市普陀区金沙江路 1517 弄",
},
{
id: 3,
date: "2016-05-01",
name: "王小虎333",
age: 23,
address: "上海市普陀区金沙江路 1519 弄",
},
{
id: 4,
date: "2016-05-03",
name: "王小虎444",
age: 24,
address: "上海市普陀区金沙江路 1516 弄",
},
{
id: 5,
date: "2016-05-08",
name: "王小虎555",
age: 25,
address: "上海市普陀区金沙江路 1518 弄",
},
]);
onMounted(() => {
rowDrop();
});
/**
* 行拖拽排序
*/
function rowDrop() {
// 要侦听拖拽响应的DOM对象
const tbody = document.querySelector(".el-table__body-wrapper tbody");
const data = tableData.value;
Sortable.create(tbody, {
// handle: ".el-icon-rank",
ghostClass: "target-row-class",
// 匹配的元素将不会触发拖动
filter: ".filtered",
// 拖拽移动事件,返回false取消移动
onMove: function ({ related }) {
return related.className.indexOf("filtered") === -1;
},
// 结束拖拽后的回调函数
onEnd({ newIndex, oldIndex }) {
// 将oldIndex位置的数据删除并取出,oldIndex后面位置的数据会依次前移一位
const currentRow = data.splice(oldIndex, 1)[0];
// 将被删除元素插入到新位置(currRow表示上面的被删除数据)
data.splice(newIndex, 0, currentRow);
},
});
}
// 设置ID为1和5的行禁止拖拽
function tableRowClassName({ row, rowIndex }) {
const freezeList = [1, 5];
return freezeList.includes(row.id) ? "filtered" : "";
}
</script>
<template>
<el-table
ref="table"
:data="tableData"
style="width: 100%"
border
row-key="id"
:row-class-name="tableRowClassName"
>
<el-table-column prop="date" label="日期" />
<el-table-column prop="name" label="姓名" />
<el-table-column prop="age" label="年龄" />
<el-table-column prop="address" label="地址" />
<!--<el-table-column align="center" width="55">-->
<!-- <i class="el-icon-rank" />-->
<!--</el-table-column>-->
</el-table>
</template>
<style scoped lang="less">
.el-table {
::v-deep {
.target-row-class {
background: #f0f9eb;
}
// 禁止拖拽行背景色
.filtered {
background-color: #1db1e7 !important;
}
}
}
</style>
列拖拽
基本实现
效果
html
<script setup>
import { onMounted, ref } from "vue";
import Sortable from "sortablejs";
const columns = ref([
{ label: "日期", prop: "date" },
{ label: "姓名", prop: "name" },
{ label: "年龄", prop: "age" },
{ label: "地址", prop: "address" },
]);
const tableData = ref([
{
id: 1,
date: "2016-05-02",
name: "王小虎111",
age: 21,
address: "上海市普陀区金沙江路 1518 弄",
},
{
id: 2,
date: "2016-05-04",
name: "王小虎222",
age: 22,
address: "上海市普陀区金沙江路 1517 弄",
},
{
id: 3,
date: "2016-05-01",
name: "王小虎333",
age: 23,
address: "上海市普陀区金沙江路 1519 弄",
},
{
id: 4,
date: "2016-05-03",
name: "王小虎444",
age: 24,
address: "上海市普陀区金沙江路 1516 弄",
},
{
id: 5,
date: "2016-05-08",
name: "王小虎555",
age: 25,
address: "上海市普陀区金沙江路 1518 弄",
},
]);
onMounted(() => {
columnDrop();
});
/**
* 列拖拽排序
*/
function columnDrop() {
// 要侦听拖拽响应的DOM对象
const wrapperTr = document.querySelector(".el-table__header-wrapper tr");
Sortable.create(wrapperTr, {
animation: 180,
delay: 0,
onEnd({ newIndex, oldIndex }) {
const draggedItem = columns.value.splice(oldIndex, 1)[0];
columns.value.splice(newIndex, 0, draggedItem);
},
});
}
</script>
<template>
<el-table
ref="table"
:data="tableData"
style="width: 100%"
border
row-key="id"
>
<el-table-column
v-for="(column, index) in columns"
:key="index"
:prop="column.prop"
:label="column.label"
/>
</el-table>
</template>
禁止某几行拖拽
效果
html
<script setup>
import { onMounted, ref } from "vue";
import Sortable from "sortablejs";
const columns = ref([
{ label: "日期", prop: "date" },
{ label: "姓名", prop: "name", draggable: true },
{ label: "年龄", prop: "age", draggable: true },
{ label: "地址", prop: "address" },
]);
const columnsDrop = ref([
{ label: "日期", prop: "date" },
{ label: "姓名", prop: "name" },
{ label: "年龄", prop: "age" },
{ label: "地址", prop: "address" },
]);
const tableData = ref([
{
id: 1,
date: "2016-05-02",
name: "王小虎111",
age: 21,
address: "上海市普陀区金沙江路 1518 弄",
},
{
id: 2,
date: "2016-05-04",
name: "王小虎222",
age: 22,
address: "上海市普陀区金沙江路 1517 弄",
},
{
id: 3,
date: "2016-05-01",
name: "王小虎333",
age: 23,
address: "上海市普陀区金沙江路 1519 弄",
},
{
id: 4,
date: "2016-05-03",
name: "王小虎444",
age: 24,
address: "上海市普陀区金沙江路 1516 弄",
},
{
id: 5,
date: "2016-05-08",
name: "王小虎555",
age: 25,
address: "上海市普陀区金沙江路 1518 弄",
},
]);
// 定义变量来记录固定列的class名
const fixedColumnClass = "filtered";
onMounted(() => {
columnDrop();
});
function setHeaderCellClass({ column }) {
const columnLabels = columns.value
.filter((column) => column.draggable)
.map((column) => column.label);
return !columnLabels.includes(column.label) ? fixedColumnClass : "";
}
/**
* 列拖拽排序
*/
function columnDrop() {
// 要侦听拖拽响应的DOM对象
const wrapperTr = document.querySelector(".el-table__header-wrapper tr");
Sortable.create(wrapperTr, {
animation: 180,
delay: 0,
ghostClass: "target-row-class",
filter: `.${fixedColumnClass}`,
// filter: (event, item) => {
// // 也可以根据item的类名、id或其他属性来决定是否应该被排除
// return !item.classList.contains("draggable");
// },
onMove: function ({ related }) {
return related.className.indexOf(fixedColumnClass) === -1;
},
onEnd({ newIndex, oldIndex }) {
const draggedItem = columnsDrop.value.splice(oldIndex, 1)[0];
columnsDrop.value.splice(newIndex, 0, draggedItem);
},
});
}
</script>
<template>
<el-table
ref="table"
:data="tableData"
style="width: 100%"
border
row-key="id"
:header-cell-class-name="setHeaderCellClass"
>
<el-table-column
v-for="(column, index) in columns"
:key="column.prop"
:prop="columnsDrop[index].prop"
:label="column.label"
/>
</el-table>
</template>
<style scoped lang="less">
.el-table {
::v-deep {
.target-row-class {
background: #f0f9eb;
}
.filtered {
background-color: #1db1e7 !important;
}
}
}
</style>
封装Vue指令
未完待续