React CRON定時器

  1. CRON概念
  2. REACT框架下制作CRON定時器
  3. 參考:在線CRON表達(dá)式生成器
  4. Github Repository - Cron Generator
  5. 使用UI組件庫:Alibaba Fusion

一纷跛、CRON 概念

  1. 七個閾:秒艳丛、分嚷缭、小時、日辜窑、月、星期纷纫、年
  2. 閾值詳解:
cron Seconds Minutes Hours Day of Month Month Day of Week Year
閾名 小時 星期
數(shù)值范圍 0-59 0-59 0-23 0-31 1-12 1-7 1970 - 2099
表達(dá)式 , - * / , - * / , - * / , - * /
? L W C
, - * / , - * /
? L C #
, - * /
  1. 表達(dá)式解釋:
表達(dá)式 解釋 樣例
- 表示范圍 周期從第1秒到第5秒:1-5
/ 起始觸發(fā)事件/相隔時間 從第5min開始里覆,每20min觸發(fā)一次(5min,25min烹骨,45min):5/20
, 枚舉值 5,12,20
L 用于“星期(Day of Week)”翻伺、“日(Day of Month)”中,表示最后沮焕。
備注:使用L的時候吨岭,不要指定范圍
本月最后一個周五觸發(fā):5L
W 用于“日(Day of Month)”中,表示距離X日最近的一個本月的工作日 距離5號的最近的一個工作日:5W
LW 某月最后一個工作日(周五)
# 用于“星期(Day of Week)”峦树,表示每月第幾個星期幾 第二周的周四:2#4
? 用于“星期(Day of Week)”辣辫、“日(Day of Month)”中,表示“不指定”

備注:關(guān)于C的解釋暫未找到相關(guān)細(xì)節(jié)

  1. 特殊約束:
  • 選擇月份的時候空入,日期不能為 * 和 ?
  • 星期默認(rèn)值為 络它?
  • 不能同時指定星期和日期,其中之一必須為 歪赢?

二、構(gòu)建核心代碼模塊

  1. 分析:
  • 七個閾值共同的部分:


    七個閾共同內(nèi)容:, - * /
  • 日期特殊部分:


    日期特殊部分:? L W
  • 星期(周)特殊部分:


    星期特殊部分:? L #
  1. 根據(jù)cron七個閾单料,創(chuàng)建所需使用的state對象:
state = {
        // 各個cron閾表達(dá)式
        express: {
            second: '*',
            minute: '*',
            hour: '*',
            date: '*',
            month: '*',
            week: '?',
            year: '*'
        },

        // 每個閾選擇的表達(dá)式類型
        expressType: {
            second: 'everyTime',
            minute: 'everyTime',
            hour: 'everyTime',
            date: 'everyTime',
            month: 'everyTime',
            week: 'everyTime',
            year: 'everyTime',
        },

        // 周期選擇 - 最小時間 / 最大時間
        periodTime: {
            second: { max:2, min:1 },
            minute: { max: 2, min: 1 },
            hour: { max: 2, min: 1 },
            date: { max: 2, min: 1 },
            month: { max: 2, min: 1 },
            week: { max: 2, min: 1 },
            year: { max: 2020, min: 2020 },
        },

        // 循環(huán)選擇 - 開始時間./ 執(zhí)行周期
        loopTime: {
            second: { startTime:1, period:1 },
            minute: { startTime: 1, period: 1 },
            hour: { startTime: 1, period: 1 },
            date: { startTime: 1, period: 1 },
            month: { startTime: 1, period: 1 },
        },

        // 枚舉指定選擇 - 指定時間節(jié)點(diǎn)數(shù)組埋凯,例如日期數(shù)組
        enumTime: {
            second: [],
            minute: [],
            hour: [],
            date: [],
            month: [],
            week: []
        },

        // 最近工作日 - 距離X日最近的工作日
        mostRecentWorkDay: 1,

        // 當(dāng)前月份最后一個星期X
        lastWeekDay: 1,

        // 指定X周的星期X - 例如:第一周的星期三
        weekday: {
            weekNum: 1,
            weekDayNum: 1
        },

        alertMsg: null
    }
  1. 選擇每個閾的類型的時候,根據(jù)所選值扫尖,獲取當(dāng)前域的表達(dá)式
    /**
  * @name: 選擇每個表達(dá)式的具體展示類型
  * @param {*} type 表達(dá)式內(nèi)容類型:everyTime, period, loop, enum 等
  * @param {*} expressType cron類型:second, minute, hour, date, month, week, year
  * @return {*}
  */
    onSelectType = (type, expressType) => {
        let expressionTypeState = this.state.expressType
        expressionTypeState[expressType] = type

        this.setState({ expressType: expressionTypeState }, () => {
            // 特殊邏輯判斷白对,獲取最新表達(dá)式
            this.judgeSpecialCRON(type, expressType)
        })
    }

