react+AntD 实现CRON表达式生成器

2020/01/10 JavaScript 本文共10807字,阅读全文约需49分钟 本文总阅读量

  因业务需要CRON生成器,网上找的插件多不可用,在线cron工具又没法直接集成到项目中,遂参考在线工具,使用react和antd实现一个CRON表达式生成器,并在接口的配合下完成合规校验。

效果图如下:

示例图片

// Cron.js

'use strict'
import React from 'react'
import {
  Input,
  InputNumber,
  Tabs,
  Radio,
  Checkbox,
  Row,
  Col
} from 'antd'

/**
 *CRON表达式生成器
 *@author Shellcoochi
 */
export default class CronGenerator extends React.Component {
  state={
    cronText: this.props.cronText || '* * * * * *',
    cronType: ['second', 'minute', 'hour', 'day', 'month', 'week'],
    radioValue: {
      second: 1,
      minute: 1,
      hour: 1,
      day: 1,
      month: 1,
      week: 1
    },
    periodValue: {
      second: { max: 1, min: 1 },
      minute: { max: 1, min: 1 },
      hour: { max: 1, min: 1 },
      day: { max: 1, min: 1 },
      month: { max: 1, min: 1 },
      week: { max: 1, min: 1 }
    },
    loopValue: {
      second: { start: 1, end: 1 },
      minute: { start: 1, end: 1 },
      hour: { start: 1, end: 1 },
      day: { start: 1, end: 1 },
      month: { start: 1, end: 1 },
      week: { start: 1, end: 1 }
    },
    cron: {
      second: '*',
      minute: '*',
      hour: '*',
      day: '*',
      month: '*',
      week: '*'
    },
    cronParams: {
      second: '*',
      minute: '*',
      hour: '*',
      day: '*',
      month: '*',
      week: '*'
    }
  }

  /**
   * 生成cron
   * @returns {Promise<void>}
   */
  createCron = async () => {
    let { cronType } = this.state
    for (let type of cronType) {
      await this.cronGenerator(type)
    }
    let { second, minute, hour, day, month, week } = this.state.cron
    let cronText = second + ' ' + minute + ' ' + hour + ' ' + day + ' ' + month + ' ' + week
    this.setState({
      cronText: cronText
    }, () => {
        // 调用接口进行校验
      this.props.getCron(this.state.cronText)
    })
  }

  /**
   * cron生成器
   * @param type
   */
  cronGenerator = (type) => {
    let srv = this.state.radioValue[type]
    let period = this.state.periodValue[type]
    let loop = this.state.loopValue[type]
    let param = this.state.cronParams[type]
    let data = ''
    switch (srv) {
      case 1: data = '*'; break
      case 2: data = '?'; break
      case 'point':
        for (let v of param) {
          data = data + v + ','
        }
        data = data.substring(0, data.length - 1); break
      case 'period':
        data = period.min + '-' + period.max; break
      case 'loop':
        data = loop.start + '/' + loop.end; break
      default:
        data = '*'
    }
    this.setState({
      cron: Object.assign({}, this.state.cron, this.cronItemGenerator(type, data))
    })
  }

  /**
   * 对象生成器
   * @param type
   * @param data
   * @returns { {second: *}|{minute: *}}
   */
  cronItemGenerator = (type, data) => {
    switch (type) {
      case 'second': return { second: data }
      case 'minute': return { minute: data }
      case 'hour': return { hour: data }
      case 'day': return { day: data }
      case 'month': return { month: data }
      case 'week': return { week: data }
    }
  }

