zl程序教程

您现在的位置是:首页 >  Java

当前栏目

Vue3项目-生成Cron表达式组件

2023-02-18 16:33:36 时间

  最近做的一个vue3项目过程中,需要用到cron表达式功能,而对于普通业务人员,他们是不懂cron表达式规则的,所以需要做一个可手动配置生成cron表达式的功能。从网上查找了一些相关资料,然后结合vue3+Element Plus,改造成适合自己项目的组件。记录代码如下:

1、components目录下新建 vue3Cron/Index.vue

src/components/vue3Cron/Index.vue

<template>
  <div class="v3c">
    <ul class="v3c-tab">
      <li class="v3c-tab-item" :class="{ 'v3c-active': tabActive == 1 }" @click="onHandleTab(1)">秒
      </li>
      <li class="v3c-tab-item" :class="{ 'v3c-active': tabActive == 2 }" @click="onHandleTab(2)">分</li>
      <li class="v3c-tab-item" :class="{ 'v3c-active': tabActive == 3 }" @click="onHandleTab(3)">时</li>
      <li class="v3c-tab-item" :class="{ 'v3c-active': tabActive == 4 }" @click="onHandleTab(4)">天</li>
      <li class="v3c-tab-item" :class="{ 'v3c-active': tabActive == 5 }" @click="onHandleTab(5)">月</li>
      <li class="v3c-tab-item" :class="{ 'v3c-active': tabActive == 6 }" @click="onHandleTab(6)">年</li>
    </ul>
    <!-- 秒 -->
    <div class="v3c-content" v-show="tabActive == 1">
      <!-- 每一秒 -->
      <div>
        <el-radio label="1" v-model="state.second.cronEvery">每一秒钟</el-radio>
      </div>
      <!-- 每隔多久 -->
      <div class="mt-15">
        <el-radio label="2" v-model="state.second.cronEvery">每隔</el-radio>
        <el-input-number v-model="state.second.incrementIncrement" :min="1" :max="60" controls-position="right" />
        <span class="ml-5 mr-5">秒执行, 从</span>
        <el-input-number v-model="state.second.incrementStart" :min="0" :max="59" controls-position="right" />
        <span>秒开始</span>
      </div>
      <!-- 具体秒数 -->
      <div class="mt-15">
        <el-radio label="3" v-model="state.second.cronEvery">具体秒数(可多选)</el-radio>
        <el-select v-model="state.second.specificSpecific" multiple clearable style="width: 140px">
          <el-option v-for="(item, index) in 60" :key="index" :label="index" :value="index" />
        </el-select>
      </div>
      <!-- 周期从 -->
      <div class="mt-15">
        <el-radio label="4" v-model="state.second.cronEvery">周期从</el-radio>
        <el-input-number v-model="state.second.rangeStart" :min="0" :max="59" controls-position="right" />
        <sapn>秒</sapn><span class="ml-10 mr-5">到</span>
        <el-input-number v-model="state.second.rangeEnd" :min="0" :max="59" controls-position="right" />
        <sapn>秒</sapn>
      </div>
    </div>
    <!-- 分钟 -->
    <div class="v3c-content" v-show="tabActive == 2">
      <!-- 每一分钟 -->
      <div>
        <el-radio label="1" v-model="state.minute.cronEvery">每一分钟</el-radio>
      </div>
      <!-- 每隔多久 -->
      <div class="mt-15">
        <el-radio label="2" v-model="state.minute.cronEvery">每隔</el-radio>
        <el-input-number v-model="state.minute.incrementIncrement" :min="1" :max="60" controls-position="right" />
        <span class="ml-5 mr-5">分执行,从</span>
        <el-input-number v-model="state.minute.incrementStart" :min="0" :max="59" controls-position="right" />
        <span>分开始</span>
      </div>
      <!-- 具体分钟数 -->
      <div class="mt-15">
        <el-radio label="3" v-model="state.minute.cronEvery">具体分钟数(可多选)</el-radio>
        <el-select v-model="state.minute.specificSpecific" multiple clearable style="width: 140px">
          <el-option v-for="(item, index) in 60" :key="index" :label="index" :value="index" />
        </el-select>
      </div>
      <!-- 周期从 -->
      <div class="mt-15">
        <el-radio label="4" v-model="state.minute.cronEvery">周期从</el-radio>
        <el-input-number v-model="state.minute.rangeStart" :min="0" :max="59" controls-position="right" />
        <span>分</span><span class="ml-10 mr-5">到</span>
        <el-input-number v-model="state.minute.rangeEnd" :min="0" :max="59" controls-position="right" />
        <span>分</span>
      </div>
    </div>
    <!-- 小时 -->
    <div class="v3c-content" v-show="tabActive == 3">
      <!-- 每一小时 -->
      <div>
        <el-radio label="1" v-model="state.hour.cronEvery">每一小时</el-radio>
      </div>
      <!-- 每隔多久 -->
      <div class="mt-15">
        <el-radio label="2" v-model="state.hour.cronEvery">每隔</el-radio>
        <el-input-number v-model="state.hour.incrementIncrement" :min="1" :max="24" controls-position="right" />
        <span class="ml-5 mr-5">小时执行,从</span>
        <el-input-number v-model="state.hour.incrementStart" :min="0" :max="23" controls-position="right" />
        <span>小时开始</span>
      </div>
      <!-- 具体小时数 -->
      <div class="mt-15">
        <el-radio label="3" v-model="state.hour.cronEvery">具体小时数(可多选)</el-radio>
        <el-select v-model="state.hour.specificSpecific" multiple clearable style="width: 140px">
          <el-option v-for="(item, index) in 24" :key="index" :label="index" :value="index" />
        </el-select>
      </div>
      <!-- 周期从 -->
      <div class="mt-15">
        <el-radio label="4" v-model="state.hour.cronEvery">周期从</el-radio>
        <el-input-number v-model="state.hour.rangeStart" :min="0" :max="23" controls-position="right" />
        <span>时</span><span class="ml-10 mr-5">到</span>
        <el-input-number v-model="state.hour.rangeEnd" :min="0" :max="23" controls-position="right" />
        <span>时</span>
      </div>
    </div>
    <!-- 天 -->
    <div class="v3c-content" v-show="tabActive == 4">
      <!-- 1 -->
      <div>
        <el-radio label="1" v-model="state.day.cronEvery">每一天</el-radio>
      </div>
      <!-- 2 -->
      <div class="mt-15">
        <el-radio label="2" v-model="state.day.cronEvery">每隔</el-radio>
        <el-input-number v-model="state.week.incrementIncrement" :min="1" :max="60" controls-position="right" />
        <span class="ml-5 mr-5">周执行,从</span>
        <el-input-number v-model="state.week.incrementStart" :min="1" :max="52" controls-position="right" />
        <span>周开始</span>
      </div>
      <!-- 3 -->
      <div class="mt-15">
        <el-radio label="3" v-model="state.day.cronEvery">每隔</el-radio>
        <el-input-number v-model="state.day.incrementIncrement" :min="1" :max="30" controls-position="right" />
        <span class="ml-5 mr-5">天执行,从</span>
        <el-input-number v-model="state.day.incrementStart" :min="1" :max="30" controls-position="right" />
        <span>天开始</span>
      </div>
      <!-- 4 -->
      <div class="mt-15">
        <el-radio label="4" v-model="state.day.cronEvery">具体星期几(可多选)</el-radio>
        <el-select v-model="state.week.specificSpecific" multiple clearable style="width: 140px">
          <el-option v-for="(item, index) in weekList" :key="index" :label="item.name" :value="item.value" />
        </el-select>
      </div>
      <!-- 5 -->
      <div class="mt-15">
        <el-radio label="5" v-model="state.day.cronEvery">具体天数(可多选)</el-radio>
        <el-select v-model="state.day.specificSpecific" multiple clearable style="width: 140px">
          <el-option v-for="(item, index) in 31" :key="index" :label="item" :value="item" />
        </el-select>
      </div>
      <!-- 6 -->
      <!-- <div class="mt-15">
        <el-radio label="6" v-model="state.day.cronEvery">在这个月的最后一天</el-radio>
      </div> -->
      <!-- 7 -->
      <!-- <div class="mt-15">
        <el-radio label="7" v-model="state.day.cronEvery">在这个月的最后一个工作日</el-radio>
      </div> -->
      <!-- 8 -->
      <!-- <div class="mt-15">
        <el-radio label="8" v-model="state.day.cronEvery">在这个月的最后一个</el-radio>
        <el-select v-model="state.day.cronLastSpecificDomDay" style="width: 140px">
          <el-option v-for="(item, index) in weekList" :key="index" :label="item.name" :value="item.val" />
        </el-select>
      </div> -->
      <!-- 9 -->
      <!-- <div class="mt-15">
          <el-radio label="9" v-model="state.day.cronEvery">{{ }}</el-radio>
          <el-input-number v-model="state.day.cronDaysBeforeEomMinus" :min="1" :max="31" controls-position="right" />
          <span>在本月底前</span>
      </div> -->
      <!-- 10 -->
      <!-- <div class="mt-15">
        <el-radio label="10" v-model="state.day.cronEvery">最近的工作日(周一至周五)至本月</el-radio>
        <el-input-number v-model="state.day.cronDaysNearestWeekday" :min="1" :max="31" controls-position="right" />
        <span>日</span>
      </div> -->
      <!-- 11 -->
      <!-- <div class="mt-15">
        <el-radio label="11" v-model="state.day.cronEvery">在这个月的第</el-radio>
        <el-input-number v-model="state.week.cronNthDayNth" :min="1" :max="5" controls-position="right" />
        <span>个</span>
        <el-select v-model="state.week.cronNthDayDay" style="width: 140px">
          <el-option v-for="(item, index) in weekList" :key="index" :label="item.name" :value="item.val" />
        </el-select>
      </div> -->
    </div>
    <!-- 月 -->
    <div class="v3c-content" v-show="tabActive == 5">
      <!-- 1 -->
      <div>
        <el-radio label="1" v-model="state.month.cronEvery">每一月</el-radio>
      </div>
      <!-- 2 -->
      <div class="mt-15">
        <el-radio label="2" v-model="state.month.cronEvery">每隔</el-radio>
        <el-input-number v-model="state.month.incrementIncrement" :min="1" :max="12" controls-position="right" />
        <span class="ml-5 mr-5">月执行,从</span>
        <el-input-number v-model="state.month.incrementStart" :min="1" :max="12" controls-position="right" />
        <span>月开始</span>
      </div>
      <!-- 3 -->
      <div class="mt-15">
        <el-radio label="3" v-model="state.month.cronEvery">具体月数(可多选)</el-radio>
        <el-select multiple clearable v-model="state.month.specificSpecific" style="width: 140px">
          <el-option v-for="(item, index) in 12" :key="index" :label="item" :value="item" />
        </el-select>
      </div>
      <!-- 4 -->
      <div class="mt-15">
        <el-radio label="4" v-model="state.month.cronEvery">周期从</el-radio>
        <el-input-number v-model="state.month.rangeStart" :min="1" :max="12" controls-position="right" />
        <span>月</span><span class="ml-10 mr-5">到</span>
        <el-input-number v-model="state.month.rangeEnd" :min="1" :max="12" controls-position="right" />
        <span>月</span>
      </div>
    </div>
    <!-- 年 -->
    <div class="v3c-content" v-show="tabActive == 6">
      <!-- 1 -->
      <div>
        <el-radio label="1" v-model="state.year.cronEvery">每一年</el-radio>
      </div>
      <!-- 2 -->
      <div class="mt-15">
        <el-radio label="2" v-model="state.year.cronEvery">每隔</el-radio>
        <el-input-number v-model="state.year.incrementIncrement" :min="1" :max="99" controls-position="right" />
        <span class="ml-5 mr-5">年执行,从</span>
        <el-input-number v-model="state.year.incrementStart" :min="currYear" :max="currYear + 10"
          controls-position="right" style="width:100px;" />
        <span>年开始</span>
      </div>
      <!-- 3 -->
      <div class="mt-15">
        <el-radio label="3" v-model="state.year.cronEvery">具体年份(可多选)</el-radio>
        <el-select multiple clearable v-model="state.year.specificSpecific" style="width: 140px">
          <el-option v-for="(item, index) in 100" :key="index" :label="currYear + item" :value="currYear + item" />
        </el-select>
      </div>
      <!-- 4 -->
      <div class="mt-15">
        <el-radio label="4" v-model="state.year.cronEvery">周期从</el-radio>
        <el-input-number v-model="state.year.rangeStart" :min="currYear" :max="currYear + 10" controls-position="right"
          style="width:100px;" />
        <span>年</span><span class="ml-10 mr-5">到</span>
        <el-input-number v-model="state.year.rangeEnd" :min="currYear" :max="currYear + 10" controls-position="right"
          style="width:100px;" />
        <span>年</span>
      </div>
    </div>
    <!-- 结果 -->
    <!-- <div class="v3c-footer">
      <div style="flex: 1">
        CRON &nbsp;: &nbsp;&nbsp;<span class="cron">{{ state.cron }}</span>
        &nbsp; &nbsp; &nbsp;
        <button class="btn-ok" @click.stop="handleChange">生成cron</button>
      </div>
    </div> -->
  </div>
