React/AntDesign 如何使行可拖动? (表格拖动排序)

Posted

技术标签:

【中文标题】React/AntDesign 如何使行可拖动? (表格拖动排序)【英文标题】:React/AntDesign How to make rows draggable? (Table Drag Sorting) 【发布时间】:2019-12-20 06:22:35 【问题描述】:

我一直在编写这个简单的单列拖放表,但是当我拖动它们时行并没有移动。我在哪里修复或检查?

我正在使用 React、AntDesign 和 javascript(使用 TypeScript)

import * as React from 'react';

import ReactDOM from "react-dom";

import  Table  from "antd";

import  DndProvider, DragSource, DropTarget  from "react-dnd";

import html5Backend from "react-dnd-html5-backend";

import update from "immutability-helper";

let dragingIndex = -1;

interface propsDD 
    isOver: any,
    connectDragSource: any,
    connectDropTarget: any,
    moveRow: any,
    restProps: 
        readonly [x: string]: any;
        children?: React.ReactNode;
    
    className: any,
    index: any,


class BodyRow extends React.Component<propsDD>
    render() 
        const  isOver, connectDragSource, connectDropTarget, moveRow, ...restProps  = this.props;
        const style =  ...restProps, cursor: 'move' ;
        let  className  = restProps;
        if (isOver) 
            if (restProps.index > dragingIndex) 
                className += " drop-over-downward";
            
            if (restProps.index < dragingIndex) 
                className += " drop-over-upward";
            
        
        return connectDragSource(
            connectDropTarget(
                <tr ...restProps className=className style=style />
            )
        );
    


const rowSource = 
    beginDrag(props: any) 
        dragingIndex = props.index;
        return 
            index: props.index,
        ;
    ,
;

const rowTarget = 
    drop(props: any, monitor: any) 
        const dragIndex = monitor.getItem().index;
        const hoverIndex = props.index;
        if (dragIndex === hoverIndex) 
            return;
        
        props.moveRow(dragIndex, hoverIndex);
        monitor.getItem().index = hoverIndex;
    ,
;

const DragableBodyRow = DropTarget("row", rowTarget, (connect, monitor) => (
    connectDropTarget: connect.dropTarget(),
    isOver: monitor.isOver()
))(
    DragSource("row", rowSource, connect => (
        connectDragSource: connect.dragSource()
    ))(BodyRow)
);

const columns = [
    
        title: 'Orden de Ejecución',
        dataIndex: 'attributes.name',
        key: 'name',
    ,
];

type propsFromList = 
    receivedTasks: Task[],
    onReceivedTasks: (tasks: Task[]) => void,


export default class DDTasks extends React.Component<propsFromList, State>
    public state: State = 
        data: [],
    ;

    components = 
        body: 
            row: DragableBodyRow,
        ,
    ;

    onReceivedTasks(tasks: Task[]): void 
        this.setState(
            data: this.props.receivedTasks,
         as State)
    

    moveRow = (dragIndex: any, hoverIndex: any) => 
        const  data  = this.state;
        const dragRow = data[dragIndex];
        this.setState(
            update(this.state, 
                data: 
                    $splice: [[dragIndex, 1], [hoverIndex, 0, dragRow]]
                
            )
        );
    ;

    render() 
        return (
            < DndProvider backend=HTML5Backend >
                <Table
                    rowKey="id"
                    bordered=true
                    pagination=false
                    columns=columns
                    dataSource=this.props.receivedTasks
                    components=this.components
                    onRow=(index) => (
                        index,
                        moveRow: this.moveRow,
                    )
                />
            </DndProvider >
        );
    

我希望拖动行。实际显示的是行的内容。

【问题讨论】:

我已经添加了拖拽效果的 CSS 样式(drop-over-downward & drop-over-upward)。 这里是实际行为的 gif makeagif.com/i/TIw7Qx 【参考方案1】:

这是一个简单的例子,我根据我在网上找到的许多不同的东西提出了一个简单的例子,并最终制定了一些适用于我的代码的东西:

