Antd Design EditableRow 不更改“编辑”按钮“保存”和“取消”

发布于 2025-01-20 09:36:05 字数 11696 浏览 2 评论 0原文

我正在使用 Antd 库,但我似乎找不到哪里有错误。

这是我的 EditableTableCell 组件

import React, {Component} from 'react';
import { Form } from '@ant-design/compatible';
import '@ant-design/compatible/assets/index.css';
import { Input, InputNumber, Select, DatePicker } from "antd";
import moment from "moment";
import {EditableContext} from "./EditableTableRow";

const FormItem = Form.Item;
const Option = Select.Option;
class EditableTableCell extends Component {
    getInput = (record, dataIndex, title, getFieldDecorator) => {
        switch (this.props.inputType) {
            case "number":
                return (
                    <FormItem style={{ margin: 0 }}>
                        {getFieldDecorator(dataIndex, {
                            rules: [
                                {
                                    required: true,
                                    message: `Please Input ${title}!`
                                }
                            ],
                            initialValue: record[dataIndex]
                        })(
                            <InputNumber formatter={value => value} parser={value => value} />
                        )}
                    </FormItem>
                );
            case "date":
                return (
                    <FormItem style={{ margin: 0 }}>
                        {getFieldDecorator(dataIndex, {
                            initialValue: moment(record[dataIndex], this.dateFormat)
                        })(<DatePicker format={this.dateFormat} />)}
                    </FormItem>
                );
            case "select":
                return (
                    <FormItem style={{ margin: 0 }}>
                        {getFieldDecorator(dataIndex, {
                            initialValue: record[dataIndex]
                        })(
                            <Select style={{ width: 150 }}>
                                {[...Array(11).keys()]
                                    .filter(x => x > 0)
                                    .map(c => `Product ${c}`)
                                    .map((p, index) => (
                                        <Option value={p} key={index}>
                                            {p}
                                        </Option>
                                    ))}
                            </Select>
                        )}
                    </FormItem>
                );
            default:
                return (
                    <FormItem style={{ margin: 0 }}>
                        {getFieldDecorator(dataIndex, {
                            rules: [
                                {
                                    required: true,
                                    message: `Please Input ${title}!`
                                }
                            ],
                            initialValue: record[dataIndex]
                        })(<Input />)}
                    </FormItem>
                );
        }
    }
    render() {
        const { editing, dataIndex, title, inputType, record, index,...restProps} = this.props;
        return (
           <EditableContext.Consumer>
               {form => {
                   const { getFieldDecorator } = form;
                   return (
                       <td {...restProps}>
                           {editing ?
                               this.getInput(record, dataIndex, title, getFieldDecorator)
                               : restProps.children}
                       </td>
                   );
               }}
           </EditableContext.Consumer>
        );
    }
}

export default EditableTableCell;

这是我的 EditableTableCell 组件

import React, {Component} from 'react';
import { Form} from '@ant-design/compatible';

export const EditableContext = React.createContext();
class EditableTableRow extends Component {
    render() {
        return (
            <EditableContext.Provider value={this.props.form}>
                <tr {...this.props} />
            </EditableContext.Provider>
        );
    }
}

export default EditableTableRow=Form.create()(EditableTableRow);

这是我的 ProductsPage 组件,其中存在错误

import React, {Component} from 'react';
import {Button, Layout, notification, Popconfirm, Space, Table,Typography} from "antd";
import {Link} from "react-router-dom";
import {Content} from "antd/es/layout/layout";
import EditableTableRow, {EditableContext} from "../components/EditableTableRow";
import EditableTableCell from "../components/EditableTableCell";
import API from "../server-apis/api";
import {employeesDataColumns} from "../tableColumnsData/employeesDataColumns";
import {CheckCircleFilled, InfoCircleFilled} from "@ant-design/icons";


