<template> <div class="j-easy-cron"> <div class="content"> <div> <a-tabs size="small" v-model="curtab"> <a-tab-pane tab="秒" key="second" v-if="!hideSecond"> <second-ui v-model="second" :disabled="disabled"></second-ui> </a-tab-pane> <a-tab-pane tab="分" key="minute"> <minute-ui v-model="minute" :disabled="disabled"></minute-ui> </a-tab-pane> <a-tab-pane tab="时" key="hour"> <hour-ui v-model="hour" :disabled="disabled"></hour-ui> </a-tab-pane> <a-tab-pane tab="日" key="day"> <day-ui v-model="day" :week="week" :disabled="disabled"></day-ui> </a-tab-pane> <a-tab-pane tab="月" key="month"> <month-ui v-model="month" :disabled="disabled"></month-ui> </a-tab-pane> <a-tab-pane tab="周" key="week"> <week-ui v-model="week" :day="day" :disabled="disabled"></week-ui> </a-tab-pane> <a-tab-pane tab="年" key="year" v-if="!hideYear && !hideSecond"> <year-ui v-model="year" :disabled="disabled"></year-ui> </a-tab-pane> </a-tabs> </div> <a-divider/> <!-- 执行时间预览 --> <a-row :gutter="8"> <a-col :span="18" style="margin-top: 22px;"> <a-row :gutter="8"> <a-col :span="8" style="margin-bottom: 8px;"> <a-input addon-before="秒" v-model="inputValues.second" @blur="onInputBlur"/> </a-col> <a-col :span="8" style="margin-bottom: 8px;"> <a-input addon-before="分" v-model="inputValues.minute" @blur="onInputBlur"/> </a-col> <a-col :span="8" style="margin-bottom: 8px;"> <a-input addon-before="时" v-model="inputValues.hour" @blur="onInputBlur"/> </a-col> <a-col :span="8" style="margin-bottom: 8px;"> <a-input addon-before="日" v-model="inputValues.day" @blur="onInputBlur"/> </a-col> <a-col :span="8" style="margin-bottom: 8px;"> <a-input addon-before="月" v-model="inputValues.month" @blur="onInputBlur"/> </a-col> <a-col :span="8" style="margin-bottom: 8px;"> <a-input addon-before="周" v-model="inputValues.week" @blur="onInputBlur"/> </a-col> <a-col :span="8" style="margin-bottom: 8px;"> <a-input addon-before="年" v-model="inputValues.year" @blur="onInputBlur"/> </a-col> <a-col :span="16" style="margin-bottom: 8px;"> <a-input addon-before="Cron" v-model="inputValues.cron" @blur="onInputCronBlur"/> </a-col> </a-row> </a-col> <a-col :span="6"> <div>近十次执行时间(不含年)</div> <a-textarea type="textarea" :value="preTimeList" :rows="5"/> </a-col> </a-row> </div> </div> </template> <script> import SecondUi from './tabs/second' import MinuteUi from './tabs/minute' import HourUi from './tabs/hour' import DayUi from './tabs/day' import WeekUi from './tabs/week' import MonthUi from './tabs/month' import YearUi from './tabs/year' import CronParser from 'cron-parser' import dateFormat from './format-date' import {simpleDebounce} from '@/utils/util' import ACol from 'ant-design-vue/es/grid/Col' export default { name: 'easy-cron', components: { ACol, SecondUi, MinuteUi, HourUi, DayUi, WeekUi, MonthUi, YearUi }, props: { cronValue: { type: String, default: '' }, disabled: { type: Boolean, default: false }, hideSecond: { type: Boolean, default: false }, hideYear: { type: Boolean, default: false }, remote: { type: Function, default: null } }, data() { return { curtab: this.hideSecond ? 'minute' : 'second', second: '*', minute: '*', hour: '*', day: '*', month: '*', week: '?', year: '*', inputValues: {second: '', minute: '', hour: '', day: '', month: '', week: '', year: '', cron: ''}, preTimeList: '执行预览,会忽略年份参数', } }, computed: { cronValue_c() { let result = [] if (!this.hideSecond) result.push(this.second ? this.second : '*') result.push(this.minute ? this.minute : '*') result.push(this.hour ? this.hour : '*') result.push(this.day ? this.day : '*') result.push(this.month ? this.month : '*') result.push(this.week ? this.week : '?') if (!this.hideYear && !this.hideSecond) result.push(this.year ? this.year : '*') return result.join(' ') }, cronValue_c2() { const v = this.cronValue_c if (this.hideYear || this.hideSecond) return v const vs = v.split(' ') if (vs.length >= 6) { // 将 Quartz 星期 的规则转换为 CronParser 的规则 vs[5] = this.convertQuartzWeekToCParser(vs[5]) } return vs.slice(0, vs.length - 1).join(' ') }, }, watch: { cronValue(newVal, oldVal) { if (newVal === this.cronValue_c) { // console.info('same cron value: ' + newVal) return } this.formatValue() }, cronValue_c(newVal, oldVal) { this.calTriggerList() this.$emit('change', newVal) this.assignInput() }, minute() { if (this.second === '*') { this.second = '0' } }, hour() { if (this.minute === '*') { this.minute = '0' } }, day(day) { if (day !== '?' && this.hour === '*') { this.hour = '0' } }, week(week) { if (week !== '?' && this.hour === '*') { this.hour = '0' } }, month() { if (this.day === '?' && this.week === '*') { this.week = '1' } else if (this.week === '?' && this.day === '*') { this.day = '1' } }, year() { if (this.month === '*') { this.month = '1' } }, }, created() { this.formatValue() this.$nextTick(() => { this.calTriggerListInner() }) }, methods: { assignInput() { Object.assign(this.inputValues, { second: this.second, minute: this.minute, hour: this.hour, day: this.day, month: this.month, week: this.week, year: this.year, cron: this.cronValue_c, }) }, formatValue() { if (!this.cronValue) return const values = this.cronValue.split(' ').filter(item => !!item) if (!values || values.length <= 0) return let i = 0 if (!this.hideSecond) this.second = values[i++] if (values.length > i) this.minute = values[i++] if (values.length > i) this.hour = values[i++] if (values.length > i) this.day = values[i++] if (values.length > i) this.month = values[i++] if (values.length > i) this.week = values[i++] if (values.length > i) this.year = values[i] this.assignInput() }, // 将 Quartz 星期 的规则转换为 CronParser 的规则: // Quartz 的规则:1 = 周日,2 = 周一,3 = 周二,4 = 周三,5 = 周四,6 = 周五,7 = 周六 // CronParser 的规则: 0 = 周日,1 = 周一,2 = 周二,3 = 周三,4 = 周四,5 = 周五,6 = 周六,7 = 周日 convertQuartzWeekToCParser(week) { let convert = (v) => { if (v === '0') { return '1' } if (v === '1') { return '0' } return (Number.parseInt(v) - 1).toString() } // 匹配示例 1-7 or 1/7 let patten1 = /^([0-7])([-/])([0-7])$/ // 匹配示例 1,4,7 let patten2 = /^([0-7])(,[0-7])+$/ if (/^[0-7]$/.test(week)) { return convert(week) } else if (patten1.test(week)) { return week.replace(patten1, ($0, before, separator, after) => { if (separator === '/') { return convert(before) + separator + after } else { return convert(before) + separator + convert(after) } }) } else if (patten2.test(week)) { return week.split(',').map(v => convert(v)).join(',') } return week }, calTriggerList: simpleDebounce(function () { this.calTriggerListInner() }, 500), calTriggerListInner() { // 设置了回调函数 if (this.remote) { this.remote(this.cronValue_c, +new Date(), v => { this.preTimeList = v }) return } const format = 'yyyy-MM-dd hh:mm:ss' const options = { currentDate: dateFormat(new Date(), format) } const iter = CronParser.parseExpression(this.cronValue_c2, options) const result = [] for (let i = 1; i <= 10; i++) { result.push(dateFormat(new Date(iter.next()), format)) } this.preTimeList = result.length > 0 ? result.join('\n') : '无执行时间' }, onInputBlur() { this.second = this.inputValues.second this.minute = this.inputValues.minute this.hour = this.inputValues.hour this.day = this.inputValues.day this.month = this.inputValues.month this.week = this.inputValues.week this.year = this.inputValues.year }, onInputCronBlur(event) { this.$emit('change', event.target.value) }, }, model: { prop: 'cronValue', event: 'change' }, } </script> <style scoped lang="less"> .j-easy-cron { /deep/ .content { .ant-checkbox-wrapper + .ant-checkbox-wrapper { margin-left: 0; } } } </style>