Form 表单

如果项目中使用的是 1.x 版本的基础组件(@alifd/next),请在左侧导航顶部切换组件版本。

安装方法

  1. 在命令行中执行以下命令npm install @icedesign/base@latest -S

开发指南

何时使用

表单校验、数据提交操作是用到。 Form表单包含了布局、数据获取、校验功能等功能, 其中 数据获取校验功能 需要配合 Field 使用才能发挥。

注意事项

  • 组件不要使用关键字 nodeName 作为 name、id

  • 使用了 Field init 过的组件,请勿在组件上面直接定义 ref value onChange 事件。

  • Form 默认使用 size=medium, 并且会控制FormItem内所有组件的size。 如果想修改组件的size <FormItem size="small" >

  • 在垂直表单中如果文字(一般 <p> 标签)或者组件向上偏离,可以通过 className="next-form-text-align" 辅助调整

API

表单

参数说明类型默认值
prefix样式前缀String'next-'
direction表单展示方向可选值:'hoz'(水平)'ver'(垂直)Enum'ver'
size单个FormItem的size自定义,优先级高于Form的size, 并且当组件与 FormItem 一起使用时,组件自身设置 size 属性无效。可选值:'large'(大)'medium'(中)'small'(小)Enum'medium'
labelAlign标签的位置可选值:'top'(上)'left'(左)'inset'(内)Enum'left'
labelTextAlign标签的左右对齐方式可选值:'''left'(左)'right'(右)Enum-
fieldnew Field(this) 初始化后,直接传给 Form 即可 用到表单校验则不可忽略此项any-
onSubmitform内有 htmlType="submit" 的元素的时候会触发签名:Function() => voidFunctionfunction(e) { e.preventDefault(); }
children子元素any-
className扩展classString-
style自定义内联样式Object-

Form.Item

参数说明类型默认值
prefix样式前缀String'next-'
labellabel 标签的文本ReactNode-
labelCollabel 标签布局,通 <Col> 组件,设置 span offset 值,如 {span: 8, offset: 16},该项仅在垂直表单有效Object-
help提示信息,如不设置,则会根据校验规则自动生成. 如果设置会受控(ps: 可以利用这点自定义错误位置,详细看demo自定义错误)ReactNode-
validateStatus校验状态,如不设置,则会根据校验规则自动生成可选值:'''success'(成功)'error'(失败)'loading'(校验中)Enum-
hasFeedback配合 validateStatus 属性使用,是否展示校验状态图标, 目前只有Input支持Booleanfalse
wrapperCol需要为输入控件设置布局样式时,使用该属性,用法同 labelColObject-
style自定义内联样式Object-
extra额外的提示信息,和 help 类似,当需要错误信息和提示文案同时出现时,可以使用这个。 位于错误信息后面ReactNode-
size单个FormItem的size自定义,优先级高于Form的size, 并且当组件与 FormItem 一起使用时,组件自身设置 size 属性无效。可选值:'', 'large', 'small', 'medium'Enum-
className扩展classString-

代码示例

自定义布局

配合 Row Col 控制表单内元素布局标签位置:上、左

Form 表单 - 图1

查看源码在线预览

  1. import {
  2. Form,
  3. Input,
  4. Switch,
  5. Grid,
  6. Button,
  7. Icon,
  8. Balloon,
  9. Field
  10. } from "@icedesign/base";
  11. const { Row, Col } = Grid;
  12. const FormItem = Form.Item;
  13. const style = {
  14. padding: "20px",
  15. background: "#F7F8FA",
  16. margin: "20px"
  17. };
  18. const formItemLayout = {
  19. labelCol: { span: 8 },
  20. wrapperCol: { span: 16 }
  21. };
  22. const label = (
  23. <span>
  24. 名称:<Balloon
  25. type="primary"
  26. trigger={<Icon type="prompt" size="small" />}
  27. closable={false}
  28. >
  29. blablablablablablablabla
  30. </Balloon>
  31. </span>
  32. );
  33. class Demo extends React.Component {
  34. constructor(props, context) {
  35. super(props, context);
  36. this.field = new Field(this);
  37. }
  38. render() {
  39. const { init, getValue } = this.field;
  40. return (
  41. <div>
  42. <h3>标签位置</h3>
  43. <Switch
  44. checkedChildren="左"
  45. unCheckedChildren="上"
  46. {...init("labelAlign", { initValue: false })}
  47. />
  48. <Form
  49. labelAlign={!getValue("labelAlign") ? "top" : "left"}
  50. style={style}
  51. >
  52. <Row>
  53. <Col>
  54. <FormItem {...formItemLayout} label={label} required>
  55. <Input placeholder="请输入搜索名称" />
  56. </FormItem>
  57. <FormItem {...formItemLayout} label="较长搜索名称:">
  58. <Input placeholder="请输入搜索名称" />
  59. </FormItem>
  60. <FormItem {...formItemLayout} label="搜索名称:">
  61. <Input placeholder="请输入搜索名称" />
  62. </FormItem>
  63. </Col>
  64. <Col>
  65. <FormItem {...formItemLayout} label="搜索名称:">
  66. <Input placeholder="请输入搜索名称" />
  67. </FormItem>
  68. <FormItem {...formItemLayout} label="较长搜索名称:">
  69. <Input placeholder="请输入搜索名称" />
  70. </FormItem>
  71. <FormItem {...formItemLayout} label="搜索名称:">
  72. <Input placeholder="请输入搜索名称" />
  73. </FormItem>
  74. </Col>
  75. <Col>
  76. <FormItem {...formItemLayout} label="搜索名称:">
  77. <Input placeholder="请输入搜索名称" />
  78. </FormItem>
  79. <FormItem {...formItemLayout} label="较长搜索名称:">
  80. <Input placeholder="请输入搜索名称" />
  81. </FormItem>
  82. <FormItem {...formItemLayout} label="搜索名称:">
  83. <Input placeholder="请输入搜索名称" />
  84. </FormItem>
  85. </Col>
  86. </Row>
  87. <Row>
  88. <Col style={{ textAlign: "right" }}>
  89. <Button type="primary" style={{ marginRight: "5px" }}>
  90. 搜索
  91. </Button>
  92. <Button>清除条件</Button>
  93. </Col>
  94. </Row>
  95. </Form>
  96. </div>
  97. );
  98. }
  99. }
  100. ReactDOM.render(<Demo />, mountNode);

固定宽度

如果组件比较靠上,可以用 class=next-form-text-align 做调整拉伸浏览器的时候label宽度不变。

