Vue + Element UI el-table + sortablejs 行、列拖拽排序

实现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指令

未完待续

相关推荐
zhangyao9403302 小时前
开发pc端时,表格的高度怎么设置才能铺满页面
前端·javascript·elementui
XinZong3 小时前
实测OpenClaw虾淘:全民工具AI时代,冷门非工具类的Skill还能出圈吗?
javascript
烛衔溟3 小时前
TypeScript 类的类型 —— 作为类型使用
javascript·ubuntu·typescript
之歆3 小时前
Day16_JavaScript 轮播图与事件工程实战(下篇)
服务器·开发语言·前端·javascript·网络·性能优化
kyriewen4 小时前
我关掉了Copilot:因为我写的代码出现在了别人的建议里
前端·javascript·ai编程
Ulyanov4 小时前
用声明式语法重新定义Python桌面UI:QML+PySide6现代开发入门(一)
开发语言·python·算法·ui·系统仿真·雷达电子对抗仿真
SmartRadio4 小时前
STM32WLE5 LoRa Smart TDMA 完整协议栈实现(工程级可直接编译)-【1】
javascript·stm32·单片机·嵌入式硬件·lora·自组网·smart tdma
竹林8185 小时前
用 wagmi v2 踩坑两天,我终于搞懂了多链钱包切换
前端·javascript
吃乔巴的糖5 小时前
Vue 3 打印模板设计器 (print-canvas-designer)
前端·vue.js
子云zy5 小时前
JS 对象与包装类:new 做了什么?字符串为什么有 length?
前端·javascript