vue.js

关注公众号 jb51net

关闭
首页 > 网络编程 > JavaScript > javascript类库 > vue.js > vue3的CRON表达式组件

vue3的CRON表达式组件用法解读

作者:没头脑和不高兴y

该文档介绍了Cron表达式生成器的使用说明,该组件通过图形化界面帮助用户配置定时任务的执行计划,支持秒、分钟、小时、日、月、周等时间单位的灵活配置,并实时计算并显示最近5次执行时间

Cron 表达式生成器使用说明文档

功能概述

本组件是一个可视化 Cron 表达式生成器,用于帮助用户通过图形化界面配置定时任务的执行计划。

支持秒、分钟、小时、日、月、周等时间单位的灵活配置,并实时计算显示最近5次执行时间。支持编辑和回显

主要功能

1. 时间单位配置

2. 实时预览

使用说明

基本操作

  1. 通过顶部标签页切换不同时间单位
  2. 选择对应的时间配置方式(单选按钮)
  3. 根据选择的配置方式填写具体参数
  4. 系统自动生成 Cron 表达式并显示执行时间

配置选项说明

秒配置
分钟配置
小时配置
日配置
月配置
周配置

技术实现

核心功能

数据流

用户通过界面配置时间参数

组件内部将配置转换为标准 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>

总结

以上为个人经验,希望能给大家一个参考,也希望大家多多支持脚本之家。

您可能感兴趣的文章:
阅读全文