Skip to content

Latest commit

 

History

History
764 lines (714 loc) · 23.9 KB

README.md

File metadata and controls

764 lines (714 loc) · 23.9 KB

Form 表单

Buy me a coffee Open in unpkg NPM Downloads npm version

由输入框、选择器、单选框、多选框等控件组成,用以收集、校验、提交数据。

import { Form, FormItem } from 'uiw';
// or
import { Form, FormItem } from '@uiw/react-form';

基本用法

import React, { useState, useRef } from "react";
import { Form, Input, Row, Col, Slider, Button, Notify } from 'uiw';

export default function Demo() {
  const form = useRef()

  const onSubmit = () => {
    form.current.onSubmit()
  }
  const resetForm = () => {
    form.current.resetForm()
  }
  const getFieldValues = () => {
    console.log('getFieldValues', form.current.getFieldValues())
  }

  const setFieldValue=()=>{
    form.current.setFieldValue('name','UIW')
  }

  return (
    <div>
      <Form
        ref={form}
        onChange={({ initial, current }) => {
          console.log('onChange', initial, current);
        }}
        onSubmit={({ initial, current }) => {
          if (current.name === initial.name) {
            Notify.error({
              title: '提交失败!',
              description: `表单提交内容为空!`,
            });
          } else {
            Notify.success({
              title: '提交成功!',
              description: `姓名为:${current.name},提交完成,将自动填充初始化值!`,
            });
          }
        }}
        fields={{
          name: {
            label: '姓名',
            children: <Input placeholder="请输入姓名" />
          },
        }}
      >
        {({ fields, state, canSubmit }) => {
          return (
            <div>
              <Row>
                <Col style={{ maxWidth: 300 }}>{fields.name}</Col>
              </Row>
              <Row>
                <Col>
                  <Button disabled={!canSubmit()} type="primary" htmlType="submit">提交</Button>
                </Col>
              </Row>
              <Row>
                <Col>
                  {JSON.stringify(state.current)}
                </Col>
              </Row>
            </div>
          )
        }}
      </Form>
      <Button type="primary" onClick={onSubmit} >submit</Button>
      <Button type="primary" onClick={resetForm}>resetForm</Button>
      <Button type="primary" onClick={getFieldValues}>getValues</Button>
      <Button type="primary" onClick={setFieldValue}>setFieldValue</Button>
    </div>
  )
}

自定义校验

一般校验可不需引入外部包解决,如果遇到大型工程表单比较多的地方推荐使用 jquense/yup

import React from 'react';
import { Form, Input, Notify, Checkbox, Switch, RadioGroup, Radio, Textarea, Row, Col, Button, Select, SearchSelect } from 'uiw';