</template>

<script>
import { reactive, computed, toRefs, defineComponent, ref, watch } from "vue";
// (默认是每一分钟一次)
export default defineComponent({
  name: "Vue3Cron",
  props: {
    maxHeight: String,
    change: Function,
    value: String,
  },
  setup(props, { emit }) {
    const weekList = ref([
      { name: '星期日', value: 'SUN', val: 1, },
      { name: '星期一', value: 'MON', val: 2, },
      { name: '星期二', value: 'TUE', val: 3, },
      { name: '星期三', value: 'WED', val: 4, },
      { name: '星期四', value: 'THU', val: 5, },
      { name: '星期五', value: 'FRI', val: 6, },
      { name: '星期六', value: 'SAT', val: 7, },
    ])

    const tabActive = ref(1);
    const currYear = ref(new Date().getFullYear());
    const onHandleTab = (index) => {
      tabActive.value = index;
    };

    // (默认是每一分钟一次)
    const state = reactive({
      second: {
        cronEvery: "1",
        incrementStart: 0,
        incrementIncrement: 1,
        rangeStart: 0,
        rangeEnd: 0,
        specificSpecific: [],
      },
      minute: {
        cronEvery: "1",
        incrementStart: 0,
        incrementIncrement: 1,
        rangeStart: 0,
        rangeEnd: 0,
        specificSpecific: [],
      },
      hour: {
        cronEvery: "1",
        incrementStart: 1,
        incrementIncrement: 1,
        rangeStart: 0,
        rangeEnd: 0,
        specificSpecific: [],
      },
      day: {
        cronEvery: "1",
        incrementStart: 1,
        incrementIncrement: 1,
        rangeStart: 0,
        rangeEnd: 0,
        specificSpecific: [],
        cronLastSpecificDomDay: 1,
        cronDaysBeforeEomMinus: 0,
        cronDaysNearestWeekday: 1,
      },
      week: {
        cronEvery: "1",
        incrementStart: 1,
        incrementIncrement: 1,
        specificSpecific: [],
        cronNthDayDay: 1,
        cronNthDayNth: 1,
      },
      month: {
        cronEvery: "1",
        incrementStart: 1,
        incrementIncrement: 1,
        rangeStart: 1,
        rangeEnd: 1,
        specificSpecific: [],
      },
      year: {
        cronEvery: "1",
        incrementStart: new Date().getFullYear(),
        incrementIncrement: 1,
        rangeStart: new Date().getFullYear(),
        rangeEnd: new Date().getFullYear(),
        specificSpecific: [],
      },
      output: {
        second: "",
        minute: "",
        hour: "",
        day: "",
        month: "",
        Week: "",
        year: "",
      },
      secondsText: computed(() => {
        let seconds = "";
        let cronEvery = state.second.cronEvery;
        switch (cronEvery?.toString()) {
          case "1":
            seconds = "*";
            break;
          case "2":
            seconds = state.second.incrementStart + "/" + state.second.incrementIncrement;
            break;
          case "3":
            state.second.specificSpecific.map((val) => {
              seconds += val + ",";
            });
            seconds = seconds.slice(0, -1);
            break;
          case "4":
            seconds = state.second.rangeStart + "-" + state.second.rangeEnd;
            break;
        }
        return seconds;
      }),
      minutesText: computed(() => {
        let minutes = "";
        let cronEvery = state.minute.cronEvery;
        switch (cronEvery?.toString()) {
          case "1":
            minutes = "*";
            break;
          case "2":
            minutes = state.minute.incrementStart + "/" + state.minute.incrementIncrement;
            break;
          case "3":
            state.minute.specificSpecific.map((val) => {
              minutes += val + ",";
            });
            minutes = minutes.slice(0, -1);
            break;
          case "4":
            minutes = state.minute.rangeStart + "-" + state.minute.rangeEnd;
            break;
        }
        return minutes;
      }),
      hoursText: computed(() => {
        let hours = "";
        let cronEvery = state.hour.cronEvery;
        switch (cronEvery?.toString()) {
          case "1":
            hours = "*";
            break;
          case "2":
            hours = state.hour.incrementStart + "/" + state.hour.incrementIncrement;
            break;
          case "3":
            state.hour.specificSpecific.map((val) => {
              hours += val + ",";
            });
            hours = hours.slice(0, -1);
            break;
          case "4":
            hours = state.hour.rangeStart + "-" + state.hour.rangeEnd;
            break;
        }
        return hours;
      }),
      daysText: computed(() => {
        let days = "";
        let cronEvery = state.day.cronEvery;
        switch (cronEvery?.toString()) {
          case "1":
            break;
          case "2":
          case "4":
          case "11":
            days = "?";
            break;
          case "3":
            days = state.day.incrementStart + "/" + state.day.incrementIncrement;
            break;
          case "5":
            state.day.specificSpecific.map((val) => {
              days += val + ",";
            });
            days = days.slice(0, -1);
            break;
          case "6":
            days = "L";
            break;
          case "7":
            days = "LW";
            break;
          case "8":
            days = state.day.cronLastSpecificDomDay + "L";
            break;
          case "9":
            days = "L-" + state.day.cronDaysBeforeEomMinus;
            break;
          case "10":
            days = state.day.cronDaysNearestWeekday + "W";
            break;
        }
        return days;
      }),
      weeksText: computed(() => {
        let weeks = "";
        let cronEvery = state.day.cronEvery;
        switch (cronEvery?.toString()) {
          case "1":
          case "3":
          case "5":
            weeks = "?";
            break;
          case "2":
            weeks = state.week.incrementStart + "/" + state.week.incrementIncrement;
            break;
          case "4":
            state.week.specificSpecific.map((val) => {
              weeks += val + ",";
            });
            weeks = weeks.slice(0, -1);
            break;
          case "6":
          case "7":
          case "8":
          case "9":
          case "10":
            weeks = "?";
            break;
          case "11":
            weeks = state.week.cronNthDayDay + "#" + state.week.cronNthDayNth;
            break;
        }
        return weeks;
      }),
      monthsText: computed(() => {
        let months = "";
        let cronEvery = state.month.cronEvery;
        switch (cronEvery?.toString()) {
          case "1":
            months = "*";
            break;
          case "2":
            months = state.month.incrementStart + "/" + state.month.incrementIncrement;
            break;
          case "3":
            state.month.specificSpecific.map((val) => {
              months += val + ",";
            });
            months = months.slice(0, -1);
            break;
          case "4":
            months = state.month.rangeStart + "-" + state.month.rangeEnd;
            break;
        }
        return months;
      }),
      yearsText: computed(() => {
        let years = "";
        // TODO,目前先不指定年份,注释以下代码
        let cronEvery = state.year.cronEvery;
        switch (cronEvery?.toString()) {
          case "1":
            years = "*";
            break;
          case "2":
            years = state.year.incrementStart + "/" + state.year.incrementIncrement;
            break;
          case "3":
            state.year.specificSpecific.map((val) => {
              years += val + ",";
            });
            years = years.slice(0, -1);
            break;
          case "4":
            years = state.year.rangeStart + "-" + state.year.rangeEnd;
            break;
        }
        return years;
      }),
      cron: computed(() => {
        return `${state.secondsText || "*"} ${state.minutesText || "*"} ${state.hoursText || "*"} ${state.daysText || "*"} ${state.monthsText || "*"} ${state.weeksText || "?"} ${state.yearsText || "*"}`;
      }),
    });

    const handleChange = () => {
      if (typeof state.cron !== "string") return false;
      emit("change", state.cron);
    };
    const rest = (data) => {
      for (let i in data) {
        if (data[i] instanceof Object) {
          this.rest(data[i]);
        } else {
          switch (typeof data[i]) {
            case "object":
              data[i] = [];
              break;
            case "string":
              data[i] = "";
              break;
          }
        }
      }
    };

    watch(
      () => state.cron,
      (value) => {
        if (typeof state.cron !== "string") return;
        emit("update:value", value);
      }
    );

    return {
      weekList,
      state,
      handleChange,
      rest,
      tabActive,
      onHandleTab,
      currYear,
    };
  },
});
</script>

