--- order: 23 title: en-US: Editable Cells zh-CN: 可编辑单元格 --- ## zh-CN 带单元格编辑功能的表格。当配合 `shouldCellUpdate` 使用时请注意[闭包问题](https://github.com/ant-design/ant-design/issues/29243)。 ## en-US Table with editable cells. When work with `shouldCellUpdate`, please take care of [closure](https://github.com/ant-design/ant-design/issues/29243). ```tsx import type { InputRef } from 'antd'; import { Button, Form, Input, Popconfirm, Table } from 'antd'; import type { FormInstance } from 'antd/es/form'; import React, { useContext, useEffect, useRef, useState } from 'react'; const EditableContext = React.createContext | null>(null); interface Item { key: string; name: string; age: string; address: string; } interface EditableRowProps { index: number; } const EditableRow: React.FC = ({ index, ...props }) => { const [form] = Form.useForm(); return (
); }; interface EditableCellProps { title: React.ReactNode; editable: boolean; children: React.ReactNode; dataIndex: keyof Item; record: Item; handleSave: (record: Item) => void; } const EditableCell: React.FC = ({ title, editable, children, dataIndex, record, handleSave, ...restProps }) => { const [editing, setEditing] = useState(false); const inputRef = useRef(null); const form = useContext(EditableContext)!; useEffect(() => { if (editing) { inputRef.current!.focus(); } }, [editing]); const toggleEdit = () => { setEditing(!editing); form.setFieldsValue({ [dataIndex]: record[dataIndex] }); }; const save = async () => { try { const values = await form.validateFields(); toggleEdit(); handleSave({ ...record, ...values }); } catch (errInfo) { console.log('Save failed:', errInfo); } }; let childNode = children; if (editable) { childNode = editing ? ( ) : (
{children}
); } return {childNode}; }; type EditableTableProps = Parameters[0]; interface DataType { key: React.Key; name: string; age: string; address: string; } type ColumnTypes = Exclude; const App: React.FC = () => { const [dataSource, setDataSource] = useState([ { key: '0', name: 'Edward King 0', age: '32', address: 'London, Park Lane no. 0', }, { key: '1', name: 'Edward King 1', age: '32', address: 'London, Park Lane no. 1', }, ]); const [count, setCount] = useState(2); const handleDelete = (key: React.Key) => { const newData = dataSource.filter(item => item.key !== key); setDataSource(newData); }; const defaultColumns: (ColumnTypes[number] & { editable?: boolean; dataIndex: string })[] = [ { title: 'name', dataIndex: 'name', width: '30%', editable: true, }, { title: 'age', dataIndex: 'age', }, { title: 'address', dataIndex: 'address', }, { title: 'operation', dataIndex: 'operation', render: (_, record: { key: React.Key }) => dataSource.length >= 1 ? ( handleDelete(record.key)}> Delete ) : null, }, ]; const handleAdd = () => { const newData: DataType = { key: count, name: `Edward King ${count}`, age: '32', address: `London, Park Lane no. ${count}`, }; setDataSource([...dataSource, newData]); setCount(count + 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 = defaultColumns.map(col => { if (!col.editable) { return col; } return { ...col, onCell: (record: DataType) => ({ record, editable: col.editable, dataIndex: col.dataIndex, title: col.title, handleSave, }), }; }); return (
'editable-row'} bordered dataSource={dataSource} columns={columns as ColumnTypes} /> ); }; export default App; ``` ```css .editable-cell { position: relative; } .editable-cell-value-wrap { padding: 5px 12px; cursor: pointer; } .editable-row:hover .editable-cell-value-wrap { padding: 4px 11px; border: 1px solid #d9d9d9; border-radius: 2px; } [data-theme='dark'] .editable-row:hover .editable-cell-value-wrap { border: 1px solid #434343; } ```