基本上这是被调用的反应组件表的主体,我正在通过索引映射一个位置列表,并且每一行都是可拖动的,以允许用户修改表。我还包括了与属性关联的函数调用

return (
        <Tbody>
            items.map((item, index) => (
                    <Tr key=index className="item.name"
                        draggable=true
                        onDragStart=this.dragstart
                        onDragEnd=this.dragend
                        onDragLeave=this.dragend
                        onDragEnter=this.dragend
                        onDrop=this.drop
                        onDragOver=this.dragend
                        id=index >
                        <Td><span>item.name</span></Td>
                        <Td><span>item.latitude</span></Td>
                        <Td><span>item.longitude</span></Td>
                        <Td type="data"><span>this.checkForZero(this.props.propDistances[index])</span></Td>
                        <Td type="data"><span>this.checkForZero(this.props.propCumulativeDist[index])</span></Td>
                    </Tr>
            ))
            this.finishItineraryTable()
        </Tbody>
    );

dragstart(event) 
    let dataTransfer = event.dataTransfer;
    let node = event.target;
    dataTransfer.setData('text/plain',node.innerHTML);
    dataTransfer.setData('id',node.id);
    event.stopPropagation();


dragend(event) 
    event.preventDefault();


drop(event) 
    event.preventDefault();
    let dragObjHtml = event.dataTransfer.getData("text/plain");
    let dragObjId = document.getElementById(event.dataTransfer.getData("id"));
    let dropTarget = event.target.closest("tr");
    let temp = dropTarget.innerHTML;
    dropTarget.innerHTML = dragObjHtml;
    dragObjId.innerHTML = temp;

【讨论】:

【参考方案2】:

在粘贴我的长长的代码之前,我想提几点:

它很大程度上基于original drag sorting example。 幸运的是,React DnD 具有完美的 TypeScript 支持,因为它是用 TypeScript 编写的。 下面的代码是 Ant 设计 &lt;table&gt; 的替代代码。 这是通用的。 它使用功能组件和钩子。 有一个onDragSort,每次发生某些拖动排序时都会回调它。该参数包括排序后的数据。

首先安装包react-dndreact-dnd-html5-backend。将此添加到您的DragSortingTable.tsx

import * as React from 'react';
import Table,  TableProps, TableComponents  from 'antd/lib/table';
import  DndProvider, DropTarget, DragSource, DragElementWrapper, DropTargetSpec  from 'react-dnd';
import HTML5Backend from 'react-dnd-html5-backend';

let dragingIndex = -1;

interface BodyRowProps 
  isOver: boolean;
  connectDragSource: DragElementWrapper<>;
  connectDropTarget: DragElementWrapper<>;
  style?: React.CSSProperties;
  index: number;
  className?: string;


const BodyRow: React.FC<BodyRowProps> = props => 
  const style =  ...props.style, cursor: 'move' ;

  let  className  = props;
  if (props.isOver) 
    if (props.index > dragingIndex) 
      className += ' drop-over-downward';
    
    if (props.index < dragingIndex) 
      className += ' drop-over-upward';
    
  

  return props.connectDragSource(
    props.connectDropTarget(
      <tr className=className style=style>
        props.children
      </tr>
    )
  );
;

const rowSource = 
  beginDrag(props) 
    dragingIndex = props.index;
    return 
      index: props.index
    ;
  
;

interface DropProps 
  index: number;
  moveRow: (dragIndex: number, hoverIndex: number) => void;


const rowTarget: DropTargetSpec<DropProps> = 
  drop(props, monitor) 
    const dragIndex = monitor.getItem().index;
    const hoverIndex = props.index;

    // Don't replace items with themselves
    if (dragIndex === hoverIndex) 
      return;
    

    // Time to actually perform the action
    props.moveRow(dragIndex, hoverIndex);

    // Note: we're mutating the monitor item here!
    // Generally it's better to avoid mutations,
    // but it's good here for the sake of performance
    // to avoid expensive index searches.
    monitor.getItem().index = hoverIndex;
  