class ProductsPage extends Component {
    constructor(props) {
        super(props);
        this.state = {
            data: [],
            error: null,
            isLoaded: false,
            editingKey: "",
            errorMessage: "",
         
        }
    }
    columns = [
        ...employeesDataColumns,
        {
            title: "Actions",
            dataIndex: "actions",
            width: "10%",
            render: (text, record) => {
                const editable = this.isEditing(record);
                return editable ? (
                    <span>
                                <EditableContext.Consumer>
                                  {form => (<a onClick={() => this.saveData(form, record.username)} style={{ marginRight: 8 }}>Save</a>)}
                                </EditableContext.Consumer>
                                <a onClick={this.cancel}>Cancel</a>
                            </span>
                ) : (
                    <Space size="middle">
                        <a onClick={() => this.edit(record.username)}>Edit</a>
                        <Popconfirm title="Are you sure you want to delete this product?"
                                    onConfirm={() => this.remove(record.username)}>
                            <a style={{color:"red"}}>Delete</a>
                        </Popconfirm>
                    </Space>
                );
            },
        }
    ];

    isEditing = (record) => {
        return record.username === this.state.editingKey;
    };

    edit(username) {
    
        this.setState({editingKey:username});
    }

    cancel = () => {
        this.setState({ editingKey: ""});
    };
    componentDidMount() {
        this.setState({ loading: true });
        const token="Bearer "+ JSON.parse(localStorage.getItem("token"));
        API.get(`users/all`,{ headers: { Authorization: token}})
            .then(res => {
                // console.log(res.data._embedded.productList);
                const employees = res.data._embedded.employeeInfoDtoList;
                this.setState({loading: false,data:employees });
            })
    }
    async remove(username) {
        const token="Bearer "+ JSON.parse(localStorage.getItem("token"));
        API.delete(`/users/${username}`,{ headers: { Authorization: token}})
            .then(() => {
                let updatedProducts = [...this.state.data].filter(i => i.username !== username);
                this.setState({data: updatedProducts});
                this.successfullyAdded("Employee is deleted. It wont have any access to the website anymore.")
            }).catch(()=>this.errorHappend("Failed to delete"));
    }

    hasWhiteSpace(s) {
        return /\s/g.test(s);
    }
    saveData(form,username) {
        form.validateFields((error, row) => {
            if (error) {
                return;
            }
            const newData = [...this.state.data];
            const index = newData.findIndex(item => username === item.username);
            const item = newData[index];
            newData.splice(index, 1, {
                ...item,
                ...row
            });
            const token="Bearer "+ JSON.parse(localStorage.getItem("token"));
            const response = API.put(`/users/${username}/update`, row,{ headers: { Authorization: token}})
                .then((response) => {
                    this.setState({ data: newData, editingKey: ""});
                    this.successfullyAdded("Empolyee info is updated")
                })
                .catch(error => {
                    this.setState({ errorMessage: error.message });
                    this.errorHappend("Failed to save changes.")
                    console.error('There was an error!', error);
                });
        });
    }
    successfullyAdded = (message) => {
        notification.info({
            message: `Notification`,
            description:message,
            placement:"bottomRight",
            icon: <CheckCircleFilled style={{ color: '#0AC035' }} />
        });
    };
    errorHappend = (error) => {
        notification.info({
            message: `Notification`,
            description:
                `There was an error! ${error}`,
            placement:"bottomRight",
            icon: <InfoCircleFilled style={{ color: '#f53333' }} />
        });
    };
    render() {
        const components = {
            body: {
                row: EditableTableRow,
                cell: EditableTableCell
            }
        };
        const columns = this.columns.map(col => {
            if (!col.editable) {
                return col;
            }
            return {
                ...col,
                onCell: record => {
                    const checkInput = index => {
                        switch (index) {
                            case "price":
                                return "number";
                            default:
                                return "text";
                        }
                    };
                    return {
                        record,
                        // inputType: col.dataIndex === "age" ? "number" : "text",
                        inputType: checkInput(col.dataIndex),
                        dataIndex: col.dataIndex,
                        title: col.title,
                        editing: this.isEditing(record)
                    };
                }
            };
        });
        const { data, loading } = this.state;
        return (
            <Layout>
                <div>
                    <Link to="/add-product">
                        <Button style={{float:"right", background: "#0AC035",marginBottom:"1em", marginTop:"1em" }}
                                type="primary">New emplyee</Button>
                    </Link>
                </div>
                <Content>
                
                    <Table components={components} bordered dataSource={data} columns={columns} loading={loading} rowKey={data.username} rowClassName="editable-row"/>
                </Content>
            </Layout>
        );
    }
}

