vue3 + element plus 实现树形表格拖拽排序

vue2版本可以参考当前vue3的优化

实现功能描述

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

UI包

element plus

额外包

使用sortablejs 实现拖拽排序

pnpm add sortablejs
// ts类型文件
pnpm add @types/sortablejs -D

代码

<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>

<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[]>([])
const expandRowKeys = ref<string[]>([])
const loading = ref<boolean>(false)
const tableRef = ref(null)

const load = (row: User, _treeNode: unknown, resolve: ((date: User[]) => void) | undefined) => {
  // 模拟后台返回的数据
  setTimeout(() => {
    const loadData = [
      {
        id: `${row.id}-31`,
        date: '2016-05-01',
        name: `${row.id}-name`,
        address: 'No. 189, Grove St, Los Angeles'
      },
      {
        id: `${row.id}-32`,
        date: '2016-05-01',
        name: `${row.id}-name`,
        address: 'No. 189, Grove St, Los Angeles'
      }
    ]
    resolve?.(loadData)
  }, 1000)
}

// 遍历一级数据手动添加 isFirst 字段,用于添加类名,用于排序,和设置expand-row-keys (展开行的key)
const data: User[] = [
  {
    id: '1',
    date: '2016-05-02',
    name: 'wangxiaohu',
    address: 'No. 189, Grove St, Los Angeles',
    isFirst: true
  },
  {
    id: '2',
    date: '2016-05-04',
    name: 'wangxiaohu',
    hasChildren: true,
    address: 'No. 189, Grove St, Los Angeles',
    isFirst: true
  },
  {
    id: '3',
    date: '2016-05-01',
    name: 'wangxiaohu',
    hasChildren: true,
    address: 'No. 189, Grove St, Los Angeles',
    isFirst: true
  },
  {
    id: '4',
    date: '2016-05-03',
    name: 'wangxiaohu',
    hasChildren: true,
    address: 'No. 189, Grove St, Los Angeles',
    isFirst: true
  }
]
// 模拟排序后的数据
const data2: User[] = [
  {
    id: '1',
    date: '2016-05-02',
    name: 'wangxiaohu',
    address: 'No. 189, Grove St, Los Angeles',
    isFirst: true
  },
  {
    id: '3',
    date: '2016-05-01',
    name: 'wangxiaohu',
    hasChildren: true,
    address: 'No. 189, Grove St, Los Angeles',
    isFirst: true
  },
  {
    id: '4',
    date: '2016-05-03',
    name: 'wangxiaohu',
    hasChildren: true,
    address: 'No. 189, Grove St, Los Angeles',
    isFirst: true
  },
  {
    id: '2',
    date: '2016-05-044',
    name: 'wangxiaohu',
    hasChildren: true,
    address: 'No. 189, Grove St, Los Angeles',
    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,处理数据节点复用,视图未更新的问题
      tableData.value = []
      nextTick(() => {
        // 下次渲染更新数据
        loading.value = true
        setTimeout(() => {
          tableData.value = data2
          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>

你可能感兴趣的:(vue.js,elementui)