antd表格拖拽

组件DragSortableTable

支持合并单元格拖拽

import React, { useState, useEffect } from 'react';
import { Table } from 'antd';
import { DndProvider, DragSource, DropTarget } from 'react-dnd';
import HTML5Backend from 'react-dnd-html5-backend';
import update from 'immutability-helper';

import { isFunction } from '@/utils/types';
import styles from './index.less';

let dragingIndex = -1;

const BodyRow = ({ isOver, connectDragSource, connectDropTarget, moveRow, ...restProps }) => {
    const style = { ...restProps.style, cursor: 'move' };

    let { className } = restProps;
    if (isOver) {
        if (restProps.index > dragingIndex) {
            className += ` ${styles.dropOverDownward}`;
        }
        if (restProps.index < dragingIndex) {
            className += ` ${styles.dropOverUpward}`;
        }
    }

    return connectDragSource(connectDropTarget(<tr {...restProps} className={className} style={style} />));
};

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

const rowTarget = {
    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('row', rowTarget, (connect, monitor) => ({
    connectDropTarget: connect.dropTarget(),
    isOver: monitor.isOver(),
}))(
    DragSource('row', rowSource, connect => ({
        connectDragSource: connect.dragSource(),
    }))(BodyRow),
);

const DragSortableTable = ({
    dataSource,
    columns,
    onSort,
    groupDrag = false, // 是否支持组拖拽
    groupDragOrder = 'name', // 合并名称,必传,否则组拖拽会出问题
    identifies = 'id', // 唯一标识,必传,否则组拖拽会出问题
    ...restProps
}) => {
    const [dataList, setDataList] = useState([]);
    const [columnsList, setColumnsList] = useState([]);
    useEffect(() => {
        if (Array.isArray(dataSource)) {
            setDataList(dataSource);
        }
    }, [dataSource]);
    useEffect(() => {
        if (Array.isArray(columns)) {
            setColumnsList(columns);
        }
    }, [columns]);

    const components = {
        body: {
            row: DragableBodyRow,
        },
    };
    const moveRow = (dragIndex, hoverIndex) => {
        const dragRow = dataList[dragIndex];
        if (groupDrag) {
            // 拖拽元素
            const rowName_drag = dataList[dragIndex][groupDragOrder];
            // 目标元素
            const rowName_hover = dataList[hoverIndex][groupDragOrder];
            // 拖拽元素项
            // const rowName_drag_item = dataList[dragIndex];
            // 目标元素项
            const rowName_hover_item = dataList[hoverIndex];
            // 拖拽元素相同项
            const sameList_drag = dataList.filter(el => el[groupDragOrder] === rowName_drag);
            // 拖拽元素不同项
            const notSameList_drag = dataList.filter(el => el[groupDragOrder] !== rowName_drag);
            // 目标元素相同项
            const sameList_hover = dataList.filter(el => el[groupDragOrder] === rowName_hover);
            // 目标元素不同项
            const notSameList_hover = dataList.filter(el => el[groupDragOrder] !== rowName_hover);
            // 目标组
            if (sameList_hover.length > 1) {
                // 拖拽组
                if (sameList_drag.length > 1) {
                    notSameList_drag.splice(
                        sameList_hover.map((item, index) => ({ ...item, index }))[0].index,
                        0,
                        ...sameList_drag,
                    );
                } else {
                    let flag = 0;
                    sameList_hover.forEach((v, index) => {
                        if (rowName_hover_item[identifies] === v[identifies]) {
                            flag = index;
                        }
                    });
                    const changeNotSameList_hover = notSameList_hover.filter(
                        h => h[groupDragOrder] !== dragRow[groupDragOrder],
                    );
                    let dedIndex = 0; // 目标index
                    // 组start
                    if (flag === 0) {
                        sameList_hover.splice(0, 0, dragRow);
                        notSameList_hover.forEach((v, index) => {
                            if (sameList_hover[0][identifies] === v[identifies]) {
                                dedIndex = index;
                            }
                        });
                        changeNotSameList_hover.splice(dedIndex, 0, ...sameList_hover);
                    } else {
                        // 组end
                        sameList_hover.splice(0, 0, dragRow);
                        notSameList_hover.forEach((v, index) => {
                            if (sameList_hover[[sameList_hover.length - 1]][identifies] === v[identifies]) {
                                dedIndex = index;
                            }
                        });
                        changeNotSameList_hover.splice(dedIndex, 0, ...sameList_hover);
                    }
                    setDataList(changeNotSameList_hover);
                    if (isFunction(onSort)) onSort(changeNotSameList_hover);
                    return;
                }
            } else {
                notSameList_drag.splice(hoverIndex, 0, ...sameList_drag);
            }
            setDataList(notSameList_drag);
            if (isFunction(onSort)) onSort(notSameList_drag);
            return;
        }
        const sortedDataList = update(dataList, {
            $splice: [
                [dragIndex, 1],
                [hoverIndex, 0, dragRow],
            ],
        });
        setDataList(sortedDataList);
        if (isFunction(onSort)) onSort(sortedDataList);
    };

    return (
        <DndProvider backend={HTML5Backend}>
            <Table
                className={styles.dragSortableTable}
                columns={columnsList}
                dataSource={dataList}
                components={components}
                onRow={(_, index) => ({
                    index,
                    moveRow,
                })}
                {...restProps}
            />
        </DndProvider>
    );
};

export default DragSortableTable;

.dragSortableTable {
    tr.dropOverDownward {
        td {
            border-bottom: 2px dashed #1890ff;
        }
    }

    tr.dropOverUpward {
        td {
            border-bottom: 2px dashed #1890ff;
        }
    }
}

使用

import React, { useState, useMemo } from 'react';
import DragSortableTable from './DragSortableTable';
/*
 *  参数text :为当前单元格的值 > string
 *	参数data :为当前table 渲染的数据 >数组
 *	参数key  : 为当前单元格的dataindex> string
 *	参数index :为当前单元格的第几行 >Number
 *  参数row :为table 当前行的数据对象  >Object
 */
const mergeCells = (text, data, key, index, row) => {
    //	如果相等则该单元格被上一列合并,直接返回rowSpan为 0
    if (index !== 0 && text === data[index - 1][key]) {
        return 0;
    }
    let rowSpan = 1;
    for (let i = index + 1; i < data.length; i++) {
        if (text !== data[i][key]) {
            break;
        } else {
            rowSpan++;
        }
    }
    return rowSpan;
};
export default () => {
    const list = [
        {
            id: '1',
            name: '组1',
            age: 32,
            address: 'New York No. 1 Lake Park',
            tags: ['nice', 'developer'],
        },
        {
            id: '2',
            name: '组1',
            age: 42,
            address: 'London No. 1 Lake Park',
            tags: ['loser'],
        },
        {
            id: '3',
            name: '组3',
            age: 32,
            address: 'Sidney No. 1 Lake Park',
            tags: ['cool', 'teacher'],
        },
        {
            id: '4',
            name: '组4',
            age: 32,
            address: 'Sidney No. 1 Lake Park',
            tags: ['cool', 'teacher'],
        },
        {
            id: '44',
            name: '组4',
            age: 32,
            address: 'Sidney No. 1 Lake Park',
            tags: ['cool', 'teacher'],
        },
        {
            id: '5',
            name: '组5',
            age: 32,
            address: 'Sidney No. 1 Lake Park',
            tags: ['cool', 'teacher'],
        },
        {
            id: '005',
            name: '组5',
            age: 32,
            address: 'Sidney No. 1 Lake Park',
            tags: ['cool', 'teacher'],
        },
        {
            id: '6',
            name: '组6',
            age: 32,
            address: 'Sidney No. 1 Lake Park',
            tags: ['cool', 'teacher'],
        },
        {
            id: '7',
            name: '组7',
            age: 32,
            address: 'Sidney No. 1 Lake Park',
            tags: ['cool', 'teacher'],
        },
    ];
    const [dataList, setDataList] = useState([...list]);

    // const [columns, setColumns] = useState([...defColumns]);
    const columns = useMemo(() => {
        const defColumns = [
            {
                title: 'ID',
                dataIndex: 'id',
                key: 'id',
            },
            {
                title: 'Name',
                dataIndex: 'name',
                key: 'name',
                render: (text, row, index) => {
                    if (dataList.length) {
                        return {
                            children: <span>{text}</span>,
                            props: {
                                rowSpan: mergeCells(text, dataList, 'name', index, row),
                            },
                        };
                    }
                },
            },
            {
                title: 'Age',
                dataIndex: 'age',
                key: 'age',
            },
            {
                title: 'Address',
                dataIndex: 'address',
                key: 'address',
            },
        ];
        return defColumns;
    }, [dataList]);
    const tableProps = {};

    return (
        <DragSortableTable
            {...tableProps}
            pagination={false}
            dataSource={dataList}
            columns={columns}
            bordered
            rowKey="id"
            groupDrag
            groupDragOrder="name"
            identifies="id" // 唯一标识,必传,否则组拖拽会出问题
            onSort={data => {
                // const ids = data.map(e => e.id);
                setDataList([...data]);
            }}
        />
    );
};

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
Antd提供了Table组件,可以实现表格的展示和编辑功能。而要实现可编辑表格拖拽功能,需要对Table组件进行一些扩展。 1. 可编辑表格 Antd的Table组件提供了editable属性,开启该属性后,表格单元格就可以进行编辑了。同时,还需要在columns中配置每一列是否可编辑,以及编辑时的类型和属性。 例如,下面的代码可以实现一个简单的可编辑表格: ```jsx import { Table, Input, InputNumber, Popconfirm, Form } from 'antd'; import React, { useState } from 'react'; const EditableCell = ({ editing, dataIndex, title, inputType, record, index, children, ...restProps }) => { const inputNode = inputType === 'number' ? <InputNumber /> : <Input />; return ( <td {...restProps}> {editing ? ( <Form.Item name={dataIndex} style={{ margin: 0, }} rules={[ { required: true, message: `Please Input ${title}!`, }, ]} > {inputNode} </Form.Item> ) : ( children )} </td> ); }; const EditableTable = () => { const [form] = Form.useForm(); const [data, setData] = useState([ { key: '1', name: 'Edward King 0', age: 32, address: 'London, Park Lane no. 0', }, { key: '2', name: 'Edward King 1', age: 32, address: 'London, Park Lane no. 1', }, ]); const [editingKey, setEditingKey] = useState(''); const isEditing = (record) => record.key === editingKey; const edit = (record) => { form.setFieldsValue({ name: '', age: '', address: '', ...record, }); setEditingKey(record.key); }; const cancel = () => { setEditingKey(''); }; const save = async (key) => { try { const row = await form.validateFields(); const newData = [...data]; const index = newData.findIndex((item) => key === item.key); if (index > -1) { const item = newData[index]; newData.splice(index, 1, { ...item, ...row }); setData(newData); setEditingKey(''); } else { newData.push(row); setData(newData); setEditingKey(''); } } catch (errInfo) { console.log('Validate Failed:', errInfo); } }; const columns = [ { title: 'Name', dataIndex: 'name', width: '25%', editable: true, }, { title: 'Age', dataIndex: 'age', width: '15%', editable: true, }, { title: 'Address', dataIndex: 'address', width: '40%', editable: true, }, { title: 'Operation', dataIndex: 'operation', render: (_, record) => { const editable = isEditing(record); return editable ? ( <span> <a href="javascript:;" onClick={() => save(record.key)} style={{ marginRight: 8, }} > Save </a> <Popconfirm title="Sure to cancel?" onConfirm={cancel}> <a>Cancel</a> </Popconfirm> </span> ) : ( <a disabled={editingKey !== ''} onClick={() => edit(record)}> Edit </a> ); }, }, ]; const mergedColumns = columns.map((col) => { if (!col.editable) { return col; } return { ...col, onCell: (record) => ({ record, inputType: col.dataIndex === 'age' ? 'number' : 'text', dataIndex: col.dataIndex, title: col.title, editing: isEditing(record), }), }; }); return ( <Form form={form} component={false}> <Table components={{ body: { cell: EditableCell, }, }} bordered dataSource={data} columns={mergedColumns} rowClassName="editable-row" pagination={{ onChange: cancel, }} /> </Form> ); }; export default EditableTable; ``` 2. 拖拽 Antd的Table组件默认不支持拖拽功能,但是可以通过一些插件来实现。 其中,react-beautiful-dnd是一个比较常用的拖拽插件,可以很方便地和Antd的Table组件结合使用。具体实现步骤如下: 1. 安装react-beautiful-dnd插件 ```bash npm install react-beautiful-dnd ``` 2. 将Table组件封装成DragDropContext组件 ```jsx import { Table } from 'antd'; import { DragDropContext, Droppable, Draggable } from 'react-beautiful-dnd'; const DragTable = ({ columns, dataSource, onDragEnd }) => { return ( <DragDropContext onDragEnd={onDragEnd}> <Droppable droppableId="droppable"> {(provided) => ( <Table {...provided.droppableProps} ref={provided.innerRef} columns={columns} dataSource={dataSource} pagination={false} rowClassName="drag-row" onRow={(record, index) => ({ index, moveRow: true, })} /> )} </Droppable> </DragDropContext> ); }; export default DragTable; ``` 3. 实现onDragEnd回调函数 ```jsx const onDragEnd = (result) => { if (!result.destination) { return; } const { source, destination } = result; // 根据拖拽后的顺序重新排序dataSource const newData = [...dataSource]; const [removed] = newData.splice(source.index, 1); newData.splice(destination.index, 0, removed); // 更新dataSource setData(newData); }; ``` 最后,在页面中引用DragTable组件即可实现拖拽功能。例如: ```jsx <DragTable columns={columns} dataSource={data} onDragEnd={onDragEnd} /> ```

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值