export default function Demo() {
  return(
    <Form
      onSubmit={({initial, current}) => {
        const errorObj = {};
        if (current.userName.startsWith('u')) {
          errorObj.userName = `姓名 ${current.userName} 不能以 ‘u’ 开头`;
        }
        if (!current.checkboxOne) {
          errorObj.checkboxOne = '一个多选条件 为必填';
        }
        if (!current.terms) {
          errorObj.terms = '必须统一服务条款';
        }
        if(Object.keys(errorObj).length > 0) {
          const err = new Error();
          err.filed = errorObj;
          throw err;
        }
        Notify.success({
          title: '提交成功!',
          description: `姓名为:${current.userName},提交完成,将自动填充初始化值!`,
        });
      }}
      onSubmitError={(error) => {
        if (error.filed) {
          return { ...error.filed };
        }
        return null;
      }}
      fields={{
        userName: {
          initialValue: 'uiw',
          label: '姓名',
          children: <Input type="text" />,
          help: '以“u”开头的名字将在此处显示错误信息'
        },
        age: {
          initialValue: '9',
          label: '年龄',
          children: <Input type="number" />
        },
        select: {
          children: (
            <Select>
              <Select.Option value="w">Choose an item...</Select.Option>
              <Select.Option value="1">One</Select.Option>
              <Select.Option value="2">Two</Select.Option>
              <Select.Option value="3">Three</Select.Option>
              <Select.Option value="4">Four</Select.Option>
            </Select>
          )
        },
        searchSelect: {
          initialValue:[{label: 'a7', value: 7},{label: 'a8', value: 8}],
          children: (
            <SearchSelect
              allowClear
              labelInValue={true}
              showSearch={true}
              mode="multiple"
              disabled={false}
              placeholder="请选择选项"
              option={[
                { label: 'a1', value: 1 },
                { label: 'a2', value: 2 },
                { label: 'a3', value: 3 },
                { label: 'a4', value: 4 },
                { label: 'a5', value: 5 },
                { label: 'a6', value: 6 },
                { label: 'a7', value: 7 },
                { label: 'a8', value: 8 },
              ]}
            />
          )
        },
        checkbox: {
          initialValue: ['四川菜'],
          label: '选择你想吃的菜',
          children: (
            <Checkbox.Group>
              <div>菜系</div>
              <Checkbox value="四川菜">四川菜</Checkbox>
              <Checkbox value="湖北菜">湖北菜</Checkbox>
              <Checkbox value="西北菜">西北菜</Checkbox>
              <Checkbox value="新疆菜">新疆菜</Checkbox>
              <Checkbox value="东北菜">东北菜</Checkbox>
              <div style={{ marginTop: 10 }}>家常菜</div>
              <Checkbox value="红烧武昌鱼">红烧武昌鱼</Checkbox>
              <Checkbox value="麻婆豆腐">麻婆豆腐</Checkbox>
              <Checkbox value="北京烤鸭">北京烤鸭</Checkbox>
            </Checkbox.Group>
          )
        },
        checkboxOne: {
          inline: true,
          label: '一个多选条件',
          children: <Checkbox value="1">四川菜</Checkbox>
        },
        switch: {
          inline: true,
          initialValue: true,
          label: '开启',
          children: <Switch size="small" />
        },
        radioGroup: {
          inline: true,
          initialValue: '男',
          label: '单选',
          children: (
            <RadioGroup name="other">
              <Radio value="男"></Radio>
              <Radio value="女"></Radio>
              <Radio value="人妖" disabled>人妖</Radio>
              <Radio value="未知">未知</Radio>
            </RadioGroup>
          )
        },
        textarea: {
          initialValue: '',
          label: '多行文本输入框',
          children: <Textarea placeholder="请输入内容" />
        },
        terms: {
          validator: (currentValue) => {
            return !currentValue ? '必须统一服务条款' : null;
          },
          style: { marginBottom: 0 },
          children: <Checkbox value="1">已阅读并同意<a href="#">服务条款</a></Checkbox>
        }
      }}
    >
      {({ fields, state, canSubmit, resetForm }) => {
        console.log('fields:-->', state);
        return (
          <div style={{ maxWidth: 500 }}>
            <Row gutter={10}>
              <Col>{fields.userName}</Col>
              <Col>{fields.age}</Col>
            </Row>
            <Row gutter={10}>
              <Col>{fields.select}</Col>
              <Col>{fields.searchSelect}</Col>
            </Row>
            <Row gutter={10}>
              <Col>{fields.checkbox}</Col>
              <Col>{fields.checkboxOne}</Col>
            </Row>
            <Row gutter={10}>
              <Col>{fields.radioGroup}</Col>
            </Row>
            <Row gutter={10}>
              <Col>{fields.switch}</Col>
            </Row>
            <Row gutter={10}>
              <Col>{fields.textarea}</Col>
            </Row>
            <Row gutter={10}>
              <Col style={{ padding: '5px 0 10px 0' }}>
                {fields.terms}
              </Col>
            </Row>
            <Row gutter={10}>
              <Col fixed>
                <Button disabled={!canSubmit()} type="primary" htmlType="submit">提交</Button>
                <Button type="light" onClick={resetForm}>重置表单</Button>
              </Col>
            </Row>
            <Row>
              <Col>
                <pre style={{ padding: '10px 0 0 10px' }}>
                  {JSON.stringify(state.current, null, 2)}
                </pre>
              </Col>
            </Row>
          </div>
        )
      }}
    </Form>
  )
}

水平登录栏

import React from 'react';
import { Form, Input, Row, Col, Notify, Button } from 'uiw';