<style lang="css" scoped>
:deep(.el-input-number) {
  width: 80px;
  margin-right: 5px;
}

:deep(.el-radio) {
  margin-right: 10px;
}

.v3c {
  width: auto;
  border: 1px solid #f5f7fa;
}

.v3c-tab {
  padding: 0;
  list-style: none;
  margin: 0;
  background-color: #f5f7fa;
  display: flex;
}

.v3c-tab-item {
  flex: 1;
  text-align: center;
  cursor: pointer;
  padding: 6px;
}

.v3c-tab-item.v3c-active {
  background-color: #409eff;
  color: #ffffff;
}

.v3c-lang-btn {
  background-color: #61ddaa;
  color: #ffffff;
  /* border-radius: 10px; */
}

.v3c-content {
  padding: 20px;
  max-height: v-bind(maxHeight);
  overflow: hidden;
  overflow-y: auto;
}

.p-20 {
  padding: 20px;
}

.v3c-footer {
  background-color: #f5f7fa;
  padding-top: 10px;
  padding-bottom: 10px;
  display: flex;
  text-align: center;
}

.v3c input[type="text"] {
  width: 80px;
}

.v3c input[type="number"] {
  width: 80px;
  height: 28px;
  border: 1px solid #d9d9d9;
}

.v3c select {
  width: 80px;
  height: 32px;
  border: 1px solid #d9d9d9;
}

