- CRON概念
- REACT框架下制作CRON定时器
- 参考:在线CRON表达式生成器
- Github Repository - Cron Generator
- 使用UI组件库:Alibaba Fusion
一、CRON 概念
- 七个阈:秒、分、小时、日、月、星期、年
- 阈值详解:
cron | Seconds | Minutes | Hours | Day of Month | Month | Day of Week | Year |
---|---|---|---|---|---|---|---|
阈名 | 秒 | 分 | 小时 | 日 | 月 | 星期 | 年 |
数值范围 | 0-59 | 0-59 | 0-23 | 0-31 | 1-12 | 1-7 | 1970 - 2099 |
表达式 | , - * / | , - * / | , - * / | , - * / ? L W C |
, - * / | , - * / ? L C # |
, - * / |
- 表达式解释:
表达式 | 解释 | 样例 |
---|---|---|
- | 表示范围 | 周期从第1秒到第5秒:1-5 |
/ | 起始触发事件/相隔时间 | 从第5min开始,每20min触发一次(5min,25min,45min):5/20 |
, | 枚举值 | 5,12,20 |
L | 用于“星期(Day of Week)”、“日(Day of Month)”中,表示最后。 备注:使用L的时候,不要指定范围 |
本月最后一个周五触发:5L |
W | 用于“日(Day of Month)”中,表示距离X日最近的一个本月的工作日 | 距离5号的最近的一个工作日:5W |
LW | 某月最后一个工作日(周五) | |
# | 用于“星期(Day of Week)”,表示每月第几个星期几 | 第二周的周四:2#4 |
? | 用于“星期(Day of Week)”、“日(Day of Month)”中,表示“不指定” |
备注:关于C的解释暂未找到相关细节
- 特殊约束:
- 选择月份的时候,日期不能为 * 和 ?
- 星期默认值为 ?
- 不能同时指定星期和日期,其中之一必须为 ?
二、构建核心代码模块
- 分析:
-
七个阈值共同的部分:
七个阈共同内容:, - * / -
日期特殊部分:
日期特殊部分:? L W -
星期(周)特殊部分:
星期特殊部分:? L #
- 根据cron七个阈,创建所需使用的state对象:
state = {
// 各个cron阈表达式
express: {
second: '*',
minute: '*',
hour: '*',
date: '*',
month: '*',
week: '?',
year: '*'
},
// 每个阈选择的表达式类型
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 },
},
// 循环选择 - 开始时间./ 执行周期
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 },
},
// 枚举指定选择 - 指定时间节点数组,例如日期数组
enumTime: {
second: [],
minute: [],
hour: [],
date: [],
month: [],
week: []
},
// 最近工作日 - 距离X日最近的工作日
mostRecentWorkDay: 1,
// 当前月份最后一个星期X
lastWeekDay: 1,
// 指定X周的星期X - 例如:第一周的星期三
weekday: {
weekNum: 1,
weekDayNum: 1
},
alertMsg: null
}
- 选择每个阈的类型的时候,根据所选值,获取当前域的表达式
/**
* @name: 选择每个表达式的具体展示类型
* @param {*} type 表达式内容类型: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 }, () => {
// 特殊逻辑判断,获取最新表达式
this.judgeSpecialCRON(type, expressType)
})
}
/**
* @name: 表达式校验并对表达式进行赋值
* @param {*} type 当前选中阈值的表达式类型,例如:everyTime
* @param {*} expressType 当前选中的阈值的类型,例如: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 = '*'
}
// 日期和星期不可同时指定内容(非?)
if (expressState.date !== '?' && expressState.week !== '?') {
Message.warning('日期和星期不可同时指定任何值')
expressState.week = '?'
}
break
}
case 'month': {
// 当月份选择为*(任意)时,星期设定为?(不指定)
Message.warning('当月份选择为任意值时,星期设定为不指定模式(?)')
if (expressState.month === '*') {
expressState.week = '?'
if (expressState.date === '?') {
expressState.date = '*'
}
}
break
}
case 'week': {
// 日期和星期不可同时为?(不指定)
if (expressState.week === '?' && expressState.date === '?') {
expressState.date = '*'
Message.warning('日期和星期不可同时均不指定任何值')
}
// 日期和星期不可同时指定内容(非?)
if (expressState.week !== '?' && expressState.date !== '?') {
expressState.date = '?'
Message.warning('日期和星期不可同时指定任何值')
}
break
}
default: break
}
this.setState({ express: expressState })
}
- 根据每个阈的规则,获取每个阈的cron表达式:
/**
* @name: 获取每个cron阈的表达式字符串
* @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表达式
* @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 '*'
}
}
- 反解析cron表达式到UI的方法:
/**
* @name: 反编译解析表达式
* @param {*} express 表达式对象
* @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表达式到UI时,每个阈内容进行解析 - 获取每个阈使用的类型和具体值
* @param {*} expStr cron表达式内容,例如:*,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
}
render()渲染部分代码:请参考github代码内容
组件引用方法:
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 (
CRON Generator Sample:
)
}
}
参数名称 | 参数描述 | 参数类型 | 默认值 | 备注 |
---|---|---|---|---|
isPreview | 是否开启预览模式 | Boolean | false | 该模式下,将不会显示新增取消按钮 |
initCron | 需传入解析到UI界面的CRON字符串 | String | - | - |
dialogVisible | CRON对话框显示 | Boolean | false | - |
onClose | 关闭对话框对应方法,且取消获取CRON | Function | - | - |
onConfirm | 关闭对话框且获取到最新的CRON表达式 | Function | - | 该方法中会传入一个参数val,代表生成的cron表达式 |
三、结果页面:
首页 - 文本输入框 + 获取cron表达式按钮,包含初始化默认表达式
second
minute
hour
Day of Month
month
Day of Week
year