Form 表单 - 图2

查看源码在线预览

  1. import { Form, Input, Button, Checkbox, Field } from "@icedesign/base";
  2. const FormItem = Form.Item;
  3. class Demo extends React.Component {
  4. constructor(props) {
  5. super(props);
  6. this.field = new Field(this);
  7. }
  8. handleSubmit() {
  9. console.log("收到表单值:", this.field.getValues());
  10. }
  11. render() {
  12. const init = this.field.init;
  13. const formItemLayout = {
  14. labelCol: {
  15. fixedSpan: 10
  16. },
  17. wrapperCol: {
  18. span: 14
  19. }
  20. };
  21. return (
  22. <Form direction="ver" field={this.field}>
  23. <FormItem label="用户名:" {...formItemLayout}>
  24. <p className="next-form-text-align">固定名称</p>
  25. </FormItem>
  26. <FormItem label="密码:" required {...formItemLayout}>
  27. <Input
  28. htmlType="password"
  29. {...init("pass")}
  30. placeholder="请输入密码"
  31. />
  32. </FormItem>
  33. <FormItem label="备注:" {...formItemLayout} help="随便写点什么">
  34. <Input multiple placeholder="随便写" {...init("remark")} />
  35. </FormItem>
  36. <FormItem label=" " {...formItemLayout}>
  37. <Checkbox {...init("agreement")}>同意</Checkbox>
  38. </FormItem>
  39. <FormItem label=" " {...formItemLayout}>
  40. <Button type="primary" onClick={this.handleSubmit.bind(this)}>
  41. 确定
  42. </Button>
  43. </FormItem>
  44. </Form>
  45. );
  46. }
  47. }
  48. ReactDOM.render(<Demo />, mountNode);

标签inset模式

通过设置labelAlign="inset" (只适用于Input、Select组件,其他组件不适用)

Form 表单 - 图3

查看源码在线预览

  1. import { Form, Input, Select } from "@icedesign/base";
  2. const FormItem = Form.Item;
  3. const formItemLayout = {
  4. labelCol: { span: 4 },
  5. wrapperCol: { span: 20 }
  6. };
  7. ReactDOM.render(
  8. <div>
  9. <h3>垂直</h3>
  10. <Form labelAlign="inset" style={{ maxWidth: "500px" }}>
  11. <FormItem {...formItemLayout} label="账户:">
  12. <Input placeholder="请输入账户名" id="userName" name="userName" />
  13. </FormItem>
  14. <FormItem {...formItemLayout} label="密码:">
  15. <Input
  16. htmlType="password"
  17. placeholder="请输入密码"
  18. id="password"
  19. name="password"
  20. />
  21. </FormItem>
  22. <FormItem {...formItemLayout} label="密码:" validateStatus="error">
  23. <Input
  24. htmlType="password"
  25. placeholder="请输入密码"
  26. id="password"
  27. name="password"
  28. />
  29. </FormItem>
  30. <FormItem {...formItemLayout} label="大小:">
  31. <Select>
  32. <div value="small">small</div>
  33. <div value="medium">medium</div>
  34. <div value="large">large</div>
  35. </Select>
  36. </FormItem>
  37. </Form>
  38. <h2>水平</h2>
  39. <Form labelAlign="inset" direction="hoz">
  40. <FormItem label="账户:">
  41. <Input placeholder="请输入账户名" id="userName" name="userName" />
  42. </FormItem>
  43. <FormItem label="密码:">
  44. <Input
  45. htmlType="password"
  46. placeholder="请输入密码"
  47. id="password"
  48. name="password"
  49. />
  50. </FormItem>
  51. <FormItem label="密码:" validateStatus="error" help="密码不能为空">
  52. <Input
  53. htmlType="password"
  54. placeholder="请输入密码"
  55. id="password"
  56. name="password"
  57. />
  58. </FormItem>
  59. <FormItem label="大小:">
  60. <Select style={{ width: 150 }}>
  61. <div value="small">small</div>
  62. <div value="medium">medium</div>
  63. <div value="large">large</div>
  64. </Select>
  65. </FormItem>
  66. </Form>
  67. </div>,
  68. mountNode
  69. );

表单组合

展示和表单相关的其他组件。

Form 表单 - 图4

