react 实现表格列表拖拽排序的示例
小刘加油! 人气:0问题描述
在项目开发中,遇到这样一个需求:需要对表格里面的数据进行拖拽排序。
效果图如下所示:
思路
安装两个插件:
- 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);
加载全部内容