export default ProductsPage;

这是我遇到的错误: 在此处输入图像描述

我希望得到像 Antd 文档中所示的结果: 在此处输入图像描述

如果您看一下并帮助我找出错误所在,我将不胜感激

Im using Antd library and i can't seem to find where i have the bug.

This is my EditableTableCell component

import React, {Component} from 'react';
import { Form } from '@ant-design/compatible';
import '@ant-design/compatible/assets/index.css';
import { Input, InputNumber, Select, DatePicker } from "antd";
import moment from "moment";
import {EditableContext} from "./EditableTableRow";

const FormItem = Form.Item;
const Option = Select.Option;
class EditableTableCell extends Component {
    getInput = (record, dataIndex, title, getFieldDecorator) => {
        switch (this.props.inputType) {
            case "number":
                return (
                    <FormItem style={{ margin: 0 }}>
                        {getFieldDecorator(dataIndex, {
                            rules: [
                                {
                                    required: true,
                                    message: `Please Input ${title}!`
                                }
                            ],
                            initialValue: record[dataIndex]
                        })(
                            <InputNumber formatter={value => value} parser={value => value} />
                        )}
                    </FormItem>
                );
            case "date":
                return (
                    <FormItem style={{ margin: 0 }}>
                        {getFieldDecorator(dataIndex, {
                            initialValue: moment(record[dataIndex], this.dateFormat)
                        })(<DatePicker format={this.dateFormat} />)}
                    </FormItem>
                );
            case "select":
                return (
                    <FormItem style={{ margin: 0 }}>
                        {getFieldDecorator(dataIndex, {
                            initialValue: record[dataIndex]
                        })(
                            <Select style={{ width: 150 }}>
                                {[...Array(11).keys()]
                                    .filter(x => x > 0)
                                    .map(c => `Product ${c}`)
                                    .map((p, index) => (
                                        <Option value={p} key={index}>
                                            {p}
                                        </Option>
                                    ))}
                            </Select>
                        )}
                    </FormItem>
                );
            default:
                return (
                    <FormItem style={{ margin: 0 }}>
                        {getFieldDecorator(dataIndex, {
                            rules: [
                                {
                                    required: true,
                                    message: `Please Input ${title}!`
                                }
                            ],
                            initialValue: record[dataIndex]
                        })(<Input />)}
                    </FormItem>
                );
        }
    }
    render() {
        const { editing, dataIndex, title, inputType, record, index,...restProps} = this.props;
        return (
           <EditableContext.Consumer>
               {form => {
                   const { getFieldDecorator } = form;
                   return (
                       <td {...restProps}>
                           {editing ?
                               this.getInput(record, dataIndex, title, getFieldDecorator)
                               : restProps.children}
                       </td>
                   );
               }}
           </EditableContext.Consumer>
        );
    }
}

export default EditableTableCell;

This is my EditableTableCell component

import React, {Component} from 'react';
import { Form} from '@ant-design/compatible';

export const EditableContext = React.createContext();
class EditableTableRow extends Component {
    render() {
        return (
            <EditableContext.Provider value={this.props.form}>
                <tr {...this.props} />
            </EditableContext.Provider>
        );
    }
}

export default EditableTableRow=Form.create()(EditableTableRow);

This is my ProductsPage component im having bug in

import React, {Component} from 'react';
import {Button, Layout, notification, Popconfirm, Space, Table,Typography} from "antd";
import {Link} from "react-router-dom";
import {Content} from "antd/es/layout/layout";
import EditableTableRow, {EditableContext} from "../components/EditableTableRow";
import EditableTableCell from "../components/EditableTableCell";
import API from "../server-apis/api";
import {employeesDataColumns} from "../tableColumnsData/employeesDataColumns";
import {CheckCircleFilled, InfoCircleFilled} from "@ant-design/icons";


