el-table实现行拖拽(包含展开项)

效果:

借助第三方插件sortablejs来实现

具体步骤:

1.安装sortablejs

npm install sortablejs

2.在vue文件中引入sortablejs

TypeScript 复制代码
import Sortable from 'sortablejs'

3.在el-table指定row-key,这个row-key必须是唯一的,否则无法正确排序

TypeScript 复制代码
<el-table
    ref="tableRef"
    v-bind="$attrs"
    :data="localData"
    :row-key="rowKey"
    :class="tableClass"
    :tree-props="treeProps"
    :row-class-name="rowClassName"
    :empty-text="emptyText"
    @expand-change="handleExpandChange"
  >
    <template v-for="column in columns" :key="column.key || column.prop || column.label">
      <el-table-column v-if="column.type === 'expand'" type="expand" :width="column.width" :fixed="column.fixed">
        <template #default="scope">
          <slot :name="column.slotName || 'expand'" v-bind="scope" :column-config="column" />
        </template>
      </el-table-column>
      <el-table-column
        v-else
        :prop="column.prop"
        :label="column.label"
        :width="column.width"
        :min-width="column.minWidth"
        :align="column.align"
        :fixed="column.fixed"
        :show-overflow-tooltip="column.showOverflowTooltip"
      >
        <template #default="scope">
          <slot v-if="column.slotName" :name="column.slotName" v-bind="scope" :column-config="column" />
          <slot v-else name="cell" v-bind="scope" :column-config="column">
            {{ column.prop ? scope.row[column.prop] : '' }}
          </slot>
        </template>
      </el-table-column>
    </template>
  </el-table>

4.具体代码实现(ts)

TypeScript 复制代码
nextTick(() => {
    const tbody = tableRef.value?.$el?.querySelectorAll('.el-table__body-wrapper tbody')[1]
    if (!tbody) return

    sortableInstance = Sortable.create(tbody, {
      animation: 150, 
      disabled: false, //false 为启用
      onChoose(e: any) {}, //选中行时
      onEnd(evt: any) { //拖拽完成
        const { oldIndex, newIndex } = evt

        if (oldIndex === newIndex) return

        const newData = [...props.data]
        const moved = newData.splice(oldIndex, 1)[0]
        newData.splice(newIndex, 0, moved)
        emit('update:data', newData)
      },
    })
  })

**注意:**如果无法实现拖拽,可检查一下tbody元素的查找是否有问题。

我的是:tableRef.value?.$el?.querySelectorAll('.el-table__body-wrapper tbody')1

我这里使用了1是因为我查找到三个tbody,我用到的是第二个。

5.完整代码(这是我封装的表格组件,数据由父组件传入)

TypeScript 复制代码
<template>
  <el-table
    ref="tableRef"
    v-bind="$attrs"
    :data="localData"
    :row-key="rowKey"
    :class="tableClass"
    :tree-props="treeProps"
    :row-class-name="rowClassName"
    :empty-text="emptyText"
    @expand-change="handleExpandChange"
  >
    <template v-for="column in columns" :key="column.key || column.prop || column.label">
      <el-table-column v-if="column.type === 'expand'" type="expand" :width="column.width" :fixed="column.fixed">
        <template #default="scope">
          <slot :name="column.slotName || 'expand'" v-bind="scope" :column-config="column" />
        </template>
      </el-table-column>
      <el-table-column
        v-else
        :prop="column.prop"
        :label="column.label"
        :width="column.width"
        :min-width="column.minWidth"
        :align="column.align"
        :fixed="column.fixed"
        :show-overflow-tooltip="column.showOverflowTooltip"
      >
        <template #default="scope">
          <slot v-if="column.slotName" :name="column.slotName" v-bind="scope" :column-config="column" />
          <slot v-else name="cell" v-bind="scope" :column-config="column">
            {{ column.prop ? scope.row[column.prop] : '' }}
          </slot>
        </template>
      </el-table-column>
    </template>
  </el-table>
</template>