  /**
   * 生成多选框
   * @param col 每行个数
   * @param minNum 最小值
   * @param maxNum 最大值
   * @param key
   */
  createCheckbox = (key, col, minNum, maxNum) => {
    let colArray = []
    let rowArray = []
    let count = col
    let keyNum = minNum
    for (minNum; minNum <= maxNum; minNum++) {
      let checkbox = <Checkbox key={key + keyNum} value={minNum}>{this.formatNum(minNum)}</Checkbox>
      if (count > 0) {
        colArray.push(checkbox)
        count--
        if (minNum === maxNum)rowArray.push(<Col key={key + keyNum} span={24}>{colArray}</Col>)
      } else {
        rowArray.push(<Col key={key + keyNum} span={24}>{colArray}</Col>)
        colArray = []
        minNum--
        count = col
      }
      keyNum++
    }
    return <Row>{rowArray}</Row>
  }
  /**
   * 格式化0~9的数字
   * @param num
   */
  formatNum = (num) => {
    if (num < 10 && num > -1) {
      return '0' + num
    }
    return num
  }

  handleRadioChange = (e, type) => {
    this.setState({
      radioValue: Object.assign({}, this.state.radioValue, this.cronItemGenerator(type, e.target.value))
    })
  }

  handleCheckboxChange = (checkedValues, type) => {
    this.setState({
      cronParams: Object.assign({}, this.state.cronParams, this.cronItemGenerator(type, checkedValues))
    })
  }

  handlePeriodChange = (e, type, tar) => {
    let data = this.state.periodValue
    data[type] = tar === 'max' ? { max: e, min: data[type].min } : { max: data[type].max, min: e }
    this.setState({
      periodValue: Object.assign({}, this.state.periodValue, this.cronItemGenerator(type, data[type]))
    })
  }

  handleLoopChange = (e, type, tar) => {
    let data = this.state.loopValue
    data[type] = tar === 'start' ? { start: e, end: data[type].end } : { start: data[type].start, end: e }
    this.setState({
      loopValue: Object.assign({}, this.state.loopValue, this.cronItemGenerator(type, data[type]))
    })
  }