.v3c select[multiple] {
  width: 80px;
  height: 100px;
  border: 1px solid #d9d9d9;
}

.btn-ok {
  line-height: 1.5715;
  position: relative;
  display: inline-block;
  font-weight: 400;
  white-space: nowrap;
  text-align: center;
  background-image: none;
  border: 1px solid transparent;
  box-shadow: 0 2px #00000004;
  cursor: pointer;
  transition: all 0.3s cubic-bezier(0.645, 0.045, 0.355, 1);
  -webkit-user-select: none;
  -moz-user-select: none;
  -ms-user-select: none;
  user-select: none;
  touch-action: manipulation;
  height: 32px;
  padding: 4px 15px;
  font-size: 14px;
  border-radius: 2px;

  color: #fff;
  background: #409eff;
  border-color: #409eff;
  text-shadow: 0 -1px 0 rgb(0 0 0 / 12%);
  box-shadow: 0 2px #0000000b;
}

.btn-close {
  line-height: 1.5715;
  position: relative;
  display: inline-block;
  font-weight: 400;
  white-space: nowrap;
  text-align: center;
  background-image: none;
  border: 1px solid transparent;
  box-shadow: 0 2px #00000004;
  cursor: pointer;
  transition: all 0.3s cubic-bezier(0.645, 0.045, 0.355, 1);
  -webkit-user-select: none;
  -moz-user-select: none;
  -ms-user-select: none;
  user-select: none;
  touch-action: manipulation;
  height: 32px;
  padding: 4px 15px;
  font-size: 14px;
  border-radius: 2px;

  color: #fff;
  background: #61ddaa;
  border-color: #61ddaa;
  text-shadow: 0 -1px 0 rgb(0 0 0 / 12%);
  box-shadow: 0 2px #0000000b;
}