class ProductsPage extends Component {
    constructor(props) {
        super(props);
        this.state = {
            data: [],
            error: null,
            isLoaded: false,
            editingKey: "",
            errorMessage: "",
         
        }
    }
    columns = [
        ...employeesDataColumns,
        {
            title: "Actions",
            dataIndex: "actions",
            width: "10%",
            render: (text, record) => {
                const editable = this.isEditing(record);
                return editable ? (
                    <span>
                                <EditableContext.Consumer>
                                  {form => (<a onClick={() => this.saveData(form, record.username)} style={{ marginRight: 8 }}>Save</a>)}
                                </EditableContext.Consumer>
                                <a onClick={this.cancel}>Cancel</a>
                            </span>
                ) : (
                    <Space size="middle">
                        <a onClick={() => this.edit(record.username)}>Edit</a>
                        <Popconfirm title="Are you sure you want to delete this product?"
                                    onConfirm={() => this.remove(record.username)}>
                            <a style={{color:"red"}}>Delete</a>
                        </Popconfirm>
                    </Space>
                );
            },
        }
    ];

    isEditing = (record) => {
        return record.username === this.state.editingKey;
    };

    edit(username) {
    
        this.setState({editingKey:username});
    }

    cancel = () => {
        this.setState({ editingKey: ""});
    };
    componentDidMount() {
        this.setState({ loading: true });
        const token="Bearer "+ JSON.parse(localStorage.getItem("token"));
        API.get(`users/all`,{ headers: { Authorization: token}})
            .then(res => {
                // console.log(res.data._embedded.productList);
                const employees = res.data._embedded.employeeInfoDtoList;
                this.setState({loading: false,data:employees });
            })
    }
    async remove(username) {
        const token="Bearer "+ JSON.parse(localStorage.getItem("token"));
        API.delete(`/users/${username}`,{ headers: { Authorization: token}})
            .then(() => {
                let updatedProducts = [...this.state.data].filter(i => i.username !== username);
                this.setState({data: updatedProducts});
                this.successfullyAdded("Employee is deleted. It wont have any access to the website anymore.")
            }).catch(()=>this.errorHappend("Failed to delete"));
    }

    hasWhiteSpace(s) {
        return /\s/g.test(s);
    }
    saveData(form,username) {
        form.validateFields((error, row) => {
            if (error) {
                return;
            }
            const newData = [...this.state.data];
            const index = newData.findIndex(item => username === item.username);
            const item = newData[index];
            newData.splice(index, 1, {
                ...item,
                ...row
            });
            const token="Bearer "+ JSON.parse(localStorage.getItem("token"));
            const response = API.put(`/users/${username}/update`, row,{ headers: { Authorization: token}})
                .then((response) => {
                    this.setState({ data: newData, editingKey: ""});
                    this.successfullyAdded("Empolyee info is updated")
                })
                .catch(error => {
                    this.setState({ errorMessage: error.message });
                    this.errorHappend("Failed to save changes.")
                    console.error('There was an error!', error);
                });
        });
    }
    successfullyAdded = (message) => {
        notification.info({
            message: `Notification`,
            description:message,
            placement:"bottomRight",
            icon: <CheckCircleFilled style={{ color: '#0AC035' }} />
        });
    };
    errorHappend = (error) => {
        notification.info({
            message: `Notification`,
            description:
                `There was an error! ${error}`,
            placement:"bottomRight",
            icon: <InfoCircleFilled style={{ color: '#f53333' }} />
        });
    };
    render() {
        const components = {
            body: {
                row: EditableTableRow,
                cell: EditableTableCell
            }
        };
        const columns = this.columns.map(col => {
            if (!col.editable) {
                return col;
            }
            return {
                ...col,
                onCell: record => {
                    const checkInput = index => {
                        switch (index) {
                            case "price":
                                return "number";
                            default:
                                return "text";
                        }
                    };
                    return {
                        record,
                        // inputType: col.dataIndex === "age" ? "number" : "text",
                        inputType: checkInput(col.dataIndex),
                        dataIndex: col.dataIndex,
                        title: col.title,
                        editing: this.isEditing(record)
                    };
                }
            };
        });
        const { data, loading } = this.state;
        return (
            <Layout>
                <div>
                    <Link to="/add-product">
                        <Button style={{float:"right", background: "#0AC035",marginBottom:"1em", marginTop:"1em" }}
                                type="primary">New emplyee</Button>
                    </Link>
                </div>
                <Content>
                
                    <Table components={components} bordered dataSource={data} columns={columns} loading={loading} rowKey={data.username} rowClassName="editable-row"/>
                </Content>
            </Layout>
        );
    }
}

