Antd 可编辑表格暂存

这篇博客介绍了如何使用 React 构建一个可编辑的表格组件,包括EditableContext、EditableRow和EditableCell的实现,以及如何在表格中集成DatePicker和Select组件。示例代码展示了如何处理单元格的编辑、保存和验证,同时提供了操作列的示例,如删除和保存功能。此外,还分享了具体的样式和实际应用场景。
//组件部分
import React, { useContext, useState, useEffect, useRef } from 'react';
import { Table, Input, Form, DatePicker, Select } from 'antd';
import styles from './index.less';
import moment from 'moment';
const EditableContext = React.createContext(null);

const { Option } = Select;

const EditableRow = ({ index, ...props }) => {
    const [form] = Form.useForm();
    return (
        <Form form={form} component={false}>
            <EditableContext.Provider value={form}>
                <tr {...props} />
            </EditableContext.Provider>
        </Form>
    );
};

const renderFormItem = (type, props, otherParam) => {
    const { optionList } = otherParam;
    console.log('optionList: ', optionList);
    switch (type) {
        case 'date':
            return <DatePicker {...props} />;
        case 'select':
            return (
                <Select placeholder="请选择" {...props}>
                    {optionList.map((item) => {
                        return (
                            <Option value={item.id} key={item.id}>
                                {item.name}
                            </Option>
                        );
                    })}
                </Select>
            );
        default:
            return <Input {...props} placeholder="请输入" />;
    }
};

const EditableCell = ({
    title,
    editable,
    children,
    dataIndex,
    record,
    handleSave,
    editType,
    rules,
    optionList,
    ...restProps
}) => {
    console.log(editType, 'editType');
    const [editing, setEditing] = useState(false);
    const editRef = useRef(null);
    const form = useContext(EditableContext);
    useEffect(() => {
        if (editing) {
            editRef.current.focus();
        }
    }, [editing]);

    const toggleEdit = () => {
        setEditing(!editing);
        // form.setFieldsValue({
        //     [dataIndex]: record[dataIndex],
        // });
    };

    const save = async () => {
        try {
            const values = await form.validateFields();
            console.log('values: ', values);
            toggleEdit();
            handleSave({ ...record, ...values });
        } catch (errInfo) {
            console.log('Save failed:', errInfo);
        }
    };

    let childNode = children;

    if (editable) {
        childNode = editing ? (
            <Form.Item
                style={{
                    margin: 0,
                }}
                name={dataIndex}
                rules={rules}
                initialValue={record[dataIndex]}
            >
                {renderFormItem(
                    editType,
                    {
                        ref: editRef,
                        onPressEnter: save,
                        onBlur: save,
                        style: { width: '100%' },
                    },
                    {
                        optionList,
                    },
                )}
            </Form.Item>
        ) : (
            <div
                className="editable-cell-value-wrap"
                style={{
                    width: '100%',
                    minHeight: 32,
                }}
                onClick={toggleEdit}
            >
                {children}
            </div>
        );
    }

    return <td {...restProps}>{childNode}</td>;
};

export default class EditableTable extends React.Component {
    constructor(props) {
        super(props);
        this.state = {
            dataSource: props.dataSource || [],
            columns: props.columns || [],
        };
    }

    handleDelete = (key) => {
        const dataSource = [...this.state.dataSource];
        this.setState({
            dataSource: dataSource.filter((item) => item.key !== key),
        });
    };

    handleSave = (row) => {
        const newData = [...this.state.dataSource];
        const index = newData.findIndex((item) => row.key === item.key);
        const item = newData[index];
        newData.splice(index, 1, { ...item, ...row });
        this.setState({
            dataSource: newData,
        });
    };

    render() {
        const { dataSource } = this.state;
        const { className, loading } = this.props;
        const components = {
            body: {
                row: EditableRow,
                cell: EditableCell,
            },
        };
        const columns = this.state.columns.map((col) => {
            console.log('col: ', col);
            if (!col.editable) {
                return col;
            }

            return {
                ...col,
                ellipsis: { showTitle: 'false' },

                onCell: (record) => ({
                    record,
                    editable: col.editable,
                    dataIndex: col.dataIndex,
                    title: col.title,
                    handleSave: this.handleSave,
                    editType: col.type,
                    rules: col.rules,
                    optionList: col.optionList || [],
                }),
            };
        });
        return (
            <Table
                rowKey={'id'}
                className={`${styles.editableTable} ${className}`}
                components={components}
                rowClassName={() => 'editable-row'}
                bordered={false}
                dataSource={dataSource}
                columns={columns}
                loading={loading}
            />
        );
    }
}

//样式部分
.editableTable {
    :global {
        .editable-cell {
            position: relative;
        }

        .editable-cell-value-wrap {
            cursor: pointer;
            display: flex;
            align-items: center;
            &:hover {
                border: 1px solid #d9d9d9;
                border-radius: 2px;
            }
        }
    }
}

//页面引用部分


const dataList = [
    { id: '1', name: '张三', date: moment() },
    { id: '2', name: '里斯' },
];

const testColumns: any = [
    {
        key: 'id',
        dataIndex: 'id',
        title: 'ID',
        readonly: true,
        editable: true,
        type: 'number',
    },
    { key: 'name', dataIndex: 'name', title: '姓名', editable: true },
    {
        key: 'date',
        dataIndex: 'date',
        title: '日期',
        editable: true,
        type: 'date',
        render: (text: any, record: any) => {
            return text ? moment(text).format('YYYY-MM-DD') : '';
        },
    },
    {
        key: 'position',
        dataIndex: 'position',
        title: '选择',
        editable: true,
        type: 'select',
        optionList: [
            { id: '1', name: '选择1' },
            { id: '2', name: '选择2' },
        ],
        render: (text: any, record: any) => {
            return text ? (text === '1' ? '选择1' : '选择2') : '';
        },
    },
    {
        key: 'action',
        title: '操作',
        align: 'center',
        render: (text: any, record: any) => {
            return (
                <Button
                    color="primary"
                    onClick={() => {
                        console.log(record, 'record');
                    }}
                >
                    保存
                </Button>
            );
        },
    },
];

import AntdTable from './AntdAllowEditTable';


 <AntdTable
                dataSource={dataList}
                columns={testColumns}
                loading={loading}
            />

Antd提供了Table组件,可以实现表格的展示和编辑功能。而要实现可编辑表格和拖拽功能,需要对Table组件进行一些扩展。 1. 可编辑表格 AntdTable组件提供了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. 拖拽 AntdTable组件默认不支持拖拽功能,但是可以通过一些插件来实现。 其中,react-beautiful-dnd是一个比较常用的拖拽插件,可以很方便地和AntdTable组件结合使用。具体实现步骤如下: 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} /> ```
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值