查看源码在线预览

  1. import {
  2. Form,
  3. Input,
  4. Button,
  5. Checkbox,
  6. Select,
  7. Range,
  8. Balloon,
  9. DatePicker,
  10. TimePicker,
  11. NumberPicker,
  12. Field,
  13. Switch,
  14. Upload,
  15. Grid
  16. } from "@icedesign/base";
  17. const { RangePicker } = DatePicker;
  18. const { Row, Col } = Grid;
  19. const FormItem = Form.Item;
  20. class Demo extends React.Component {
  21. field = new Field(this);
  22. handleSubmit(e) {
  23. e.preventDefault();
  24. console.log("收到表单值:", this.field.getValues());
  25. this.field.validate();
  26. }
  27. getValueFromFile(e) {
  28. if (Array.isArray(e)) {
  29. return e;
  30. }
  31. return e && e.fileList;
  32. }
  33. render() {
  34. const init = this.field.init;
  35. const formItemLayout = {
  36. labelCol: { span: 6 },
  37. wrapperCol: { span: 14 }
  38. };
  39. return (
  40. <Form field={this.field}>
  41. <FormItem label="密码:" {...formItemLayout}>
  42. <Balloon
  43. trigger={<Input htmlType="password" />}
  44. align="r"
  45. closable={false}
  46. triggerType="hover"
  47. >
  48. input password
  49. </Balloon>
  50. </FormItem>
  51. <FormItem label="我是标题:" {...formItemLayout}>
  52. <p className="next-form-text-align">唧唧复唧唧木兰当户织呀</p>
  53. <p className="next-form-text-align">
  54. <a href="#">链接文字</a>
  55. </p>
  56. </FormItem>
  57. <FormItem label="NumberPicker 数字输入框:" {...formItemLayout}>
  58. <NumberPicker
  59. min={1}
  60. max={10}
  61. {...init("numberPicker", { initValue: 3 })}
  62. />
  63. <span> 台机器</span>
  64. </FormItem>
  65. <FormItem label="Switch 开关:" {...formItemLayout} required>
  66. <Switch
  67. {...init("switch", { valueName: "checked", initValue: true })}
  68. />
  69. </FormItem>
  70. <FormItem label="Range 滑动输入条:" {...formItemLayout} required>
  71. <Range
  72. defaultValue={30}
  73. scales={[0, 100]}
  74. style={{ marginTop: "10px" }}
  75. marks={[0, 100]}
  76. {...init("range")}
  77. />
  78. </FormItem>
  79. <FormItem label="Select 选择器:" {...formItemLayout} required>
  80. <Select style={{ width: 200 }} {...init("select")}>
  81. <Option value="jack">jack</Option>
  82. <Option value="lucy">lucy</Option>
  83. <Option value="disabled" disabled>
  84. disabled
  85. </Option>
  86. <Option value="hugohua">hugohua</Option>
  87. </Select>
  88. </FormItem>
  89. <FormItem
  90. label="DatePicker 日期选择框:"
  91. labelCol={{ span: 6 }}
  92. required
  93. >
  94. <Row>
  95. <FormItem style={{ marginRight: 10 }}>
  96. <DatePicker {...init("startDate")} />
  97. </FormItem>
  98. <FormItem>
  99. <DatePicker {...init("endDate")} />
  100. </FormItem>
  101. </Row>
  102. </FormItem>
  103. <FormItem
  104. label="RangePicker 范围选择框:"
  105. labelCol={{ span: 6 }}
  106. required
  107. >
  108. <RangePicker {...init("rangeDate")} />
  109. </FormItem>
  110. <FormItem label="TimePicker 时间选择器:" {...formItemLayout} required>
  111. <TimePicker
  112. {...init("time", {
  113. getValueFromEvent: time => {
  114. time =
  115. time &&
  116. time.toLocaleTimeString("zh-CN", {
  117. hour12: false
  118. });
  119. return time;
  120. }
  121. })}
  122. />
  123. </FormItem>
  124. <FormItem
  125. className="next-form-text-align"
  126. label="Checkbox 多选框:"
  127. {...formItemLayout}
  128. >
  129. <Checkbox {...init("checkbox1")}>选项一 </Checkbox>
  130. <Checkbox {...init("checkbox2")}>选项二 </Checkbox>
  131. <Checkbox disabled {...init("checkbox3")}>
  132. 选项三(不可选)
  133. </Checkbox>
  134. </FormItem>
  135. <FormItem label="logo图:" {...formItemLayout}>
  136. <Upload
  137. action="/upload.do"
  138. listType="text"
  139. onChange={this.handleUpload}
  140. {...init("upload", {
  141. valueName: "fileList",
  142. getValueFromEvent: this.getValueFromFile
  143. })}
  144. >
  145. <Button type="primary" style={{ margin: "0 0 10px" }}>
  146. 上传文件
  147. </Button>
  148. </Upload>
  149. </FormItem>
  150. <Row style={{ marginTop: 24 }}>
  151. <Col offset="6">
  152. <Button type="primary" onClick={this.handleSubmit.bind(this)}>
  153. 确定
  154. </Button>
  155. </Col>
  156. </Row>
  157. </Form>
  158. );
  159. }
  160. }
  161. ReactDOM.render(<Demo />, mountNode);

Item 嵌套

FormItem嵌套

Form 表单 - 图5

查看源码在线预览

  1. import { Form, Input, Button, Field, Grid } from "@icedesign/base";
  2. const { Row, Col } = Grid;
  3. const FormItem = Form.Item;
  4. class Demo extends React.Component {
  5. field = new Field(this);
  6. handleSubmit(e) {
  7. e.preventDefault();
  8. console.log("收到表单值:", this.field.getValues());
  9. }
  10. normFile(e) {
  11. if (Array.isArray(e)) {
  12. return e;
  13. }
  14. return e && e.fileList;
  15. }
  16. render() {
  17. const init = this.field.init;
  18. const formItemLayout = {
  19. labelCol: { span: 6 },
  20. wrapperCol: { span: 14 }
  21. };
  22. const insetLayout = {
  23. labelCol: { fixedSpan: 4 }
  24. };
  25. return (
  26. <Form field={this.field}>
  27. <FormItem id="control-input" label="输入框:" {...formItemLayout}>
  28. <Row>
  29. <Col>
  30. <FormItem
  31. label="内嵌模式"
  32. required={false}
  33. labelAlign="inset"
  34. {...insetLayout}
  35. >
  36. <Input
  37. placeholder="Please enter..."
  38. style={{ width: "100%" }}
  39. {...init("firstname", {
  40. rules: [{ required: true, trigger: "onBlur" }]
  41. })}
  42. />
  43. </FormItem>
  44. </Col>
  45. <Col>
  46. <FormItem
  47. label="内嵌模式"
  48. required={false}
  49. labelAlign="inset"
  50. {...insetLayout}
  51. >
  52. <Input
  53. placeholder="need onChange"
  54. style={{ width: "100%" }}
  55. {...init("secondname", {
  56. rules: [{ required: true }]
  57. })}
  58. />
  59. </FormItem>
  60. </Col>
  61. </Row>
  62. </FormItem>
  63. <FormItem label="银行账户:" {...formItemLayout}>
  64. <Row>
  65. <Col>
  66. <FormItem>
  67. <Input
  68. {...init("A", {
  69. rules: [{ required: true, trigger: "onBlur" }]
  70. })}
  71. />
  72. </FormItem>
  73. </Col>
  74. <Col>
  75. <FormItem>
  76. <Input
  77. {...init("B", {
  78. rules: [{ required: true, trigger: "onBlur" }]
  79. })}
  80. />
  81. </FormItem>
  82. </Col>
  83. <Col>
  84. <FormItem>
  85. <Input
  86. {...init("C", {
  87. rules: [{ required: true, trigger: "onBlur" }]
  88. })}
  89. />
  90. </FormItem>
  91. </Col>
  92. <Col>
  93. <FormItem>
  94. <Input
  95. {...init("D", {
  96. rules: [{ required: true, trigger: "onBlur" }]
  97. })}
  98. />
  99. </FormItem>
  100. </Col>
  101. </Row>
  102. </FormItem>
  103. <Row style={{ marginTop: 24 }}>
  104. <Col offset="6">
  105. <Button type="primary" onClick={this.handleSubmit.bind(this)}>
  106. 确定
  107. </Button>
  108. </Col>
  109. </Row>
  110. </Form>
  111. );
  112. }
  113. }
  114. ReactDOM.render(<Demo />, mountNode);

回车提交

需要Form里面有 htmlType="submit" 的元素

Form 表单 - 图6