.cron {
  background-color: #61ddaa;
  padding: 5px;
  padding-left: 10px;
  padding-right: 10px;
  color: #ffffff;
}
</style>

2、业务代码中使用vue3Cron/Index.vue

<template>
  <el-drawer :model-value="isShow" direction="rtl" :show-close="false" :destroy-on-close="true"
    :close-on-click-modal="false" :title="!!curRecord ? '编辑***' : '创建***'" @open="onOpen" @closed="onCancel"
    size="500px">
    <div class="pr-20">
      <el-form class="edit-form" :model="formData" ref="formRef" :rules="rules" label-width="100px">
        <el-form-item label="名称" prop="name">
          <el-input v-model="formData.name" placeholder="请输入****" />
        </el-form-item>
        <el-form-item label="cron表达式" prop="logicConfig">
          <el-input v-model="formData.logicConfig" placeholder="请输入cron表达式">
            <template #append>
              <el-tooltip content="配置cron表达式" placement="top">
                <el-button :icon="ArrowDown" @click="() => { isShowCronCore = !isShowCronCore }" />
              </el-tooltip>
            </template>
          </el-input>
        </el-form-item>
        <div style="width:100%;padding-left: 10px;margin-top: -5px;" v-show="isShowCronCore">
          <Vue3Cron @change="changeCron" v-model:value="formData.logicConfig" />
        </div>
      </el-form>
    </div>
    <template #footer>
      <span class="dialog-footer">
        <el-button @click="onCancel">取 消</el-button>
        <el-button type="primary" v-if="modalType != 'view'" :loading="submiting" @click="onSubmit">确 定</el-button>
      </span>
    </template>
  </el-drawer>
