react?實現(xiàn)表格列表拖拽排序的示例
問題描述
在項目開發(fā)中,遇到這樣一個需求:需要對表格里面的數(shù)據(jù)進(jìn)行拖拽排序。
效果圖如下所示:

思路
安裝兩個插件:
- 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 高階組件(參數(shù)或返回值為函數(shù)),用于實現(xiàn)拖動排序功能,可以將任何列表轉(zhuǎn)換為動畫,可訪問和觸摸友好的可排序列表??梢院同F(xiàn)有組件集成,支持拖動手柄、自動滾動、鎖定軸和操作事件等功能,有著流程的動畫效果。可水平、垂直拖動。
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 屬性來進(jìn)行對每個要拖拽元素的排序
SortableContainer 提供一個方法 onSortEnd,這個方法可以解構(gòu)兩個形參:{ oldIndex , newIndex },一個是拖拽元素的標(biāo)記,一個是將要放的地方的標(biāo)記。
最后在使用 arrayMoveImmutable 交換數(shù)組的位置。
axis 表示拖拽的方向,x 是水平拖拽,y 是垂直拖拽,默認(rèn)是垂直拖拽
2. array-move
array-move 其實就是一個 API,它的主要作用是用來交換數(shù)組中元素的位置。
看下面的實例:
// 在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)
// 結(jié)果輸入為: [ '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 參數(shù),意思是: 使用行把手拖拽行排序
// disableAutoscroll 參數(shù),禁止自動滾動
// helperClass 參數(shù),可修改拖拽樣式
// onSortEnd `SortableContainer` 提供的一個方法,這個方法可以解構(gòu)兩個形參:`{ oldIndex , newIndex }`,一個是拖拽元素的標(biāo)記,一個是將要放的地方的標(biāo)記,用于表格拖拽排序
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 表格每行的數(shù)據(jù)
* @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: '暫無數(shù)據(jù)' }}
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);到此這篇關(guān)于react 實現(xiàn)表格列表拖拽排序的示例的文章就介紹到這了,更多相關(guān)react 表格列表拖拽排序內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
詳解開發(fā)react應(yīng)用最好用的腳手架 create-react-app
本篇文章主要介紹了詳解開發(fā)react應(yīng)用最好用的腳手架 create-react-app,小編覺得挺不錯的,現(xiàn)在分享給大家,也給大家做個參考。一起跟隨小編過來看看吧2018-04-04
react基于Ant Desgin Upload實現(xiàn)導(dǎo)入導(dǎo)出
本文主要介紹了react基于Ant Desgin Upload實現(xiàn)導(dǎo)入導(dǎo)出,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2024-01-01
React?中如何將CSS?visibility?屬性設(shè)置為?hidden
這篇文章主要介紹了React中如何將CSS?visibility屬性設(shè)置為?hidden,本文通過實例代碼給大家介紹的非常詳細(xì),對大家的學(xué)習(xí)或工作具有一定的參考借鑒價值,需要的朋友可以參考下2023-05-05
使用React hook實現(xiàn)remember me功能
相信大家在使用 React 寫頁面的時候都遇到過完成 Remember me 的需求吧!本文就將這個需求封裝在一個 React hook 中以供后續(xù)的使用,覺得有用的同學(xué)可以收藏起來以備不時之需,感興趣的小伙伴跟著小編一起來看看吧2024-04-04
React報錯之Parameter event implicitly has a
這篇文章主要為大家介紹了React報錯之Parameter event implicitly has an any type,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2022-08-08
React利用路由實現(xiàn)登錄界面的跳轉(zhuǎn)
這篇文章主要介紹了React利用路由實現(xiàn)登錄界面的跳轉(zhuǎn),文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2021-04-04
React Native按鈕Touchable系列組件使用教程示例
這篇文章主要為大家介紹了React Native按鈕Touchable系列組件使用教程示例,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2023-11-11