;

const DragableBodyRow = DropTarget<DropProps>('row', rowTarget, (connect, monitor) => (
  connectDropTarget: connect.dropTarget(),
  isOver: monitor.isOver()
))(
  DragSource('row', rowSource, connect => (
    connectDragSource: connect.dragSource()
  ))(BodyRow)
);

interface DragSortingTableProps<T> extends TableProps<T> 
  onDragSort?: (sortedData: T[]) => void;


type extractTableType<T> = T extends DragSortingTableProps<infer T> ? T : never;

export const DragSortingTable: <T>(
  props: DragSortingTableProps<T>
) => React.ReactElement<DragSortingTableProps<T>> = props => 
  const [dataSource, setDataSource] = React.useState(props.dataSource);

  React.useEffect(() => 
    setDataSource(props.dataSource);
  , [props.dataSource]);

  const components: TableComponents = 
    body: 
      row: DragableBodyRow
    
  ;

  const moveRow: DropProps['moveRow'] = (dragIndex, hoverIndex) => 
    const dragRow = dataSource[dragIndex];
    const remaining = dataSource.filter(i => i !== dragRow);
    const sorted = [...remaining.slice(0, hoverIndex), dragRow, ...remaining.slice(hoverIndex)];

    setDataSource(sorted);

    if (props.onDragSort) 
      props.onDragSort(sorted);
    
  ;

  const tableProps: TableProps<extractTableType<typeof props>> = 
    ...props,
    className: props.className ? props.className + ' drag-sorting-table' : 'drag-sorting-table',
    components,
    dataSource,
    onRow: (_record, index) => ( index, moveRow )
  ;

  return (
    <DndProvider backend=HTML5Backend>
      <Table ...tableProps>props.children</Table>
    </DndProvider>
  );
;

将以下样式添加到您的 antd.less 覆盖:

.drag-sorting-table tr.drop-over-downward td 
  border-bottom: 2px dashed @primary-color;


.drag-sorting-table tr.drop-over-upward td 
  border-top: 2px dashed @primary-color;

按如下方式使用您的新拖动排序表:

<DragSortingTable<Users>
      dataSource=props.users
      rowKey='id'
    >
      <Column
        title='Title'
        dataIndex='id'
        key='id'
      />
</DragSortingTable>

【讨论】:

【参考方案3】:

你添加了antd sortable table的CSS来展示拖拽效果吗?

#components-table-demo-drag-sorting tr.drop-over-downward td 
  border-bottom: 2px dashed #1890ff;


#components-table-demo-drag-sorting tr.drop-over-upward td 
  border-top: 2px dashed #1890ff;

这里是sandbox link

【讨论】:

是的,正如你所说,我已经添加了 CSS 样式。在之前的测试中,显示了拖拽效果,但是在这个版本中没有。 你用的是哪个版本的antd,能不能更新一下你的antd版本再查看 我不太确定我使用的是哪个 AntD 版本,但是,这是我试图复制的原始源代码:ant.design/components/table/#components-table-demo-drag-sorting【参考方案4】:

我也在寻找类似的功能 并在 antd 表的帮助下开发了我自己的支持多行拖放的组件。

拖放表格行以重新排序表格数据。

用户可以通过“cntrl + click”选择多行

https://github.com/sojinantony01/react-multi-row-dragable-table-antd

【讨论】:

以上是关于React/AntDesign 如何使行可拖动? (表格拖动排序)的主要内容,如果未能解决你的问题,请参考以下文章

如何在jQuery中移动表格行?

如何在 Visual Studio 中从数据库中获取数据(使用 C#)时使 HTML 行可点击

JQuery UI Sortable + Laravel Collection Chunk:行可拖动而不是单个元素

A13 React+AntDesign 路由配置 react-router5.1.2

React AntDesign 内容被推到一边

react antDesign hook 大数据表格虚拟滚动