export default function Demo() {
  return (
    <div>
      <Form
        onSubmit={({initial, current}) => {
          const errorObj = {};
          if (!current.username) {
            errorObj.username = '用户名不能为空!';
          }
          if (!current.password) {
            errorObj.password = '密码不能为空!';
          }
          if(Object.keys(errorObj).length > 0) {
            const err = new Error();
            err.filed = errorObj;
            Notify.error({ title: '提交失败!', description: '请确认提交表单是否正确!' });
            throw err;
          }
          console.log('-->>', initial, current);
          Notify.success({ title: '提交成功!', description: '恭喜你登录成功!' });
        }}
        onSubmitError={(error) => {
          if (error.filed) {
            return { ...error.filed };
          }
          return null;
        }}
        fields={{
          username: {
            labelClassName: 'fieldLabel',
            labelFor: 'username-inline',
            children: <Input preIcon="user" id="username-inline" />
          },
          password: {
            labelClassName: 'fieldLabel',
            labelFor: 'password-inline',
            children: <Input preIcon="lock" id="password-inline" type="password" />
          },
        }}
      >
        {({ fields, state, canSubmit, resetForm }) => {
          console.log('fields:', state);
          return (
            <div>
              <Row gutter={10}>
                <Col fixed>{fields.username}</Col>
                <Col fixed>{fields.password}</Col>
              </Row>
              <Row gutter={10}>
                <Col>
                  <Button disabled={!canSubmit()} type="primary" htmlType="submit">提交</Button>
                  <Button type="danger" onClick={resetForm}>重置表单</Button>
                </Col>
              </Row>
            </div>
          )
        }}
      </Form>
    </div>
  );
}

登录

import React from 'react';
import { Form, Input, Row, Col, Checkbox, Notify, Button } from 'uiw';

export default function Demo() {
  return (
    <div>
      <Form
        onSubmit={({initial, current}) => {
          console.log('-->>', initial, current);
        }}
        fields={{
          username: {
            labelClassName: 'fieldLabel',
            labelStyle: { width: 60 },
            labelFor: 'username',
            children: <Input preIcon="user" id="username" />
          },
          password: {
            labelClassName: 'fieldLabel',
            labelStyle: { width: 60 },
            labelFor: 'password',
            children: <Input preIcon="lock" id="password" type="password" />
          },
          terms: {
            validator: (currentValue) => !currentValue ? '必须统一服务条款' : null,
            children: <Checkbox value="1">已阅读并同意</Checkbox>
          }
        }}
      >
        {({ fields, state, canSubmit }) => {
          console.log('fields:', state);
          return (
            <div>
              <Row>
                <Col fixed>{fields.username}</Col>
              </Row>
              <Row>
                <Col fixed>{fields.password}</Col>
              </Row>
              <Row>
                <Col fixed align="middle">{fields.terms}</Col>
                <Col fixed style={{ marginTop: -4 }}><a href="#">服务条款</a></Col>
              </Row>
              <Row>
                <Col fixed>
                  <Button disabled={!canSubmit()} type="primary" htmlType="submit">提交</Button>
                </Col>
              </Row>
            </div>
          )
        }}
      </Form>
    </div>
  )
}

表单提交

import React from 'react';
import { Form, Input, Select, Row, Col, Button } from 'uiw';

export default function Demo() {
  return (
    <div>
      <Form
        onSubmit={({initial, current}) => {
          console.log('-->>', initial, current);
        }}
        fields={{
          firstName: {
            labelClassName: 'fieldLabel',
            labelStyle: { width: 60 },
            inline: true,
            label: '姓氏',
            children: <Input />
          },
          lastName: {
            labelClassName: 'fieldLabel',
            labelStyle: { width: 60 },
            initialValue: '先生',
            inline: true,
            label: '名字',
            children: <Input />
          },
          email: {
            labelClassName: 'fieldLabel',
            labelStyle: { width: 60 },
            validator: (currentValue) => {
              return currentValue && currentValue.length < 2 ? 'Password must be 8+ characters' : null;
            },
            inline: true,
            label: 'Email',
            children: <Input />
          },
          select: {
            labelClassName: 'fieldLabel',
            labelStyle: { width: 60 },
            inline: true,
            label: '选择器',
            children: (
              <Select>
                <Select.Option>Choose an item...</Select.Option>
                <Select.Option value="1">One</Select.Option>
                <Select.Option value="2">Two</Select.Option>
                <Select.Option value="3">Three</Select.Option>
                <Select.Option value="4">Four</Select.Option>
              </Select>
            ),
          },
        }}
      >
        {({ fields, state, canSubmit }) => {
          console.log('fields:', state);
          return (
            <div>
              <Row gutter={10} style={{ marginBottom: 10 }}>
                <Col>{fields.firstName}</Col>
                <Col>{fields.lastName}</Col>
              </Row>
              <Row gutter={10}>
                <Col>{fields.email}</Col>
                <Col>{fields.select}</Col>
              </Row>
              <Row gutter={10}>
                <Col />
                <Col fixed align="bottom"><Button disabled={!canSubmit()} type="primary" htmlType="submit">提交</Button></Col>
              </Row>
            </div>
          )
        }}
      </Form>
    </div>
  )
}

