【问题标题】:React/AntDesign How to make rows draggable? (Table Drag Sorting)React/AntDesign 如何使行可拖动? (表格拖动排序)
【发布时间】: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

标签: javascript reactjs typescript drag-and-drop antd


【解决方案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

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2022-06-20
        • 1970-01-01
        • 2020-02-23
        • 1970-01-01
        • 2020-04-26
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多