  render () {
    const { TabPane } = Tabs
    const { cronText, radioValue } = this.state
    const { Search } = Input
    const radioStyle = {
      display: 'block',
      height: '30px',
      lineHeight: '30px'
    }
    const secondCheckbox = this.createCheckbox('second', 10, 0, 59)
    const minuteCheckbox = this.createCheckbox('minute', 10, 0, 59)
    const hourCheckbox = this.createCheckbox('hour', 10, 0, 23)
    const dayCheckbox = this.createCheckbox('day', 10, 1, 31)
    const monthCheckbox = this.createCheckbox('month', 10, 1, 12)
    const weekCheckbox = this.createCheckbox('week', 7, 1, 7)

    return (
      <>
        <span style={ { color: 'red' }}>*</span>
        <label style={ { color: 'rgba(0,0,0,.85)', marginLeft: '5px' }}>时间参数</label>
        <Tabs type='card' style={ { height: '300px', marginTop: '6px' }}>
          <TabPane tab='秒' key='1'>
            <Radio.Group
              onChange={e => this.handleRadioChange(e, 'second')}
              value={radioValue['second']}
            >
              <Radio style={radioStyle} value={1}>
                每秒执行
              </Radio>
              <Radio style={radioStyle} value='period'>
                周期从
                <InputNumber
                  size='small'
                  min={1}
                  max={59}
                  defaultValue={1}
                  onChange={e => this.handlePeriodChange(e, 'second', 'min')} />
                -
                <InputNumber
                  size='small'
                  min={1}
                  max={59}
                  defaultValue={1}
                  onChange={e => this.handlePeriodChange(e, 'second', 'max')} /></Radio>
              <Radio style={radioStyle} value='loop'><InputNumber
                  size='small'
                  min={1}
                  max={59}
                  defaultValue={1}
                  onChange={e => this.handleLoopChange(e, 'second', 'start')} />
                秒开始,每
                <InputNumber
                  size='small'
                  min={1}
                  max={59}
                  defaultValue={1}
                  onChange={e => this.handleLoopChange(e, 'second', 'end')}
                />
                秒执行一次
              </Radio>
              <Radio style={radioStyle} value='point'>
                <Checkbox.Group
                  style={ { width: '100%' }}
                  onChange={e => this.handleCheckboxChange(e, 'second')}
                >
                  指定
                  {secondCheckbox}
                </Checkbox.Group>
              </Radio>
            </Radio.Group>
          </TabPane>
          <TabPane tab='分' key='2'>
            <Radio.Group
              value={radioValue['minute']}
              onChange={e => this.handleRadioChange(e, 'minute')}
            >
              <Radio style={radioStyle} value={1}>
                每分执行
              </Radio>
              <Radio style={radioStyle} value='period'>
                周期从
                <InputNumber
                  size='small'
                  min={1}
                  max={59}
                  defaultValue={1}
                  onChange={e => this.handlePeriodChange(e, 'minute', 'min')} />
                -
                <InputNumber
                  size='small'
                  min={1}
                  max={59}
                  defaultValue={1}
                  onChange={e => this.handlePeriodChange(e, 'minute', 'max')} /></Radio>
              <Radio style={radioStyle} value='loop'><InputNumber
                  size='small'
                  min={1}
                  max={59}
                  defaultValue={1}
                  onChange={e => this.handleLoopChange(e, 'minute', 'start')} />
                分开始,每
                <InputNumber
                  size='small'
                  min={1}
                  max={59}
                  defaultValue={1}
                  onChange={e => this.handleLoopChange(e, 'minute', 'end')} />
                分执行一次
              </Radio>
              <Radio style={radioStyle} value='point'>
                <Checkbox.Group
                  style={ { width: '100%' }}
                  onChange={e => this.handleCheckboxChange(e, 'minute')}>
                  指定
                  {minuteCheckbox}
                </Checkbox.Group>
              </Radio>
            </Radio.Group>
          </TabPane>
          <TabPane tab='时' key='3'>
            <Radio.Group
              onChange={e => this.handleRadioChange(e, 'hour')}
              value={radioValue['hour']}>
              <Radio style={radioStyle} value={1}>
                每小时执行
              </Radio>
              <Radio style={radioStyle} value='period'>
                周期从
                <InputNumber
                  size='small'
                  min={0}
                  max={23}
                  defaultValue={1}
                  onChange={e => this.handlePeriodChange(e, 'hour', 'min')} />
                -
                <InputNumber
                  size='small'
                  min={0}
                  max={23}
                  defaultValue={1}
                  onChange={e => this.handlePeriodChange(e, 'hour', 'max')} /></Radio>
              <Radio style={radioStyle} value='loop'><InputNumber
                  size='small'
                  min={0}
                  max={23}
                  defaultValue={1}
                  onChange={e => this.handleLoopChange(e, 'hour', 'start')} />
                时开始,每
                <InputNumber
                  size='small'
                  min={1}
                  max={59}
                  defaultValue={1}
                  onChange={e => this.handleLoopChange(e, 'hour', 'end')} />
                时执行一次
              </Radio>
              <Radio style={radioStyle} value='point'>
                <Checkbox.Group
                  style={ { width: '100%' }}
                  onChange={e => this.handleCheckboxChange(e, 'hour')}>
                  指定
                  {hourCheckbox}
                </Checkbox.Group>
              </Radio>
            </Radio.Group>
          </TabPane>
          <TabPane tab='日' key='4'>
            <Radio.Group
              onChange={e => this.handleRadioChange(e, 'day')}
              value={radioValue['day']}>
              <Radio style={radioStyle} value={1}>
                每日执行
              </Radio>
              <Radio style={radioStyle} value={2}>
                不指定
              </Radio>
              <Radio style={radioStyle} value='period'>
                周期从
                <InputNumber
                  size='small'
                  min={1}
                  max={31}
                  defaultValue={1}
                  onChange={e => this.handlePeriodChange(e, 'day', 'min')} />
                -
                <InputNumber
                  size='small'
                  min={1}
                  max={31}
                  defaultValue={1}
                  onChange={e => this.handlePeriodChange(e, 'day', 'max')} /></Radio>
              <Radio style={radioStyle} value='loop'><InputNumber
                  size='small'
                  min={1}
                  max={31}
                  defaultValue={1}
                  onChange={e => this.handleLoopChange(e, 'day', 'start')} />
                日开始,每
                <InputNumber
                  size='small'
                  min={1}
                  max={31}
                  defaultValue={1}
                  onChange={e => this.handleLoopChange(e, 'day', 'end')} />
                日执行一次
              </Radio>
              <Radio style={radioStyle} value='point'>
                <Checkbox.Group
                  style={ { width: '100%' }}
                  onChange={e => this.handleCheckboxChange(e, 'day')}>
                  指定
                  {dayCheckbox}
                </Checkbox.Group>
              </Radio>
            </Radio.Group>
          </TabPane>
          <TabPane tab='月' key='5'>
            <Radio.Group
              onChange={e => this.handleRadioChange(e, 'month')}
              value={radioValue['month']}>
              <Radio style={radioStyle} value={1}>
                每月执行
              </Radio>
              <Radio style={radioStyle} value={2}>
                不指定
              </Radio>
              <Radio style={radioStyle} value='period'>
                周期从
                <InputNumber
                  size='small'
                  min={1}
                  max={12}
                  defaultValue={1}
                  onChange={e => this.handlePeriodChange(e, 'month', 'min')} />
                -
                <InputNumber
                  size='small'
                  min={1}
                  max={12}
                  defaultValue={1}
                  onChange={e => this.handlePeriodChange(e, 'month', 'max')} /></Radio>
              <Radio style={radioStyle} value='loop'><InputNumber
                  size='small'
                  min={1}
                  max={12}
                  defaultValue={1}
                  onChange={e => this.handleLoopChange(e, 'month', 'start')} />
                月开始,每
                <InputNumber
                  size='small'
                  min={1}
                  max={12}
                  defaultValue={1}
                  onChange={e => this.handleLoopChange(e, 'month', 'end')} />
                月执行一次
              </Radio>
              <Radio style={radioStyle} value='point'>
                <Checkbox.Group style={ { width: '100%' }} onChange={e => this.handleCheckboxChange(e, 'month')}>
                  指定
                  {monthCheckbox}
                </Checkbox.Group>
              </Radio>
            </Radio.Group>
          </TabPane>
          <TabPane tab='周' key='6'>
            <Radio.Group
              onChange={e => this.handleRadioChange(e, 'week')}
              value={radioValue['week']}>
              <Radio style={radioStyle} value={1}>
                每周执行
              </Radio>
              <Radio style={radioStyle} value={2}>
                不指定
              </Radio>
              <Radio style={radioStyle} value='period'>
                周期从 周
                <InputNumber
                  size='small'
                  min={1}
                  max={7}
                  defaultValue={1}
                  onChange={e => this.handlePeriodChange(e, 'week', 'min')} />
                - 周
                <InputNumber
                  size='small'
                  min={1}
                  max={7}
                  defaultValue={1}
                  onChange={e => this.handlePeriodChange(e, 'week', 'max')} />
              </Radio>
              <Radio style={radioStyle} value='point'>
                <Checkbox.Group
                  style={ { width: '100%' }}
                  onChange={e => this.handleCheckboxChange(e, 'week')}>
                  指定
                  {weekCheckbox}
                </Checkbox.Group>
              </Radio>
            </Radio.Group>
          </TabPane>
        </Tabs>
        <Search
          placeholder='生成Cron'
          enterButton='生成'
          style={ { width: 400, margin: '10px 0 18px' }}
          onSearch={this.createCron}
          value={cronText}
        />
      </>
    )
  }
}

使用:

import CronGenerator from './Cron'

// 调用接口校验
getCronTime = (cronText) => {
    this.props.actions.getCron(cronText)
}

<CronGenerator
    // ref主要用于从自组件获取cronText
    ref={this.refCronGenerator}
    cronText={this.state.cronText}
    getCron={this.getCronTime}
/>

错误解决:{ {second: *}本来前两个花括号之间没有空格({{second: *}),导致报错‘Variable ‘{{second: *}’ was not properly terminated with regexp: /}}/ in…’,可以用转译,也可以用空格隔开。也就是说不能直接出现两个前花括号连着的情况。

后期优化参考:

Search

    欢迎与我交流

    江南逰子

    Table of Contents