</template>
<script setup>
import { ref, reactive } from "vue";
import { ElMessage } from "element-plus";
import { ArrowDown } from '@element-plus/icons-vue'
import cronParse from 'cron-parser';
import triggerApi from '@/api/triggerApi';
import Vue3Cron from "@/components/vue3Cron/Index.vue";

// 其他代码省略*******// 定义数据
const isShowCronCore = ref(true); // 是否打开cron表达式配置框
const submiting = ref(false);     // 是否正在提交中
const formRef = ref(null);
const formData = reactive({  // 表单数据
  name: "", // 名称
  logicConfig: "* * * * * ? *", // cron表达式
});

const changeCron = (val) => {
  if (typeof val !== "string") return false;
  formData.logicConfig = val;
};

3、效果

例如:勾选配置为:每天10:30执行一次,输入框显示的表达式如下

注:目前还没做根据表达式回显秒分时天月年的配置项的功能,其实也很简单,只需要稍加理解一下cron表达式的规则,然后逐一解析即可。另外实际项目中根据情况判断需不需要显示年的选项

附上cron表达式基本介绍:

1、Cron表达式是一个字符串,字符串以5或6个空格隔开,分为6或7个域,每一个域代表一个含义,Cron有如下两种语法格式:

  (1)、秒 分 时 日 月 周 年

  (2)、秒 分 时 日 月 周