查看源码在线预览

  1. import { Form, Input, Button } from "@icedesign/base";
  2. const FormItem = Form.Item;
  3. class Demo extends React.Component {
  4. onSubmit(e) {
  5. e.preventDefault();
  6. console.log("onsubmit");
  7. }
  8. render() {
  9. return (
  10. <Form onSubmit={this.onSubmit.bind(this)}>
  11. <FormItem>
  12. <Input placeholder="回车也能触发onSubmit" />
  13. </FormItem>
  14. <Button htmlType="submit">submit</Button>
  15. </Form>
  16. );
  17. }
  18. }
  19. ReactDOM.render(<Demo />, mountNode);

手动设置错误

如果需要自己控制错误位置,可以让help="" 然后自己放置展示错误的地方在redux中结合 componentWillReceiveProps setErrors 使用

Form 表单 - 图7

查看源码在线预览

  1. import { Form, Input, Button, Field } from "@icedesign/base";
  2. import { combineReducers, createStore } from "redux";
  3. import { Provider, connect } from "react-redux";
  4. const initState = {
  5. values: { email: "", username: "xiachi" },
  6. errors: {}
  7. };
  8. function formReducer(state = initState, action) {
  9. switch (action.type) {
  10. case "save_fields":
  11. return {
  12. ...state,
  13. values: {
  14. ...state.values,
  15. ...action.values
  16. }
  17. };
  18. case "set_errors":
  19. return {
  20. ...state,
  21. errors: {
  22. ...state.errors,
  23. ...action.errors
  24. }
  25. };
  26. default:
  27. return state;
  28. }
  29. }
  30. class FormDemo extends React.Component {
  31. static propTypes = {
  32. formData: React.PropTypes.object,
  33. email: React.PropTypes.object,
  34. dispatch: React.PropTypes.func
  35. };
  36. constructor(props) {
  37. super(props);
  38. this.field = new Field(this, {
  39. onChange: (name, value) => {
  40. console.log("onChange", name, value, this.field.getError(name));
  41. this.props.dispatch({
  42. type: "save_fields",
  43. values: {
  44. [name]: value
  45. }
  46. });
  47. this.props.dispatch({
  48. type: "set_errors",
  49. errors: {
  50. [name]: this.field.getError(name)
  51. }
  52. });
  53. }
  54. });
  55. }
  56. componentWillReceiveProps(nextProps) {
  57. this.field.setValues(nextProps.formData.values);
  58. this.field.setErrors(nextProps.formData.errors);
  59. }
  60. setEmail() {
  61. this.props.dispatch({
  62. type: "save_fields",
  63. values: {
  64. email: "qq@gmail.com"
  65. }
  66. });
  67. }
  68. setName() {
  69. this.props.dispatch({
  70. type: "save_fields",
  71. values: {
  72. username: "frank"
  73. }
  74. });
  75. }
  76. setError() {
  77. this.props.dispatch({
  78. type: "set_errors",
  79. errors: {
  80. email: "来自远程的错误消息"
  81. }
  82. });
  83. }
  84. setErrors() {
  85. this.props.dispatch({
  86. type: "set_errors",
  87. errors: {
  88. email: "来自远程的错误消息A",
  89. username: "来自远程的错误消息B"
  90. }
  91. });
  92. }
  93. render() {
  94. const init = this.field.init;
  95. return (
  96. <Form field={this.field}>
  97. <Form.Item>
  98. <Input
  99. {...init("email", {
  100. initValue: this.props.formData.values.email,
  101. rules: [{ required: true, type: "email" }]
  102. })}
  103. />
  104. </Form.Item>
  105. <Form.Item help="">
  106. <Input
  107. {...init("username", {
  108. initValue: this.props.formData.values.username,
  109. rules: [{ required: true, message: "不能为空" }]
  110. })}
  111. />
  112. <p style={{ color: "blue" }}>{this.field.getError("username")}</p>
  113. </Form.Item>
  114. <p>email: {this.props.email && this.props.email.value}</p>
  115. <Button onClick={this.setEmail.bind(this)}>setEmail</Button>
  116. <Button onClick={this.setName.bind(this)}>setName</Button>
  117. <Button onClick={this.setError.bind(this)}>setError</Button>
  118. <Button onClick={this.setErrors.bind(this)}>setErrors</Button>
  119. <Button onClick={() => this.field.reset()}>reset</Button>
  120. </Form>
  121. );
  122. }
  123. }
  124. const ReduxFormDemo = connect(state => {
  125. return {
  126. formData: state.formReducer
  127. };
  128. })(FormDemo);
  129. const store = createStore(
  130. combineReducers({
  131. formReducer
  132. })
  133. );
  134. ReactDOM.render(
  135. <Provider store={store}>
  136. <div>
  137. <ReduxFormDemo />
  138. </div>
  139. </Provider>,
  140. mountNode
  141. );

配合redux使用

redux中结合 componentWillReceiveProps setValues 使用

Form 表单 - 图8

