组件的名称统一大写,以大驼峰命名,和 Antd组件保持一致

  1. import React, { useState, useRef } from 'react';
  2. import PropTypes from 'prop-types';
  3. import { Button, Drawer } from 'antd';
  4. import { CreateSuccess } from '@constants/message'
  5. import { FormBuild } from '@components/Form'
  6. import { formData } from './_formData';
  7. function App() {
  8. function onSubmit() {
  9. const form = formRef.current;
  10. }
  11. function buttonClick() {
  12. CreateSuccess()
  13. }
  14. return (
  15. <FormBuild
  16. col={24}
  17. dataSource={formData(buttonClick)}
  18. ref={formRef}
  19. onSubmit={onSubmit}
  20. />
  21. )
  22. }

FormBuild

FormBuild/index.js

  1. import React from 'react';
  2. import PropTypes from 'prop-types';
  3. import classnames from 'classnames';
  4. import { Row, Col, Form, Button, Tooltip, Icon } from 'antd';
  5. import FormConfig from './FormConfig';
  6. const itemLayout = {
  7. labelCol: { span: 6 }, wrapperCol: { span: 18 },
  8. };
  9. FormBuild.propTypes = {
  10. dataSource: PropTypes.array.isRequired,
  11. onSubmit: PropTypes.func,
  12. action: PropTypes.bool,
  13. };
  14. FormBuild.defaultProps = {
  15. onSubmit: () => {},
  16. action: false,
  17. };
  18. function FormBuild(props) {
  19. const {
  20. form, dataSource,className, action,
  21. layout = {}, col: colWidth = 8,
  22. } = props;
  23. if (!dataSource.length) return null;
  24. const { getFieldDecorator } = form;
  25. // 表单样式
  26. const formClass = classnames({
  27. mb16: true,
  28. [className]: Boolean(className)
  29. });
  30. // 表单布局
  31. const formLayout = { ...itemLayout, ...layout };
  32. const handleSubmit = (ev) => {
  33. ev.preventDefault();
  34. // const values = form.getFieldsValue();
  35. form.validateFieldsAndScroll((err, values) => {
  36. if(err) return;
  37. onSubmit(values);
  38. // onReset();
  39. });
  40. };
  41. function onReset() {
  42. form.resetFields();
  43. };
  44. return (
  45. <Form
  46. {...formLayout}
  47. className={formClass}
  48. onSubmit={handleSubmit}
  49. >
  50. <Row gutter={16}>
  51. {
  52. dataSource.map(item => {
  53. // 解构掉不需要传递给表单组件的参数
  54. const {
  55. label, name, colon = true, extra, rules, col = colWidth,
  56. valuePropName = 'value', value, noStyle, ...args
  57. } = item;
  58. const COMPONENT = <FormConfig {...args} />;
  59. return (
  60. <Col span={col} key={name}>
  61. <Form.Item
  62. label={label}
  63. extra={extra}
  64. colon={colon}
  65. >
  66. {
  67. noStyle
  68. ? COMPONENT
  69. : getFieldDecorator(name, {
  70. initialValue: value,
  71. rules,
  72. valuePropName,
  73. })(COMPONENT)
  74. }
  75. </Form.Item>
  76. </Col>
  77. );
  78. })
  79. }
  80. </Row>
  81. {
  82. action && (
  83. <Col span={24}>
  84. <Button className='mr8' type='primary' htmlType='submit'>查询</Button>
  85. <Button onClick={onReset}>重置</Button>
  86. </Col>
  87. )
  88. }
  89. </Form>
  90. );
  91. }
  92. export default Form.create()(FormBuild);

label表单提示

image.png
修改 Form.Item的 label为 jsx
用不到这个功能,就不用替换,不用搞得太复杂

  1. function LabelHelp({label, help}) {
  2. if(!help) return label;
  3. return (
  4. <span>
  5. {label}&nbsp;
  6. <Tooltip title={help}>
  7. <Icon type='question-circle-o' />
  8. </Tooltip>
  9. </span>
  10. )
  11. }
  12. // 修改为
  13. <Col span={col} key={name}>
  14. <Form.Item label={<LabelHelp label={label} help={help} />}>
  15. // ...
  16. </Form.Item>
  17. </Col>

Form.Item

  1. <Form.Item
  2. label={
  3. <span>
  4. Nickname&nbsp;
  5. <Tooltip title="What do you want others to call you?">
  6. <Icon type="question-circle-o" />
  7. </Tooltip>
  8. </span>
  9. }
  10. >
  11. {getFieldDecorator('nickname', {
  12. rules: [{ required: true, message: 'Please input your nickname!', whitespace: true }],
  13. })(<Input />)}
  14. </Form.Item>

FormConfig.js

FormBuild/FormConfig.js
初始化表单相关的组件,通过 props给表单添加属性

  • 表单为受控组件,通过 antd的 Form,自动获取值
  • value & onChange ```jsx import React from ‘react’; import { Input, InputNumber, Radio, DatePicker, Switch, AutoComplete, Button, } from ‘antd’;

import SelectArray from ‘@components/Select/SelectArray’;

const { TextArea, Password } = Input; const { RangePicker } = DatePicker;

const COMPONENT = { Input, Password, TextArea, InputNumber, Select: SelectArray, RadioGroup: Radio.Group, Switch, DatePicker, RangePicker, AutoComplete, Button, };

function FormConfig({ component, children, …props }) { const Component = COMPONENT[component]; if (!Component) return null;

return {children}; }

export default FormConfig;

  1. <a name="mJ8hY"></a>
  2. ## formData.js
  3. ```jsx
  4. export const dataSource = [
  5. { label: '基金', value: 'fund' },
  6. { label: '黄金', value: 'gold' },
  7. ];
  8. // 函数返回一个数组
  9. export function formData({type, buttonClick}) {
  10. return [
  11. {
  12. label: '类型',
  13. component: 'Select',
  14. name: 'type',
  15. value: type,
  16. rules: [
  17. { required: true, message: '请选择数据源类型' },
  18. ],
  19. help: '必填项',
  20. dataSource,
  21. },
  22. {
  23. label: '名称',
  24. component: 'Input',
  25. name: 'name',
  26. value: '',
  27. rules: [
  28. { required: true, message: '请输入名称' },
  29. ],
  30. },
  31. {
  32. label: '描述',
  33. component: 'TextArea',
  34. name: 'desc',
  35. value: '',
  36. extra: '描述最大长度200个字符',
  37. },
  38. {
  39. label: ' ',
  40. children: '测试配置',
  41. component: 'Button',
  42. type: 'dashed',
  43. colon: false,
  44. onClick: buttonClick,
  45. noStyle: true,
  46. },
  47. {
  48. label: '开关',
  49. component: 'Switch',
  50. name: 'lock',
  51. value: true,
  52. valuePropName: 'checked',
  53. checkedChildren: '是',
  54. unCheckedChildren: '否',
  55. },
  56. ]
  57. }

回显表单值

form.setFieldsValue

  1. useEffect(() => {
  2. const values = dataSource.reduce((prev, next) => {
  3. const {name, value} = next;
  4. prev[name] = value
  5. return prev;
  6. }, {});
  7. form.setFieldsValue(values);
  8. }, [dataSource]);