corn从左到右(用空格隔开):秒 分 小时 月份中的日期 月份 星期中的日期 年份

二、各字段的含义

字段

允许值

允许的特殊字符

秒(Seconds)

0~59的整数

, - * /    四个字符

分(Minutes)

0~59的整数

, - * /    四个字符

小时(Hours)

0~23的整数

, - * /    四个字符

日期(DayofMonth)

1~31的整数(但是你需要考虑你月的天数)

,- * ? / L W C     八个字符

月份(Month)

1~12的整数或者 JAN-DEC

, - * /    四个字符

星期(DayofWeek)

1~7的整数或者 SUN-SAT (1=SUN)

, - * ? / L C #     八个字符

年(可选,留空)(Year)

1970~2099

, - * /    四个字符

注意事项:

  每一个域都使用数字,但还可以出现如下特殊字符,它们的含义是:

  (1)*:表示匹配该域的任意值。假如在Minutes域使用*, 即表示每分钟都会触发事件。

  (2)?:只能用在DayofMonth和DayofWeek两个域。它也匹配域的任意值,但实际不会。因为DayofMonth和DayofWeek会相互影响。例如想在每月的20日触发调度,不管20日到底是星期几,则只能使用如下写法: 13 13 15 20 * ?, 其中最后一位只能用?,而不能使用*,如果使用*表示不管星期几都会触发,实际上并不是这样。

  (3)-:表示范围。例如在Minutes域使用5-20,表示从5分到20分钟每分钟触发一次 

  (4)/:表示起始时间开始触发,然后每隔固定时间触发一次。例如在Minutes域使用5/20,则意味着5分钟触发一次,而25,45等分别触发一次. 

  (5),:表示列出枚举值。例如:在Minutes域使用5,20,则意味着在5和20分每分钟触发一次。 

  (6)L:表示最后,只能出现在DayofWeek和DayofMonth域。如果在DayofWeek域使用5L,意味着在最后的一个星期四触发。 

  (7)W:表示有效工作日(周一到周五),只能出现在DayofMonth域,系统将在离指定日期的最近的有效工作日触发事件。例如:在 DayofMonth使用5W,如果5日是星期六,则将在最近的工作日:星期五,即4日触发。如果5日是星期天,则在6日(周一)触发;如果5日在星期一到星期五中的一天,则就在5日触发。另外一点,W的最近寻找不会跨过月份 。

  (8)LW:这两个字符可以连用,表示在某个月最后一个工作日,即最后一个星期五。 

  (9)#:用于确定每个月第几个星期几,只能出现在DayofMonth域。例如在4#2,表示某月的第二个星期三。