<script setup lang="ts">
import { ref, computed, onBeforeUnmount, nextTick, watch } from 'vue'
import Sortable from 'sortablejs'
import type { TableInstance } from 'element-plus'
import type { OperationTableColumn, OperationTableRow, OperationTableRowClassName } from '@/views/statistics/types'

defineOptions({ inheritAttrs: false })

const props = withDefaults(
  defineProps<{
    columns: OperationTableColumn[]
    data: OperationTableRow[]
    rowKey?: string | ((row: OperationTableRow) => string)
    treeProps?: Record<string, string | boolean>
    rowClassName?: OperationTableRowClassName
    emptyText?: string
    tableClass?: string
    isSort?: boolean
  }>(),
  {
    rowKey: 'id',
    treeProps: () => ({ children: 'children' }),
    rowClassName: '',
    emptyText: '',
    tableClass: '',
  },
)

const emit = defineEmits<{
  'update:data': [val: OperationTableRow[]]
  'expand-change': [row: OperationTableRow, expanded: OperationTableRow[] | boolean]
}>()
let sortableInstance: Sortable | null = null

const localData = computed({
  get() {
    return props.data
  },
  set(val) {
    emit('update:data', val)
  },
})
const tableRef = ref<TableInstance>()

const handleExpandChange = (row: OperationTableRow, expanded: OperationTableRow[] | boolean) => {
  emit('expand-change', row, expanded)
}

const toggleRowExpansion = (row: OperationTableRow, expanded?: boolean) => {
  tableRef.value?.toggleRowExpansion(row, expanded)
}
const initSortable = () => {
  if (!props.isSort) return

  nextTick(() => {
    const tbody = tableRef.value?.$el?.querySelectorAll('.el-table__body-wrapper tbody')[1]
    if (!tbody) return

    sortableInstance = Sortable.create(tbody, {
      animation: 150,
      disabled: false,
      onChoose(e: any) {},
      onEnd(evt: any) {
        const { oldIndex, newIndex } = evt

        if (oldIndex === newIndex) return

        const newData = [...props.data]
        const moved = newData.splice(oldIndex, 1)[0]
        newData.splice(newIndex, 0, moved)
        emit('update:data', newData)
      },
    })
  })
}

const destroySortable = () => {
  if (sortableInstance) {
    sortableInstance.destroy()
    sortableInstance = null
  }
}

watch(
  () => props.isSort,
  val => {
    if (val) {
      initSortable()
    } else {
      destroySortable()
    }
  },
  { immediate: true },
)

onBeforeUnmount(() => {
  destroySortable()
})
defineExpose({
  tableRef,
  toggleRowExpansion,
})
</script>
相关推荐
程序员小八777几秒前
Go Web 工程化:日志、配置与错误处理中间件,让服务「能上线」
前端·中间件·golang
SquabbyZhu20 分钟前
从 4 个 URL 到 1 个入口:Peaks-Loop 驱动的微前端聚合实践
前端
Hilaku29 分钟前
技术好就能升职是前端圈最大的谎言!
前端·javascript·程序员
lhldsg30 分钟前
树洞倾诉的核心需求与产品定位误区
java·前端·小程序
光影少年43 分钟前
react navite高频手写/实操题
前端·javascript·react native·react.js·前端框架
hunterandroid1 小时前
HarmonyOS 弱网与离线优先架构实战:请求队列、本地缓存与增量同步
前端·前端框架
lhldsg1 小时前
宠物同城领养平台开发实战:从需求分析到上线部署指南
java·前端·小程序·需求分析·宠物
hunterandroid1 小时前
Android 内存泄漏排查实战:从 LeakCanary 报警到根因定位
android·前端
leoZ2311 小时前
AI+前端提效-12 AI辅助前端性能优化与监控:从开发到线上全流程提效
前端·人工智能·神经网络·自然语言处理·性能优化·keras·知识图谱
阿图灵1 小时前
LangGraph 实战 03:Workflows 与 Agents——六种工作流模式与智能体实战(附 6 个可运行示例)
java·前端·javascript·工作流·ai agent·智能体·langgraph