精彩文章免费看

antd 常用知识点和小技巧总结

目录

  • 1 form 表单中 FormItem 的布局
  • 2 form 表单,FormItem 的 rules 中新增 validator,实时请求校验
  • 3 利用 validator 和正则,验证中文
  • 4 form.validateFields 直接获取表单的值
  • 5 form 表单提交 htmlType,改为 onClick
  • 6 Input 组件,利用 maxLength 属性,限制最大输入内容长度
  • 7 InputNumber 只能输入数字
  • 8 menu 实现回缩效果注意点
  • 9 左侧菜单调整宽度设置
  • 10 表格 Columns 字段 id 页面不展示情况
  • 11 自定义 Modal
  • 12 Select 组件清除选框内容
  • 13 antd、mobx @注入的顺序
  • 14 解决 table 组件, key 警告
    14.1 方法一:使用 rowKey
    14.2 方法二:dataSource 数据新增 key
  • 15 Form.create 方式
    15.1 方式一:@ 注解
    15.2 方式二:高阶写法
  • 16 Form initialValue 值编辑后,表单的值不改变问题
    16.1 方法一
    16.2 方法二
  • 17 Modal 是否显示 footer 底部按钮
  • 18 有 connect 和 Form 表单
  • 19 Tree 树组件增加右键菜单
  • 20 直接使用 rc-form 库 createForm,与 antd Form 的 Form.create() 设置样式不同
  • 21 Form 表单实时校验 _.debounce 应用,和不同字段相互校验影响
  • 22 Form 组件方法 getFieldsValue 获取自定义组件的值
  • 23 Form 表单清空和重置的区别以及方法
  • 24 DatePicker 组件,部分日期/时间为可选
  • 1 form表单中FormItem的布局

    使用 getFieldDecorator 包裹的输入框或者 Select,必须是在最外层,也就是只有一层,否则,检验会一直不通过,所以,需要重新布局应该在 getFieldDecorator 的外层添加父节点,而不应该在里面。

    <FormItem
      {...formItemLayout}
      label="所属应用"
        {getFieldDecorator('apiwgAppName', {
          rules: [{ required: false, message: '请选择' }],
        initialValue: apiwgAppName || ""
          <Input disabled={this.store.data.apiId ? true : false}
            className="control-special" readOnly style={{ width: "70%" }}
            onClick={this.showModal.bind(this, "apiwgApp")} />
        <Button className="btn-modal" type="primary" onClick={this.showModal.bind(this, "apiwgApp")}
          disabled={this.store.data.apiId ? true : false}
        >选择所属应用
        </Button>
          style={{ marginLeft: '8px' }}
          onClick={this.openNewAppDlg.bind(this)}
          className={`api-add ${
            this.store.data.apiId ? 'disabled' : ''
          +新增应用
    </FormItem>
    

    2 form表单,FormItem 的 rules 中新增 validator,实时请求校验

    <FormItem
      labelCol={{ span: 8 }}
      wrapperCol={{ span: 15 }}
      label="菜单名称"
      {form.getFieldDecorator('menuName', {
        rules: [
          { required: true, message: '菜单名称不能为空' },
          { type: 'string', max: 30, message: '菜单名称过长' },
          { validator: this.handleCheckName }, 
          { whitespace: true, message: '请输入非空白内容' }
        initialValue: this.props.menuSysData.menuName,
        <Input
          // placeholder="请输入菜单名称"
          disabled={disableFlag}
    </FormItem>
    // 实时校验
      handleCheckName = (rule, value, callback) => {
        const { checkName, actionType } = this.state;
        if (!this.trim(value) || (checkName && actionType === 'M' && this.trim(value) === checkName)) {
          callback();
          return;
        let params = {
          menuName: value,
          state: "00A"
        MenuSysService.checkMenuName(params).then(result => {
          if (!result || !result.resultObject) {
            return;
          let code = result.resultObject.code;
          if (code && code > 0) {
            callback('系统名称已存在!');
          callback();
    

    3 利用 validator 和正则,验证中文

    <FormItem
            hasFeedback={!disableFlag}
            labelCol={{ span: 6 }}
            wrapperCol={{ span: 15 }}
            label="账号" >
            {form.getFieldDecorator('userCode', {
              initialValue: '',
              rules: [
                { required: !disableFlag, validator: this.usercodeValidator },
                { type: 'string', max: 30, message: '账号过长' },
                { whitespace: true, message: '内容不能为空' }
              <Input placeholder="请输入账号" disabled={account} maxLength="30" autoComplete="false" />)}
    </FormItem>
    usercodeValidator = (rule, value, callback) => {
        const { userData } = this.props;
        if (!value) {
          callback('内容不能为空');
          return;
      // !!!中文验证
        const reg = /[\u4E00-\u9FA5]{1,4}/;   /*定义验证表达式*/
        if (reg.test(value)) { /*进行验证*/
          callback('账号不能为中文');
          return;
        if (userData.userCode === value) {
          callback();
        else {
          let params = {
            userCode: value + "",  // 查一下有没有这个编码
            useState: '10301'
          SysUserMgService.checkUserCode(params).then(result => {
            if (!result || result.code !== '0') {
              callback(result.message);
              return;
            if (result.resultObject && result.resultObject.num !== 0) {
              callback('该账号已存在');
              return;
            callback();
    

    4 form.validateFields 直接获取表单的值

    this.props.form.validateFields((err, fieldsValue) => {
         if (err) return;
         this.handleSubmit(fieldsValue);
    

    5 form 表单提交 htmlType,改为 onClick

    说明:因为之前遇到过使用 htmlType 提交表单会有问题,但是改为 onClick 后,就没问题了,所以,也记录一下。
    htmlType 是官网使用的方式,具体问题本人当时忘记截个图了。

    <Form layout="inline" onSubmit={this.handleSubmit}>
            <FormItem
              validateStatus={userNameError ? 'error' : ''}
              help={userNameError || ''}
              {getFieldDecorator('userName', {
                rules: [{ required: true, message: 'Please input your username!' }],
                <Input prefix={<Icon type="user" style={{ color: 'rgba(0,0,0,.25)' }} />} placeholder="Username" />
            </FormItem>
            <FormItem
              validateStatus={passwordError ? 'error' : ''}
              help={passwordError || ''}
              {getFieldDecorator('password', {
                rules: [{ required: true, message: 'Please input your Password!' }],
                <Input prefix={<Icon type="lock" style={{ color: 'rgba(0,0,0,.25)' }} />} type="password" placeholder="Password" />
            </FormItem>
            <FormItem>
              <Button
                type="primary"
                htmlType="submit"
                disabled={hasErrors(getFieldsError())}
                Log in
              </Button>
            </FormItem>
          </Form>
    // 改变后:
    <Form layout="inline" >
            <FormItem
              validateStatus={userNameError ? 'error' : ''}
              help={userNameError || ''}
              {getFieldDecorator('userName', {
                rules: [{ required: true, message: 'Please input your username!' }],
                <Input prefix={<Icon type="user" style={{ color: 'rgba(0,0,0,.25)' }} />} placeholder="Username" />
            </FormItem>
            <FormItem
              validateStatus={passwordError ? 'error' : ''}
              help={passwordError || ''}
              {getFieldDecorator('password', {
                rules: [{ required: true, message: 'Please input your Password!' }],
                <Input prefix={<Icon type="lock" style={{ color: 'rgba(0,0,0,.25)' }} />} type="password" placeholder="Password" />
            </FormItem>
            <FormItem>
              <Button
                type="primary"
                disabled={hasErrors(getFieldsError())}
                onClick={() => this.handleSubmit()}
                Log in
              </Button>
            </FormItem>
          </Form>
    

    6 Input 组件,利用 maxLength 属性,限制最大输入内容长度

    <Input 
      placeholder="请输入账号" 
      disabled={account} 
      maxLength="30" 
      autoComplete="off" 
    

    7 InputNumber 只能输入数字:

    <InputNumber
         formatter={value => value}
         parser={value => parseInt(value) || ''}
         style={{ width: '100%' }}
         step={1}
         onChange={(val) => this.onChangeIpt(1, val)}
    

    8 menu 实现回缩效果注意点

    说明:menu 必须放在 Sider 中,才能实现缩回去的,这个有特定的布局。

    <Sider 
      style={{ background: '#1D2023', height: '100%' }}
      trigger={null} 
      collapsible 
      collapsed={this.state.collapsed} 
      width={140} 
      collapsedWidth={40} 
        <BaseMenu
           toggle={this.toggle}
           collapsed={this.state.collapsed}
           history={history}
           location={location}
    </Sider>
    

    9 左侧菜单调整宽度设置

    说明:通过在 Sider 组件,设置 width,调整菜单的宽度,通过设置 collapsedWidth,调整调整缩进的宽度。

    <Sider 
      style={{ background: '#1D2023', height: '100%' }}
      trigger={null} 
      collapsible 
      collapsed={this.state.collapsed} 
      width={140} 
      collapsedWidth={40} 
        <BaseMenu
           toggle={this.toggle}
           collapsed={this.state.collapsed}
           history={history}
           location={location}
    </Sider>
    

    10 表格 Columns 字段 id 页面不展示情况

    说明:一般而言,表格 Columns 字段 id 是在界面不展示的,但是,对于有些逻辑的处理,又是需要的,可以使用相应样式隐藏的处理方式。

    常规展示的情况:
      title: '序号',
      dataIndex: 'algoId',
      key: 'algoId'
    不展示id字段:
      title: '',
      dataIndex: 'algoId',
      key: 'algoId',
      width: 0,
      render: item => {
        return (
          <span style={{ display: 'none' }} title={item}>
            {item}
          </span>
    

    11 自定义 Modal

    查看元素可知,Modal 是在界面构建完成之后,由 js 控制,动态的添加,所以想事先获取 ant-modal-body 中 DOM 元素的节点是不可能的,但是一般情况也不会去获取它。
    自定义 Modal,解决上述的问题。

    关键代码:
    1:因为我们使用的是 antd,所以,下面的样式是不需要引入的。这个跟 antd 的 Modal 样式重复。
    2:Modal 的隐藏和显示,是通过控制 class 为 ant-modal-mask 和 ant-modal-wrap 两个 div 的显示和隐藏。

  • 通过给 ant-modal-mask 的 div,添加另外一个 className:ant-modal-mask-hidden,来控制其隐藏,也可以通过 display 来控制。
  • 通过给 ant-modal-wrap 设置行内样式 display: none,来控制其隐藏。不过,也可以使用 className,随便都可以。
  • 界面布局:
    <div className="ant-modal-mask" ></div>
    <div tabIndex="-1" className="ant-modal-wrap " role="dialog" aria-labelledby="rcDialogTitle0" style={{}}>
      <div role="document" className="ant-modal" style={{ width: '920px' }}>
        <div className="ant-modal-content">
          <div className="ant-modal-header">
          <div className="ant-modal-body" style={{ background: 'rgb(16, 16, 17)' }}>
      <div tabIndex="0" style={{ width: '0px', height: '0px', overflow: 'hidden' }}>
        sentinel
    .ant-modal-mask { // 遮罩层
      position: fixed;
      top: 0;
      right: 0;
      left: 0;
      bottom: 0;
      background-color: rgba(0, 0, 0, 0.65);
      height: 100%;
      z-index: 1000;
      filter: alpha(opacity=50);
    .ant-modal-wrap {
      position: fixed;
      overflow: auto;
      top: 0;
      right: 0;
      bottom: 0;
      left: 0;
      z-index: 1000;
      -webkit-overflow-scrolling: touch;
      outline: 0;
    .ant-modal {
      font-family: "Chinese Quote", -apple-system, BlinkMacSystemFont, "Segoe UI", "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei", "Helvetica Neue", Helvetica, Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol";
      font-size: 14px;
      font-variant: tabular-nums;
      line-height: 1.5;
      color: rgba(0, 0, 0, 0.65);
      -webkit-box-sizing: border-box;
      box-sizing: border-box;
      margin: 0;
      padding: 0;
      list-style: none;
      position: relative;
      width: 920px;
      margin: 0 auto;
      top: 100px;
      padding-bottom: 24px;
    

    12 Select组件清除选框内容

    通过给 Select 组件新增 allowClear 属性。注意:allowClear 也会触发 onChange 方法,所以,也要单独处理一下,因为 value 和 element 为undefined。

    <Select {...this.props} placeholder="请选择" allowClear={true} >
    </Select>
    

    13 antd、mobx @注入的顺序

    16.1 方法一

    其实,只要编辑成功后,回调调用 form.resetFields(),就可以了,如果
    是使用 modal 框弹出的表单,就可以直接使用 destroyOnClose = {true} 属性。

    import React from 'react';
    import { Input, Modal, Form } from 'antd';
    import styles from './UserModal.less';
    const FormItem = Form.Item;
    const UserModal = ({ currentItem, dispatch, form, visible }) => {
      function handleOk() {
        form.validateFields((err, fieldsValue) => {
          if (err) return;
          dispatch({
            type: 'demo/update',
            payload: {
              currentItem: fieldsValue
      function handleCancel() {
        dispatch({
          type: 'demo/hideModal'
      const formItemLayout = {
        labelCol: {
          xs: { span: 24 },
          sm: { span: 4 },
        wrapperCol: {
          xs: { span: 24 },
          sm: { span: 20 },
      const { getFieldDecorator } = form;
      return (
        <div className={styles.root}>
          <Modal
            title="编辑"
            visible={visible}
            onOk={() => handleOk()}
            onCancel={() => handleCancel()}
            destroyOnClose={true}
              <FormItem
                {...formItemLayout}
                label="用户名"
                {getFieldDecorator('name', {
                  initialValue: currentItem.name,
                  rules: [{
                    required: true, message: 'Please input your name!',
                  <Input placeholder="请输入用户名" />
              </FormItem>
              <FormItem
                {...formItemLayout}
                label="年龄"
                {getFieldDecorator('age', {
                  initialValue: currentItem.age,
                  rules: [{
                    required: true, message: 'Please input your age!',
                  <Input placeholder="请输入年龄" />
              </FormItem>
              <FormItem
                {...formItemLayout}
                label="地址"
                {getFieldDecorator('address', {
                  initialValue: currentItem.address,
                  rules: [{
                    required: true, message: 'Please input your address!',
                  <Input placeholder="请输入地址" />
              </FormItem>
            </Form>
          </Modal>
    export default (Form.create({})(UserModal));
    主要代码:destroyOnClose={true}
    <Modal
            title="编辑"
            visible={visible}
            onOk={() => handleOk()}
            onCancel={() => handleCancel()}
            destroyOnClose={true}
    </Modal>
    

    16.2 方法二

    如果是 class 类,可以使用钩子。

      componentDidUpdate = (prevProps, prevState) => {
        if (!prevProps.visible) {
          prevProps.form.resetFields();
    

    代码参考:

    import React from 'react';
    import { Input, Modal, Form } from 'antd';
    import styles from './UserModal.less';
    const FormItem = Form.Item;
    @Form.create({})
    class UserModal extends React.PureComponent {
      componentDidUpdate = (prevProps, prevState) => {
        if (!prevProps.visible) {
          prevProps.form.resetFields();
      handleOk = () => {
        const { dispatch, form } = this.props;
        form.validateFields((err, fieldsValue) => {
          if (err) return;
          dispatch({
            type: 'demo/update',
            payload: {
              currentItem: fieldsValue
      handleCancel = () => {
        const { dispatch } = this.props;
        dispatch({
          type: 'demo/hideModal'
      render() {
        const { currentItem, form, visible } = this.props;
        const formItemLayout = {
          labelCol: {
            xs: { span: 24 },
            sm: { span: 4 },
          wrapperCol: {
            xs: { span: 24 },
            sm: { span: 20 },
        const { getFieldDecorator } = form;
        return (
          <div className={styles.root}>
            <Modal
              title="编辑"
              visible={visible}
              onOk={() => this.handleOk()}
              onCancel={() => this.handleCancel()}
                <FormItem
                  {...formItemLayout}
                  label="用户名"
                  {getFieldDecorator('name', {
                    initialValue: currentItem.name,
                    rules: [{
                      required: true, message: 'Please input your name!',
                    <Input placeholder="请输入用户名" />
                </FormItem>
                <FormItem
                  {...formItemLayout}
                  label="年龄"
                  {getFieldDecorator('age', {
                    initialValue: currentItem.age,
                    rules: [{
                      required: true, message: 'Please input your age!',
                    <Input placeholder="请输入年龄" />
                </FormItem>
                <FormItem
                  {...formItemLayout}
                  label="地址"
                  {getFieldDecorator('address', {
                    initialValue: currentItem.address,
                    rules: [{
                      required: true, message: 'Please input your address!',
                    <Input placeholder="请输入地址" />
                </FormItem>
              </Form>
            </Modal>
    export default UserModal;
    // export default (Form.create({})(UserModal));
                    pageY: e.event.pageY,
                    id: e.node.props['data-key'],
                    categoryName: e.node.props['data-title']
    

    // id 和 categoryName 是生成时绑上去的

    <TreeNode
                            key={item.id}
                            title={title}
                            data-key={item.id}
                            data-title={item.categoryName}
    

    // 最后绑个菜单就可以实现了

    getNodeTreeRightClickMenu() {
            const {pageX, pageY} = {...this.state.rightClickNodeTreeItem};
            const tmpStyle = {
                position: 'absolute',
                left: `${pageX - 220}px`,
                top: `${pageY - 70}px`
            const menu = (
                    onClick={this.handleMenuClick}
                    style={tmpStyle}
                    className={style.categs_tree_rightmenu}
                    <Menu.Item key='1'><Icon type='plus-circle'/>{'加同级'}</Menu.Item>
                    <Menu.Item key='2'><Icon type='plus-circle-o'/>{'加下级'}</Menu.Item>
                    <Menu.Item key='4'><Icon type='edit'/>{'修改'}</Menu.Item>
                    <Menu.Item key='3'><Icon type='minus-circle-o'/>{'删除目录'}</Menu.Item>
                </Menu>
            return (this.state.rightClickNodeTreeItem == null) ? '' : menu;
    

    getNodeTreeRightClickMenu 方法放在 render 中:

    getNodeTreeRightClickMenu 方法是放在生成主界面的方法里( render ),因为每一次 state 的变化后,render 方法都会执行,所以变一下任意的 this.state 里面的状态,就会执行 render 方法 ,这样 getNodeTreeRightClickMenu 方法放在 render 方法里来生成界面的一部分。就可以了

    项目中实现关键代码:

    * @Author: lin.zehong * @Date: 2018-12-02 22:13:59 * @Last Modified by: lin.zehong * @Last Modified time: 2018-12-19 16:36:27 * @Desc: 收藏夹--树 import React from 'react'; import { connect } from 'dva'; import { Tree, Menu } from 'antd'; import Zcon from 'zteui-icon'; import styles from './TreeCollect.less'; const { TreeNode } = Tree; class TreeCollect extends React.Component { state = { expandedKeys: ['-1'], // 树节点右键事件 treeNodeonRightClick = ({ event, node }) => { event.persist(); const { offsetLeft, _isCollapsed } = this.props; const menuWidth = _isCollapsed ? 80 : 200; const { favorites, favoritesDetail } = node.props; this.changefavorites(favorites); const hasChild = !!(favorites && favorites.scjId); // 收藏夹 this.setState({ rightClickNodeTreeItem: { pageX: event.pageX - offsetLeft - 16 - menuWidth, pageY: event.target.offsetTop + 28, key: node.props.eventKey, id: node.props.eventKey, title: node.props.title, favorites, favoritesDetail, hasChild, // 右键节点页面展示 getNodeTreeRightClickMenu = () => { const { rightClickNodeTreeItem } = this.state; const { pageX, pageY, hasChild, key } = { ...rightClickNodeTreeItem }; const tmpStyle = { position: 'absolute', left: `${pageX}px`, top: `${pageY}px`, boxShadow: '2px 2px 10px #333333', const menuHasNode = ( onClick={this.handleMenuClick} style={tmpStyle} className={styles.categs_tree_rightmenu} <Menu.Item key='1'>自动巡查</Menu.Item> <Menu.Item key='2'>重命名</Menu.Item> <Menu.Item key='3'>添加同级目录</Menu.Item> <Menu.Item key='4'>添加子目录</Menu.Item> <Menu.Item key='5'>删除</Menu.Item> </Menu> const menuRoot = ( onClick={this.handleMenuClick} style={tmpStyle} className={styles.categs_tree_rightmenu} <Menu.Item key='1'>自动巡查</Menu.Item> <Menu.Item key='2'>重命名</Menu.Item> <Menu.Item key='4'>添加子目录</Menu.Item> </Menu> const menuNoNode = ( onClick={this.handleMenuClick} style={tmpStyle} className={styles.categs_tree_rightmenu} <Menu.Item key='6'>取消收藏</Menu.Item> </Menu> const menu = hasChild ? (key === "-1" ? menuRoot : menuHasNode) : menuNoNode; return (rightClickNodeTreeItem == null) ? '' : menu; // 隐藏右键菜单 hideTreeRight = () => { this.setState({ rightClickNodeTreeItem: null }); render() { const { expandedKeys, selectedKeys } = this.state; const { isExpand, gData } = this.props; const loop = data => data.map((item) => { if (item.children && item.favorites) { return <TreeNode key={item.key} icon={<Zcon type="thing" />} title={item.title} favorites={item.favorites}>{loop(item.children)}</TreeNode>; return <TreeNode key={item.favoritesDetail.sxtxxId} title={item.title} favoritesDetail={item.favoritesDetail} />; return ( <div className={`${styles.root} ${isExpand ? '' : styles.hideTree}`} onClick={() => this.hideTreeRight()}> showIcon className="draggable-tree" defaultExpandedKeys={expandedKeys} selectedKeys={selectedKeys} onRightClick={this.treeNodeonRightClick} onSelect={this.onSelect} {loop(gData)} </Tree> {this.getNodeTreeRightClickMenu()} function mapStateToProps({ onlineCamera, publicModel }) { return { gData: onlineCamera.collectTree, cameraNum: onlineCamera.cameraNum, inspectionCamera: onlineCamera.inspectionCamera, _isCollapsed: publicModel._isCollapsed, export default connect(mapStateToProps)(TreeCollect);

    20 直接使用 rc-form 库 createForm,与 antd Form 的 Form.create() 设置样式不同

  • 使用 antd Form 的 Form.create()
  • import React from 'react'
    import PropTypes from 'prop-types'
    import { Form, Button } from 'antd'
    class BalloonContent extends React.Component {
      render() {
        const { form } = this.props;
        return (
              size='medium'
              className={Styles.wrapForm}
              <Form.Item
                label="算子输出"
                {form.getFieldDecorator('stdioOutput', {
                  rules: [
                      required: true,
                      message: '输出不能为空',
                })(<Input />)}
              </Form.Item>
            </Form>
    export default Form.create()(BalloonContent) // !!!
    
    import React from 'react'
    import PropTypes from 'prop-types'
    import { Form, Button } from 'antd'
    import { createForm } from 'rc-form'
    class BalloonContent extends React.Component {
      render() {
        const { form } = this.props;
        const { getFieldDecorator, getFieldError } = form ;
        const stdioOutputError = getFieldError('stdioOutput'); // !!!
        return (
              size='medium'
              className={Styles.wrapForm}
              <Form.Item
                label="算子输出"
                required // !!!
                validateState={stdioOutputError ? 'error' : 'success'} // !!!
                help={stdioOutputError} // !!!
                {form.getFieldDecorator('stdioOutput', {
                  rules: [
                      required: true,
                      message: '输出不能为空',
                })(<Input />)}
              </Form.Item>
            </Form>
    export default createForm ()(BalloonContent) // !!!
    compareToFirstPassword = (rule, value, callback) => {
      const { form } = this.props;
      if (value && value !== form.getFieldValue('password')) {
        callback('Two passwords that you enter is inconsistent!');
      } else {
        callback();
    validateToNextPassword = (rule, value, callback) => {
      const { form } = this.props;
      if (value && this.state.confirmDirty) {
        form.validateFields(['confirm'], { force: true });
      callback();
    <Form.Item label="Password" hasFeedback>
      {getFieldDecorator('password', {
        rules: [
            required: true,
            message: 'Please input your password!',
            validator: this.validateToNextPassword,
    })(<Input.Password />)}
    </Form.Item>
    <Form.Item label="Confirm Password" hasFeedback>
      {getFieldDecorator('confirm', {
        rules: [
            required: true,
            message: 'Please confirm your password!',
            validator: this.compareToFirstPassword,
    })(<Input.Password onBlur={this.handleConfirmBlur} />)}
    </Form.Item>
    

    实际项目例子,选择所属数据库,校验表名:

    // 写入新表,选择数据库,需要校验已有的表名 validateToTableName = (rule, value, callback) => { const { form: { getFieldValue, validateFields }} = this.props; const targetTableCode = getFieldValue("targetTableCode"); if (targetTableCode) { validateFields(['targetTableCode'], { force: true }); callback(); // 写入新表,校验表名 // eslint-disable-next-line validateTableExist = _.debounce((rule, value, callback) => { const { form: { getFieldValue }, dispatch } = this.props; const targetDataSource = getFieldValue("targetDataSource"); const targetTableCode = getFieldValue("targetTableCode"); dispatch({ type: "applyDetail/tableExist", payload: { dataSourceCode: targetDataSource, table: targetTableCode, }).then(result => { if (result) { callback("该表名已存在"); } else { callback(); }, 500); ? dataSourceList[0].code : undefined), })(dataBaseComponent({ className: styles.formInput }))} </Form.Item> <Form.Item label="表名"> {getFieldDecorator("targetTableCode", { rules: [ required: true, message: "请输入新表表名", pattern: checkBackEndTableName, message: "只支持英文字母、数字、英文格式、下划线", validator: this.validateTableExist, // !!! initialValue: (exchangeFormat.formatType === WRITE_IN_NEW_TABLE ? exchangeFormat.targetTableCode : undefined) || undefined, <Input className={styles.formInput} disabled={disabled} placeholder="请输入" </Form.Item>

    22 Form 组件方法 getFieldsValue 获取自定义组件的值

    项目实例:对 antd RangePicker 抽取完独立组件后,form 表单获取不到值

    自定义组件被 getFieldsValue 包裹,会获得以下属性:

    onChange方法, 子组件调用此方法,可将值传给父组件,从而Form可拿到自定义组件的值 value属性,获得初始值

    <Form.Item label="发送时间">
      {getFieldDecorator('range-time-picker', {
        rules: [{ required: false, message: '请输入开始时间-结束时间' }],
        <RangePickerPage />
    </Form.Item>
    

    下面是对 antd RangePicker 进行封装,通过组件 RangePicker 本身的 onChange 方法,调用 this.props.onChange(子组件不用传 onChange 方法,自定义组件被 getFieldsValue 包裹,会自动获取 onChage 属性),则通过 form.validateFields 可以获取到值。

    * Author: lin.zehong * Date: 2019-10-04 09:14:52 * Last Modified by: lin.zehong * Last Modified time: 2019-10-04 09:14:52 * Desc: 对 antd RangePicker 进行封装 import React from "react"; import moment from "moment"; import { DatePicker } from "antd"; const { RangePicker } = DatePicker; class RangePickerPage extends React.Component { range = (start, end) => { const result = []; for (let i = start; i < end; i += 1) { result.push(i); return result; disabledDate = (current) => { // Can not select days before today and today return current && current < moment().endOf('day'); disabledRangeTime = (_, type) => { if (type === 'start') { return { disabledHours: () => this.range(0, 60).splice(4, 20), disabledMinutes: () => this.range(30, 60), disabledSeconds: () => [55, 56], return { disabledHours: () => this.range(0, 60).splice(20, 4), disabledMinutes: () => this.range(0, 31), disabledSeconds: () => [55, 56], onChange = (dates, dateStrings) => { const { onChange } = this.props; // !!! onChange(dateStrings); render() { return ( <RangePicker allowClear disabledDate={this.disabledDate} disabledTime={this.disabledRangeTime} showTime={{ hideDisabledOptions: true, defaultValue: [moment('00:00:00', 'HH:mm:ss'), moment('11:59:59', 'HH:mm:ss')], format="YYYY-MM-DD HH:mm:ss" onChange={this.onChange} // !!! export default RangePickerPage;

    参考:https://juejin.im/post/5c9c6c08e51d4503e514eaac

    23 Form 表单清空和重置的区别以及方法

    这里首先需要明确,清空和重置是不同的概念,清空是把内容都清空掉,而重置是恢复 form 表单初始值。

    例如:新增功能,清空和重置就是一样的效果,而对于编辑,清空就是把初始值都清空掉,重置就是恢复刚开始的初始值。

    form.setFieldsValue({"fieldName": ""});
    
    form.resetFields();
    

    24 DatePicker 组件,部分日期/时间为可选

    24.1 不能选择今天之前的日期,包括今天的日期也不可以选择

      const disabledDate = (current) => {
        return current && current < moment().endOf('day');
    

    24.2 不能选择今天之前的日期,今天日期可以选择

      const disabledDate = (current) => {
        return current && current < moment().subtract(1, 'day');
    

    24.3 当前时间之后的时间点,精确到小时

    const [upgradeTime, setUpgradeTime] = useState(moment('00:00:00', 'HH:mm:ss'))
    const disabledDate = (current) => {
      return current && current < moment().subtract(1, 'day'); // 今天可以选择
    const disabledDateTime = () => {
      const hours = moment().hours(); // 0~23
      // 当日只能选择当前时间之后的时间点
      if (upgradeTime.date() === moment().date()) {
        return {
          disabledHours: () => range(0, hours + 1),
    <Form.Item label="发送时间">
      {getFieldDecorator('pushTime', {
        rules: [{ required: false, message: '请输入发送时间' }],
        initialValue: record.pushType === 0 ? null :
          (record.pushTime ? moment(record.pushTime, 'YYYY-MM-DD HH:mm:ss') : null), // 定时发送才显示时间
        <DatePicker
          format="YYYY-MM-DD HH:mm:ss"
          disabledDate={disabledDate}
          disabledTime={disabledDateTime}
          style={{ width: "100%" }}
          onChange={(timer) => setUpgradeTime(timer)} // !!!
          showTime={{ defaultValue: moment(upgradeTime) }} // !!!
    </Form.Item>
    

    24.4 当前时间之后的时间点,精确到分

    const [upgradeTime, setUpgradeTime] = useState(moment('00:00:00', 'HH:mm:ss'))
    const disabledDate = (current) => {
      return current && current < moment().subtract(1, 'day'); // 今天可以选择
    const disabledDateTime = () => {
      const hours = moment().hours(); // 0~23
      const minutes = moment().minutes(); // 0~59
      // 当日只能选择当前时间之后的时间点
      if (upgradeTime.date() === moment().date()) {
        return {
          disabledHours: () => range(0, hours), 
          disabledMinutes: () => range(0, minutes), // 精确到分
    <Form.Item label="发送时间">
      {getFieldDecorator('pushTime', {
        rules: [{ required: false, message: '请输入发送时间' }],
        initialValue: record.pushType === 0 ? null :
          (record.pushTime ? moment(record.pushTime, 'YYYY-MM-DD HH:mm:ss') : null), // 定时发送才显示时间
        <DatePicker
          format="YYYY-MM-DD HH:mm:ss"
          disabledDate={disabledDate}
          disabledTime={disabledDateTime}
          style={{ width: "100%" }}
          onChange={(timer) => setUpgradeTime(timer)} // !!!
          showTime={{ defaultValue: moment(upgradeTime) }} // !!!
    </Form.Item>
    
    最后编辑于:2019-10-28 21:56