三、常用表达式例子

  (1)0 0 2 1 * ? *   表示在每月的1日的凌晨2点调整任务

  (2)0 15 10 ? * MON-FRI   表示周一到周五每天上午10:15执行作业

  (3)0 15 10 ? 6L 2002-2006   表示2002-2006年的每个月的最后一个星期五上午10:15执行作

  (4)0 0 10,14,16 * * ?   每天上午10点,下午2点,4点 

  (5)0 0/30 9-17 * * ?   朝九晚五工作时间内每半小时 

  (6)0 0 12 ? * WED    表示每个星期三中午12点 

  (7)0 0 12 * * ?   每天中午12点触发 

  (8)0 15 10 ? * *    每天上午10:15触发 

  (9)0 15 10 * * ?     每天上午10:15触发 

  (10)0 15 10 * * ? *    每天上午10:15触发 

  (11)0 15 10 * * ? 2005    2005年的每天上午10:15触发 

  (12)0 * 14 * * ?     在每天下午2点到下午2:59期间的每1分钟触发 

  (13)0 0/5 14 * * ?    在每天下午2点到下午2:55期间的每5分钟触发 

  (14)0 0/5 14,18 * * ?     在每天下午2点到2:55期间和下午6点到6:55期间的每5分钟触发 

  (15)0 0-5 14 * * ?    在每天下午2点到下午2:05期间的每1分钟触发 

  (16)0 10,44 14 ? 3 WED    每年三月的星期三的下午2:10和2:44触发 

  (17)0 15 10 ? * MON-FRI    周一至周五的上午10:15触发 

  (18)0 15 10 15 * ?    每月15日上午10:15触发 

  (19)0 15 10 L * ?    每月最后一日的上午10:15触发 

  (20)0 15 10 ? * 6L    每月的最后一个星期五上午10:15触发 

  (21)0 15 10 ? * 6L 2002-2005   2002年至2005年的每月的最后一个星期五上午10:15触发 

  (22)0 15 10 ? * 6#3   每月的第三个星期五上午10:15触发

注:

  (1)有些子表达式能包含一些范围或列表

  例如:子表达式(天(星期))可以为 “MON-FRI”,“MON,WED,FRI”,“MON-WED,SAT”

“*”字符代表所有可能的值

  因此,“*”在子表达式(月)里表示每个月的含义,“*”在子表达式(天(星期))表示星期的每一天

  “/”字符用来指定数值的增量    例如:在子表达式(分钟)里的“0/15”表示从第0分钟开始,每15分钟  在子表达式(分钟)里的“3/20”表示从第3分钟开始,每20分钟(它和“3,23,43”)的含义一样

  “?”字符仅被用于天(月)和天(星期)两个子表达式,表示不指定值    当2个子表达式其中之一被指定了值以后,为了避免冲突,需要将另一个子表达式的值设为“?”

  “L” 字符仅被用于天(月)和天(星期)两个子表达式,它是单词“last”的缩写    但是它在两个子表达式里的含义是不同的。    在天(月)子表达式中,“L”表示一个月的最后一天    在天(星期)自表达式中,“L”表示一个星期的最后一天,也就是SAT

  如果在“L”前有具体的内容,它就具有其他的含义了

  例如:“6L”表示这个月的倒数第6天,“FRIL”表示这个月的最一个星期五    注意:在使用“L”参数时,不要指定列表或范围,因为这会导致问题