/**
  * @name: 表達(dá)式校驗(yàn)并對表達(dá)式進(jìn)行賦值
  * @param {*} type 當(dāng)前選中閾值的表達(dá)式類型,例如:everyTime
  * @param {*} expressType 當(dāng)前選中的閾值的類型换怖,例如:date甩恼、minute
  * @return {*}
  */
    judgeSpecialCRON = (type, expressType) => {
        let expressState = JSON.parse(JSON.stringify(this.state.express))

        if (type === this.state.expressType[expressType]) {
            expressState[expressType] = this.getExpressStr(type, expressType)
        }

        // 特殊賦值邏輯
        switch (expressType) {
            case 'date': {
                // 日期和星期不可同時為?(不指定)
                if (expressState.date === '?' && expressState.week === '?') {
                    Message.warning('日期和星期不可同時均不指定任何值')
                    expressState.week = '*'
                }
                // 日期和星期不可同時指定內(nèi)容(非?)
                if (expressState.date !== '?' && expressState.week !== '?') {
                    Message.warning('日期和星期不可同時指定任何值')
                    expressState.week = '?'
                }
                break
            }
            case 'month': {
                // 當(dāng)月份選擇為*(任意)時条摸,星期設(shè)定為悦污?(不指定)
                Message.warning('當(dāng)月份選擇為任意值時,星期設(shè)定為不指定模式(?)')
                if (expressState.month === '*') {
                    expressState.week = '?'
                    if (expressState.date === '?') {
                        expressState.date = '*'
                    }
                }
                break
            }
            case 'week': {
                // 日期和星期不可同時為钉蒲?(不指定)
                if (expressState.week === '?' && expressState.date === '?') {
                    expressState.date = '*'
                    Message.warning('日期和星期不可同時均不指定任何值')
                }
                // 日期和星期不可同時指定內(nèi)容(非切端?)
                if (expressState.week !== '?' && expressState.date !== '?') {
                    expressState.date = '?'
                    Message.warning('日期和星期不可同時指定任何值')
                }
                break
            }
            default: break
        }

        this.setState({ express: expressState })
    }
  1. 根據(jù)每個閾的規(guī)則,獲取每個閾的cron表達(dá)式:
  /**
  * @name: 獲取每個cron閾的表達(dá)式字符串
  * @param {*} type
  * @param {*} expressType
  * @return {*}
  */
    getExpressStr = (type, expressType) => {
            switch (type) {
                case 'everyTime': return '*'
                case 'none': return '?'
                case 'lastDay': return 'L'
                case 'period': return this.expressGenerator(type, this.state.periodTime[expressType])
                case 'loop': return this.expressGenerator(type, this.state.loopTime[expressType])
                case 'enum': return this.expressGenerator(type, this.state.enumTime[expressType])
                case 'mostRecentWorkDay': return this.state.mostRecentWorkDay + 'W'
                case 'lastWeekDay': return this.state.lastWeekDay + 'L'
                case 'weekday': return this.expressGenerator(type, this.state.weekday)
                case 'optional': return ''
                default: return
            }
    }

  /**
  * @name: 獲取cron表達(dá)式
  * @param {type}
  * @return {type}
  */
    expressGenerator = (type, value) => {
        function getEnumString (values) {
            if (values.length > 0) {
                let str = ''
                values.map(item => {
                    str = str + ',' + item
                })
                str = str.substring(1, str.length)
                return str
            }
            else
                return '*'
        }
        switch (type) {
            case 'everyTime': return '*'
            case 'period': return value ? value.min + '-' + value.max : ''
            case 'loop': return value ? value.startTime + '/' + value.period : ''
            case 'enum': return getEnumString(value)
            case 'weekday': return value ? value.weekNum + '#' + value.weekDayNum : ''
            default: return '*'
        }
    }
  1. 反解析cron表達(dá)式到UI的方法:
/**
  * @name: 反編譯解析表達(dá)式
  * @param {*} express 表達(dá)式對象
  * @return {*}
  */ 
    reverseGenerateCRON = (express) => {
        let newExpressType = JSON.parse(JSON.stringify(this.state.expressType))

        newExpressType.second = this.getType(express.second, 'second')
        newExpressType.minute = this.getType(express.minute, 'minute')
        newExpressType.hour = this.getType(express.hour, 'hour')
        newExpressType.date = this.getType(express.date, 'date')
        newExpressType.month = this.getType(express.month, 'month')
        newExpressType.week = this.getType(express.week, 'week')
        newExpressType.year = this.getType(express.year, 'year')

        this.setState({ expressType: newExpressType })

    }

 /**
  * @name: 反解析cron表達(dá)式到UI時顷啼,每個閾內(nèi)容進(jìn)行解析 - 獲取每個閾使用的類型和具體值
  * @param {*} expStr cron表達(dá)式內(nèi)容踏枣,例如:*,5L
  * @param {*} expressType cron閾類型钙蒙,例如:minute茵瀑,hour,week
  * @return {*} 每個閾使用的類型
  */ 
    getType = (expStr, expressType) => {
        if (expStr === '*') return 'everyTime'

        else if (expStr === '?') return 'none'

        else if (expStr === 'L') return 'lastDay'

        else if (new RegExp('[-]').test(expStr)) {
            let newPeriodTime = this.state.periodTime
            let values = expStr.split('-')
            newPeriodTime[expressType] = { max: parseInt(values[1]), min: parseInt(values[0]) }

            this.setState({ periodTime: newPeriodTime })
            return 'period'
        }

        else if (new RegExp('[/]').test(expStr)) {
            let newLoopTime = this.state.loopTime
            let values = expStr.split('/')
            newLoopTime[expressType] = { startTime: parseInt(values[0]), period: parseInt(values[1]) }

            this.setState({ loopTime: newLoopTime })
            return 'loop'
        }

        else if (new RegExp('[,]').test(expStr)) {
            let newEnumTime = this.state.enumTime
            let values = expStr.split(',')
            for (let i = 0; i < values.length; i++) {
                values[i] = parseInt(values[i])
            }
            newEnumTime[expressType] = values

            this.setState({ enumTime: newEnumTime })
            return 'enum'
        }

        else if (new RegExp('[W]').test(expStr)) {
            let value = parseInt(expStr.substring(0, 1))
            console.log(value)
            this.setState({ mostRecentWorkDay: value })
            return 'mostRecentWorkDay'
        }

        else if (new RegExp('[L]').test(expStr)) {
            this.setState({ lastWeekDay: parseInt(expStr.substring(0, 1)) })
            return 'lastWeekDay'
        }

        else if (new RegExp('[#]').test(expStr)) {
            let newWeekday = this.state.weekday
            let values = expStr.split('#')

            newWeekday = { weekNum: parseInt(values[0]), weekDayNum: parseInt(values[1]) }

            this.setState({ weekday: newWeekday })
            return 'weekday'
        }

        else if (expStr === '') {
            return 'optional'
        }

        else return
    }
  1. render()渲染部分代碼:請參考github代碼內(nèi)容

  2. 組件引用方法:

import CronGenerator from '@/components/CronGenerator'
export default class App extends React.Component {
  state = {
    cronGeneratorVisible: false,
    cronString: '* * * ? * * *'
  }

  onClose = () => {
    this.setState({ cronGeneratorVisible: false })
  }
  
  onConfirm = (val) => {
    this.setState({ cronString: val})
  }

