antd 动态增减表单项的使用

news2024/9/24 13:25:53

需求

  1. 首先这是基于antd的Form组件,
  2. 需求1: 单选按钮组 选择设置时间 展示时间选择器
  3. 需求2: 动态添加时间选择器(最多添加10个、时间为空校验、时间段重叠校验)
  4. 需求3: 开关
  5. 需求4:编辑时赋值

在这里插入图片描述

1. 单选钮组

  • <Radio.Group>组件中放置<Radio >组件就可以形成单选按钮组。<Radio >需要有value,并且通过<Space> 改为纵向(实际上就是添加了div)。
  • defaultValue 默认值,默认选中不限制,当然还有个作用就是回显时的赋值(这里我们通过判断时间选择器的数组是否有数据来展示选中状态的)。这个值我们并不需要提交给后端,只是用于判断是否展示时间选择器。
  • onChange事件去触发我们定义的changeBuyTimeRadio方法,这里onChange的值通过e?.target?.value获得。如果选中值为1(也就是设置时间单选按钮),通过antd的form.setFieldsValue(以对象作为参数)设置表单的buyPeriods(也就是我们时间选择器的数组)值。因为选中设置时间后,我们要有一个时间选择器输入框,所以在数组中要添加一个空对象。如果选中的是0(不限制)则将buyPeriods置空。
  const changeBuyTimeRadio = (type) => {
    if (type) {
      form.setFieldsValue({ buyPeriods: [{}] });
    } else {
      form.setFieldsValue({ buyPeriods: [] });
    }
  };
  
  <Form.Item label="购买时间">
          <Radio.Group
            defaultValue={step3FormData?.buyPeriods?.length > 0 ? 1 : 0}
            onChange={(e) => {
              changeBuyTimeRadio(e?.target?.value);
            }}
          >
            <Space direction="vertical">
              <Radio value={0}>
                <Form.Item style={{ marginBottom: 0 }}>
                  <span>不限制</span>
                </Form.Item>
              </Radio>
              <Radio value={1}>
                <Form.Item style={{ marginBottom: 0 }}>
                  <span>设置时间(最多可设置10个购买时间)</span>
                </Form.Item>
              </Radio>
            </Space>
          </Radio.Group>
        </Form.Item>

2. 动态添加时间选择器

2.1 antd 的动态增减表单项

  • <Form.List> name用于Form收集字段,rules是对应字段的校验规则
  • 回调函数中用于遍历输入项 ,三个参数分别是:fields、{ add, remove } (解构出add和remove方法)、和 { errors }(解构出错误信息)。

在这里插入图片描述

import { MinusCircleOutlined, PlusOutlined } from '@ant-design/icons';
import { Button, Form, Input } from 'antd';
import React from 'react';

const formItemLayout = {
  labelCol: {
    xs: { span: 24 },
    sm: { span: 4 },
  },
  wrapperCol: {
    xs: { span: 24 },
    sm: { span: 20 },
  },
};
const formItemLayoutWithOutLabel = {
  wrapperCol: {
    xs: { span: 24, offset: 0 },
    sm: { span: 20, offset: 4 },
  },
};

const App: React.FC = () => {
  const onFinish = (values: any) => {
    console.log('Received values of form:', values);
  };

  return (
    <Form name="dynamic_form_item" {...formItemLayoutWithOutLabel} onFinish={onFinish}>
      <Form.List
        name="names"
        rules={[
          {
            validator: async (_, names) => {
              if (!names || names.length < 2) {
                return Promise.reject(new Error('At least 2 passengers'));
              }
            },
          },
        ]}
      >
        {(fields, { add, remove }, { errors }) => (
          <>
            {fields.map((field, index) => (
              <Form.Item
                {...(index === 0 ? formItemLayout : formItemLayoutWithOutLabel)}
                label={index === 0 ? 'Passengers' : ''}
                required={false}
                key={field.key}
              >
                <Form.Item
                  {...field}
                  validateTrigger={['onChange', 'onBlur']}
                  rules={[
                    {
                      required: true,
                      whitespace: true,
                      message: "Please input passenger's name or delete this field.",
                    },
                  ]}
                  noStyle
                >
                  <Input placeholder="passenger name" style={{ width: '60%' }} />
                </Form.Item>
                {fields.length > 1 ? (
                  <MinusCircleOutlined
                    className="dynamic-delete-button"
                    onClick={() => remove(field.name)}
                  />
                ) : null}
              </Form.Item>
            ))}
            <Form.Item>
              <Button
                type="dashed"
                onClick={() => add()}
                style={{ width: '60%' }}
                icon={<PlusOutlined />}
              >
                Add field
              </Button>
              <Button
                type="dashed"
                onClick={() => {
                  add('The head item', 0);
                }}
                style={{ width: '60%', marginTop: '20px' }}
                icon={<PlusOutlined />}
              >
                Add field at head
              </Button>
              <Form.ErrorList errors={errors} />
            </Form.Item>
          </>
        )}
      </Form.List>
      <Form.Item>
        <Button type="primary" htmlType="submit">
          Submit
        </Button>
      </Form.Item>
    </Form>
  );
};

