You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何对比表单新旧提交对象 校验同键值是否符合增减规则

跨提交数据对比校验落地方案

你现有Formik+Yup的技术栈完全可以无缝接入该能力,无需引入额外重型依赖,整体按三层实现即可:

1. 抽离可配置的字段对比规则

不要将增减判断逻辑硬编码在校验函数中,适配动态表单的特性,直接将历史对比规则和原有单字段校验规则并列配置在表单字段元信息中,支持三类规则即可覆盖需求:

  • increase:当前提交值需严格大于历史提交值
  • decrease:当前提交值需严格小于历史提交值
  • none:不做历史值对比校验

配置示例:

// 表单字段配置
const formFieldsConfig = [
  {
    name: 'NumberOne',
    label: '统计指标1',
    // 原有单字段Yup校验规则
    baseValidate: Yup.number().required('该字段为必填项'),
    // 新增历史对比规则
    historyCompareRule: 'increase',
    compareErrorMsg: '统计指标1需大于上一次提交的数值'
  },
  {
    name: 'NumberTwo',
    label: '统计指标2',
    baseValidate: Yup.number().required('该字段为必填项'),
    historyCompareRule: 'increase',
    compareErrorMsg: '统计指标2需大于上一次提交的数值'
  },
  {
    name: 'NumberThree',
    label: '统计指标3',
    baseValidate: Yup.number().required('该字段为必填项'),
    historyCompareRule: 'decrease',
    compareErrorMsg: '统计指标3需小于上一次提交的数值'
  }
]

2. 扩展Yup自定义校验,注入历史数据上下文

利用Yup原生的test自定义校验能力,结合Formik支持传入函数生成validationSchema的特性,将历史数据作为上下文传入校验流程,完全复用你现有的表单校验、错误提示渲染逻辑,无需重构原有代码。
核心实现代码:

import * as Yup from 'yup';
import { Formik, Form } from 'formik';

/**
 * 生成包含历史对比逻辑的校验Schema
 * @param {Array} fieldsConfig 表单字段配置
 * @param {Object} historyData 历史提交数据
 */
const buildValidationSchema = (fieldsConfig, historyData) => {
  const schemaConfig = {};
  fieldsConfig.forEach(field => {
    let validator = field.baseValidate;
    // 配置了对比规则的字段追加自定义校验
    if (['increase', 'decrease'].includes(field.historyCompareRule)) {
      validator = validator.test(
        'historyValueCompare',
        field.compareErrorMsg,
        (currentVal) => {
          const historyVal = historyData?.[field.name];
          // 空值场景跳过对比,交给非空、格式类基础校验处理,避免误报
          if ([null, undefined, ''].includes(currentVal) || [null, undefined, ''].includes(historyVal)) {
            return true;
          }
          // 按配置规则做数值判断
          return field.historyCompareRule === 'increase' 
            ? Number(currentVal) > Number(historyVal)
            : Number(currentVal) < Number(historyVal)
        }
      )
    }
    schemaConfig[field.name] = validator;
  })
  return Yup.object().shape(schemaConfig);
}

// 表单组件内使用
function DataCollectionForm() {
  // 实际场景下该值从接口拉取对应用户的历史提交记录
  const historyRecord = {
    NumberOne: 100,
    NumberTwo: 200,
    NumberThree: 300
  }

  return (
    <Formik
      initialValues={{ NumberOne: '', NumberTwo: '', NumberThree: '' }}
      validationSchema={() => buildValidationSchema(formFieldsConfig, historyRecord)}
      onSubmit={async (values) => {
        // 原有提交逻辑,校验通过后才会触发
        console.log('提交数据', values)
      }}
    >
      {({ values, errors, touched, handleChange, handleBlur }) => (
        <Form>
          {/* 表单字段渲染逻辑和原有实现完全一致 */}
          {formFieldsConfig.map(field => (
            <div key={field.name} style={{ margin: '12px 0' }}>
              <label>{field.label}:</label>
              <input
                type="number"
                name={field.name}
                value={values[field.name]}
                onChange={handleChange}
                onBlur={handleBlur}
              />
              {touched[field.name] && errors[field.name] && (
                <div style={{ color: '#f5222d', fontSize: 12 }}>{errors[field.name]}</div>
              )}
            </div>
          ))}
          <button type="submit">提交表单</button>
        </Form>
      )}
    </Formik>
  )
}

3. 扩展能力兼容

后续如果需要更复杂的对比规则,只需要调整字段配置和校验判断逻辑即可,核心流程不用改动:

  • 若需要支持固定差值校验(如当前值需比历史值高至少50),新增minDiff配置项,判断逻辑改为差值计算即可
  • 若表单为嵌套对象结构,将字段name改为路径写法(如extend.NumberOne),替换直接取值逻辑为路径取值方法即可支持
  • 若需要支持百分比增减规则,新增compareType配置区分固定值对比/百分比对比即可

内容的提问来源于stack exchange,提问作者Iamtheburnt

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.30 11:00:59