vue3 + elementplus + sortablejs实现树形表格拖拽排序

2 篇文章 0 订阅
2 篇文章 0 订阅

实现功能

1、树形表格每次只展开一行,新的一行展开,旧行就需要收起(手风琴效果)
2、一级行能够拖拽排序,所有子级行不能拖拽
3、树形表格需要实现懒加载

UI包和sortablejs包

element plus
sortable

代码

html代码

<template>
  <el-table
    v-loading="loading"
    ref="tableRef"
    :data="tableData"
    style="width: 100%"
    row-key="id"
    border
    lazy
    :load="load"
    :tree-props="{ children: 'children', hasChildren: 'hasChildren' }" //指定那些行包含子节点
    @expand-change="handleExpand"
    :expand-row-keys="expandRowKeys"
    :row-class-name="rowClassName"
  >
    <el-table-column prop="date" label="Date" />
    <el-table-column prop="name" label="Name" />
    <el-table-column prop="address" label="Address" />
  </el-table>
</template>

JS代码


<script setup lang="ts">
import { ref, onMounted, nextTick } from 'vue'
import Sortable from 'sortablejs'

interface User {
  id: string
  date: string
  name: string
  address: string
  hasChildren?: boolean
  children?: User[]
  isFirst?: boolean  //是否一级节点
}

const tableData = ref<User[]>([])
//设置 Table 目前的展开行
const expandRowKeys = ref<string[]>([]) 
const loading = ref<boolean>(false)
const tableRef = ref()

const load = (row: User, _treeNode: unknown, resolve: ((date: User[]) => void) | undefined) => {
  // 模拟接口返回的数据
  setTimeout(() => {
    const loadData = [
      {
        id: `${row.id}-1`,
        date: '2024-09-01',
        name: `${row.id}-name`,
        address: 'china beijing'
      },
      {
        id: `${row.id}-2`,
        date: '2024-09-02',
        name: `${row.id}-name`,
        address: 'china beijing'
      }
    ]
    resolve?.(loadData)
  }, 1000)
}

// 遍历一级数据手动添加 isFirst 字段,用于添加类名,用于排序,和设置expand-row-keys (展开行的key)
const data: User[] = [
  {
    id: '1',
    date: '2024-09-01',
    name: 'zhangsan',
    address: 'china beijing',
    isFirst: true
  },
  {
    id: '2',
    date: '2024-09-02',
    name: 'zhangsan',
    hasChildren: true,
    address: 'china beijing',
    isFirst: true
  },
  {
    id: '3',
    date: '2024-09-09',
    name: 'zhangsan',
    hasChildren: true,
    address: 'china beijing',
    isFirst: true
  },
  {
    id: '4',
    date: '2024-09-11',
    name: 'zhangsan',
    hasChildren: true,
    address: 'china beijing',
    isFirst: true
  }
]

const rowClassName = ({ row }: { row: User }) => {
  // 给第一级行 设置类名 用于sortable的draggable字段,指定类可排序
  if (row?.isFirst) {
    return 'sortItem'
  }
  return ''
}

const handleExpand = (row: User, expanded: boolean) => {
  // 一级行实现手风琴
  if (row?.isFirst) {
    expandRowKeys.value = expanded ? [row.id] : []
  }
}

const rowDrop = () => {
  const tbody = document.querySelector('.el-table__body-wrapper tbody') as HTMLElement
  Sortable.create(tbody, {
    draggable: '.sortItem', // 拥有sortItem类名的行才能排序
    onStart: function () {
      // 拖拽开始收起所有张开的行
      expandRowKeys.value = []
    },
    onEnd(evt: any) {
      // newDraggableIndex , oldDraggableIndex 是可拖拽元素的下标,更具这两个下表获取id,传递给后端排序,重新获取数据
      //const { newDraggableIndex, oldDraggableIndex } = evt
      // const fromId = tableData.value[evt.oldDraggableIndex].id
      // const toId = tableData.value[evt.newDraggableIndex].id

      // 清空数据,重新渲染node,处理数据节点复用,视图未更新的问题
      const {newIndex, oldIndex} = evt
      if (oldIndex === newIndex) return
      nextTick(() => {
        // 下次渲染更新数据
        loading.value = true
		const currRow = tableData.value.splice(oldIndex, 1)[0]
        setTimeout(() => {
          tableData.value.splice(newIndex, 0, currRow) //排序后的数据
          loading.value = false
        }, 200)
      })
    }
  })
}

onMounted(() => {
  // 首次加载获取数据
  loading.value = true
  setTimeout(() => {
    tableData.value = data
    loading.value = false
  }, 3000)
  // 拖拽处理
  document.body.ondrop = function (event) {
    event.preventDefault()
    event.stopPropagation()
  }
  // 拖拽功能与配置
  rowDrop()
})
</script>


下面是一个完整的示例代码,基于Vue2,antd和sortablejs实现了两个表格之间的数据拖拽功能。 ```html <template> <div> <h2>表格1</h2> <a-table :columns="columns" :dataSource="list1" rowKey="id"> <template #name="{text, record}"> <span v-text="text" v-sortable="'table1'"></span> </template> </a-table> <h2>表格2</h2> <a-table :columns="columns" :dataSource="list2" rowKey="id"> <template #name="{text, record}"> <span v-text="text" v-sortable="'table2'"></span> </template> </a-table> </div> </template> <script> import Sortable from 'sortablejs' import { Table } from 'ant-design-vue' export default { components: { 'a-table': Table, }, data() { return { columns: [ { title: '姓名', dataIndex: 'name' }, { title: '年龄', dataIndex: 'age' }, ], list1: [ { id: 1, name: '张三', age: 18 }, { id: 2, name: '李四', age: 20 }, { id: 3, name: '王五', age: 22 }, ], list2: [], } }, mounted() { Sortable.create(this.$el.querySelector('.ant-table-tbody'), { group: { name: 'table1', put: ['table2'], }, animation: 150, fallbackOnBody: true, swapThreshold: 0.65, onEnd: this.onEnd, }) Sortable.create(this.$el.querySelectorAll('.ant-table-tbody')[1], { group: { name: 'table2', put: ['table1'], }, animation: 150, fallbackOnBody: true, swapThreshold: 0.65, onEnd: this.onEnd, }) }, methods: { onEnd(evt) { const { oldIndex, newIndex, item } = evt if (oldIndex !== newIndex) { const table = evt.to.dataset.table if (table === 'table1') { // 从表格1拖拽表格2 this.list2.splice(newIndex, 0, this.list1.splice(oldIndex, 1)[0]) } else { // 从表格2拖拽表格1 this.list1.splice(newIndex, 0, this.list2.splice(oldIndex, 1)[0]) } } }, }, } </script> ``` 在此示例中,我们使用了Ant Design Vue中的`<a-table>`组件来渲染表格,并在模板中使用了Vue的`v-sortable`指令来绑定Sortable实例。在mounted钩子函数中,我们为每个表格的tbody元素创建了Sortable实例,并将它们分组为“table1”和“table2”。然后,我们在onEnd方法中处理拖拽完成事件,并根据拖拽的方向和目标表格移动数据。 需要注意的是,在模板中,我们使用了`<span>`元素包装了表格中的文本数据,来实现拖拽时只能拖拽文本数据而不是整个行。同时,在Sortable实例的options中,我们设置了`fallbackOnBody`为true,这样拖拽元素在拖拽结束时会回到原来的位置,而不是消失。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值