查看源码在线预览

  1. import { Form, Input, Button, Checkbox, Field } from "@icedesign/base";
  2. import { combineReducers, createStore } from "redux";
  3. import { Provider, connect } from "react-redux";
  4. const CheckboxGroup = Checkbox.Group;
  5. function formReducer(
  6. state = { email: "", username: "xiachi", fruit: ["apple"] },
  7. action
  8. ) {
  9. switch (action.type) {
  10. case "save_fields":
  11. return {
  12. ...state,
  13. ...action.payload
  14. };
  15. default:
  16. return state;
  17. }
  18. }
  19. class FormDemo extends React.Component {
  20. static propTypes = {
  21. formData: React.PropTypes.object,
  22. email: React.PropTypes.object,
  23. dispatch: React.PropTypes.func
  24. };
  25. constructor(props) {
  26. super(props);
  27. this.field = new Field(this, {
  28. onChange: (name, value) => {
  29. console.log("onChange", name, value, this.field.getValues());
  30. this.props.dispatch({
  31. type: "save_fields",
  32. payload: {
  33. [name]: value
  34. }
  35. });
  36. /* 方法2, 所有值全部更新
  37. this.props.dispatch({
  38. type: 'save_fields',
  39. payload: this.field.getValues()
  40. });
  41. */
  42. }
  43. });
  44. }
  45. componentWillReceiveProps(nextProps) {
  46. this.field.setValues(nextProps.formData);
  47. }
  48. setEmail() {
  49. this.props.dispatch({
  50. type: "save_fields",
  51. payload: {
  52. email: "qq@gmail.com"
  53. }
  54. });
  55. }
  56. setName() {
  57. this.props.dispatch({
  58. type: "save_fields",
  59. payload: {
  60. username: "frank"
  61. }
  62. });
  63. }
  64. setGroup() {
  65. this.props.dispatch({
  66. type: "save_fields",
  67. payload: {
  68. fruit: ["pear"]
  69. }
  70. });
  71. }
  72. render() {
  73. const init = this.field.init;
  74. return (
  75. <Form field={this.field}>
  76. <Form.Item>
  77. <Input
  78. {...init("email", {
  79. rules: [{ required: true, message: "不能为空!" }]
  80. })}
  81. />
  82. </Form.Item>
  83. <Form.Item>
  84. <Input
  85. defaultValue={this.props.formData.username}
  86. {...init("username", {
  87. rules: [{ required: true, message: "不能为空" }]
  88. })}
  89. />
  90. </Form.Item>
  91. <Form.Item>
  92. <CheckboxGroup
  93. dataSource={[
  94. { label: "苹果", value: "apple" },
  95. { label: "梨", value: "pear" }
  96. ]}
  97. defaultValue={this.props.formData.fruit}
  98. {...init("fruit", {
  99. rules: [{ required: true, type: "array", message: "不能为空" }]
  100. })}
  101. />
  102. </Form.Item>
  103. <p>email: {this.props.email && this.props.email.value}</p>
  104. <Button onClick={this.setEmail.bind(this)}>setEmail</Button>
  105. <Button onClick={this.setName.bind(this)}>setName</Button>
  106. <Button onClick={this.setGroup.bind(this)}>setGroup</Button>
  107. </Form>
  108. );
  109. }
  110. }
  111. const ReduxFormDemo = connect(state => {
  112. return {
  113. formData: state.formReducer
  114. };
  115. })(FormDemo);
  116. const store = createStore(
  117. combineReducers({
  118. formReducer
  119. })
  120. );
  121. ReactDOM.render(
  122. <Provider store={store}>
  123. <div>
  124. <ReduxFormDemo />
  125. </div>
  126. </Provider>,
  127. mountNode
  128. );

尺寸+标签

labelTextAlign 文字左右对齐方式labelAlign label方位size 会强制设置 FormItem 下的所有组件的size

Form 表单 - 图9

查看源码在线预览

  1. import { Form, Input, Select, Field } from "@icedesign/base";
  2. const FormItem = Form.Item;
  3. const formItemLayout = {
  4. labelCol: { span: 4 },
  5. wrapperCol: { span: 20 }
  6. };
  7. class Demo extends React.Component {
  8. constructor(props, context) {
  9. super(props, context);
  10. this.field = new Field(this);
  11. }
  12. render() {
  13. const { init, getValue } = this.field;
  14. return (
  15. <div>
  16. <Form direction="hoz" className="demo-ctl" field={this.field}>
  17. <FormItem label="大小:">
  18. <Select {...init("size", { initValue: "medium" })}>
  19. <div value="small">small</div>
  20. <div value="medium">medium</div>
  21. <div value="large">large</div>
  22. </Select>
  23. </FormItem>
  24. <FormItem label="标签对齐方式:" help="inset只能用于 Input、Select">
  25. <Select {...init("labelAlign", { initValue: "left" })}>
  26. <div value="top">标签在上 top</div>
  27. <div value="left">标签在左 left</div>
  28. <div value="inset">标签在内 inset</div>
  29. </Select>
  30. </FormItem>
  31. <FormItem label="标签左右对齐方式:">
  32. <Select {...init("labelTextAlign")}>
  33. <div value="">系统自动</div>
  34. <div value="left">left</div>
  35. <div value="right">right</div>
  36. </Select>
  37. </FormItem>
  38. </Form>
  39. <h3>垂直</h3>
  40. <Form
  41. field={this.field}
  42. size={getValue("size")}
  43. labelAlign={getValue("labelAlign")}
  44. labelTextAlign={getValue("labelTextAlign")}
  45. style={{ maxWidth: "500px" }}
  46. >
  47. <FormItem {...formItemLayout} label="账户:">
  48. <Input placeholder="请输入账户名" id="userName" name="userName" />
  49. </FormItem>
  50. <FormItem {...formItemLayout} required label="密码:">
  51. <Input
  52. htmlType="password"
  53. placeholder="请输入密码"
  54. id="password"
  55. name="password"
  56. />
  57. </FormItem>
  58. <FormItem {...formItemLayout} label="密码:" validateStatus="error">
  59. <Input
  60. htmlType="password"
  61. placeholder="请输入密码"
  62. id="password"
  63. name="password"
  64. />
  65. </FormItem>
  66. <FormItem {...formItemLayout} label="大小:">
  67. <Select style={{ width: "100%" }} {...init("size")}>
  68. <div value="small">small</div>
  69. <div value="medium">medium</div>
  70. <div value="large">large</div>
  71. </Select>
  72. </FormItem>
  73. </Form>
  74. <h3>水平</h3>
  75. <Form
  76. size={getValue("size")}
  77. direction="hoz"
  78. labelAlign={getValue("labelAlign")}
  79. >
  80. <FormItem label="账户:">
  81. <Input placeholder="请输入账户名" id="userName" name="userName" />
  82. </FormItem>
  83. <FormItem label="密码:">
  84. <Input
  85. htmlType="password"
  86. placeholder="请输入密码"
  87. id="password"
  88. name="password"
  89. />
  90. </FormItem>
  91. <FormItem label="密码:" validateStatus="error">
  92. <Input
  93. htmlType="password"
  94. placeholder="请输入密码"
  95. id="password"
  96. name="password"
  97. />
  98. </FormItem>
  99. <FormItem label="大小:">
  100. <Select style={{ width: "100%" }} {...init("size")}>
  101. <div value="small">small</div>
  102. <div value="medium">medium</div>
  103. <div value="large">large</div>
  104. </Select>
  105. </FormItem>
  106. </Form>
  107. </div>
  108. );
  109. }
  110. }
  111. ReactDOM.render(<Demo />, mountNode);
  1. .demo-ctl {
  2. background-color: #f1f1f1;
  3. padding: 10.0px;
  4. color: #0a7ac3;
  5. border-left: 4.0px solid #0d599a;
  6. }

校验

基本的表单校验例子。

Form 表单 - 图10

