在项目中遇到了很多可编辑表格,这里做个总结:
先说第一种实现方式:
antd自带的可编辑表格进行改进
代码如下:
import React, { useContext, useState, useEffect } from 'react';
import {
Table,
Input,
Popconfirm,
Form,
Tooltip,
Select,
DatePicker,
TimePicker,
} from 'antd';
import moment from 'moment';
import { PlusOutlined } from '@ant-design/icons';
const budget = [
{
key: '0',
name: 'flower',
budget: '32',
desc: moment('2021-08-27'),
},
{
key: '1',
name: 'ball',
budget: '32',
desc: moment('2021-08-17'),
},
];
const EditableContext = React.createContext(null);
const EditableRow = ({ index, ...props }) => {
const [form] = Form.useForm();
return (
<Form form={form} component={false}>
<EditableContext.Provider value={form}>
<tr {...props} />
</EditableContext.Provider>
</Form>
);
};
const EditableCell = ({
title,
editable,
children,
dataIndex,
record,
rules,
handleSave,
valueType,
...restProps
}) => {
const form = useContext(EditableContext);
useEffect(() => {
form.setFieldsValue({
[dataIndex]: record?.[dataIndex],
});
// eslint-disable-next-line react-hooks/exhaustive-deps
}, []);
const save = async () => {
try {
const values = await form.validateFields();
handleSave({ ...record, ...values });
} catch (errInfo) {
console.log('Save failed:', errInfo);
}
};
// 这里可以自行扩展
const renderNode = (valueType) => {
switch (valueType) {
case 'input':
return <Input onPressEnter={save} onBlur={save} />;
case 'select':
return (
<Select allowClear onBlur={save}>
<Select.Option key="1" value={1}>
1
</Select.Option>
<Select.Option key="2" value={2}>
2
</Select.Option>
</Select>
);
case 'date':
return <DatePicker onBlur={save} />;
case 'dateRange':
return <DatePicker.RangePicker onBlur={save} />;
case 'time':
return <TimePicker onBlur={save} />;
default:
break;
}
};
let childNode = valueType ? (
<Form.Item
style={{
margin: 0,
}}
name={dataIndex}
rules={
rules || [
{
required: true,
message: `${title} 不能为空.`,
},
]
}
>
{renderNode(valueType)}
</Form.Item>
) : (
children
);
return <td {...restProps}>{childNode}</td>;
};
export default function EditTable({ value = budget, onChange }) {
const [dataSource, setDataSource] = useState(value);
const columns = [
{
title: 'name',
dataIndex: 'name',
width: '30%',
editable: true,
valueType: 'select',
rules: [{ required: true, message: '测试' }],
},
{
title: 'budget',
dataIndex: 'budget',
editable: true,
valueType: 'input',
},
{
title: 'describe',
dataIndex: 'desc',
editable: true,
valueType: 'date',
},
{
title: 'operation',
dataIndex: 'operation',
render: (_, record) =>
dataSource.length >= 1 ? (
<Tooltip placement="bottom" title="删除当前行">
<Popconfirm
title="确认删除?"
onConfirm={() => handleDelete(record.key)}
>
<a>link</a>
</Popconfirm>
</Tooltip>
) : null,
},
];
const handleDelete = (key) => {
setDataSource(dataSource.filter((item) => item.key !== key));
onChange?.(dataSource.filter((item) => item.key !== key));
};
const handleAdd = () => {
const newData = {
key: new Date().getTime(),
name: '-',
budget: '-',
desc: '-',
};
setDataSource([...dataSource, newData]);
onChange?.([...dataSource, newData]);
};
const handleSave = (row) => {
const newData = [...dataSource];
const index = newData.findIndex((item) => row.key === item.key);
const item = newData[index];
newData.splice(index, 1, { ...item, ...row });
setDataSource(newData);
onChange?.(newData);
};
const components = {
body: {
row: EditableRow,
cell: EditableCell,
},
};
const newColumns = columns.map((col) => {
if (!col.editable) {
return col;
}
return {
...col,
onCell: (record) => ({
record,
editable: col.editable,
dataIndex: col.dataIndex,
title: col.title,
valueType: col.valueType,
rules: col.rules,
handleSave: handleSave,
}),
};
});
useEffect(() => {
onChange?.([...dataSource]);
// eslint-disable-next-line react-hooks/exhaustive-deps
}, []);
return (
<div>
<Table
components={components}
rowClassName="rules_edit_row"
rowKey={(record) => record.key}
bordered
pagination={false}
showHeader={false}
dataSource={dataSource}
columns={newColumns}
/>
<span
style={{
color: '#3880ff',
cursor: 'pointer',
position: 'relative',
top: 6,
}}
onClick={handleAdd}
>
<PlusOutlined />
添加
</span>
</div>
);
}