react 实现表格列表拖拽排序的示例

问题描述

在项目开发中,遇到这样一个需求:需要对表格里面的数据进行拖拽排序。

效果图如下所示:

在这里插入图片描述

思路

安装两个插件:

  • react-sortable-hoc (或者 react-beautiful-dnd)
  • array-move
npm install --save react-sortable-hoc
npm install --save array-move

解析

1. react-sortable-hoc

react-sortable-hoc 是一组 react 高阶组件(参数或返回值为函数),用于实现拖动排序功能,可以将任何列表转换为动画,可访问和触摸友好的可排序列表。可以和现有组件集成,支持拖动手柄、自动滚动、锁定轴和操作事件等功能,有着流程的动画效果。可水平、垂直拖动。

react-sortable-hoc 的使用:

react-sortable-hoc 提供了两个特别重要的API

  • SortableContainer :是所有可拖拽排序元素的容器
  • SortableElement :是每个要拖拽排序元素的容器
  • SortableHandle :是定义拖拽手柄的容器
import { SortableHandle } from 'react-sortable-hoc';
import { MenuOutlined } from '@ant-design/icons';

const DragHandle = SortableHandle(() => <MenuOutlined style={{ cursor: 'grab', color: '#999' }} />)


{
 title: '拖动排序',
 dataIndex: 'sort',
 width: 120,
 align: 'center',
 className: 'drag-visible',
 editable: false,
 render: () =>{
 if (editable) return <DragHandle />;
 return <span>禁止拖动</span>
 },
 },

SortableHandle 就是指下面的箭头部分

在这里插入图片描述

SortableElement 提供了一个 index 属性来进行对每个要拖拽元素的排序

SortableContainer 提供一个方法 onSortEnd,这个方法可以解构两个形参:{ oldIndex , newIndex },一个是拖拽元素的标记,一个是将要放的地方的标记。

最后在使用 arrayMoveImmutable 交换数组的位置。

axis 表示拖拽的方向,x 是水平拖拽,y 是垂直拖拽,默认是垂直拖拽

2. array-move

array-move 其实就是一个 API,它的主要作用是用来交换数组中元素的位置。

看下面的实例:

// 在tsx文件中
import React, { useEffect } from 'react';
import { arrayMoveImmutable } from 'array-move';

const Index = () => {
	useEffect(() => {
	let arr = ['a', 'b', 'c']
	let result = arrayMoveImmutable(arr, 1 , 2)
	console.log(result)
	// 结果输入为: [ 'a', 'c', 'b' ]
	})
}

export default Index

使用

import { SortableContainer, SortableElement, SortableHandle } from 'react-sortable-hoc';
import { arrayMoveImmutable } from 'array-move';

// 定义拖拽的table 容器
const DragTableContainer = SortableContainer((props) => <tbody {...props}>)

// 定义 拖拽的 行
const DragTableItem = SortableElement((props) => <tr {...props}>)

// 定义拖拽手柄
const DragHandle = SortableHandle(() => (
	<MenuOutlined title='拖拽排序' />
))

// 表格排序方法
const onSortEnd = ({oldIndex, newIndex}: {oldIndex: number; newIndex: number }) => {
 if (oldIndex !== newIndex) {
 const newData: any[] = arrayMoveImmutable(([] as any[]).concat(dataSource), oldIndex, newIndex).filter((el: any) => !!el);
 handleAllSave(newData) // 父组件传过来的方法,用于更新表格第一列的序号
 }
 }

// 所有可拖拽排序元素的容器
// DragTableContainer 是上面通过 SortableContainer 定义的拖拽的table 容器
// useDragHandle 参数,意思是: 使用行把手拖拽行排序
// disableAutoscroll 参数,禁止自动滚动
// helperClass 参数,可修改拖拽样式
// onSortEnd `SortableContainer` 提供的一个方法,这个方法可以解构两个形参:`{ oldIndex , newIndex }`,一个是拖拽元素的标记,一个是将要放的地方的标记,用于表格拖拽排序

const DraggableContainer = (props: any) => <DragTableContainer useDragHandle disableAutoscroll helperClass="row-dragging" onSortEnd={onSortEnd} {...props}/>


// 定义 拖拽的 行

// DraggableBodyRow 返回的是由 SortableItem 包裹的每一行元素

const DraggableBodyRow = ({ className, style, ...restProps}: any) => {
 const index = dataSource.findIndex((x: any) => x.orderNum === restProps['data-row-key']);
 return (<SortableItem index={index} {...restProps} />)
}


// ----------------------------------------------------------------------------------------
// ----------------------------------------------------------------------------------------

// 封装的子组件
const EditableTable = (props: any) => {
 let { title = '', subtitle = '', columns, rowClassName = () => 'editable-row', dataSource, handleSave, handleAllSave, rowKey, placeholder, clickRow, loading = false, scroll } = props;
 const styles = {
 tabletitle: { fontWeight: 800, color: '#0095ff', fontSize: '16px' },
 subtitle: { color: '#000000', fontSize: '12px' },
 };

 columns = columns.map((col: any) => {
 if (!col.editable) {
 return col;
 }
 return {
 ...col,
 onCell: (record: any) => ({
 record,
 isRowDisable: col.isRowDisable,
 isNumber: col.isNumber,
 editable: col.editable,
 editdisable: col.editdisable,
 dataIndex: col.dataIndex,
 title: col.title,
 handleSave: handleSave,
 formRules: col.rules,
 placeholder: col?.placeholder,
 precision: col?.precision,
 min: col?.min,
 step: col?.step,
 max: col?.max,
 formatter: col?.formatter,
 parser: col?.parser,
 }),
 };
 });
 /**
 * 表格行属性
 * @param record 表格每行的数据
 * @returns
 */
 const onRow = (record: any) => {
 return {
 onClick: clickRow ? () => clickRow(record) : undefined,
 }
 }

 const onSortEnd = ({oldIndex, newIndex}: {oldIndex: number; newIndex: number }) => {
 if (oldIndex !== newIndex) {
 const newData: any[] = arrayMoveImmutable(([] as any[]).concat(dataSource), oldIndex, newIndex).filter((el: any) => !!el);
 handleAllSave(newData)
 }
 }

 const DraggableContainer = (props: any) => <SortableBody useDragHandle disableAutoscroll helperClass="row-dragging" onSortEnd={onSortEnd} {...props}/>

 const DraggableBodyRow = ({ className, style, ...restProps}: any) => {
 const index = dataSource.findIndex((x: any) => x.orderNum === restProps['data-row-key']);
 return (<SortableItem index={index} {...restProps} />)
 }

 return (
 <Fragment>
 <div style={{ display: 'flex', marginBottom: '6px' }}>
 <Table
 className="wrap"
 style={{ width: '100%' }}
 locale={{ emptyText: '暂无数据' }}
 components={{
 body: {
 wrapper: DraggableContainer,
 row: DraggableBodyRow,
 // cell: EditableCell
 }
 }}
 rowClassName={rowClassName}
 bordered
 dataSource={dataSource}
 columns={columns}
 pagination={false}
 rowKey='orderNum'
 scroll={scroll || { y: 500 }}
 onRow={onRow}
 loading={loading}
 />
 </div>
 </Fragment>
 );
};

export default memo(EditableTable);
作者:小刘加油!原文地址:https://blog.csdn.net/qq_41131745/article/details/128429108

%s 个评论

要回复文章请先登录注册