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 的外层添加父节点,而不应该在里面。

例:

 


  
{getFieldDecorator('apiwgAppName', { rules: [{ required: false, message: '请选择' }], initialValue: apiwgAppName || "" })( )} +新增应用

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

 


  {form.getFieldDecorator('menuName', {
    rules: [
      { required: true, message: '菜单名称不能为空' },
      { type: 'string', max: 30, message: '菜单名称过长' },
      { validator: this.handleCheckName }, 
      { whitespace: true, message: '请输入非空白内容' }
    ],
    initialValue: this.props.menuSysData.menuName,
  })(
    
  )}




// 实时校验
  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 和正则,验证中文

 


        {form.getFieldDecorator('userCode', {
          initialValue: '',
          rules: [
            { required: !disableFlag, validator: this.usercodeValidator },
            { type: 'string', max: 30, message: '账号过长' },
            { whitespace: true, message: '内容不能为空' }
          ],
        })(
          )}




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 是官网使用的方式,具体问题本人当时忘记截个图了。

 

{getFieldDecorator('userName', { rules: [{ required: true, message: 'Please input your username!' }], })( } placeholder="Username" /> )} {getFieldDecorator('password', { rules: [{ required: true, message: 'Please input your Password!' }], })( } type="password" placeholder="Password" /> )}
// 改变后:
{getFieldDecorator('userName', { rules: [{ required: true, message: 'Please input your username!' }], })( } placeholder="Username" /> )} {getFieldDecorator('password', { rules: [{ required: true, message: 'Please input your Password!' }], })( } type="password" placeholder="Password" /> )}

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

 




7 InputNumber 只能输入数字:

 

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


8 menu 实现回缩效果注意点

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

 


    



9 左侧菜单调整宽度设置

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

 


    



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

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

 

常规展示的情况:
{
  title: '序号',
  dataIndex: 'algoId',
  key: 'algoId'
},

不展示id字段:
{
  title: '',
  dataIndex: 'algoId',
  key: 'algoId',
  width: 0,
  render: item => {
    return (
      
        {item}
      
    );
  }
},


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,随便都可以。

 

界面布局:

...
...
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。

 




13 antd、mobx @注入的顺序

顺序

14 解决table组件, key警告

一般都是使用 rowkey 方法一解决(后台数据要保证没有重复);

14.1 方法一:使用 rowKey

antd 常用知识点和小技巧总结_第1张图片

使用irowKey

14.2 方法二:dataSource 数据新增 key

antd 常用知识点和小技巧总结_第2张图片

dataSource数据新增key

15 Form.create 方式

15.1 方式一:@注解

 

@Form.create({})

15.2 方式二:高阶写法

 

export default (Form.create({})(APP));

16 Form initialValue 值编辑后,表单的值不改变问题

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 (
    
handleOk()} onCancel={() => handleCancel()} destroyOnClose={true} >
{getFieldDecorator('name', { initialValue: currentItem.name, rules: [{ required: true, message: 'Please input your name!', }], })( )} {getFieldDecorator('age', { initialValue: currentItem.age, rules: [{ required: true, message: 'Please input your age!', }], })( )} {getFieldDecorator('address', { initialValue: currentItem.address, rules: [{ required: true, message: 'Please input your address!', }], })( )}
) } export default (Form.create({})(UserModal)); 主要代码:destroyOnClose={true} handleOk()} onCancel={() => handleCancel()} destroyOnClose={true} > ...

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 (
      
this.handleOk()} onCancel={() => this.handleCancel()} >
{getFieldDecorator('name', { initialValue: currentItem.name, rules: [{ required: true, message: 'Please input your name!', }], })( )} {getFieldDecorator('age', { initialValue: currentItem.age, rules: [{ required: true, message: 'Please input your age!', }], })( )} {getFieldDecorator('address', { initialValue: currentItem.address, rules: [{ required: true, message: 'Please input your address!', }], })( )}
) } } export default UserModal; // export default (Form.create({})(UserModal));

17 Modal 是否显示 footer 底部按钮

一般应用场景,详情不需要底部按钮,新增和修改需要。

 

api

解决:
通过父组件传递一个空的字符串或者 {footer: null} 给 Modal 组件进行属性解构。

antd 常用知识点和小技巧总结_第3张图片

父组件需要传入的值

antd 常用知识点和小技巧总结_第4张图片

子组件 Modal

18 有 connect 和 Form 表单

 

function mapStateToProps({ onlineCamera }) {
  return {
    favorites: onlineCamera.favorites,
  };
}

export default connect(mapStateToProps)(Form.create()(TreeModal));

19 Tree 树组件增加右键菜单

参考:
https://github.com/ant-design/ant-design/issues/5151

关键代码:

// 实现这个方法 treeNodeonRightClick

 

treeNodeonRightClick(e) {
        this.setState({
            rightClickNodeTreeItem: {
                pageX: e.event.pageX,
                pageY: e.event.pageY,
                id: e.node.props['data-key'],
                categoryName: e.node.props['data-title']
            }
        });
    }


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

 

);


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

 

getNodeTreeRightClickMenu() {
        const {pageX, pageY} = {...this.state.rightClickNodeTreeItem};
        const tmpStyle = {
            position: 'absolute',
            left: `${pageX - 220}px`,
            top: `${pageY - 70}px`
        };
        const 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 = (
      
        自动巡查
        重命名
        添加同级目录
        添加子目录
        删除
      
    );
    const menuRoot = (
      
        自动巡查
        重命名
        添加子目录
      
    );
    const menuNoNode = (
      
        取消收藏
      
    );

    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 } title={item.title} favorites={item.favorites}>{loop(item.children)};
      }
      return ;
    });
    return (
      
this.hideTreeRight()}> {loop(gData)} {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 (
      
{form.getFieldDecorator('stdioOutput', { rules: [ { required: true, message: '输出不能为空', }, ], })()}
) } } export default Form.create()(BalloonContent) // !!!

antd 常用知识点和小技巧总结_第5张图片

结果样式

  • 直接使用 rc-form 库 createForm

 

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 (
      
{form.getFieldDecorator('stdioOutput', { rules: [ { required: true, message: '输出不能为空', }, ], })()}
) } } export default createForm ()(BalloonContent) // !!!

antd 常用知识点和小技巧总结_第6张图片

结果

21 Form 表单实时校验 _.debounce 应用,和不同字段相互校验影响

例如:表单字段,密码和确认密码,改变 Password,如果与 Confirm Password 不一致,也会在 Confirm Password 做提示:

antd 常用知识点和小技巧总结_第7张图片

 

官网示例:注册新用户,主要代码

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();
};



  {getFieldDecorator('password', {
    rules: [
      {
        required: true,
        message: 'Please input your password!',
      },
      {
        validator: this.validateToNextPassword,
      },
    ],
})()}



  {getFieldDecorator('confirm', {
    rules: [
      {
        required: true,
        message: 'Please confirm your password!',
      },
      {
        validator: this.compareToFirstPassword,
      },
    ],
})()}



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

antd 常用知识点和小技巧总结_第8张图片

 

主要代码:

import _ from "lodash";

// 写入新表,选择数据库,需要校验已有的表名
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);





  {getFieldDecorator("targetDataSource", {
    rules: [
      {
        required: true,
        message: "请选择所属数据库",
      },
      {
        validator: this.validateToTableName, // !!!
      },
    ],
    initialValue:
      exchangeFormat.targetDataSource ||
      (dataSourceList.length > 0
        ? dataSourceList[0].code
        : undefined),
  })(dataBaseComponent({ className: styles.formInput }))}



  {getFieldDecorator("targetTableCode", {
    rules: [
      {
        required: true,
        message: "请输入新表表名",
      },
      {
        pattern: checkBackEndTableName,
        message: "只支持英文字母、数字、英文格式、下划线",
      },
      {
        validator: this.validateTableExist, // !!!
      },
    ],
    initialValue:
      (exchangeFormat.formatType === WRITE_IN_NEW_TABLE
        ? exchangeFormat.targetTableCode
        : undefined) || undefined,
  })(
    
  )}




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

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

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

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

 


  {getFieldDecorator('range-time-picker', {
    rules: [{ required: false, message: '请输入开始时间-结束时间' }],
  })(
    
  )}


下面是对 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 (
      
    );
  }
}

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),
    };
  }
}



  {getFieldDecorator('pushTime', {
    rules: [{ required: false, message: '请输入发送时间' }],
    initialValue: record.pushType === 0 ? null :
      (record.pushTime ? moment(record.pushTime, 'YYYY-MM-DD HH:mm:ss') : null), // 定时发送才显示时间
  })(
     setUpgradeTime(timer)} // !!!
      showTime={{ defaultValue: moment(upgradeTime) }} // !!!
    />,
  )}


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), // 精确到分
    };
  }
}



  {getFieldDecorator('pushTime', {
    rules: [{ required: false, message: '请输入发送时间' }],
    initialValue: record.pushType === 0 ? null :
      (record.pushTime ? moment(record.pushTime, 'YYYY-MM-DD HH:mm:ss') : null), // 定时发送才显示时间
  })(
     setUpgradeTime(timer)} // !!!
      showTime={{ defaultValue: moment(upgradeTime) }} // !!!
    />,
  )}


 

你可能感兴趣的:(antd 常用知识点和小技巧总结)