查看源码在线预览

  1. import { Form, Input, Button, Radio, Field } from "@icedesign/base";
  2. const { Item: FormItem } = Form;
  3. const { Group: RadioGroup } = Radio;
  4. class BasicDemo extends React.Component {
  5. constructor(props) {
  6. super(props);
  7. this.field = new Field(this);
  8. }
  9. handleReset(e) {
  10. e.preventDefault();
  11. this.field.reset();
  12. }
  13. handleSubmit(e) {
  14. e.preventDefault();
  15. this.field.validate((errors, values) => {
  16. if (errors) {
  17. console.log("Errors in form!!!");
  18. return;
  19. }
  20. console.log("Submit!!!");
  21. console.log(values);
  22. });
  23. }
  24. userExists(rule, value, callback) {
  25. if (!value) {
  26. callback();
  27. } else {
  28. setTimeout(() => {
  29. if (value === "frank") {
  30. callback([new Error("抱歉,该用户名已被占用。")]);
  31. } else {
  32. callback();
  33. }
  34. }, 1000);
  35. }
  36. }
  37. checkPass(rule, value, callback) {
  38. const { validate } = this.field;
  39. if (value) {
  40. validate(["rePasswd"]);
  41. }
  42. callback();
  43. }
  44. checkPass2(rule, value, callback) {
  45. const { getValue } = this.field;
  46. if (value && value !== getValue("passwd")) {
  47. callback("两次输入密码不一致!");
  48. } else {
  49. callback();
  50. }
  51. }
  52. render() {
  53. const { init, getError, getState } = this.field;
  54. const formItemLayout = {
  55. labelCol: {
  56. span: 6
  57. },
  58. wrapperCol: {
  59. span: 14
  60. }
  61. };
  62. return (
  63. <Form field={this.field}>
  64. <FormItem
  65. label="用户名:"
  66. {...formItemLayout}
  67. hasFeedback
  68. help={
  69. getState("name") === "validating"
  70. ? "校验中..."
  71. : (getError("name") || []).join(", ")
  72. }
  73. >
  74. <Input
  75. maxLength={20}
  76. hasLimitHint
  77. placeholder="实时校验,输入 frank 看看"
  78. {...init("name", {
  79. rules: [
  80. { required: true, min: 5, message: "用户名至少为 5 个字符" },
  81. { validator: this.userExists }
  82. ]
  83. })}
  84. />
  85. </FormItem>
  86. <FormItem label="邮箱:" {...formItemLayout} hasFeedback>
  87. <Input
  88. type="email"
  89. placeholder="onBlur 与 onChange 相结合"
  90. {...init("email", {
  91. rules: [
  92. { required: true, trigger: "onBlur" },
  93. {
  94. type: "email",
  95. message: <span>请输入正确的邮箱地址</span>,
  96. trigger: ["onBlur", "onChange"]
  97. }
  98. ]
  99. })}
  100. />
  101. </FormItem>
  102. <FormItem label="密码:" {...formItemLayout} hasFeedback>
  103. <Input
  104. htmlType="password"
  105. {...init("passwd", {
  106. rules: [
  107. { required: true, whitespace: true, message: "请填写密码" },
  108. { validator: this.checkPass.bind(this) }
  109. ]
  110. })}
  111. />
  112. </FormItem>
  113. <FormItem label="确认密码:" {...formItemLayout} hasFeedback>
  114. <Input
  115. htmlType="password"
  116. placeholder="两次输入密码保持一致"
  117. {...init("rePasswd", {
  118. rules: [
  119. {
  120. required: true,
  121. whitespace: true,
  122. message: "请再次输入密码"
  123. },
  124. {
  125. validator: this.checkPass2.bind(this)
  126. }
  127. ]
  128. })}
  129. />
  130. </FormItem>
  131. <FormItem label="性别:" hasFeedback {...formItemLayout}>
  132. <RadioGroup
  133. {...init("radio", {
  134. rules: [{ required: true, message: "请选择您的性别" }]
  135. })}
  136. >
  137. <Radio value="male">男</Radio>
  138. <Radio value="female">女</Radio>
  139. </RadioGroup>
  140. </FormItem>
  141. <FormItem label="备注:" {...formItemLayout}>
  142. <Input
  143. multiple
  144. maxLength={20}
  145. hasLimitHint
  146. placeholder="随便写"
  147. {...init("textarea", {
  148. rules: [{ required: true, message: "真的不打算写点什么吗?" }]
  149. })}
  150. />
  151. </FormItem>
  152. <FormItem wrapperCol={{ offset: 6 }}>
  153. <Button type="primary" onClick={this.handleSubmit.bind(this)}>
  154. 确定
  155. </Button>
  156. &nbsp;&nbsp;&nbsp;
  157. <Button onClick={this.handleReset.bind(this)}>重置</Button>
  158. </FormItem>
  159. </Form>
  160. );
  161. }
  162. }
  163. ReactDOM.render(<BasicDemo />, mountNode);

自定义校验规则

这里使用了 validation 的 validate(fields, callback) 方法,在对第一次输入的密码进行校验时会触发二次密码的校验。密码校验实例。

Form 表单 - 图11

