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>

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
实现el-tab-pane拖拽排序的步骤如下: 1. 安装sortablejs和@vue/composition-api ``` npm install sortablejs @vue/composition-api --save ``` 2. 在Vue组件中导入Sortable和reactive ```javascript import Sortable from 'sortablejs'; import { reactive } from '@vue/composition-api'; ``` 3. 创建一个响应式对象来存储tab列表 ```javascript const tabs = reactive({ list: [ { label: 'Tab 1', content: 'Content of Tab 1' }, { label: 'Tab 2', content: 'Content of Tab 2' }, { label: 'Tab 3', content: 'Content of Tab 3' } ] }); ``` 4. 在页面中使用el-tabs和el-tab-pane组件来展示tab列表,并使用v-for指令循环展示tab列表 ```html <el-tabs v-model="activeTab"> <el-tab-pane v-for="(tab, index) in tabs.list" :key="index" :label="tab.label"> {{ tab.content }} </el-tab-pane> </el-tabs> ``` 5. 在mounted钩子函数中使用Sortable创建可拖拽的tab列表,并监听其排序事件 ```javascript mounted() { let el = this.$el.querySelector('.el-tabs__nav-wrap'); new Sortable(el, { animation: 150, onEnd: (evt) => { let fromIndex = evt.oldIndex; let toIndex = evt.newIndex; tabs.list.splice(toIndex, 0, tabs.list.splice(fromIndex, 1)[0]); } }); } ``` 6. 完成后,你就可以在页面上看到一个可拖拽排序的tab列表了。 完整代码如下: ```html <template> <el-tabs v-model="activeTab"> <el-tab-pane v-for="(tab, index) in tabs.list" :key="index" :label="tab.label"> {{ tab.content }} </el-tab-pane> </el-tabs> </template> <script> import Sortable from 'sortablejs'; import { reactive } from '@vue/composition-api'; export default { name: 'SortableTabs', setup() { const tabs = reactive({ list: [ { label: 'Tab 1', content: 'Content of Tab 1' }, { label: 'Tab 2', content: 'Content of Tab 2' }, { label: 'Tab 3', content: 'Content of Tab 3' } ] }); return { tabs }; }, mounted() { let el = this.$el.querySelector('.el-tabs__nav-wrap'); new Sortable(el, { animation: 150, onEnd: (evt) => { let fromIndex = evt.oldIndex; let toIndex = evt.newIndex; this.tabs.list.splice(toIndex, 0, this.tabs.list.splice(fromIndex, 1)[0]); } }); } }; </script> ```

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值