likes
comments
collection
share

技术分享:Antd 4.x 版本 表单嵌套表格功能实现分享

作者站长头像
站长
· 阅读数 47

前言

起因是,最近来了一个需求,需要在一个表格中每行的数据中,上传图片。对于在 Antd Design表格中的数据,进行上传图片对我来说是第一次。

在此之前我了解过官网上的例子关于,可编辑表格的如下图

技术分享:Antd 4.x 版本 表单嵌套表格功能实现分享

是针对每行的数据,进行表单的修改的操作。举一反三,我觉得可以按照官网的例子,进行一波魔改,核心的逻辑一样是 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;

关键点说明:

  1. 状态管理 dataSource:

    • 使用 useState 钩子来管理 dataSource,并确保只有一个数据源。
  2. 表单管理:

    • 使用 Form 组件包裹 Table 以管理表单数据。
    • Form.Item 中使用 name 属性,按 record.key 为每行的表单项命名,从而区分不同的表单项。
  3. 表单项渲染:

    • Tablecolumns 属性中,使用 render 函数来自定义每一列的渲染方式。
    • render 函数中,使用 Form.Item 包裹表单控件,并绑定初始值和验证规则。
  4. 保存数据:

    • 在点击保存按钮时,通过 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 中,配置 valuePropNamegetValueFromEvent 来管理上传的文件列表。
  • beforeUpload={() => false} 防止文件自动上传,可以通过自定义上传逻辑来处理文件。

总结

其实这个需求点对于有经验的开发者来说,并不难。但我觉得有必要总结一下经验,方便下次写的时候有加深对应的印象。我们平常其实更多的是跟使用的 UI框架 打交道,这方面的经验更加偏向实战方面。我觉得温故而知新对于一个开发者来说很重要。 简单分享一下,我对于这部分开发的内容的总结,希望可以帮助到你们。

转载自:https://juejin.cn/post/7396921720722276389
评论
请登录