  render () {
    return (
      <div className="cronGeneratorApp">
          <p>CRON Generator Sample:</p>
          <div className="cron_input">
            <Input value={cronString} />
            <Button type="primary" onClick={() => { this.setState({ cronGeneratorVisible: true }) }}>CRON</Button>
          </div>

          <CronGenerator
            isPreview
            initCron={cronString}
            dialogVisible={cronGeneratorVisible}
            onClose={this.onClose}
            onConfirm={this.onConfirm}
          />
        </div>
    )
  }
}
參數(shù)名稱 參數(shù)描述 參數(shù)類型 默認(rèn)值 備注
isPreview 是否開啟預(yù)覽模式 Boolean false 該模式下躬厌,將不會顯示新增取消按鈕
initCron 需傳入解析到UI界面的CRON字符串 String - -
dialogVisible CRON對話框顯示 Boolean false -
onClose 關(guān)閉對話框?qū)?yīng)方法马昨,且取消獲取CRON Function - -
onConfirm 關(guān)閉對話框且獲取到最新的CRON表達(dá)式 Function - 該方法中會傳入一個參數(shù)val,代表生成的cron表達(dá)式

三烤咧、結(jié)果頁面:

首頁 - 文本輸入框 + 獲取cron表達(dá)式按鈕偏陪,包含初始化默認(rèn)表達(dá)式
second
minute
hour
Day of Month
month
Day of Week
year
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市煮嫌,隨后出現(xiàn)的幾起案子笛谦,更是在濱河造成了極大的恐慌,老刑警劉巖昌阿,帶你破解...
    沈念sama閱讀 222,729評論 6 517
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件饥脑,死亡現(xiàn)場離奇詭異,居然都是意外死亡懦冰,警方通過查閱死者的電腦和手機(jī)灶轰,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 95,226評論 3 399
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來刷钢,“玉大人笋颤,你說我怎么就攤上這事∧诘兀” “怎么了伴澄?”我有些...
    開封第一講書人閱讀 169,461評論 0 362
  • 文/不壞的土叔 我叫張陵,是天一觀的道長阱缓。 經(jīng)常有香客問我非凌,道長,這世上最難降的妖魔是什么荆针? 我笑而不...
    開封第一講書人閱讀 60,135評論 1 300
  • 正文 為了忘掉前任敞嗡,我火速辦了婚禮颁糟,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘喉悴。我一直安慰自己棱貌,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 69,130評論 6 398
  • 文/花漫 我一把揭開白布粥惧。 她就那樣靜靜地躺著键畴,像睡著了一般。 火紅的嫁衣襯著肌膚如雪突雪。 梳的紋絲不亂的頭發(fā)上起惕,一...
    開封第一講書人閱讀 52,736評論 1 312
  • 那天,我揣著相機(jī)與錄音咏删,去河邊找鬼惹想。 笑死,一個胖子當(dāng)著我的面吹牛督函,可吹牛的內(nèi)容都是我干的嘀粱。 我是一名探鬼主播,決...
    沈念sama閱讀 41,179評論 3 422
  • 文/蒼蘭香墨 我猛地睜開眼辰狡,長吁一口氣:“原來是場噩夢啊……” “哼锋叨!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起宛篇,我...
    開封第一講書人閱讀 40,124評論 0 277
  • 序言:老撾萬榮一對情侶失蹤娃磺,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后叫倍,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體偷卧,經(jīng)...
    沈念sama閱讀 46,657評論 1 320
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 38,723評論 3 342
  • 正文 我和宋清朗相戀三年吆倦,在試婚紗的時候發(fā)現(xiàn)自己被綠了听诸。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 40,872評論 1 353
  • 序言:一個原本活蹦亂跳的男人離奇死亡蚕泽,死狀恐怖晌梨,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情须妻,我是刑警寧澤派任,帶...
    沈念sama閱讀 36,533評論 5 351
  • 正文 年R本政府宣布,位于F島的核電站璧南,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏师逸。R本人自食惡果不足惜司倚,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 42,213評論 3 336
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧动知,春花似錦皿伺、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,700評論 0 25
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至丹皱,卻和暖如春妒穴,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背摊崭。 一陣腳步聲響...
    開封第一講書人閱讀 33,819評論 1 274
  • 我被黑心中介騙來泰國打工讼油, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人呢簸。 一個月前我還...
    沈念sama閱讀 49,304評論 3 379
  • 正文 我出身青樓矮台,卻偏偏與公主長得像,于是被迫代替她去往敵國和親根时。 傳聞我的和親對象是個殘疾皇子瘦赫,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,876評論 2 361

推薦閱讀更多精彩內(nèi)容