自定义控件应用

下面实例是在 <Form /> 表单组件中,应用自定义 <CustomSelect /> 控件组件。

⚠️ 注意,自定义控件需要两个必要的 props 参数,valueonChange

  • value 用于值传递,
  • onChange(value) 用于值变更需要执行的回调函数,回调函数第一个参数必须是 value
import React from 'react';
import { Form, Row, Col, Dropdown, Menu, Icon, Button, Notify } from 'uiw';

// 自定义组件
function CustomSelect(props) {
  const { option = [], onChange } = props;
  const [value, setValue] = React.useState(props.value);
  const [isOpen, setIsOpen] = React.useState(false);

  React.useEffect(() => {
    if (value !== props.value) {
      setValue(props.value);
    }
  }, [props.value]);
  const label = option.find(item => value === item.value);
  return (
    <Dropdown
      trigger="click"
      onVisibleChange={(open) => setIsOpen(open)}
      isOpen={isOpen}
      menu={
        <Menu bordered style={{ minWidth: 120 }}>
          {option.map((item, idx) => (
            <Menu.Item active={value === item.value} key={idx} text={item.label}
              onClick={() => {
                setValue(item.value);
                setIsOpen(false)
                onChange && onChange(item.value);
              }}
            />
          ))}
        </Menu>
      }
    >
      <Button
        style={{
          boxShadow: 'inset 0 0 0 1px rgba(16, 22, 26, 0.2), inset 0 -1px 0 rgba(16, 22, 26, 0.1)'
        }}
        type="link"
      >
        {label.label}<Icon type={isOpen ? 'up' : 'down'} />
      </Button>
    </Dropdown>
  );
}

// 自定义组件应用实例
export default function Demo() {
  return (
    <div>
      <Form
        onSubmitError={(error) => {
          if (error.filed) {
            return { ...error.filed };
          }
          return null;
        }}
        onSubmit={({initial, current}) => {
          console.log('~~~', current);
          const errorObj = {};
          if (!current.select) {
            errorObj.select = '内容为空,请输入内容';
          }
          if(Object.keys(errorObj).length > 0) {
            const err = new Error();
            err.filed = errorObj;
            Notify.error({ title: '提交失败!', description: '请确认提交表单是否正确!' });
            throw err;
          }
          Notify.success({
            title: '提交成功!',
            description: `表单提交成功,内容为:${current.select},将自动填充初始化值!`,
          });
        }}
        fields={{
          select: {
            initialValue: 0,
            children: (
              <CustomSelect option={[
                { label: '请选择', value: 0 },
                { label: '经济舱', value: 1 },
                { label: '豪华经济舱', value: 2 },
                { label: '商务舱', value: 3 },
                { label: '头等舱', value: 4 },
              ]} />
            )
          },
        }}
      >
        {({ fields, state, canSubmit }) => {
          return (
            <div>
              <Row>
                <Col style={{ maxWidth: 300 }}>{fields.select}</Col>
              </Row>
              <Row>
                <Col fixed>
                  <Button disabled={!canSubmit()} type="primary" htmlType="submit">提交</Button>
                </Col>
              </Row>
            </div>
          )
        }}
      </Form>
    </div>
  )
}

FormItem 竖排

对组件 FormItem 竖排展示示例。

⚠️ 注意:FormItem 组件只在 Form 组件中使用,在 @v4.10.4+ 以上版本可以当普通 form 使用。

