技术分享:Antd 4.x 版本 表单嵌套表格功能实现分享
前言
起因是,最近来了一个需求,需要在一个表格中每行的数据中,上传图片。对于在 Antd Design表格中的数据,进行上传图片对我来说是第一次。
在此之前我了解过官网上的例子关于,可编辑表格的如下图
是针对每行的数据,进行表单的修改的操作。举一反三,我觉得可以按照官网的例子,进行一波魔改,核心的逻辑一样是 Form
表单中嵌套 Table
组件,用 Form
表单来收集每一行中的数据,至于上传图片其实和 Input
输入框是没有区别的。
那么我们开始编码,我们先实现一个 Input
的表单结构组件
过程
第一步 实现一个 Input 版本的 表单嵌套
import React, { useState } from 'react';
import { Table, Form, Input, Button } from 'antd';
const EditableTable = () => {
const [dataSource, setDataSource] = useState([
{ key: '1', name: 'John Doe', age: 32 },
{ key: '2', name: 'Jane Doe', age: 28 },
]); // 只管理一个数据源
const [form] = Form.useForm(); // 初始化 使用 Antd 自带的钩子
const handleSave = () => {
// 收集数据&绑定数据
form
.validateFields()
.then((values) => {
// Merge form values into dataSource
const updatedDataSource = dataSource.map((item) => ({
...item,
...values[item.key],
}));
setDataSource(updatedDataSource); // 最后合并处理数据
})
.catch((errorInfo) => {
console.log('Validate Failed:', errorInfo);
});
};
// 初始化列的数据
const columns = [
{
title: 'Name',
dataIndex: 'name',
key: 'name',
render: (text, record) => (
<Form.Item
name={[record.key, 'name']}
initialValue={record.name}
rules={[{ required: true, message: 'Name is required' }]}
>
<Input />
</Form.Item>
),
},
{
title: 'Age',
dataIndex: 'age',
key: 'age',
render: (text, record) => (
<Form.Item
name={[record.key, 'age']}
initialValue={record.age}
rules={[{ required: true, message: 'Age is required' }]}
>
<Input />
</Form.Item>
),
},
];
return (
<Form form={form} component={false}>
<Table
dataSource={dataSource}
columns={columns}
pagination={false}
rowClassName={() => 'editable-row'}
/>
{ /* 这里添加 editable-row 编辑行的数据 可以区分当前编辑的是具体哪一行 这里就不展开了 */}
<Button type="primary" onClick={handleSave}>
Save
</Button>
</Form>
);
};
export default EditableTable;
关键点说明:
-
状态管理
dataSource
:- 使用
useState
钩子来管理dataSource
,并确保只有一个数据源。
- 使用
-
表单管理:
- 使用
Form
组件包裹Table
以管理表单数据。 - 在
Form.Item
中使用name
属性,按record.key
为每行的表单项命名,从而区分不同的表单项。
- 使用
-
表单项渲染:
- 在
Table
的columns
属性中,使用render
函数来自定义每一列的渲染方式。 - 在
render
函数中,使用Form.Item
包裹表单控件,并绑定初始值和验证规则。
- 在
-
保存数据:
- 在点击保存按钮时,通过
form.validateFields()
验证表单数据。 - 验证通过后,将表单数据合并到
dataSource
中,并更新dataSource
的状态。
- 在点击保存按钮时,通过
这样,你可以在一个 Table
中嵌套 Form
表单,并且只管理一个 dataSource
数据源
第二步 实现上传图片的版本
其实第二步只是在第一步的基础上面修改一点点
import React, { useState } from 'react';
import { Table, Form, Input, Button, Upload } from 'antd';
import { UploadOutlined } from '@ant-design/icons';
const EditableTable = () => {
const [dataSource, setDataSource] = useState([
{ key: '1', name: 'John Doe', age: 32, image: '' },
{ key: '2', name: 'Jane Doe', age: 28, image: '' },
]);
const [form] = Form.useForm();
const handleSave = () => {
form
.validateFields()
.then((values) => {
const updatedDataSource = dataSource.map((item) => ({
...item,
...values[item.key],
}));
setDataSource(updatedDataSource);
})
.catch((errorInfo) => {
console.log('Validate Failed:', errorInfo);
});
};
// antd design 处理文件格式专用的方法 必须要使用
const normFile = (e) => {
if (Array.isArray(e)) {
return e;
}
return e?.fileList;
};
// 添加的是 Image 图片上传的组件
const columns = [
{
title: 'Name',
dataIndex: 'name',
key: 'name',
render: (text, record) => (
<Form.Item
name={[record.key, 'name']}
initialValue={record.name}
rules={[{ required: true, message: 'Name is required' }]}
>
<Input />
</Form.Item>
),
},
{
title: 'Age',
dataIndex: 'age',
key: 'age',
render: (text, record) => (
<Form.Item
name={[record.key, 'age']}
initialValue={record.age}
rules={[{ required: true, message: 'Age is required' }]}
>
<Input />
</Form.Item>
),
},
{
title: 'Image',
dataIndex: 'image',
key: 'image',
render: (text, record) => (
<Form.Item
name={[record.key, 'image']}
valuePropName="fileList" // 对应下面Upload 组件的 fileList Key
getValueFromEvent={normFile} // 获取 Value 的方法
initialValue={record.image}
rules={[{ required: true, message: 'Image is required' }]}
>
<Upload
name="logo"
listType="picture"
beforeUpload={() => false} // Prevent automatic upload
>
<Button icon={<UploadOutlined />}>Click to upload</Button>
</Upload>
</Form.Item>
),
},
];
return (
<Form form={form} component={false}>
<Table
dataSource={dataSource}
columns={columns}
pagination={false}
rowClassName={() => 'editable-row'}
/>
<Button type="primary" onClick={handleSave}>
Save
</Button>
</Form>
);
};
export default EditableTable;
图片上传组件:
- 使用
Upload
组件嵌入到Form.Item
中,配置valuePropName
和getValueFromEvent
来管理上传的文件列表。 beforeUpload={() => false}
防止文件自动上传,可以通过自定义上传逻辑来处理文件。
总结
其实这个需求点对于有经验的开发者来说,并不难。但我觉得有必要总结一下经验,方便下次写的时候有加深对应的印象。我们平常其实更多的是跟使用的 UI框架 打交道,这方面的经验更加偏向实战方面。我觉得温故而知新对于一个开发者来说很重要。 简单分享一下,我对于这部分开发的内容的总结,希望可以帮助到你们。
转载自:https://juejin.cn/post/7396921720722276389