export default App;

2.2 修改成我们的

先看下代码

  {
            <Form.List name="buyPeriods">
              {(fields, { add, remove }) => (
                <>
                  {fields.map(({ key, name, ...restField }) => (
                    <Space key={key} style={{ display: 'flex', marginBottom: 8 }} align="baseline">
                      <Form.Item
                        {...restField}
                        name={[name, 'date']}
                        rules={[
                          { required: true, message: '请设置时间' },
                          {
                            validator: async (rule, value) => {
                              const index = rule?.field?.split('.')[1];
                              if (timeRangeVaild(value, index)) {
                                // eslint-disable-next-line prefer-promise-reject-errors
                                return Promise.reject('每个购买时间段之间不可时间交叉');
                              }
                              return Promise.resolve();
                            },
                          },
                        ]}
                        className="step_cover_global"
                      >
                        <RangePicker
                          showTime
                        />
                      </Form.Item>
                      {fields.length > 1 && (
                        <MinusCircleFilled
                          onClick={() => remove(name)}
                          className={styles.minusIcon}
                        />
                      )}
                      {fields.length - 1 === name && (
                        <PlusCircleFilled
                          onClick={() => {
                            if (fields.length < 10) {
                              add();
                            } else {
                              message.error('最多可设置10个购买时间');
                            }
                          }}
                          className={styles.plusIcon}
                        />
                      )}
                    </Space>
                  ))}
                </>
              )}
            </Form.List>
          }
  • 我们Form 提交的字段名是buyPeriods
  • 校验规则:1.不为空,2.时间段交叉校验

我们说一下时间段交叉校验,validatorr: async (rule, value) =>{} 用于自定义校验规则,然后使用Promise去返回校验响应。
我们打印看一下rule和value分别是什么
在这里插入图片描述
rule中包含着当前字段field,其中包含着当前字段(时间选择器)的索引值,value是我们当前时间选择器中选中的时间。首先,我们要用当前选中时间和buyPeriods中的所以时间段比较(但是不要和其自身比较),所以收集一下当前选中的时间选择器的索引值const index = rule?.field?.split('.')[1]; 将value和index传给我们自定义的timeRangeVaild方法。

如下是时间范围检验的方法,用到了moment的isBetween

 // 时间范围校验
  const timeRangeVaild = (date, sortIndex) => {
   // 通过form.getFieldValue获得当前的buyPeriods值(是个数组) 下面我们打印了
    const dateJson = form.getFieldValue('buyPeriods');
    let flag = false;
    // 当 buyPeriods 中的内容多余1项时我们开始比较
    if (dateJson.length > 1) {
      dateJson?.map((item, index) => {
        // 不比较自身(根据上面传入的sortIndex和当前遍历的index进行比较)
        if (Number(index) !== Number(sortIndex)) {
           // 当前选中时间选择器的起始时间 格式化为YYYY-MM-DD HH:mm:ss格式
          const beginRange = item.date[0].format('YYYY-MM-DD HH:mm:ss');
          // 当前选中时间选择器的结束时间
          const endRange = item.date[1].format('YYYY-MM-DD HH:mm:ss');
          // 当前选中时间选择器的开始时间或结束时间只要在 其它时间段内就 将flag置为true
          flag = date[0].isBetween(beginRange, endRange) || date[1].isBetween(beginRange, endRange);
        }
      });
    }
    return flag;
  };

