react实现页面动态表单设计器(自定义推拽表单)-含完整代码讲解

文章详细介绍了如何使用React构建一个动态表单设计器,包括左侧拖拽组件、中间表单组件和右侧属性设置组件的功能和代码实现,支持自定义标签、必填、校验规则等特性。此外,还涉及到HTML5的拖放API和表单验证规则。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

含完整代码讲解

实现效果

在这里插入图片描述

左侧为拖拽表单,中间为组件,右侧为属性,可设置label,输入限制等

安装插件

cnpm i dynamic-customization-form

使用

 <Dynamicforms
   getlistChange={getlistChange}   //获取表单保存的值
   width={1300} //动态设置宽高
   height={650} />

组件介绍

左侧
多个表单控件,可自由选择拖拽至中间

中间
对推拽后的空间进行值的输入和选择

右侧

基本设置,可设置控件标签,是否必填,校验规则

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-jEeWzU2S-1690273324713)(https://gitee.com/jumping-little-stars/dynamic-forms/raw/master/assets/right1.png)]

校验规则有如下几种

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-j0Z3RBci-1690273324713)(https://gitee.com/jumping-little-stars/dynamic-forms/raw/master/assets/right2.png)]

多选,下拉,单选可动态设置每个选择的label以及值

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-D7a1eLAW-1690273324713)(https://gitee.com/jumping-little-stars/dynamic-forms/raw/master/assets/right3.png)]

最后可点击表单保存,也可以表单重置

代码解析

可以通过代码自己理解去封装组件!
此为发包代码讲解,自己写,可以在项目里封装,导出引入即可!

在这里插入图片描述

left 左侧拖拽组件

在这里插入图片描述

left-index.jsx 拖动功能

import React, { useState } from "react";
import "./index.less";
import { example } from "./interface";

const Config = (props) => {
    const [list, setList] = useState(example);
    // fn 拖动开始
    const dragStartFn=(item)=> {
        // 自动识别配置必要参数
        let param = {
            ...item,
            required: false, //是否必填
        };
        props.setNewParams(param);
        props.setDragEnd(false);
    }
    // fn 拖动结束
    const dragEndFn=()=> {
        props.setDragEnd(true);
    }

    const renderList=()=> {
        return list.map((item, index) => {
            return (
                <div
                    onDragStart={()=>dragStartFn(item)}
                    className="catalogue-item"
                    draggable="true"
                    onDragEnd={dragEndFn}
                    key={index}
                >
                    {item.label}
                </div>
            );
        });
    }

    return (
        <div
            onDragEnter={(e) => {
                if (!props.valid) return;
                e.preventDefault();
                props.setValid(false);
            }}
            className="catalogue"
        >
            {renderList()}
        </div>
    );
};

export default Config;

html5拖拽和释放功能知识扩展

默认情况下,图片、链接和文本是可拖动的。HTML5 在所有 HTML 元素上规定了一个 draggable 属性, 表示元素是否可以拖动。图片和链接的 draggable 属性自动被设置为 true,而其他所有元素此属性的默认值为 false。

拖拽:Drag
释放:Drop

  • 某个元素被拖动时,会依次触发以下事件:

ondragstart:拖动开始,当鼠标按下并且开始移动鼠标时,触发此事件;整个周期只触发一次;
ondrag:只要元素仍被拖拽,就会持续触发此事件;
ondragend:拖拽结束,当鼠标松开后,会触发此事件;整个周期只触发一次。

  • 当把拖拽元素移动到一个有效的放置目标时,目标对象会触发以下事件:

ondragenter:只要一把拖拽元素移动到目标时,就会触发此事件;
ondragover:拖拽元素在目标中拖动时,会持续触发此事件;
ondragleave:拖拽元素离开目标时(没有在目标上放下),会触发ondragleave
ondrop: 当拖拽元素在目标放下(松开鼠标),则触发ondrop事件。

left-interface.js 需要渲染的组件遍历列表

  • 你可以自己增加修改删除哦!
export const example = [
    {
        label: "单行文本", // 标签名称
        tips: "请输入", // 提示语
        rule: "validateNone", // 需要限制规则名称
        type: "Input", // 控件类型
        value: "",
        allowClear: true, //是否允许清除-默认true
    },
    {
        label: "多行文本", // 标签名称
        tips: "请输入", // 提示语
        rule: "validateNone", // 需要限制规则名称
        type: "InputTextera", // 控件类型
        value: "",
        allowClear: true, //是否允许清除-默认true
    },
    {
        label: "数字框", // 标签名称
        tips: "请输入", // 提示语
        rule: "validateNone", // 需要限制规则名称
        type: "InputNumber", // 控件类型
        value: null,
    },
    {
        label: "下拉框", // 标签名称
        tips: "请输入", // 提示语
        rule: "validateNone", // 需要限制规则名称
        type: "Select", // 控件类型
        options: [{ label: "选项1", value: 1 }], // 控件选项参数名称
        value: "",
    },
    {
        label: "单选", // 标签名称
        tips: "请输入", // 提示语
        rule: "validateNone", // 需要限制规则名称
        type: "Radio", // 控件类型
        options: [{ label: "选项1", value: 1}], // 控件选项参数名称
        value: "",
    },
    {
        label: "复选框", // 标签名称
        tips: "请输入", // 提示语
        rule: "validateNone", // 需要限制规则名称
        type: "Checkbox", // 控件类型
        options: [{ label: "选项1", value: 1 }], // 控件选项参数名称
        value: "",
    },
    {
        label: "开关", // 标签名称
        tips: "开启", // 提示语
        rule: "validateNone", // 需要限制规则名称
        type: "Switch", // 控件类型
        value: false,
    },
    {
        label: "日期选择", // 标签名称
        tips: "请选择", // 提示语
        rule: "validateNone", // 需要限制规则名称
        type: "DatePicker", // 控件类型
        value: "",
    },
    {
        label: "日期区间", // 标签名称
        tips: "请选择", // 提示语
        rule: "validateNone", // 需要限制规则名称
        type: "RangePicker", // 控件类型
        value: [],
    },
];

left-index.less样式设置

.catalogue {
  width: 15%;
  border: 1px solid #f1e8e8;
  padding: 15px;
  height: 100%;
  &-item {
    display: flex;
    align-items: center;
    justify-content: center;
    margin-bottom: 5px;
    cursor: move;
    color: #000;
    user-select: none;
    padding: 8px 10px;
    background: #f6f7ff;
    font-size: 12px;
    cursor: move;
    border: 1px dashed #f6f7ff;
    border-radius: 3px;
  }
  > div:last-child {
    margin-bottom: 0;
  }
}

center 中间表单组件

在这里插入图片描述

center-index.jsx 表单功能

import React, { useEffect, useState } from 'react';
import moment from 'moment';
import {
  Form,
  Checkbox,
  Switch,
  Input,
  Select,
  Radio,
  InputNumber,
  DatePicker,
} from 'antd';
import * as validateParams from '../left/validate';
import './index.less';

let draging = null;

const Option = Select.Option;
const CheckboxGroup = Checkbox.Group;
const { RangePicker } = DatePicker;
let targetName = 'drag-move'; //需要换位目标
let targetIndex = -1; //当前换位位置
let prevIndex = -1; //原坐标
const UiForm = (props) => {
  const {
    formList,
    newParams,
    setList,
    dragEnd,
    valid,
    setValid,
    setActive,
    active,
  } = props;

  useEffect(() => {
    renderForm(props.formList);
  }, [props.formList]);

  // render 表单
  function renderForm(arr) {
    return arr.map((item, index) => {
      if (!item || !item.label) return;
      return (
        <div
          key={item.key}
          onClick={setActive.bind(this, index)}
          onDragStart={dragStartFn.bind(this, index)}
          onDragOver={dragOverFn.bind(this, index)}
          onDragEnd={dragEndFn.bind(this)}
          draggable
          className={`drag-move drag-item drag-item-${active == index ? 'active' : ''
            }`}
        >
          <Form.Item
            label={item.label}
            name={index}
            hasFeedback
            rules={[
              {
                required: item.required,
                message: item.tips,
              },
              {
                validator: validateParams[item.rule],
              },
            ]}
          >
            {renderSwatch(item, index)}
          </Form.Item>
        </div>
      );
    });
  }
  // render 表单类型判定
  function renderSwatch(item, index) {
    switch (item.type) {
      case 'Input':
        return (
          <Input
            allowClear={item.allowClear === undefined ? true : item.allowClear}
            onChange={checkNoSpace.bind(this, index)}
            placeholder={item.tips}
            key={item.key}
            value={item.value}
          />
        );
      case 'InputTextera':
        return (
          <Input.TextArea
            allowClear={item.allowClear === undefined ? true : item.allowClear}
            onChange={checkNoSpace.bind(this, index)}
            placeholder={item.tips}
            value={item.value}
            rows={5}
          />
        );
      case 'InputNumber':
        return (
          <InputNumber
            min={item.min}
            onChange={checkNoSpace.bind(this, index)}
            placeholder={item.tips}
            key={item.key}
            style={{ width: '50%' }}
            value={item.value}
          />
        );
      case 'Select':
        return (
          <Select
            showSearch
            mode={item.mode}
            onChange={checkNoSpace.bind(this, index)}
            placeholder={item.tips}
            allowClear={item.allowClear === undefined ? true : item.allowClear}
            value={item.value}
            key={item.key}
            filterOption={(input, option) => (option?.children).includes(input)}
          >
            {item.options.map((its) => {
              return (
                <Option key={its.value} value={its.value}>
                  {' '}
                  {its.label}{' '}
                </Option>
              );
            })}{' '}
          </Select>
        );
      case 'Radio':
        return (
          <Radio.Group
            onChange={checkNoSpace.bind(this, index)}
            key={item.key}
            value={item.value}
          >
            {item.options.map((its) => {
              return (
                <Radio key={its.value} value={its.value}>
                  {' '}
                  {its.label}{' '}
                </Radio>
              );
            })}
          </Radio.Group>
        );
      case 'Checkbox':
        return (
          <CheckboxGroup
            onChange={checkNoSpace.bind(this, index)}
            options={item.options}
            value={item.value}
          />
        );
      case 'Switch':
        return (
          <Switch
            onChange={checkNoSpace.bind(this, index)}
            checked={item.value}
            key={item.key}
          />
        );
      case 'DatePicker':
        return (
          <DatePicker
            onChange={checkSetTime.bind(this, index)}
            placeholder={item.tips}
            key={item.key}
            value={item.value}
          />
        );
      case 'RangePicker':
        return (
          <RangePicker
            format={item.format ? item.format : 'YY-MM-DD HH:mm:ss'}
            showTime
            allowClear={item.allowClear === undefined ? true : item.allowClear}
            value={item.value}
            showNow={false}
            key={item.key}
            placeholder={item.tips}
            onChange={checkSetArrTime.bind(this, index)}
            onOk={checkSetArrTime.bind(this, index)}
          />
        );
      default:
        return '';
    }
  }
  // fn 拖动开始
  function dragStartFn(index, e) {
    e.dataTransfer.setData('te', e.target.innerText); //不能使用text,firefox会打开新tab
    draging = e.target;
    prevIndex = index;
  }

  // fn 拖动中
  function dragOverFn(index, e) {
    e.preventDefault();
    let target = getParentNode(e.target);
    if (!target || target.className.includes(targetName)) return;
    if (target !== draging) {
      targetIndex = index;
    }
  }

  // fn 拖动结束
  function dragEndFn() {
    if (prevIndex === -1 || targetIndex === -1) return;
    let newList = [...formList];
    let prev = newList[prevIndex];
    let target = newList[targetIndex];
    newList.splice(prevIndex, 1, target);
    newList.splice(targetIndex, 1, prev);
    setList(newList);
  }

  // 数组转对象
  function arrToObj1(arr, idx) {
    return arr.reduce((obj, item, index) => {
      if (index == idx) {
        obj[index] = '';
      } else {
        obj[index] = item;
      }
      return obj;
    }, {});
  }

  // fn 新生成元素
  function dargAddFn() {
    let newForm = Object.values(props.form.getFieldValue());
    let setform = arrToObj1(newForm, formList.length);
    props.form.setFieldsValue({
      ...setform,
    });
  
    // 直接新增
    if (!dragEnd || !newParams.label) return;
    var code = '' + (parseInt(Math.random() * 1000000) + 1000000);
    code = code.substring(1, 7);
    newParams.key = newParams.type + code;
    setList([...formList, newParams]);
  }

  // 阶梯查询父级元素
  function getParentNode(el) {
    try {
      if (el.className === '') return getParentNode(el.parentNode);
      if (el.className.includes(targetName)) return el;
      let parentName = el.parentNode.className;
      if (parentName.includes(targetName)) return getParentNode(el.parentNode);
      if (!parentName.includes(targetName)) return el.parentNode;
    } catch (error) {
      console.log(error);
      console.log(el.className);
      return undefined;
    }
  }

  // 时间选择传值
  function checkSetTime(index, val) {
    let newList = [...formList];
    newList[index].value = moment(val._d).format('YYYY-MM-DD');
    setList(newList);
  }

  // 时间区间选择传值
  function checkSetArrTime(index, val) {
    let param;
    if (val === null) param = val;
    else if (val[1] != null) param = moment(val[1]._d).format('YYYY-MM-DD');
    else param = moment(val[0]._d).format('YYYY-MM-DD');

    let newList = [...formList];
    newList[index].value = param;
    setList(newList);
  }

  // 表单传值
  function checkNoSpace(index, e) {
    let param = e && e.target ? e.target.value : e;
    let newList = [...formList];
    newList[index].value = param;
    setList(newList);
  }

  useEffect(() => {
    if (valid) dargAddFn();
    if (dragEnd) {
      draging = null;
      targetIndex = -1;
    }
  }, [dragEnd]);

  return (
    <div
      onDragEnter={(e) => {
        if (valid) return;
        e.preventDefault();
        setValid(true);
      }}
      id="uiform"
      className="uiform"
    >
      <Form form={props.form}>{renderForm(formList)}</Form>
    </div>
  );
};
export default UiForm;

center-index.less

@color: 'red';
@hover: '#333';
@disabled: '#000';
@error: 'red';
.drag-item {
  transition: all 0.3s;
  margin: 5px 0;
  cursor: move;
  &-active {
    box-shadow: 0 0 2px @color;
  }
}
.uiform {
  .ant-input-textarea-affix-wrapper {
    .ant-input-clear-icon {
      position: absolute;
      right: 30px !important;
    }
    .ant-input-textarea-suffix {
      .ant-form-item-feedback-icon {
        position: absolute;
        top: 6px;
        right: 0px;
        font-size: 14px;
      }
    }
  }
}

right -右侧所选控件设置组件-label、value、表单校验、下拉、单选、复选等选项数据值设置

在这里插入图片描述

right-index.jsx

import React, { useState } from 'react';
import { Switch, Input, Select, Button, Tag } from 'antd';
import './index.less';
import If from './If';
import { OptionsValidate } from './validate';
import {
  ArrowUpOutlined,
  ArrowDownOutlined,
  DeleteOutlined,
} from '@ant-design/icons';

let draging = null;
let targetName = 'config-item-option'; //需要换位目标
let targetIndex = -1; //当前换位位置
let prevIndex = -1; //原坐标
const Option = Select.Option;
const Config = (props) => {
  const { active, formList, setList, setActive } = props;
  // fn 拖动开始
  function dragStartFn(index, e) {
    e.dataTransfer.setData('te', e.target.innerText); //不能使用text,firefox会打开新tab
    draging = e.target;
    prevIndex = index;
  }
  // fn 拖动中
  function dragOverFn(index, e) {
    e.preventDefault();
    let target = getParentNode(e.target);
    if (!target || target.className !== targetName) return;
    if (target !== draging && draging) {
      //getBoundingClientRect()用于获取某个元素相对于视窗的位置集合
      let targetRect = target.getBoundingClientRect();
      let dragingRect = draging.getBoundingClientRect();
      if (target && target.animated) return;
      targetIndex = index;
    }
  }
  // fn 拖动结束
  function dragEndFn() {
    let newList = [...formList];
    let param = newList[active].options;
    let prev = param[prevIndex];
    let target = param[targetIndex];
    param.splice(prevIndex, 1, target);
    param.splice(targetIndex, 1, prev);
    newList[active].options = [...param];
    setList([...newList]);
  }
  // 阶梯查询父级元素
  function getParentNode(el) {
    if (el.className === '') return getParentNode(el.parentNode);
    if (el.className === targetName) return el;
    let parentName = el.parentNode.className;
    if (parentName !== targetName) return getParentNode(el.parentNode);
    if (parentName === targetName) return el.parentNode;
  }
  //获取元素在父元素中的index
  function dragIndex(el) {
    let index = 0;
    if (!el || !el.parentNode) return -1;
    //previousElementSibling属性返回指定元素的前一个兄弟元素(相同节点树层中的前一个元素节点)。
    while (el && (el = el.previousElementSibling)) {
      index++;
    }
    return index;
  }
  function dragAnimate(prevRect, target) {
    let ms = 300;
    let currentRect = target.getBoundingClientRect();
    //nodeType 属性返回以数字值返回指定节点的节点类型。1=元素节点  2=属性节点
    if (prevRect.nodeType === 1) {
      prevRect = prevRect.getBoundingClientRect();
    }
    dragStyle(target, 'transition', 'none');
    dragStyle(
      target,
      'transform',
      'translate3d(' +
        (prevRect.left - currentRect.left) +
        'px,' +
        (prevRect.top - currentRect.top) +
        'px,0)',
    );

    target.offsetWidth; // 触发重绘
    dragStyle(target, 'transition', 'all ' + ms + 'ms');
    dragStyle(target, 'transform', 'translate3d(0,0,0)');

    clearTimeout(target.animated);
    target.animated = setTimeout(() => {
      dragStyle(target, 'transition', '');
      dragStyle(target, 'transform', '');
      target.animated = false;
    }, ms);
  }
  //给元素添加style
  function dragStyle(el, prop, val) {
    let style = el && el.style;
    if (!style) return false;
    if (val === void 0) {
      //使用DefaultView属性可以指定打开窗体时所用的视图
      if (document.defaultView && document.defaultView.getComputedStyle) {
        val = document.defaultView.getComputedStyle(el, '');
      } else if (el.currentStyle) {
        val = el.currentStyle;
      }

      return prop === void 0 ? val : val[prop];
    } else {
      if (!(prop in style)) prop = '-webkit-' + prop;
      style[prop] = val + (typeof val === 'string' ? '' : 'px');
    }
  }
  // 表单传值
  function changeValue(name, e) {
    let param = e && e.target ? e.target.value : e;
    let arr = [...formList];
    arr[active][name] = param;
    setList(arr);
  }
  // 表单传值-option
  function changeOptionValue(index, e) {
    let value = e && e.target ? e.target.value : e;
    let arr = [...formList];
    let param = arr[active].options;
    param[index].value = value;
    setList(arr);
  }
  function changeOptionLabel(index, e) {
    let value = e && e.target ? e.target.value : e;
    let arr = [...formList];
    let param = arr[active].options;
    param[index].label = value;
    setList(arr);
  }

  // 数组转对象
  function arrToObj1(arr) {
    return arr.reduce((obj, item, index) => {
      obj[index] = item;
      return obj;
    }, {});
  }
  // fn 删除控件
  function delItemFn() {
    let newList = [...formList];
    newList.splice(active, 1);
    let newForm = newList.map((o) => o.value);
    let setform = {};
    if (newForm.length > 0) {
      setform = arrToObj1(newForm);
    }
    props.form.setFieldsValue({
      ...setform,
    });
    setList(newList);
    setActive(-1);
  }
  // fn option 新增
  function addOptionFn() {
    let index = formList[active]?.options?.length || 0;
    let param = {
      label: `选项${index}`,
      value: index,
    };
    let newList = [...formList];
    let options = newList[active].options;
    newList[active].options = [...options, param];
    setList(newList);
  }
  // fn 删除 options
  function delOptionFn(index) {
    let newList = [...formList];
    newList[active]?.options?.splice(index, 1);
    setList(newList);
  }
  // fn 排序 options
  function orderOptionFn(index, desc, disabled) {
    if (disabled) return; //排除无法换位的情况
    let newList = [...formList];
    let param = newList[active]?.options;
    let prev = param ? param[index] : '';
    let newIndex;
    switch (desc) {
      case true:
        newIndex = param ? param[index + 1] : '';
        newList[active]?.options?.splice(index + 1, 1, prev);
        break;

      default:
        newIndex = param ? param[index - 1] : '';
        newList[active]?.options?.splice(index - 1, 1, prev);
    }
    newList[active]?.options?.splice(index, 1, newIndex);
    setList(newList);
  }
  // render options 选项卡
  function renderOption() {
    return formList[active]?.options?.map((item, index) => {
      let max = (formList[active]?.options?.length || 1) - 1;
      return (
        <div
          onDragStart={dragStartFn.bind(this, index)}
          onDragOver={dragOverFn.bind(this, index)}
          onDragEnd={dragEndFn.bind(this)}
          draggable
          key={index}
          className="config-item-option"
        >
          <input
            onChange={changeOptionLabel.bind(this, index)}
            value={item.label}
            type="text"
          />
          <input
            onChange={changeOptionValue.bind(this, index)}
            type="text"
            value={item.value}
          />
          <div className="config-item-option-edit">
            <ArrowUpOutlined
              className={`config-item-option-disa${index === 0 ? 'bled' : ''}`}
              onClick={orderOptionFn.bind(this, index, false, index === 0)}
            />
            <ArrowDownOutlined
              className={`config-item-option-disa${
                index === max ? 'bled' : ''
              }`}
              onClick={orderOptionFn.bind(this, index, true, index === max)}
            />
          </div>
          <DeleteOutlined
            onClick={delOptionFn.bind(this, index)}
            className="config-item-option-del"
          />
        </div>
      );
    });
  }
  return (
    <div className="config">
      <If show={active !== -1}>
        <>
          {/* 标签 */}
          <div className="config-item">
            <p className="config-item-title">标签</p>
            <div>
              <Input
                allowClear={true}
                onChange={changeValue.bind(this, 'label')}
                placeholder="请输入"
                value={formList[active]?.label}
              />
            </div>
          </div>
          {/* 是否必填 */}
          <div className="config-item">
            <p className="config-item-title">是否必填</p>
            <div>
              <Switch
                onChange={changeValue.bind(this, 'required')}
                checked={formList[active]?.required}
              />
            </div>
          </div>
          {/* 提示语 */}
          <div className="config-item">
            <p className="config-item-title">提示语</p>
            <div>
              <Input
                allowClear={true}
                onChange={changeValue.bind(this, 'tips')}
                placeholder="请输入"
                value={formList[active]?.tips}
              />
            </div>
          </div>
          {/* rule */}
          <div className="config-item">
            <p className="config-item-title">限制规则</p>
            <div>
              <Select
                showSearch
                onChange={changeValue.bind(this, 'rule')}
                placeholder="请选择"
                allowClear={true}
                style={{ width: '100%' }}
                value={formList[active]?.rule}
                filterOption={(input, option) =>
                  (option?.children).includes(input)
                }
              >
                {OptionsValidate.map((its) => {
                  return (
                    <Option key={its.value} value={its.value}>
                      {its.label}
                    </Option>
                  );
                })}
              </Select>
            </div>
          </div>
          {/* options */}
          <If show={active !== -1 && formList[active]?.options !== undefined}>
            <div className="config-item">
              <p className="config-item-title">选项</p>
              <Tag
                type="primary"
                style={{ marginBottom: '10px', cursor: 'pointer' }}
                onClick={addOptionFn.bind(this)}
                color="blue"
              >
                新增选项
              </Tag>
              你可以对选项进行新增修改删除操作
              <div>{renderOption()}</div>
            </div>
          </If>
          <Button onClick={delItemFn.bind(this)} type="primary">
            删除
          </Button>
        </>
      </If>
    </div>
  );
};
export default Config;

right-index.less

@color: '#000';
@hover: '#0958d9';
@disabled: '#ccc';
@error: '#000';
.config {
  width: 30%;
  padding: 15px;
  border: 1px solid #f1e8e8;
  overflow: auto;
  &-item {
    margin-bottom: 10px;
    padding-bottom: 15px;
    border-bottom: 1px solid rgba(0, 0, 0, 0.07);
    &-title {
      color: #666;
    }
    &-option {
      display: flex;
      align-items: center;
      justify-content: space-between;
      margin-bottom: 5px;
      padding: 5px;
      user-select: none;
      cursor: move;

      input {
        border: none;
        outline: none;
        padding: 2px 5px;
        width: 30%;
        background-color: #fff;
        border: 1px solid #d9d9d9;
        border-radius: 5px;
      }
      &-edit {
        display: flex;
        align-items: center;
        > span {
          margin-left: 5px;
        }
      }
      &-disa {
        cursor: pointer;
        color: @color;
        &:hover {
          color: @hover;
        }
      }
      &-disabled {
        cursor: not-allowed;
        color: @disabled;
      }
      &-del {
        color: @error;
        cursor: pointer;
      }
    }
  }
}

right-If.jsx 控制下拉、单选、复选等选项数据值设置显影

const conditionContainer = (props) => {
  return props.show ? props.children : '';
};

export default conditionContainer;

right-validate.js 表单校验规则

// 密码校验
export const validatePass = (rule, value) => {
  if (value === '') {
    return Promise.resolve();
  } else {
    if (!/^(?:\d+|[a-zA-Z]+|[.!@#$%^&*]+){6,12}$/.test(value)) {
      return Promise.reject('请输入6-12位密码');
    } else {
      return Promise.resolve();
    }
  }
};
// 数字
export const validateNumber = (rule, value) => {
  if (value != '') {
    if (!/^[1-9]\d*$/.test(value)) {
      return Promise.reject('只能输入数字');
    } else {
      return Promise.resolve();
    }
  } else {
    return Promise.resolve();
  }
};
// 金额
export const validatePrice = (rule, value) => {
  if (value != '' && value != undefined && value != null) {
    let num = parseFloat(value);
    if (isNaN(num)) {
      return Promise.reject('请输入有效金额');
    } else {
      return Promise.resolve();
    }
  } else {
    return Promise.resolve();
  }
};
// 不作为
export const validateNone = () => {
  return Promise.resolve();
};
// 真实姓名
export const validateName = (rule, value) => {
  if (value === '' || !value) {
    return Promise.resolve();
  } else {
    if (!/^([\u4e00-\u9fa5]{1,20}|[a-zA-Z\.\s]{2,5})$/.test(value)) {
      return Promise.reject('请输入真实姓名');
    } else {
      return Promise.resolve();
    }
  }
};
// 身份证
export const validateIdCard = (rule, value) => {
  if (value != '' && value != undefined) {
    if (
      !/^[1-9]\d{7}((0\d)|(1[0-2]))(([0|1|2]\d)|3[0-1])\d{3}$|^[1-9]\d{5}[1-9]\d{3}((0\d)|(1[0-2]))(([0|1|2]\d)|3[0-1])\d{3}([0-9]|X)$/.test(
        value,
      )
    ) {
      return Promise.reject('请输入有效身份证');
    } else {
      return Promise.resolve();
    }
  } else {
    return Promise.resolve();
  }
};
//手机号
export const validatePhone = (rule, value) => {
  if (value != '' && value != undefined) {
    if (
      !/^1(3[0-9]|4[5,7]|5[0,1,2,3,5,6,7,8,9]|6[2,5,6,7]|7[0,1,7,8]|8[0-9]|9[1,8,9])\d{8}$/.test(
        value,
      )
    ) {
      return Promise.reject('请输入有效手机号');
    } else {
      return Promise.resolve();
    }
  } else {
    return Promise.resolve();
  }
};
// 检测支付宝账号
export const validateZFB = (rule, value) => {
  if (
    /^([a-zA-Z\d])(\w|\-)+@[a-zA-Z\d]+\.[a-zA-Z]{2,4}$/.test(value) ||
    /^1[3456789]\d{9}$/.test(value)
  ) {
    return Promise.resolve();
  } else {
    return Promise.reject('请输入有效支付宝账号');
  }
};
// 邮箱
export const validateEmail = (rule, value) => {
  let email = /^([a-zA-Z\d])(\w|\-)+@[a-zA-Z\d]+\.[a-zA-Z]{2,4}$/.test(value);
  if (email) {
    return Promise.resolve();
  } else {
    return Promise.reject('请输入有效邮箱');
  }
};

export const OptionsValidate = [
  { label: '密码校验', value: 'validatePass' },
  { label: '数字', value: 'validateNumber' },
  { label: '金额', value: 'validatePrice' },
  { label: '不限制', value: 'validateNone' },
  { label: '真实姓名', value: 'validateName' },
  { label: '身份证', value: 'validateIdCard' },
  { label: '手机号', value: 'validatePhone' },
  { label: '支付宝账号', value: 'validateZFB' },
  { label: '邮箱', value: 'validateEmail' },
];

大功告成!此时你就得到自定义表单拉!!!

gitee代码地址

gitee代码地址
在这里插入图片描述

评论 7
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值