import React from 'react';
import { Form, FormItem, Button, Input } from 'uiw';

export default function Demo() {
  const [formData, setFormData] = React.useState({});
  const handleSubmit = (_, e) => {
    e && e.preventDefault();
    const fData = new FormData(e.target);
    const data = {};
    fData.forEach((value, key) => { data[key] = value; });
    setFormData(data);
  }
  return (
    <Form onSubmit={handleSubmit} onReset={() => setFormData({})}>
      <FormItem
        label="可选字段"
        labelFor="item-basic-input"
        help={<span>在上面的字段中输入一个值</span>}
      >
        <Input id="item-basic-input" name="basic" type="text"/>
      </FormItem>
      <FormItem
        label="用户名"
        labelFor="item-username-input"
        help={(!formData.username || formData.username.length < 8) ? "用户名长度至少为8个字符串。" : "用户名正确 √ "}
        hasError={(!formData.username || formData.username.length < 8)}
      >
        <Input id="item-username-input" name="username" type="text"/>
      </FormItem>
      <FormItem>
        <Button type="success" htmlType="submit"> Submit </Button>
        <Button type="light" htmlType="reset"> Reset </Button>
      </FormItem>
      <pre>
      {JSON.stringify(formData, null, 2)}
      </pre>
    </Form>
  );
}

FormItem 横排

对组件 FormItem 横排展示示例。

⚠️ 注意:FormItem 组件只在 Form 组件中使用,在 @v4.10.4+ 以上版本可以当普通 form 使用。

import React from 'react';
import { Form, FormItem, Input } from 'uiw';

export default function Demo() {
  return (
    <Form>
      <FormItem
        inline={true}
        label="可选字段"
        labelFor="basic-input-inline"
        help={<span>在上面的字段中输入一个值</span>}
        onChange={() => {
          console.log('TEST::');
        }}
      >
        <Input id="basic-input-inline" name="basic" type="text"/>
      </FormItem>
      <FormItem
        inline={true}
        label="用户名"
        labelFor="username-input-inline"
        labelClassName="username"
        help="用户名长度至少为8个字符串。"
        hasError={true}
      >
        <Input id="username-input-inline" name="username" type="text"/>
      </FormItem>
    </Form>
  )
}

Form

参数 说明 类型 默认值
fields 设置字段 object -
children 回调 {fields, state, canSubmit, resetForm} function -
onSubmit 提交表单时调用 (state: FormSubmitProps, event: React.FormEvent) => any -
afterSubmit @3.0.0+ 提交回调 {initial, current} function({ initial, current }) -
onChange 表单发生改变回调函数 {initial, current} function({ initial, current }) -
onSubmitError 调用 onSubmit 抛出的任何错误。从字段名称返回对象映射。 function -
resetOnSubmit onSubmit 成功后将表单重置为其初始状态。 bool true
ref 返回form各种内部函数,可用于主动触发事件 Ref -

fields

{
  firstName: {
    initialValue: '王',
    inline: true,
    label: '姓',
    labelClassName: 'fieldLabel',
    labelStyle: { width: 60 },
    // 验证,通过 `canSubmit()` 方法获得,提交按钮是否被禁用
    validator: (currentValue) => {},
    help: '帮助提示信息!',
    children: <Input type="number" />
  },
}

ref

const form = useRef()
render(<Form ref={form}/>)
form.current.onSubmit()                                   // 提交表单
form.current.resetForm()                                  // 重置form
const fieldValues = form.current.getFieldValues()         // 获取所有 field的 value对象
const error = form.current.getError()                     // 获取所有提交时验证错误
form.current.setFields({ /** [fieldName]: value **/  })   // 设置表单的值,覆盖 form所有 field的值
form.current.setFieldValue(fieldName, value)              // 对单个 field设置 value,如果 value为数组请自行深度拷贝后传值,以免破坏原数组

FormItem

参数 说明 类型 默认值
label 表单标题展示 string -
required 表单是否必填标识 boolean -
labelClassName 表单标题样式名称 string -
labelStyle 表单标题样式 object -
labelFor 列的宽度相对于同一网格中其他列的比率 number -
help 提示信息 ReactNode -
hasError 如果为true,则应用错误CSS。转动边框并帮助文字变红。 number -