如下当检验函数返回true 我们会进行提示每个”购买时间段之间不可时间交叉“(这里用到Promise.reject)。如果通过校验则返回Promise.resolve();

 <Form.Item
                        {...restField}
                        name={[name, 'date']}
                        rules={[
                          { required: true, message: '请设置时间' },
                          {
                            validator: async (rule, value) => {
                              console.log(rule,value,'🐷');
                              const index = rule?.field?.split('.')[1];
                              if (timeRangeVaild(value, index)) {
                                // eslint-disable-next-line prefer-promise-reject-errors
                                return Promise.reject('每个购买时间段之间不可时间交叉');
                              }
                              return Promise.resolve();
                            },
                          },
                        ]}
                        className="step_cover_global"
                      >

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/137508.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

4.6.1、路由选择协议概述

1、静态/动态路由 2、主要特点 因特网所采用的路由选择协议的主要特点 例如&#xff1a;一个较大的因特网提供商就可划分为一个自治系统 在自治系统内部和外部采用不同类别的路由选择协议&#xff0c;分别进行路由选择 3、分层次的路由选择协议 如下所示&#xff0c;将网络…

ArcGIS基础实验操作100例--实验44融合细碎多边形

本实验专栏参考自汤国安教授《地理信息系统基础实验操作100例》一书 实验平台&#xff1a;ArcGIS 10.6 实验数据&#xff1a;请访问实验1&#xff08;传送门&#xff09; 高级编辑篇--实验44 融合细碎多边形 目录 一、实验背景 二、实验数据 三、实验步骤 &#xff08;1&am…

k8s基础

一、基本介绍 Kubernetes&#xff0c;简称K8s&#xff0c;是用8代替8个字符“ubernete”而成的缩写。是一个开源的&#xff0c;用于管理云平台中多个主机上的容器化的应用&#xff0c;Kubernetes的目标是让部署容器化的应用简单并且高效(powerful) , Kubernetes提供了应用部署…

Linux 下 python3.9.8的安装

1. 准备安装环境 yum install gcc zlib* openssl* -y 2. linux 下 python 安装包的获取 官网下载地址: https://www.python.org/downloads/ 找到 自己想要的版本。 方式一&#xff1a;直接点击该链接下载&#xff0c;然后将安装包通过xftp 传送到远程服务器 的 /opt 路…

k8s之搭建单机集群

写在前面 本文一起看下如何在单机环境下搭建k8s集群。 基础环境准备参考这篇文章 。 1&#xff1a;k8s的前世今生 现在当我们提到容器技术时&#xff0c;首先想到的肯定是docker&#xff0c;但其实在docker之前&#xff0c;谷歌公司就已经有了类似的技术&#xff0c;我们知道…

借助免费AI艺术平台生成头像

随着 AI 的兴起&#xff0c;看到越来越多的实例通过 OpenAI 的举措变得轻松&#xff0c;使得 AI 艺术在今天早已不是什么新鲜事物&#xff0c;而且在游戏领域也开始有所应用。人工智能&#xff08;AI&#xff0c;artificial intelligence&#xff09;艺术&#xff0c;更准确地说…

数据挖掘与机器学习作业_09 贝叶斯

贝叶斯 贝叶斯公式 后验概率 先验概率 * 似然估计 from sklearn.model_selection import GridSearchCV from sklearn.naive_bayes import BernoulliNB from sklearn.naive_bayes import GaussianNB from sklearn.naive_bayes import MultinomialNB from sklearn.naive_bayes…

关于进程间的通信方式的总结

一、背景 在人类思想史上,马克思第一次对人的本质作出科学界定:人的本质是一切社会关系的总和。时间万物都存在或多或少的关系。那么人除了天生父子这样的家族关系&#xff0c;还有后天 通过 语言 &#xff0c;这样区别于其他动物的方式来进行和其他人的交流产生关系。 在计算…

PTL仓库提货解决方案

电子标签拣货系统是采用先进电子技术和通信技术开发而成的物流辅助作业系统&#xff0c;通常使用在仓储或现代化物流中心分拣环节&#xff0c;具有拣货速度快、效率高、差错率低、无纸化、标准化的作业特点&#xff0c;电子标签辅助拣货系统作为一种先进的作业手段&#xff0c;…

【小程序】如何开发属于自己的一款小程序

