react hooks form搜索框、弹窗表单封装

16 篇文章 1 订阅
3 篇文章 0 订阅

form.js组件

import React, { useState, useEffect, useImperativeHandle, forwardRef } from 'react';
import {
  Modal,
  Form,
  Input,
  Button,
  Row,
  Col,
  Radio,
  Select,
  Cascader,
  DatePicker,
  InputNumber,
  TreeSelect,
  Switch,
  Checkbox,
  Slider,
  Upload,
  Alert,
  Transfer,
} from 'antd';
import './form.less';
import { PlusOutlined, InboxOutlined } from '@ant-design/icons';
import { cacheData } from '@/utils';

const { RangePicker } = DatePicker;

const SearchForm = (props) => {
  const [form] = Form.useForm();
  let {
    cRef,
    defaultInitialValues, //form的initialValues值,只渲染一次
    initialValues, //相当于form的setFieldsValue,只要改变就重新渲染
    ColSpan,
    FormItems = [],
    formName,
    layout,
    tailLayout,
    showModal,
    modalData = { visible: true },
    action,
    hasLoding = true,
    hasSearchBtn = true,
    formProps = {},
    modalProps = {},
  } = props;
  const [visible, setVisible] = useState(false);
  const [confirmLoading, setConfirmLoading] = useState(false);
  const [previewVisible, setPreviewVisible] = useState(false);
  const [previewImage, setPreviewImage] = useState();
  const [previewTitle, setPreviewTitle] = useState();
  const [fileList, setFileList] = useState([]);
  const [errorInfo, setErrorInfo] = useState();
  const [formValues, setFormValues] = useState();

  useImperativeHandle(cRef, () => ({
    setVisible: (val) => {
      handleShowModal(val);
    },
    setLoading: (val) => {
      setConfirmLoading(val);
    },
    getFieldsValue: () => {
      return formValues;
    },
    setFieldsValue: (values) => {
      form.setFieldsValue(values);
    },
    resetFields: () => {
      form.resetFields();
    },
    getForm: () => {
      return form;
    },
  }));

  useEffect(() => {
    setVisible(modalData.visible);
  }, [modalData.visible]);
  useEffect(() => {
    initialValues && props.onValuesChange && props.onValuesChange(initialValues, initialValues);
    form.setFieldsValue(initialValues);
    setFormValues(initialValues);
  }, [initialValues]);

  const onFinish = (values, action) => {
    // 校验通过
    let newvalues = { ...values };
    FormItems &&
      FormItems.map((item) => {
        let oldValue = newvalues[item.name];
        switch (item.type) {
          case 'RangePicker':
            if (item.name instanceof Array) {
              oldValue = newvalues[item.name[0]][item.name[1]];
              item.name.map((nameItem, index) => {
                newvalues[nameItem] =
                  action == 'reset' || !oldValue
                    ? undefined
                    : oldValue[index].format(item.format || 'YYYY-MM-DD');
              });
            } else {
              newvalues[item.name] = [
                oldValue[0].format(item.format || 'YYYY-MM-DD'),
                oldValue[1].format(item.format || 'YYYY-MM-DD'),
              ];
            }
            break;
          case 'DatePicker':
            newvalues[item.name] = oldValue.format('YYYY-MM-DD');
            break;
          case 'Monthpicker':
            newvalues[item.name] = oldValue.format('YYYY-MM');
            break;
          case 'RangeTimePicker':
            newvalues[item.name] = [
              oldValue[0].format('YYYY-MM-DD HH:mm:ss'),
              oldValue[1].format('YYYY-MM-DD HH:mm:ss'),
            ];
            break;
          case 'TimePicker':
            newvalues[item.name] = oldValue.format('HH:mm:ss');
            break;
          default:
            break;
        }
        newvalues = {
          ...newvalues,
        };
      });
    props.onFinish(newvalues, action);
  };

  const onFinishFailed = (errorInfo) => {
    // 校验未通过
    setErrorInfo(errorInfo);
    props.onFinishFailed(errorInfo);
  };

  // upload组件方法
  const getBase64 = (file) => {
    return new Promise((resolve, reject) => {
      const reader = new FileReader();
      reader.readAsDataURL(file);
      reader.onload = () => resolve(reader.result);
      reader.onerror = (error) => reject(error);
    });
  };
  const handleUploadModalCancel = () => setPreviewVisible(false);
  const handleUploadPreview = async (file) => {
    if (!file.url && !file.preview) {
      file.preview = await getBase64(file.originFileObj);
    }
    setPreviewImage(file.url || file.preview);
    setPreviewVisible(true);
    setPreviewTitle(file.name || file.url.substring(file.url.lastIndexOf('/') + 1));
  };
  const handleUploadChange = ({ fileList }) => setFileList(fileList);
  const normFile = (e) => {
    if (Array.isArray(e)) {
      return e;
    }
    return e && e.fileList;
  };

  // 模态框
  const handleClick = () => {
    props.handleClick && props.handleClick();
    form.resetFields();
    handleShowModal(true);
  };
  const handleShowModal = (status) => {
    console.log('form handleShowModal', status);
    props.handleShowModal && props.handleShowModal(status);
    setVisible(status);
    if (status == false) {
      form.resetFields();
    }
    setConfirmLoading(false);
  };

  const handleOk = () => {
    form.validateFields().then((values) => {
      onFinish(values, action);
      if (hasLoding) {
        setConfirmLoading(true);
      }
    });
  };
  const onReset = () => {
    const { showModal } = props;
    handleShowModal(false);
    if (!showModal) {
      // 为模态框取消按钮的时候不做任何操作
      const values = form.getFieldsValue();
      onFinish(values, 'reset');
    }
  };

  const getFields = (item) => {
    let html = null;
    switch (item.type) {
      case 'Text':
        if (item.render) {
          html = item.render(initialValues);
        } else {
          html =item.name&&initialValues && initialValues[item.name];
        }
        html = item.suffix ? html + item.suffix : html; //suffix后缀
        break;
      case 'Input':
        html = <Input placeholder="请输入" {...item.props} />;
        break;
      case 'InputPassword':
        html = <Input.Password placeholder="请输入" {...item.props} />;
        break;

      case 'TextArea':
        html = <Input.TextArea placeholder="请输入" autoSize={{ minRows: 4 }} {...item.props} />;
        break;
      case 'Select':
        html = (
          <Select placeholder="请选择" {...item.props}>
            {item.data &&
              item.data.map((innerItem, index) => (
                <Select.Option
                  disabled={innerItem.available && innerItem.available === true ? true : false}
                  key={`mySelect${index}`}
                  value={innerItem[item.fieldNames ? item.fieldNames.value : 'value']}
                >
                  {innerItem[item.fieldNames ? item.fieldNames.label : 'label']}
                </Select.Option>
              ))}
          </Select>
        );
        break;
      case 'TreeSelect':
        html = <TreeSelect placeholder="请选择" {...item.props} />;
        break;
      case 'Cascader':
        html = (
          <Cascader
            placeholder="请选择"
            {...item.props}
            options={(item && item.props && item.props.options) || []}
          />
        );
        break;
      case 'DatePicker':
        html = <DatePicker placeholder="请选择" {...item.props} />;
        break;
      case 'RangePicker':
        html = <RangePicker format={'YYYY/MM/DD'} {...item.props} />;
        break;
      case 'InputNumber':
        html = <InputNumber placeholder="请输入" {...item.props} />;
        break;
      case 'Switch':
        html = (
          <Switch
            defaultChecked={(initialValues && initialValues[item.name]) || false}
            {...item.props}
          />
        );
        break;
      case 'RadioButton':
        html = (
          <Radio.Group name="radiogroup">
            <Row>
              {item.data &&
                item.data.map((innerItem, index) => {
                  return (
                    <Col span={item.colSpan} key={`myRadio${index}`}>
                      <Radio.Button
                        value={innerItem[item.fieldNames ? item.fieldNames.value : 'value']}
                        style={{ lineHeight: item.lineHeight ? item.lineHeight : '32px' }}
                        {...item.props}
                      >
                        {innerItem[item.fieldNames ? item.fieldNames.label : 'label']}
                      </Radio.Button>
                    </Col>
                  );
                })}
            </Row>
          </Radio.Group>
        );
        break;
      case 'Radio':
        html = (
          <Radio.Group name="radiogroup" {...item.props}>
            <Row>
              {item.data &&
                item.data.map((innerItem, index) => {
                  return (
                    <Col span={item.colSpan} key={`myRadio${index}`}>
                      <Radio
                        value={innerItem[item.fieldNames ? item.fieldNames.value : 'value']}
                        style={{ lineHeight: item.lineHeight ? item.lineHeight : '32px' }}
                        {...item.props}
                      >
                        {innerItem[item.fieldNames ? item.fieldNames.label : 'label']}
                      </Radio>
                    </Col>
                  );
                })}
            </Row>
          </Radio.Group>
        );
        break;
      case 'Slider':
        html = <Slider marks={item.data} />;
        break;
      case 'Checkbox':
        html = (
          <Checkbox.Group {...item.props}>
            <Row>
              {item.data &&
                item.data.map((innerItem, index) => (
                  <Col span={item.colSpan} key={`myCheckbox${index}`}>
                    <Checkbox
                      value={innerItem[item.fieldNames ? item.fieldNames.value : 'value']}
                      style={{ lineHeight: item.lineHeight ? item.lineHeight : '32px' }}
                    >
                      {innerItem[item.fieldNames ? item.fieldNames.label : 'label']}
                    </Checkbox>
                  </Col>
                ))}
            </Row>
          </Checkbox.Group>
        );
        break;
      case 'Upload':
        html = (
          <Upload
            onPreview={this.handleUploadPreview}
            onChange={this.handleUploadChange}
            {...item.props}
          >
            {fileList.length >= item.maxLength ? null : (
              <div>
                <PlusOutlined />
                <div className="ant-upload-text">Upload</div>
              </div>
            )}
          </Upload>
        );
        break;
      case 'Dragger':
        html = (
          <Upload.Dragger name="files" action="/upload.do">
            <p className="ant-upload-drag-icon">
              <InboxOutlined />
            </p>
            <p className="ant-upload-text">Click or drag file to this area to upload</p>
            <p className="ant-upload-hint">Support for a single or bulk upload.</p>
          </Upload.Dragger>
        );
        break;
      case 'Transfer':
        html = <Transfer {...item.props} />;
        break;
      default:
        html = null;
    }
    return html;
  };

  const getForm = () => {
    layout = layout || {
      labelCol: { span: 7 },
      wrapperCol: { span: 17 },
    };
    tailLayout = tailLayout || {
      wrapperCol: { span: 24, offset: 0 },
    };
    ColSpan = ColSpan || 6;
    const html = (
      <Form
        {...layout}
        form={form}
        name={formName}
        {...formProps}
        onFieldsChange={(changedFields, allFields) =>
          props.onFieldsChange && props.onFieldsChange(changedFields, allFields, action)
        }
        onValuesChange={(changedValues, allValues) => {
          setFormValues(allValues);
          props.onValuesChange && props.onValuesChange(changedValues, allValues, action);
        }}
        onFinish={(values) => onFinish(values, action)}
        onFinishFailed={onFinishFailed}
        initialValues={defaultInitialValues}
      >
        <Row>
          {FormItems.map((item, index) => {
            if (!item.type) {
              return;
            }
            if (item.type == 'Alert') {
              return (
                <Alert
                  key={`myForm${index}`}
                  message="This is a warning notice about copywriting"
                  description=""
                  type="warning"
                  showIcon
                  banner
                  style={
                    item.whiteBg
                      ? { marginBottom: '16px', backgroundColor: 'white', padding: 0 }
                      : { marginBottom: '16px' }
                  }
                  {...item.props}
                />
              );
            }
            if (item.type == 'ReactNode') {
              return (
                <div
                  style={item.style ? item.style : { width: '100%', marginBottom: '16px' }}
                  key={`myForm${index}`}
                >
                  {item.children}
                </div>
              );
            }
            return (
              <Col
                span={ColSpan}
                // className="flexCenter"
                key={`myForm${index}`}
              >
                <Form.Item
                  label={item.label}
                  name={item.name}
                  rules={item.rules}
                  help={item.help || undefined}
                  extra={item.extra || ''}
                >
                  {getFields(item, index)}
                </Form.Item>
              </Col>
            );
          })}
          {!showModal && hasSearchBtn && (
            <Col span={ColSpan}>
              <Form.Item {...tailLayout}>
                <Button
                  type="primary"
                  htmlType="submit"
                  style={
                    FormItems.length % (24 / ColSpan) == 0
                      ? { marginRight: '8px' }
                      : { marginRight: '8px', marginLeft: '16px' }
                  }
                >
                  查询
                </Button>
                <Button onClick={() => onReset('reset')}>重置</Button>
              </Form.Item>
            </Col>
          )}
        </Row>
      </Form>
    );
    return html;
  };
  return (
    <>
      <>
        {showModal && (
          <div className="modalFormCmpBox" style={{ display: 'inline-block' }}>
            {modalData.clickNode ? (
              <span onClick={handleClick}>{modalData.clickNode}</span>
            ) : (
              <Button type="primary" onClick={handleClick}>
                {modalData.btnText}
              </Button>
            )}
            <Modal
              title={modalData.title}
              maskClosable={false}
              width={modalData.width || 480}
              visible={visible}
              destroyOnClose={true}
              {...modalProps}
              onCancel={() => onReset('cancle')}
              footer={[
                <Button key="submit" type="primary" loading={confirmLoading} onClick={handleOk}>
                  确定
                </Button>,
                <Button key="back" onClick={() => onReset('cancle')}>
                  取消
                </Button>,
              ]}
            >
              {getForm()}
            </Modal>
          </div>
        )}
      </>
      {!showModal && getForm()}
    </>
  );
};
export default forwardRef(SearchForm);
  • 2
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值