vue3+ant-design-vue4.x+sortablejs 实现可拖拽行表格

vue3+ant-design-vue4.x+sortablejs 实现可拖拽行表格

sortablejs网址:sortablejs.com/

npm安装 npm install sortablejs --save

js 复制代码
<template>
  <div class="draggable-table-wrapper">
    <a-table
      ref="tableRef"
      :columns="columns"
      :data-source="tableData"
      row-key="id"
      :pagination="false"
      class="draggable-table"
    >
      <!-- 可根据需要添加操作列 -->
      <template #action="{ record }">
        <a-button size="small" type="text" @click="handleView(record)"> 查看 </a-button>
      </template>
    </a-table>
  </div>
</template>

<script setup>
import { ref, onMounted, nextTick, watch } from 'vue'
import { Table, Card, Button, message } from 'ant-design-vue'
import Sortable from 'sortablejs'

// 表格数据
const tableData = ref([
  { id: '1', name: '张三', age: 28, department: '产品部', email: 'zhangsan@example.com' },
  { id: '2', name: '李四', age: 32, department: '开发部', email: 'lisi@example.com' },
  { id: '3', name: '王五', age: 25, department: '设计部', email: 'wangwu@example.com' },
  { id: '4', name: '赵六', age: 35, department: '测试部', email: 'zhaoliu@example.com' },
  { id: '5', name: '钱七', age: 29, department: '市场部', email: 'qianqi@example.com' },
  { id: '6', name: '孙八', age: 31, department: '开发部', email: 'sunba@example.com' },
  { id: '7', name: '周九', age: 27, department: '产品部', email: 'zhoujiu@example.com' }
])

// 表格列定义
const columns = [
  {
    title: '姓名',
    dataIndex: 'name',
    key: 'name',
    width: 120
  },
  {
    title: '年龄',
    dataIndex: 'age',
    key: 'age',
    width: 80
  },
  {
    title: '部门',
    dataIndex: 'department',
    key: 'department',
    width: 120
  },
  {
    title: '邮箱',
    dataIndex: 'email',
    key: 'email'
  },
  {
    title: '操作',
    key: 'action',
    width: 100,
    slots: { customRender: 'action' }
  }
]

// 分页配置
const pagination = ref({
  pageSize: 5,
  showSizeChanger: true,
  pageSizeOptions: ['5', '10', '20'],
  showTotal: (total) => `共 ${total} 条记录`
})

// 表格引用
const tableRef = ref()
// 最后更新时间
const lastUpdated = ref('')
// 排序实例
let sortableInstance = null

// 初始化拖拽功能
const initSortable = () => {
  // 确保先销毁已存在的实例
  if (sortableInstance) {
    sortableInstance.destroy()
  }

  nextTick(() => {
    // 获取表格的tbody元素
    const tableBody = tableRef.value.$el.querySelector('.ant-table-tbody')
    console.log(tableBody, tableRef.value, tableRef.value.$el, tableRef.value.table, 'tableBody')
    if (tableBody) {
      // 初始化Sortable
      sortableInstance = new Sortable(tableBody, {
        animation: 150, // 动画时间
        delay: 100, // 延迟开始拖拽,防止误操作
        delayOnTouchOnly: true,
        ghostClass: 'sortable-ghost', // 拖拽时的占位元素样式
        chosenClass: 'sortable-chosen', // 选中元素的样式
        dragClass: 'sortable-drag', // 拖拽元素的样式

        onStart: () => {},

        onEnd: (evt) => {
          const { oldIndex, newIndex } = evt

          // 处理拖拽结束后的逻辑
          if (oldIndex !== newIndex && oldIndex !== undefined && newIndex !== undefined) {
            // 复制原数据并调整顺序
            const newData = [...tableData.value]
            const [movedItem] = newData.splice(oldIndex, 1)
            newData.splice(newIndex, 0, movedItem)

            // 更新数据
            tableData.value = newData

            // 更新最后操作时间
            lastUpdated.value = new Date().toLocaleString()

            // 这里可以添加保存到服务器的逻辑
            // saveNewOrder(newData.map(item => item.id));
          }
        }
      })
    }
  })
}

// 监听分页变化,重新初始化拖拽
watch(
  () => pagination.value.current,
  () => {
    initSortable()
  }
)

watch(
  () => pagination.value.pageSize,
  () => {
    initSortable()
  }
)

// 组件挂载时初始化拖拽
onMounted(() => {
  initSortable()
})

// 查看详情
const handleView = (record) => {
  message.info(`查看 ${record.name} 的详情`)
  // 这里可以添加查看详情的逻辑
}
</script>

<style scoped>
:deep(.ant-table-tbody > tr) {
  &:hover {
    td {
      cursor: move;
      &:first-child {
        &::before {
          content: '';
          position: absolute;
          left: 0;
          top: 0;
          width: 20px;
          height: 56px;
          background: url('@/pc/assets/images/tableDrag.svg');
          background-size: 100% 100%;
        }
      }
    }
  }
}
</style>
相关推荐
豆苗学前端6 分钟前
vue3+TypeScript 实现一个图片占位符生成器
前端·面试·github
neon12048 分钟前
Vue 3 父子组件通信核心机制详解:defineProps、defineEmits 与 defineExpose 完全指南
前端·javascript·vue.js·前端框架
Juchecar25 分钟前
Vue3 开发环境搭建及循序渐进学习指南
前端·javascript
Data_Adventure41 分钟前
@scqilin/phone-ui手机外观组件库
前端
一点一木1 小时前
Vue Vapor 事件机制深潜:从设计动机到源码解析
前端·vue.js·vapor
FSHOW1 小时前
记一次开源_大量SVG的高性能渲染
前端·react.js
小牛.7931 小时前
Web第二次作业
前端·javascript·css
二闹1 小时前
都2025了还要用Layui做下拉控件-我只能说你有水平
前端
Pikachu8031 小时前
揭秘 tyarn:一个为大型 TypeScript Monorepo 优化的 Yarn 性能猛兽
前端·javascript
用户49430538293801 小时前
大规模建筑自动贴图+单体化效果,cesium脚本
前端·javascript·算法