EditCellTable
import React, { useContext, useState, useEffect, useRef } from 'react';
import { Table, Input, Form } from 'antd';
import { FormInstance } from 'antd/lib/form';
import style from './index.less'
const EditableContext = React.createContext<FormInstance<any> | null>(null);
interface Item {
key: string;
name: string;
age: string;
address: string;
}
const EditableRow: React.FC<{index: number}> = ({ index, ...props }) => {
const [form] = Form.useForm();
return (
<Form form={form} component={false}>
<EditableContext.Provider value={form}>
<tr {...props} />
</EditableContext.Provider>
</Form>
);
};
interface EditableCellProps {
title: React.ReactNode;
editable: boolean;
children: React.ReactNode;
dataIndex: keyof Item;
record: Item;
handleSave: (record: Item) => void;
}
const EditableCell: React.FC<EditableCellProps> = ({
title,
editable,
children,
dataIndex,
record,
handleSave,
...restProps
}) => {
const [editing, setEditing] = useState(false);
const inputRef = useRef<Input>(null);
const form = useContext(EditableContext)!;
useEffect(() => {
if (editing) {
if(inputRef.current) {
inputRef.current!.focus();
}
}
}, [editing]);
useEffect(() => {
if(!(record && record[dataIndex])) {
setEditing(true)
}
},[record])
const toggleEdit = () => {
setEditing(!editing);
form.setFieldsValue({ [dataIndex]: record[dataIndex] });
};
const save = async () => {
try {
const values = await form.validateFields();
if(values && values?.name) {
setEditing(!editing);
form.setFieldsValue({ [dataIndex]: record[dataIndex] });
}
handleSave({ ...record, ...values });
} catch (errInfo) {
console.log('Save failed:', errInfo);
}
};
let childNode = children;
if (editable) {
childNode = editing ? (
<Form.Item
style={{ margin: 0 }}
name={dataIndex}
>
<Input ref={inputRef} onPressEnter={save} onBlur={save} placeholder="请输入"/>
</Form.Item>
) : (
<div className={style.editableWrap} style={{ paddingRight: 24 }} onClick={toggleEdit}>
{children}
</div>
);
}
return <td {...restProps}>{childNode}</td>;
};
type EditableTableProps = Parameters<typeof Table>[0];
interface DataType {
key: React.Key;
name: any;
age: string;
address: string;
}
interface EditableTableState {
dataSource: DataType[];
count: number;
}
type ColumnTypes = Exclude<EditableTableProps['columns'], undefined>;
const EditCellTable: React.FC<EditableTableProps & EditableTableState> = (props: EditableTableProps) => {
const initColumns: (ColumnTypes[number] & { editable?: boolean; dataIndex: string })[] = [
{
title: 'name',
dataIndex: 'name',
width: '30%',
editable: true,
},
{
title: 'age',
dataIndex: 'age',
},
{
title: 'address',
dataIndex: 'address',
},
];
const [dataSource, setDataSource ] = useState<DataType[]>([
{
key: '0',
name: null,
age: '32',
address: 'London, Park Lane no. 0',
},
{
key: '1',
name: 'Edward King 1',
age: '32',
address: 'London, Park Lane no. 1',
},
])
const handleSave = (row: DataType) => {
const newData = [...dataSource];
const index = newData.findIndex(item => row.key === item.key);
const item = newData[index];
newData.splice(index, 1, {
...item,
...row,
});
setDataSource(newData);
};
const components = {
body: {
row: EditableRow,
cell: EditableCell,
},
};
const columns = initColumns.map(col => {
if (!col.editable) {
return col;
}
return {
...col,
onCell: (record: DataType) => ({
record,
editable: col.editable,
dataIndex: col.dataIndex,
title: col.title,
handleSave: handleSave,
}),
};
});
return (
<div>
<Table
components={components}
rowClassName={style.editableRow}
bordered
dataSource={dataSource}
columns={columns as ColumnTypes}
/>
</div>
);
}
export default EditCellTable;
.editableWrap {
padding: 5px 12px;
cursor: pointer;
}
.editableRow:hover .editableWrap {
padding: 4px 11px;
border: 1px solid #d9d9d9;
border-radius: 2px;
}