export default ProductsPage;

This is the bug I'm having:
enter image description here

And i want to have this result like its shown in Antd docs:
enter image description here

Id really appreciate if you take a look and help me figure out where im wrong

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(1

独自唱情﹋歌 2025-01-27 09:36:05

更新的解决方案:
我发现问题。在绘制列的渲染中,如果不是可编辑的列,则只需返回列即可。您可以检查下面的代码。我添加了一个检查,如果是dataindex ==='action',然后返回以下代码:
请点击链接:
https://react-ts-v3fbst.stackblitz.io

强>

1.在列中,我从动作对象中删除渲染函数:

{
      title: 'Actions',
      dataIndex: 'actions',
      width: '10%',
},

2。在映射列的渲染函数中,在此条件之前添加以下代码if(



if (col.dataIndex === 'actions') {
    return {
        ...col,
        render: (text, record) => {
            const editable = this.isEditing(record);
            return editable ? (
                <span>
                    <EditableContext.Consumer>
                        {(form) => (
                            <a onClick={() => this.saveData(form, record.username)} style={{ marginRight: 8 }}>
                                Save
                            </a>
                        )}
                    </EditableContext.Consumer>
                    <a onClick={this.cancel}>Cancel</a>
                </span>
            ) : (
                <Space size='middle'>
                    <a onClick={() => this.edit(record.username)}>Edit</a>
                    <Popconfirm title='Are you sure you want to delete this product?' onConfirm={() => this.remove(record.username)}>
                        <a style={{ color: 'red' }}>Delete</a>
                    </Popconfirm>
                </Space>
            );
        }
    };
}

!该特定的编辑行,请确保您在每个记录中都有用户名。我使用以下数据对此进行了测试:

const data = [
    { id: 8, name: 'baun', model: '2022', color: 'black', price: 358, quantity: 3, username: 'brvim' },
    { id: 3, name: 'galileo', model: '20221', color: 'white', price: 427, quantity: 7, username: 'john' }
];

最重要的是,您应该选择该属性作为所有记录中唯一的键。当您使用用户名时,我不知道您的业务逻辑或数据是什么样子,但是从技术上讲,每个记录都可以具有相同的用户名。因此,您必须选择在完整数据中始终是唯一的东西。

Updated Solution:
I find the issue. In render where you map the columns, you just return the column if it's not an editable column. You can check the code below. I added a check if it's dataIndex === 'actions', then return the following code:
Please Follow the link:
https://react-ts-v3fbst.stackblitz.io

Changes:

1.In columns, i remove the render function from the action object:

{
      title: 'Actions',
      dataIndex: 'actions',
      width: '10%',
},

2. In render function where you map the columns, add the following code before this condition if(!col.editable) {,:



if (col.dataIndex === 'actions') {
    return {
        ...col,
        render: (text, record) => {
            const editable = this.isEditing(record);
            return editable ? (
                <span>
                    <EditableContext.Consumer>
                        {(form) => (
                            <a onClick={() => this.saveData(form, record.username)} style={{ marginRight: 8 }}>
                                Save
                            </a>
                        )}
                    </EditableContext.Consumer>
                    <a onClick={this.cancel}>Cancel</a>
                </span>
            ) : (
                <Space size='middle'>
                    <a onClick={() => this.edit(record.username)}>Edit</a>
                    <Popconfirm title='Are you sure you want to delete this product?' onConfirm={() => this.remove(record.username)}>
                        <a style={{ color: 'red' }}>Delete</a>
                    </Popconfirm>
                </Space>
            );
        }
    };
}

When you click on edit, you set the username as key for that particular row for editing, make sure you have username in each record. I tested this using the following data:

const data = [
    { id: 8, name: 'baun', model: '2022', color: 'black', price: 358, quantity: 3, username: 'brvim' },
    { id: 3, name: 'galileo', model: '20221', color: 'white', price: 427, quantity: 7, username: 'john' }
];

Most important, you should select that attribute as key that is unique in all records. As you are using username, i don't know what is your business logic or data looks like, but technically each record can have same username. So you must select something that would always be unique in your complete data.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文