查看源码在线预览

  1. import { Form, Input, Button, Dialog, Field, Grid } from "@icedesign/base";
  2. import classNames from "classnames";
  3. const { Row, Col } = Grid;
  4. const FormItem = Form.Item;
  5. function noop() {
  6. return false;
  7. }
  8. class Demo extends React.Component {
  9. constructor(props, context) {
  10. super(props, context);
  11. this.field = new Field(this);
  12. this.state = {
  13. passBarShow: false, // 是否显示密码强度提示条
  14. rePassBarShow: false,
  15. passStrength: "L", // 密码强度
  16. rePassStrength: "L",
  17. visible: false
  18. };
  19. }
  20. handleSubmit() {
  21. this.field.validate((errors, values) => {
  22. if (errors) {
  23. console.log("Errors in form!!!");
  24. return;
  25. }
  26. console.log("Submit!!!");
  27. console.log(values);
  28. this.setState({ visible: false });
  29. });
  30. }
  31. getPassStrenth(value, type) {
  32. if (value) {
  33. let strength;
  34. // 密码强度的校验规则自定义,这里只是做个简单的示例
  35. if (value.length < 6) {
  36. strength = "L";
  37. } else if (value.length <= 9) {
  38. strength = "M";
  39. } else {
  40. strength = "H";
  41. }
  42. if (type === "pass") {
  43. this.setState({ passBarShow: true, passStrength: strength });
  44. } else {
  45. this.setState({ rePassBarShow: true, rePassStrength: strength });
  46. }
  47. } else if (type === "pass") {
  48. this.setState({ passBarShow: false });
  49. } else {
  50. this.setState({ rePassBarShow: false });
  51. }
  52. }
  53. showDialog() {
  54. this.setState({ visible: true });
  55. }
  56. hideDialog() {
  57. this.setState({ visible: false });
  58. }
  59. checkPass(rule, value, callback) {
  60. const field = this.field;
  61. this.getPassStrenth(value, "pass");
  62. if (field.getValue("pass")) {
  63. field.validate(["rePass"], { force: true });
  64. }
  65. callback();
  66. }
  67. checkPass2(rule, value, callback) {
  68. const field = this.field;
  69. this.getPassStrenth(value, "rePass");
  70. if (value && value !== field.getValue("pass")) {
  71. callback("两次输入密码不一致!");
  72. } else {
  73. callback();
  74. }
  75. }
  76. renderPassStrengthBar(type) {
  77. const strength =
  78. type === "pass" ? this.state.passStrength : this.state.rePassStrength;
  79. const classSet = classNames({
  80. "pwd-strength": true,
  81. "pwd-strength-low": strength === "L",
  82. "pwd-strength-medium": strength === "M",
  83. "pwd-strength-high": strength === "H"
  84. });
  85. const level = {
  86. L: "低",
  87. M: "中",
  88. H: "高"
  89. };
  90. return (
  91. <div>
  92. <ul className={classSet}>
  93. <li className="pwd-strength-item pwd-strength-item-1" />
  94. <li className="pwd-strength-item pwd-strength-item-2" />
  95. <li className="pwd-strength-item pwd-strength-item-3" />
  96. <span>{level[strength]}</span>
  97. </ul>
  98. </div>
  99. );
  100. }
  101. render() {
  102. const init = this.field.init;
  103. return (
  104. <div>
  105. <Button type="primary" onClick={this.showDialog.bind(this)}>
  106. 修改密码
  107. </Button>
  108. <Dialog
  109. title="修改密码"
  110. visible={this.state.visible}
  111. onOk={this.handleSubmit.bind(this)}
  112. onCancel={this.hideDialog.bind(this)}
  113. onClose={this.hideDialog.bind(this)}
  114. >
  115. <Form field={this.field} style={{ width: 500 }}>
  116. <Row>
  117. <Col span="16">
  118. <FormItem
  119. label="密码:"
  120. labelCol={{ span: 10 }}
  121. wrapperCol={{ span: 14 }}
  122. >
  123. <Input
  124. htmlType="password"
  125. {...init("pass", {
  126. rules: [
  127. {
  128. required: true,
  129. whitespace: true,
  130. message: "请填写密码"
  131. },
  132. { validator: this.checkPass.bind(this) }
  133. ]
  134. })}
  135. onContextMenu={noop}
  136. onPaste={noop}
  137. onCopy={noop}
  138. onCut={noop}
  139. autoComplete="off"
  140. id="pass"
  141. />
  142. </FormItem>
  143. </Col>
  144. <Col span="8">
  145. {this.state.passBarShow
  146. ? this.renderPassStrengthBar("pass")
  147. : null}
  148. </Col>
  149. </Row>
  150. <Row>
  151. <Col span="16">
  152. <FormItem
  153. label="确认密码:"
  154. labelCol={{ span: 10 }}
  155. wrapperCol={{ span: 14 }}
  156. >
  157. <Input
  158. htmlType="password"
  159. {...init("rePass", {
  160. rules: [
  161. {
  162. required: true,
  163. whitespace: true,
  164. message: "请再次输入密码"
  165. },
  166. {
  167. validator: this.checkPass2.bind(this)
  168. }
  169. ]
  170. })}
  171. onContextMenu={noop}
  172. onPaste={noop}
  173. onCopy={noop}
  174. onCut={noop}
  175. autoComplete="off"
  176. id="rePass"
  177. />
  178. </FormItem>
  179. </Col>
  180. <Col span="8">
  181. {this.state.rePassBarShow
  182. ? this.renderPassStrengthBar("rePass")
  183. : null}
  184. </Col>
  185. </Row>
  186. </Form>
  187. </Dialog>
  188. </div>
  189. );
  190. }
  191. }
  192. ReactDOM.render(<Demo />, mountNode);
  1. .pwd-strength {
  2. display: inline-block;
  3. margin-left: 8px;
  4. margin-top: 0;
  5. line-height: 32px;
  6. height: 32px;
  7. vertical-align: middle;
  8. }
  9. .pwd-strength-item {
  10. float: left;
  11. margin-right: 1px;
  12. margin-top: 12px;
  13. width: 19px;
  14. height: 8px;
  15. line-height: 8px;
  16. list-style: none;
  17. background-color: #f3f3f3;
  18. transition: all 0.3s cubic-bezier(0.645, 0.045, 0.355, 1);
  19. }
  20. .pwd-strength-item-1 {
  21. border-top-left-radius: 6px;
  22. border-bottom-left-radius: 6px;
  23. }
  24. .pwd-strength-item-2 {
  25. width: 20px;
  26. }
  27. .pwd-strength-item-3 {
  28. border-top-right-radius: 6px;
  29. border-bottom-right-radius: 6px;
  30. margin-right: 8px;
  31. }
  32. .pwd-strength-low .pwd-strength-item-1, .pwd-strength-medium .pwd-strength-item-1, .pwd-strength-high .pwd-strength-item-1 {
  33. background-color: #FAC450;
  34. }
  35. .pwd-strength-medium .pwd-strength-item-2, .pwd-strength-high .pwd-strength-item-2 {
  36. background-color: rgba(135, 208, 104, .6);
  37. filter:progid:DXImageTransform.Microsoft.gradient(startColorstr=#9987D068,endColorstr=#9987D068);
  38. }
  39. .pwd-strength-high .pwd-strength-item-3 {
  40. background-color: #87D068;
  41. }

校验其他组件

Select Radio DatePicker NumberPicker 。提供以下组件表单域的校验。

Form 表单 - 图12

查看源码在线预览

  1. import {
  2. Form,
  3. Button,
  4. Radio,
  5. Select,
  6. DatePicker,
  7. NumberPicker,
  8. Field
  9. } from "@icedesign/base";
  10. const FormItem = Form.Item;
  11. const RadioGroup = Radio.Group;
  12. class Demo extends React.Component {
  13. constructor(props) {
  14. super(props);
  15. this.field = new Field(this);
  16. }
  17. handleReset(e) {
  18. e.preventDefault();
  19. this.field.reset();
  20. }
  21. handleSubmit(e) {
  22. e.preventDefault();
  23. this.field.validate((errors, values) => {
  24. if (errors) {
  25. console.log("Errors in form!!!");
  26. return;
  27. }
  28. console.log("Submit!!!");
  29. console.log(values);
  30. });
  31. }
  32. checkBirthday(rule, value, callback) {
  33. console.log(value);
  34. if (value && value.getTime() >= Date.now()) {
  35. callback(new Error("你不可能在未来出生吧!"));
  36. } else {
  37. callback();
  38. }
  39. }
  40. checkPrime(rule, value, callback) {
  41. if (value !== 11) {
  42. callback(new Error("8~12之间的质数明明是11啊!"));
  43. } else {
  44. callback();
  45. }
  46. }
  47. render() {
  48. const init = this.field.init;
  49. const formItemLayout = {
  50. labelCol: {
  51. span: 6
  52. },
  53. wrapperCol: {
  54. span: 14
  55. }
  56. };
  57. return (
  58. <Form field={this.field}>
  59. <FormItem label="国籍:" {...formItemLayout}>
  60. <Select
  61. placeholder="请选择国家"
  62. style={{ width: 200 }}
  63. {...init("select", {
  64. rules: [{ required: true, message: "请选择您的国籍" }]
  65. })}
  66. >
  67. <li value="china">中国</li>
  68. <li value="use">美国</li>
  69. <li value="japan">日本</li>
  70. <li value="korean">韩国</li>
  71. <li value="Thailand">泰国</li>
  72. </Select>
  73. </FormItem>
  74. <FormItem label="喜欢的颜色:" {...formItemLayout}>
  75. <Select
  76. multiple
  77. placeholder="请选择颜色"
  78. style={{ width: 200 }}
  79. {...init("multiSelect", {
  80. rules: [{ required: true, message: "请选择您喜欢的颜色" }]
  81. })}
  82. >
  83. <li value="red">红色</li>
  84. <li value="orange">橙色</li>
  85. <li value="yellow">黄色</li>
  86. <li value="green">绿色</li>
  87. <li value="blue">蓝色</li>
  88. </Select>
  89. </FormItem>
  90. <FormItem label="性别:" hasFeedback {...formItemLayout}>
  91. <RadioGroup
  92. {...init("radio", {
  93. rules: [{ required: true, message: "请选择您的性别" }]
  94. })}
  95. >
  96. <Radio value="male">男</Radio>
  97. <Radio value="female">女</Radio>
  98. </RadioGroup>
  99. </FormItem>
  100. <FormItem label="生日:" {...formItemLayout}>
  101. <DatePicker
  102. {...init("birthday", {
  103. rules: [
  104. {
  105. required: true,
  106. type: "date",
  107. message: "你的生日是什么呢?"
  108. },
  109. {
  110. validator: this.checkBirthday
  111. }
  112. ]
  113. })}
  114. />
  115. </FormItem>
  116. <FormItem label="8~12间的质数:" {...formItemLayout}>
  117. <NumberPicker
  118. min={8}
  119. max={12}
  120. {...init("primeNumber", {
  121. rules: [{ validator: this.checkPrime }]
  122. })}
  123. />
  124. </FormItem>
  125. <FormItem wrapperCol={{ span: 16, offset: 6 }}>
  126. <Button type="primary" onClick={this.handleSubmit.bind(this)}>
  127. 确定
  128. </Button>
  129. &nbsp;&nbsp;&nbsp;
  130. <Button onClick={this.handleReset.bind(this)}>重置</Button>
  131. </FormItem>
  132. </Form>
  133. );
  134. }
  135. }
  136. ReactDOM.render(<Demo />, mountNode);

校验提示

注意: 反馈图标只对 <Input /> 有效。如果是 <Input> 组件, 可在<FormItem>上面添加 hasFeedback 控制图标的展示为 <FormItem> 定义 validateStatus 属性控制三种校验状态。

Form 表单 - 图13

查看源码在线预览

  1. import {
  2. Form,
  3. Input,
  4. DatePicker,
  5. Radio,
  6. NumberPicker,
  7. Select
  8. } from "@icedesign/base";
  9. const FormItem = Form.Item;
  10. const RadioGroup = Radio.Group;
  11. const formItemLayout = {
  12. labelCol: {
  13. span: 6
  14. },
  15. wrapperCol: {
  16. span: 14
  17. }
  18. };
  19. ReactDOM.render(
  20. <Form>
  21. <FormItem
  22. label="失败校验:"
  23. {...formItemLayout}
  24. validateStatus="error"
  25. help="请输入数字和字母组合"
  26. >
  27. <Input defaultValue="无效选择" id="error" />
  28. </FormItem>
  29. <FormItem
  30. label="校验中:"
  31. {...formItemLayout}
  32. hasFeedback
  33. validateStatus="loading"
  34. help="信息审核中..."
  35. >
  36. <Input defaultValue="我是被校验的内容" id="loading" />
  37. </FormItem>
  38. <FormItem
  39. label="成功校验:"
  40. {...formItemLayout}
  41. hasFeedback
  42. validateStatus="success"
  43. >
  44. <Input defaultValue="我是正文" id="success" />
  45. </FormItem>
  46. <FormItem
  47. label="失败校验:"
  48. {...formItemLayout}
  49. hasFeedback
  50. validateStatus="error"
  51. help="请输入数字和字母组合"
  52. >
  53. <Input defaultValue="无效选择" id="error" />
  54. </FormItem>
  55. <FormItem
  56. label="Datepicker:"
  57. {...formItemLayout}
  58. validateStatus="error"
  59. help="请选择正确日期"
  60. >
  61. <DatePicker />
  62. </FormItem>
  63. <FormItem label="性别:" validateStatus="success" {...formItemLayout}>
  64. <RadioGroup>
  65. <Radio value="male">男</Radio>
  66. <Radio value="female">女</Radio>
  67. </RadioGroup>
  68. </FormItem>
  69. <FormItem
  70. label="年龄:"
  71. validateStatus="error"
  72. {...formItemLayout}
  73. help="请选择国家"
  74. >
  75. <Select placeholder="请选择国家">
  76. <li value="china">中国</li>
  77. <li value="use">美国</li>
  78. <li value="japan">日本</li>
  79. <li value="korean">韩国</li>
  80. <li value="Thailand">泰国</li>
  81. </Select>
  82. </FormItem>
  83. <FormItem label="国家:" validateStatus="error" {...formItemLayout}>
  84. <NumberPicker />
  85. </FormItem>
  86. </Form>,
  87. mountNode
  88. );

相关区块

Form 表单 - 图14

暂无相关区块