vue3的CRON表达式组件用法解读
作者:没头脑和不高兴y
该文档介绍了Cron表达式生成器的使用说明,该组件通过图形化界面帮助用户配置定时任务的执行计划,支持秒、分钟、小时、日、月、周等时间单位的灵活配置,并实时计算并显示最近5次执行时间
Cron 表达式生成器使用说明文档
功能概述
本组件是一个可视化 Cron 表达式生成器,用于帮助用户通过图形化界面配置定时任务的执行计划。
支持秒、分钟、小时、日、月、周等时间单位的灵活配置,并实时计算显示最近5次执行时间。支持编辑和回显
主要功能
1. 时间单位配置
- 秒:支持每秒、周期范围、间隔执行和指定秒数
- 分钟:支持每分钟、周期范围、间隔执行和指定分钟数
- 小时:支持每小时、周期范围、间隔执行和指定小时数
- 日:支持每日、不指定、周期范围、间隔执行、工作日、月末和指定日期
- 月:支持每月、不指定、周期范围、间隔执行和指定月份
- 周:支持每周、不指定、周期范围、间隔执行、月末周和指定星期
2. 实时预览
- 实时生成并显示 Cron 表达式
- 自动计算并显示最近5次执行时间
使用说明
基本操作
- 通过顶部标签页切换不同时间单位
- 选择对应的时间配置方式(单选按钮)
- 根据选择的配置方式填写具体参数
- 系统自动生成 Cron 表达式并显示执行时间
配置选项说明
秒配置
- 每秒:任务每秒执行一次
- 周期从...到...秒:在指定秒数范围内执行
- 周期从...秒开始,每...秒执行一次:从指定秒数开始,按间隔执行
- 指定秒数:勾选具体的秒数执行
分钟配置
- 每分钟:任务每分钟执行一次
- 周期从...到...分钟:在指定分钟范围内执行
- 周期从...分钟开始,每...分钟执行一次:从指定分钟开始,按间隔执行
- 指定分钟数:勾选具体的分钟数执行
小时配置
- 每小时:任务每小时执行一次
- 周期从...到...小时:在指定小时范围内执行
- 周期从...小时开始,每...小时执行一次:从指定小时开始,按间隔执行
- 指定小时数:勾选具体的小时数执行
日配置
- 每日:任务每天执行一次
- 不指定:不指定具体日期
- 周期从...到...日:在指定日期范围内执行
- 周期从...日开始,每...日执行一次:从指定日期开始,按间隔执行
- 每月...号最近的那个工作日:指定日期最近的工作日执行
- 本月最后一天:每月最后一天执行
- 指定日期:勾选具体的日期执行
月配置
- 每月:任务每月执行一次
- 不指定:不指定具体月份
- 周期从...到...月:在指定月份范围内执行
- 周期从...月开始,每...月执行一次:从指定月份开始,按间隔执行
- 指定月份:勾选具体的月份执行
周配置
- 每周:任务每周执行一次
- 不指定:不指定具体星期
- 周期从星期...到星期...:在指定星期范围内执行
- 第...周的星期...:指定某周的某天执行
- 本月最后一个星期...:每月最后一个指定星期执行
- 指定星期:勾选具体的星期执行
技术实现
核心功能
- 使用
cron-parser库解析和计算 Cron 表达式 - 使用
dayjs处理日期时间格式化 - 基于 Vue 3 的 Composition API 实现响应式逻辑
- 使用 Element Plus 组件库构建 UI
数据流
用户通过界面配置时间参数
组件内部将配置转换为标准 Cron 表达式
通过 v-model 将表达式传递给父组件
同时计算并显示最近5次执行时间
<template>
<div style="width: 700px; margin: 0 auto; padding: 20px" class="cron-selector">
<el-tabs v-model="activeTab" type="border-card" :lazy="true">
<el-tab-pane name="秒" label="秒">
<el-radio-group @change="() => resetRadioValues('second')" v-model="secondRadio">
<el-radio value="1" size="large">每秒 允许的通配符[,-*/]</el-radio>
<el-radio value="2" size="large">
<template #default>
<div style="display: flex; align-items: center">
<span style="margin-right: 8px">周期从</span>
<el-input v-model="secondStart1" style="width: 60px; margin-right: 8px" />
<span style="margin-right: 8px">到</span>
<el-input v-model="secondEnd1" style="width: 60px; margin-right: 8px" />
<span>秒</span>
</div>
</template>
</el-radio>
<el-radio value="3" size="large">
<template #default>
<div style="display: flex; align-items: center">
<span style="margin-right: 8px">周期从</span>
<el-input v-model="secondStart2" style="width: 60px; margin-right: 8px" />
<span style="margin-right: 8px">秒开始,每</span>
<el-input v-model="secondEnd2" style="width: 60px; margin-right: 8px" />
<span>秒执行一次</span>
</div>
</template>
</el-radio>
<el-radio value="4" size="large">
<template #default>
<div style="display: flex; align-items: center">
<span style="margin-right: 8px">指定</span>
<el-checkbox-group :disabled="secondRadio !== '4'" v-model="checkSeconds">
<el-checkbox
v-for="(item, index) in secondList"
:label="item"
:value="item"
:key="index"
/>
</el-checkbox-group>
</div>
</template>
</el-radio>
</el-radio-group>
</el-tab-pane>
<el-tab-pane name="分钟" label="分钟">
<el-radio-group @change="() => resetRadioValues('minutes')" v-model="minutesRadio">
<el-radio value="1" size="large">每分钟 允许的通配符[,-*/]</el-radio>
<el-radio value="2" size="large">
<template #default>
<div style="display: flex; align-items: center">
<span style="margin-right: 8px">周期从</span>
<el-input v-model="minutesStart1" style="width: 60px; margin-right: 8px" />
<span style="margin-right: 8px">到</span>
<el-input v-model="minutesEnd1" style="width: 60px; margin-right: 8px" />
<span>分钟</span>
</div>
</template>
</el-radio>
<el-radio value="3" size="large">
<template #default>
<div style="display: flex; align-items: center">
<span style="margin-right: 8px">周期从</span>
<el-input v-model="minutesStart2" style="width: 60px; margin-right: 8px" />
<span style="margin-right: 8px">分钟开始,每</span>
<el-input v-model="minutesEnd2" style="width: 60px; margin-right: 8px" />
<span>分钟执行一次</span>
</div>
</template>
</el-radio>
<el-radio value="4" size="large">
<template #default>
<div style="display: flex; align-items: center">
<span style="margin-right: 8px">指定</span>
<el-checkbox-group v-model="checkMinutes" :disabled="minutesRadio !== '4'">
<el-checkbox
v-for="(item, index) in minuteList"
:label="item"
:value="item"
:key="index"
/>
</el-checkbox-group>
</div>
</template>
</el-radio>
</el-radio-group>
</el-tab-pane>
<el-tab-pane name="小时" label="小时">
<el-radio-group @change="() => resetRadioValues('hour')" v-model="hourRadio">
<el-radio value="1" size="large">每小时 允许的通配符[,-*/]</el-radio>
<el-radio value="2" size="large">
<template #default>
<div style="display: flex; align-items: center">
<span style="margin-right: 8px">周期从</span>
<el-input v-model="hourStart1" style="width: 60px; margin-right: 8px" />
<span style="margin-right: 8px">到</span>
<el-input v-model="hourEnd1" style="width: 60px; margin-right: 8px" />
<span>小时</span>
</div>
</template>
</el-radio>
<el-radio value="3" size="large">
<template #default>
<div style="display: flex; align-items: center">
<span style="margin-right: 8px">周期从</span>
<el-input v-model="hourStart2" style="width: 60px; margin-right: 8px" />
<span style="margin-right: 8px">小时开始,每</span>
<el-input v-model="hourEnd2" style="width: 60px; margin-right: 8px" />
<span>小时执行一次</span>
</div>
</template>
</el-radio>
<el-radio value="4" size="large">
<template #default>
<div style="display: flex; align-items: center">
<span style="margin-right: 8px">指定</span>
<el-checkbox-group v-model="checkHour" :disabled="hourRadio !== '4'">
<el-checkbox
v-for="(item, index) in hourList"
:label="item"
:value="item"
:key="index"
/>
</el-checkbox-group>
</div>
</template>
</el-radio>
</el-radio-group>
</el-tab-pane>
<el-tab-pane name="日" label="日">
<el-radio-group @change="() => resetRadioValues('day')" v-model="dayRadio">
<el-radio value="1" size="large">每日 允许的通配符[,-*/]</el-radio>
<el-radio value="2" size="large">不指定</el-radio>
<el-radio value="3" size="large">
<template #default>
<div style="display: flex; align-items: center">
<span style="margin-right: 8px">周期从</span>
<el-input v-model="dayStart1" style="width: 60px; margin-right: 8px" />
<span style="margin-right: 8px">到</span>
<el-input v-model="dayEnd1" style="width: 60px; margin-right: 8px" />
<span>日</span>
</div>
</template>
</el-radio>
<el-radio value="4" size="large">
<template #default>
<div style="display: flex; align-items: center">
<span style="margin-right: 8px">周期从</span>
<el-input v-model="dayStart2" style="width: 60px; margin-right: 8px" />
<span style="margin-right: 8px">日开始,每</span>
<el-input v-model="dayEnd2" style="width: 60px; margin-right: 8px" />
<span>日执行一次</span>
</div>
</template>
</el-radio>
<el-radio value="5" size="large">
<template #default>
<div style="display: flex; align-items: center">
<span style="margin-right: 8px">每月</span>
<el-input v-model="day" style="width: 60px; margin-right: 8px" />
<span>号最近的那个工作日</span>
</div>
</template>
</el-radio>
<el-radio value="6" size="large">本月最后一天</el-radio>
<el-radio value="7" size="large">
<template #default>
<div style="display: flex; align-items: center">
<span style="margin-right: 8px">指定</span>
<el-checkbox-group v-model="checkDay" :disabled="dayRadio !== '7'">
<el-checkbox
v-for="(item, index) in dayList"
:label="item"
:value="item"
:key="index"
/>
</el-checkbox-group>
</div>
</template>
</el-radio>
</el-radio-group>
</el-tab-pane>
<el-tab-pane name="月" label="月">
<el-radio-group @change="() => resetRadioValues('month')" v-model="monthRadio">
<el-radio value="1" size="large">每月 允许的通配符[,-*/]</el-radio>
<el-radio value="2" size="large">不指定</el-radio>
<el-radio value="3" size="large">
<template #default>
<div style="display: flex; align-items: center">
<span style="margin-right: 8px">周期从</span>
<el-input v-model="monthStart1" style="width: 60px; margin-right: 8px" />
<span style="margin-right: 8px">到</span>
<el-input v-model="monthEnd1" style="width: 60px; margin-right: 8px" />
<span>月</span>
</div>
</template>
</el-radio>
<el-radio value="4" size="large">
<template #default>
<div style="display: flex; align-items: center">
<span style="margin-right: 8px">周期从</span>
<el-input v-model="monthStart2" style="width: 60px; margin-right: 8px" />
<span style="margin-right: 8px">月开始,每</span>
<el-input v-model="monthEnd2" style="width: 60px; margin-right: 8px" />
<span>月执行一次</span>
</div>
</template>
</el-radio>
<el-radio value="5" size="large">
<template #default>
<div style="display: flex; align-items: center">
<span style="margin-right: 8px;x">指定</span>
<el-checkbox-group
style="margin-top: 10px"
:disabled="monthRadio !== '5'"
v-model="selectedMonth"
>
<el-checkbox
v-for="(item, index) in monthList"
:label="item"
:value="item"
:key="index"
/>
</el-checkbox-group>
</div>
</template>
</el-radio>
</el-radio-group>
</el-tab-pane>
<el-tab-pane name="周" label="周">
<el-radio-group @change="() => resetRadioValues('week')" v-model="weekRadio">
<el-radio value="1" size="large">每周 允许的通配符[,-*/]</el-radio>
<el-radio value="2" size="large">不指定</el-radio>
<el-radio value="3" size="large">
<template #default>
<div style="display: flex; align-items: center">
<span style="margin-right: 8px">周期从星期</span>
<el-input v-model="weekStart1" style="width: 60px; margin-right: 8px" />
<span style="margin-right: 8px">到星期</span>
<el-input v-model="weekEnd1" style="width: 60px; margin-right: 8px" />
</div>
</template>
</el-radio>
<el-radio value="4" size="large">
<template #default>
<div style="display: flex; align-items: center">
<span style="margin-right: 8px">第</span>
<el-input v-model="weekStart2" style="width: 60px; margin-right: 8px" />
<span style="margin-right: 8px">周的星期</span>
<el-input v-model="weekEnd2" style="width: 60px; margin-right: 8px" />
</div>
</template>
</el-radio>
<el-radio value="5" size="large">
<template #default>
<div style="display: flex; align-items: center">
<span style="margin-right: 8px">本月最后一个星期</span>
<el-input v-model="week" style="width: 60px; margin-right: 8px" />
</div>
</template>
</el-radio>
<el-radio value="6" size="large">
<template #default>
<div style="display: flex; align-items: center">
<span style="margin-right: 8px">指定</span>
<el-checkbox-group v-model="selectedWeek" :disabled="weekRadio !== '6'">
<el-checkbox
v-for="(item, index) in weekList"
:label="item"
:value="item"
:key="index"
style="margin-bottom: 0"
/>
</el-checkbox-group>
</div>
</template>
</el-radio>
</el-radio-group>
</el-tab-pane>
<!-- <el-tab-pane name="年" label="年">
<el-radio-group @change="() => resetRadioValues('year')" v-model="yearRadio">
<el-radio value="1" size="large">不指定 允许的通配符[, - * /]</el-radio>
<el-radio value="2" size="large">每年</el-radio>
<el-radio value="3" size="large">
<template #default>
<div style="display: flex; align-items: center">
<span style="margin-right: 8px">周期从</span>
<el-input v-model="yearStart" style="width: 60px; margin-right: 8px" />
<span style="margin-right: 8px">到</span>
<el-input v-model="yearEnd" style="width: 60px; margin-right: 8px" />
</div>
</template>
</el-radio>
</el-radio-group>
</el-tab-pane> -->
</el-tabs>
<!-- 添加最近运行时间展示区域 -->
<div class="next-runs" v-if="nextRuns.length > 0">
<h4>最近5次运行时间:</h4>
<ul>
<li v-for="(run, index) in nextRuns" :key="index">{{ run }}</li>
</ul>
</div>
</div>
</template>
<script setup>
import { reactive, ref, watch, watchEffect } from 'vue'
import cronParser from 'cron-parser'
import dayjs from 'dayjs'
import { onMounted } from 'vue'
const props = defineProps({
modelValue: {
type: String,
default: ''
}
})
const activeTab = ref('秒')
const emit = defineEmits(['update:modelValue'])
const ruleFormRef = ref()
const ruleForm = ref({})
const pad = (n) => n.toString().padStart(2, '0')
const secondList = new Array(60).fill(0).map((_, index) => {
return pad(index)
})
const hourList = new Array(24).fill(0).map((_, index) => {
return pad(index)
})
const weekList = new Array(7).fill(0).map((_, index) => {
return pad(index + 1)
})
const dayList = new Array(31).fill(0).map((_, index) => {
return pad(index + 1) // 修改为从1开始,生成01,02,...31
})
const month = new Array(12).fill(0).map((_, index) => {
return pad(index + 1)
})
const minuteList = new Array(60).fill(0).map((_, index) => {
return pad(index)
})
const monthList = new Array(12).fill(0).map((_, index) => {
return pad(index + 1)
})
// 秒选框
const secondRadio = ref('1')
const secondStart1 = ref(1)
const secondEnd1 = ref(2)
const secondStart2 = ref(0)
const secondEnd2 = ref(1)
// 分钟选框
const minutesRadio = ref('1')
const minutesStart1 = ref(1)
const minutesEnd1 = ref(2)
const minutesStart2 = ref(0)
const minutesEnd2 = ref(1)
// 小时选框
const hourRadio = ref('1')
const hourStart1 = ref(1)
const hourEnd1 = ref(2)
const hourStart2 = ref(0)
const hourEnd2 = ref(1)
// 日选框
// 日选框默认值修改
const dayRadio = ref('1')
const dayStart1 = ref(1) // 原值1保持不变
const dayEnd1 = ref(31) // 改为31
const dayStart2 = ref(1) // 原值1保持不变
const dayEnd2 = ref(1) // 原值1保持不变
const day = ref(1) // 原值1保持不变
// 月选框
const monthRadio = ref('1')
const monthStart1 = ref(1)
const monthEnd1 = ref(2)
const monthStart2 = ref(1)
const monthEnd2 = ref(1)
// 周选框
const weekRadio = ref('2')
const weekStart1 = ref(1)
const weekEnd1 = ref(2)
const weekStart2 = ref(1)
const weekEnd2 = ref(1)
const week = ref('')
// 年
const yearRadio = ref('1')
const yearStart = ref('')
const yearEnd = ref('')
const checkSeconds = ref([])
const checkMinutes = ref([])
const checkHour = ref([])
const checkDay = ref([])
const selectedMonth = ref([])
const selectedWeek = ref([])
const fieldVal = ref({
second: '*',
minutes: '*',
hour: '*',
day: '*',
month: '*',
week: '?'
})
const resetRadioValues = (field) => {
switch (field) {
case 'second':
secondStart1.value = 1
secondEnd1.value = 2
secondStart2.value = 0
secondEnd2.value = 1
checkSeconds.value = []
break
case 'minutes':
minutesStart1.value = 1
minutesEnd1.value = 2
minutesStart2.value = 0
minutesEnd2.value = 1
checkMinutes.value = []
break
case 'hour':
hourStart1.value = 1
hourEnd1.value = 2
hourStart2.value = 0
hourEnd2.value = 1
checkHour.value = []
break
case 'day':
dayStart1.value = 1
dayEnd1.value = 2
dayStart2.value = 1
dayEnd2.value = 1
day.value = 1
checkDay.value = []
break
case 'month':
monthStart1.value = 1
monthEnd1.value = 2
monthStart2.value = 1
monthEnd2.value = 1
selectedMonth.value = []
break
case 'week':
weekStart1.value = 1
weekEnd1.value = 2
weekStart2.value = 1
weekEnd2.value = 1
week.value = ''
selectedWeek.value = []
break
case 'year':
yearStart.value = ''
yearEnd.value = ''
break
}
}
watchEffect(() => {
// 秒
if (secondRadio.value == '1') {
resetRadioValues('second')
fieldVal.value.second = '*'
} else if (secondRadio.value == '2') {
fieldVal.value.second = `${secondStart1.value}-${secondEnd1.value}`
} else if (secondRadio.value == '3') {
fieldVal.value.second = `${secondStart2.value}/${secondEnd2.value}`
} else if (secondRadio.value == '4') {
const list = checkSeconds.value.map((v) => parseInt(v))
list.sort((a, b) => a - b)
fieldVal.value.second = list.join(',')
}
// 分
if (minutesRadio.value == '1') {
resetRadioValues('minutes')
fieldVal.value.minutes = '*'
} else if (minutesRadio.value == '2') {
fieldVal.value.minutes = `${minutesStart1.value}-${minutesEnd1.value}`
} else if (minutesRadio.value == '3') {
fieldVal.value.minutes = `${minutesStart2.value}/${minutesEnd2.value}`
} else if (minutesRadio.value == '4') {
const list = checkMinutes.value.map((v) => parseInt(v))
list.sort((a, b) => a - b)
fieldVal.value.minutes = list.join(',')
}
// 时
if (hourRadio.value == '1') {
resetRadioValues('hour')
fieldVal.value.hour = '*'
} else if (hourRadio.value == '2') {
fieldVal.value.hour = `${hourStart1.value}-${hourEnd1.value}`
} else if (hourRadio.value == '3') {
fieldVal.value.hour = `${hourStart2.value}/${hourEnd2.value}`
} else if (hourRadio.value == '4') {
const list = checkHour.value.map((v) => parseInt(v))
list.sort((a, b) => a - b)
fieldVal.value.hour = list.join(',')
}
// 日
switch (dayRadio.value) {
case '1':
fieldVal.value.day = '*'
break
case '2':
fieldVal.value.day = '?'
break
case '3':
fieldVal.value.day = `${dayStart1.value}-${dayEnd1.value}`
break
case '4':
fieldVal.value.day = `${dayStart2.value}/${dayEnd2.value}`
break
case '5':
fieldVal.value.day = `${day.value}W`
break
case '6':
fieldVal.value.day = `L`
break
case '7':
const list = checkDay.value.map((v) => parseInt(v))
list.sort((a, b) => a - b)
fieldVal.value.day = list.join(',')
break
}
// 月
switch (monthRadio.value) {
case '1':
fieldVal.value.month = '*'
break
case '2':
fieldVal.value.month = '?'
break
case '3':
fieldVal.value.month = `${monthStart1.value}-${monthEnd1.value}`
break
case '4':
fieldVal.value.month = `${monthStart2.value}/${monthEnd2.value}`
break
case '5':
const list = selectedMonth.value.map((v) => parseInt(v))
list.sort((a, b) => a - b)
fieldVal.value.month = list.join(',')
break
}
// 周
switch (weekRadio.value) {
case '1':
fieldVal.value.week = '*'
break
case '2':
fieldVal.value.week = '?'
break
case '3':
fieldVal.value.week = `${weekStart1.value}-${weekEnd1.value}`
break
case '4':
fieldVal.value.week = `${weekStart2.value}/${weekEnd2.value}`
break
case '5':
fieldVal.value.week = `${week.value}L`
break
case '6':
const list = selectedWeek.value.map((v) => parseInt(v))
list.sort((a, b) => a - b)
fieldVal.value.week = list.join(',')
break
}
switch (yearRadio.value) {
case '1':
fieldVal.value.year = ''
break
case '2':
fieldVal.value.year = '*'
break
case '3':
fieldVal.value.year = `${yearStart.value}-${yearEnd.value}`
break
}
})
// 移除mounted中的强制设置
onMounted(() => {
console.log('activeTab value:', activeTab.value)
// 移除这行强制设置
// activeTab.value = '秒'
})
// 在解析方法中添加activeTab自动切换逻辑
const parseField = (value, field, radioRef, checkRef, start1Ref, end1Ref, start2Ref, end2Ref) => {
// 新增字段类型映射
const fieldMap = {
second: '秒',
minutes: '分钟',
hour: '小时',
day: '日',
month: '月',
week: '周',
year: '年'
}
// 在解析时自动切换tab
// if (value !== '*' && value !== '?') {
// activeTab.value = fieldMap[field]
// }
if (value === '*') {
radioRef.value = '1'
checkRef.value = []
} else if (value.includes('-')) {
radioRef.value = '2'
const [start, end] = value.split('-')
start1Ref.value = parseInt(start)
end1Ref.value = parseInt(end)
} else if (value.includes('/')) {
radioRef.value = '3'
const [start, end] = value.split('/')
start2Ref.value = parseInt(start)
end2Ref.value = parseInt(end)
} else if (value.includes(',')) {
radioRef.value = '4'
checkRef.value = value.split(',').map((v) => pad(parseInt(v)))
} else {
// 新增单个数值处理
radioRef.value = '4'
checkRef.value = [pad(parseInt(value))]
}
}
// 添加解析日字段的特殊处理
const parseDayField = (
value,
radioRef,
checkRef,
start1Ref,
end1Ref,
start2Ref,
end2Ref,
dayRef
) => {
if (value === '*') {
radioRef.value = '1'
} else if (value === '?') {
radioRef.value = '2'
} else if (value.includes('-')) {
radioRef.value = '3'
const [start, end] = value.split('-')
start1Ref.value = parseInt(start)
end1Ref.value = parseInt(end)
} else if (value.includes('/')) {
radioRef.value = '4'
const [start, end] = value.split('/')
start2Ref.value = parseInt(start)
end2Ref.value = parseInt(end)
} else if (value.endsWith('W')) {
radioRef.value = '5'
dayRef.value = parseInt(value.replace('W', ''))
} else if (value === 'L') {
radioRef.value = '6'
} else if (value.includes(',')) {
radioRef.value = '7'
checkRef.value = value.split(',').map((v) => pad(parseInt(v)))
} else {
// 新增单个数值处理
radioRef.value = '7'
checkRef.value = [pad(parseInt(value))]
}
}
// 添加解析周字段的特殊处理
const parseWeekField = (
value,
radioRef,
checkRef,
start1Ref,
end1Ref,
start2Ref,
end2Ref,
weekRef
) => {
if (value === '*') {
radioRef.value = '1'
} else if (value === '?') {
radioRef.value = '2'
} else if (value.includes('-')) {
radioRef.value = '3'
const [start, end] = value.split('-')
start1Ref.value = parseInt(start)
end1Ref.value = parseInt(end)
} else if (value.includes('/')) {
radioRef.value = '4'
const [start, end] = value.split('/')
start2Ref.value = parseInt(start)
end2Ref.value = parseInt(end)
} else if (value.endsWith('L')) {
radioRef.value = '5'
weekRef.value = value.replace('L', '')
} else if (value.includes(',')) {
radioRef.value = '6'
checkRef.value = value.split(',').map((v) => pad(parseInt(v)))
} else {
// 新增单个数值处理
radioRef.value = '6'
checkRef.value = [pad(parseInt(value))]
}
}
const cron = ref('')
// 修改watch部分
// watch(
// () => fieldVal.value,
// (newVal) => {
// const { second, minutes, hour, day, month, week } = newVal
// // 只使用前6个标准字段
// const cronValue = `${second} ${minutes} ${hour} ${day} ${month} ${week}`
// cron.value = cronValue
// emit('update:modelValue', cronValue)
// },
// { immediate: true, deep: true }
// )
let isInternalUpdate = false
// 添加初始化方法,解析传入的cron表达式
watch(
() => props.modelValue,
(newVal, oldVal) => {
if (isInternalUpdate) {
isInternalUpdate = false
return
}
if (newVal && newVal !== oldVal) {
nextTick(() => {
setTimeout(() => {
console.log('newVal', newVal)
try {
const parts = newVal.split(' ')
if (parts.length >= 6) {
clear(false)
// 解析秒
parseField(
parts[0],
'second',
secondRadio,
checkSeconds,
secondStart1,
secondEnd1,
secondStart2,
secondEnd2
)
// 解析分钟
parseField(
parts[1],
'minutes',
minutesRadio,
checkMinutes,
minutesStart1,
minutesEnd1,
minutesStart2,
minutesEnd2
)
// 解析小时
parseField(
parts[2],
'hour',
hourRadio,
checkHour,
hourStart1,
hourEnd1,
hourStart2,
hourEnd2
)
// 解析日
parseDayField(
parts[3],
dayRadio,
checkDay,
dayStart1,
dayEnd1,
dayStart2,
dayEnd2,
day
)
// 解析月
parseField(
parts[4],
'month',
monthRadio,
selectedMonth,
monthStart1,
monthEnd1,
monthStart2,
monthEnd2
)
// 解析周
parseWeekField(
parts[5],
weekRadio,
selectedWeek,
weekStart1,
weekEnd1,
weekStart2,
weekEnd2,
week
)
// 最后设置activeTab
const priorityFields = [
{ index: 0, tab: '秒' },
{ index: 1, tab: '分钟' },
{ index: 2, tab: '小时' },
{ index: 3, tab: '日' },
{ index: 4, tab: '月' },
{ index: 5, tab: '周' }
]
const firstNonDefault = priorityFields.find(
({ index }) => parts[index] !== '*' && parts[index] !== '?'
)
activeTab.value = firstNonDefault?.tab || '秒'
}
} catch (e) {
console.error('解析Cron表达式出错:', e)
}
}, 200)
})
}
},
{ immediate: true }
)
// 添加计算最近运行时间的函数
const nextRuns = ref([])
const calculateNextRuns = async () => {
try {
if (!cron.value || cron.value.trim() === '') {
nextRuns.value = ['请先配置有效的Cron表达式']
return
}
const parser = cronParser.default || cronParser
const interval = parser.parse(cron.value, {
currentDate: new Date(),
endDate: new Date(Date.now() + 365 * 24 * 60 * 60 * 1000)
})
const runs = []
for (let i = 0; i < 5; i++) {
try {
const nextDate = interval.next().toDate()
// 使用dayjs格式化日期
const formattedDate = dayjs(nextDate).format('YYYY-MM-DD HH:mm:ss')
runs.push(formattedDate)
} catch (e) {
break
}
}
nextRuns.value = runs.length > 0 ? runs : ['无有效执行时间']
} catch (e) {
console.error('解析Cron表达式出错:', e)
nextRuns.value = [`无效的Cron表达式: ${cron.value}`]
}
}
// 修改watch部分,使其成为异步函数
watch(
() => fieldVal.value,
async (newVal) => {
isInternalUpdate = true
const { second, minutes, hour, day, month, week, year } = newVal
const cronValue = `${second} ${minutes} ${hour} ${day} ${month} ${week} ${year}`
cron.value = cronValue
emit('update:modelValue', cronValue)
await calculateNextRuns()
},
{ immediate: true, deep: true }
)
const clear = (resetRadio = true) => {
// 仅在需要重置radio时修改activeTab
if (resetRadio) {
activeTab.value = '秒'
}
// 重置所有radio选项为默认值
secondRadio.value = '1'
minutesRadio.value = '1'
hourRadio.value = '1'
dayRadio.value = '1'
monthRadio.value = '1'
weekRadio.value = '2'
yearRadio.value = '1'
// 清空所有checkbox选择
checkSeconds.value = []
checkMinutes.value = []
checkHour.value = []
checkDay.value = []
selectedMonth.value = []
selectedWeek.value = []
// 重置所有输入框为默认值
secondStart1.value = 1
secondEnd1.value = 2
secondStart2.value = 0
secondEnd2.value = 1
minutesStart1.value = 1
minutesEnd1.value = 2
minutesStart2.value = 0
minutesEnd2.value = 1
hourStart1.value = 1
hourEnd1.value = 2
hourStart2.value = 0
hourEnd2.value = 1
dayStart1.value = 1
dayEnd1.value = 2
dayStart2.value = 1
dayEnd2.value = 1
day.value = 1
monthStart1.value = 1
monthEnd1.value = 2
monthStart2.value = 1
monthEnd2.value = 1
weekStart1.value = 1
weekEnd1.value = 2
weekStart2.value = 1
weekEnd2.value = 1
week.value = ''
yearStart.value = ''
yearEnd.value = ''
}
// watch(activeTab, () => {
// clear(false) // 传入false不清空radio选项,只清空输入值和checkbox
// })
// 暴露clear方法
defineExpose({
clear
})
</script>
<style lang="scss" scoped>
.form-title {
line-height: 50px;
font-weight: 700;
font-weight: bold;
font-size: 18px;
position: relative;
text-indent: 14px;
&::before {
content: ' ';
position: absolute;
width: 4px;
left: 0;
top: calc(50% - 10px);
height: 20px;
border-radius: 2px;
background: #62a7f1;
}
}
.cron-selector {
::v-deep(.el-tabs__content) {
padding: 10px;
}
::v-deep(.el-radio-group) {
.el-radio {
width: 100%;
height: auto;
margin-bottom: 10px;
min-height: 32px;
}
}
::v-deep(.el-checkbox-group) {
display: flex;
flex-wrap: wrap;
.el-checkbox {
margin-right: 10px;
height: auto;
margin-bottom: 10px;
}
}
}
/* 添加样式 */
.next-runs {
margin-top: 20px;
padding: 15px;
background-color: #fff;
border-radius: 4px;
h4 {
margin-bottom: 10px;
color: #333;
}
ul {
padding-left: 20px;
margin: 0;
li {
line-height: 1.8;
color: #333;
}
}
}
</style>
总结
以上为个人经验,希望能给大家一个参考,也希望大家多多支持脚本之家。