文章目录小程序简介概念小程序与普通网页开发的区别微信开发者工具小程序代码构成项目结构JSON 配置文件WXML 模板WXSS 样式JS 逻辑交互小程序的宿主环境宿主环境简介通信模型运行机制组件常用的视图容器类组件常用的基础内容组件其它常用组件API协同工作小程序成员管理小程序的…

数据完整性测试之【三】Redis缓存和数据库表里的记录

本文为博主原创&#xff0c;未经授权&#xff0c;严禁转载及使用。 本文链接&#xff1a;https://blog.csdn.net/zyooooxie/article/details/119377944 前面分享过 接口返回值 和 表记录 的校验 、 导出的CSV、Excel文件 和 表记录 的校验&#xff0c;最近 我们项目常常用到Re…

【大小端问题】

什么是大小端&#xff1f; 为什么存在大小端&#xff1f;如何判断计算机的大小端存储模式&#xff1f; 大小端是什么&#xff1f; 计算机在内存存储中有两中存储模式&#xff1a; 大端字节序存储模式和小端字节序存储模式。 大端存储模式&#xff0c;是指数据的低位保存在内…

API接口测试简介

今天继续给大家介绍渗透测试相关知识&#xff0c;本文主要内容是API接口测试简介。 免责声明&#xff1a; 本文所介绍的内容仅做学习交流使用&#xff0c;严禁利用文中技术进行非法行为&#xff0c;否则造成一切严重后果自负&#xff01; 再次强调&#xff1a;严禁对未授权设备…

Hadoop高手之路8-Flume日志采集

文章目录Hadoop高手之路8-Flume日志采集一、Flume概述1. Flume简介2. Flume运行机制3. Flume日志采集系统结构图二、Flume的搭建1. 下载2. 上传3. 解压4. 配置环境变量5. 配置flume三、Flume入门使用1. 配置数据采集方案1) 查看官网2) 案例需求3) 创建新的配置文件4) 复制官网的…

公司业财一体化详解

一、传统财务会计如何手工做账1.没有财务系统&#xff08;软件&#xff09;时公司会计用手工记账&#xff0c;流程包括&#xff1a;建立总账&#xff1b;首先建立账簿&#xff0c;登记会计账簿时&#xff0c;应当将会计凭证日期、编号、业务内容摘要、金额和其他有关资料逐项计…

GAMES101作业5及框架梳理

闲言碎语 emmm&#xff0c;上一次写还是2022年4月份的事情了&#xff0c;真的有点恍如隔世&#xff0c;4月到9月主要是在准备保研的事情&#xff0c;然后10月到12月基本上是在适应实习生活&#xff08;没错&#xff0c;保完研之后因为种种原因就直接开始实习了&#xff0c;害&…

[Vue]Vue3学习笔记(尚硅谷)

文章目录&#x1f97d; 创建Vue3项目&#x1f30a; vue-cli&#x1f30a; vite&#x1f97d; 项目结构&#x1f97d; Vue3开发者工具的安装&#x1f97d; 初识setup&#x1f97d; ref 函数&#x1f97d; reactive函数&#x1f97d;Vue3.0中的响应式原理&#x1f30a; vue2.x的响…

微服务架构解决方案介绍

1、微服务架构 目前微服务是非常火的架构或者说概念&#xff0c;也是在构建大型互联网项目时采用的架构方式。 1.1 单体架构 在软件设计中&#xff0c;经常提及和使用经典的3层模型&#xff0c;即表示层、业务逻辑层和数据访问层。 表示层&#xff1a;用于直接和用户交互&a…

内网穿透(mac,window,linux通用)1分钟实现外网访问电脑本地服务器

我们在做开发时&#xff0c;不想购买服务器&#xff0c;只想搭建我们本地的服务器&#xff0c;我们搭建的本地服务器只能供我们自己电脑的浏览器访问&#xff0c;或者处于同一个wifi下的手机访问&#xff0c;但是我们如果想让别人访问到我们的本地服务器&#xff0c;尤其做微信…

共享模型之管程(三)

1.Synchronized优化原理 1.1.轻量级锁(Lock Record) 1.1.1.简介 1>.轻量级锁的使用场景:如果一个对象虽然有多个线程访问,但是多个线程访问的时间是错开的(即没有竞争),那么可以使用轻量级锁来进行优化; 2>.轻量级锁对使用者是透明的,即语法仍然是"synchronized…