vue3 添加编辑页使用 cron 表达式生成
示例效果图
1、添加组件
<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="mt-15"><el-input v-model="value"/></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: '星期日', val: 'SUN', value: 1,},{name: '星期一', val: 'MON', value: 2,},{name: '星期二', val: 'TUE', value: 3,},{name: '星期三', val: 'WED', value: 4,},{name: '星期四', val: 'THU', value: 5,},{name: '星期五', val: 'FRI', value: 6,},{name: '星期六', val: 'SAT', value: 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(() => {console.log("------------")let weeks = "";let cronEvery = state.day.cronEvery;switch (cronEvery?.toString()) {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 "5":weeks = "?";break;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(() => {let secondsText = `${state.secondsText || "*"}`let minutesText = `${state.minutesText || "*"}`let hoursText = `${state.hoursText || "*"}`let daysText = `${state.daysText || "*"}`let monthsText = `${state.monthsText || "*"}`let weeksText = `${state.weeksText || "?"}`let yearsText = `${state.yearsText || "*"}`if (minutesText !== '*') {secondsText = secondsText === '*' ? '0' : secondsText;}if (hoursText !== '*') {secondsText = secondsText === '*' ? '0' : secondsText;minutesText = minutesText === '*' ? '0' : minutesText;}if (daysText !== '*') {secondsText = secondsText === '*' ? '0' : secondsText;minutesText = minutesText === '*' ? '0' : minutesText;hoursText = hoursText === '*' ? '0' : hoursText;}if (weeksText !== '?') {secondsText = secondsText === '*' ? '0' : secondsText;minutesText = minutesText === '*' ? '0' : minutesText;hoursText = hoursText === '*' ? '0' : hoursText;}if (monthsText !== '*') {secondsText = secondsText === '*' ? '0' : secondsText;minutesText = minutesText === '*' ? '0' : minutesText;hoursText = hoursText === '*' ? '0' : hoursText;daysText = daysText === '*' ? '1' : daysText;}if (yearsText !== '*') {secondsText = secondsText === '*' ? '0' : secondsText;minutesText = minutesText === '*' ? '0' : minutesText;hoursText = hoursText === '*' ? '0' : hoursText;// daysText = daysText === '*' ? '1' : daysText;// monthsText = monthsText === '*' ? '1' : monthsText;}return secondsText + " " + minutesText + " " + hoursText + " " + daysText + " " + monthsText + " " + weeksText + " " + yearsText;}),});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;}}}};// 点击变更调用方数据const handleChange = () => {if (typeof state.cron !== "string") return false;emit("change", state.cron);};// 数据变化时变更调用方数据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-content > div {line-height: 50px;
}.v3c-content {padding: 20px;max-height: v-bind(maxHeight);overflow: hidden;overflow-y: auto;
}.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;
}
</style>
2、使用组件
2.1、定义相关前置参数
import {defineAsyncComponent} from "vue";
export default {components: {Vue3Cron: defineAsyncComponent(() => import('@/components/vue3-cron/cron.vue')),},data() {return {// 表单参数obj: {},// 定义弹窗开关字段cronDialogVisible: false,cronTimes: [],uri: {// 定义预览 uri 的接口地址temporalPrediction: "/api/admin/dispatch/dispatchTask/temporalPrediction",}}},methods: {// 获取调度的预计执行时间temporalPrediction(cron, startTime, endTime) {this.crud.get(this.uri.temporalPrediction, {cron: cron, startTime: startTime, endTime: endTime}).then(res => {this.cronTimes = res.data.data;})}, }
2.2、 添加插槽 (添加/编辑页中)
<!-- cron 表达式插槽 -->
<template #cron="{size,row,index}"><!-- 展示当前cron 表达式--><el-input v-model="this.obj.cron" placeholder=""/><!-- 打开 cron 生成弹窗,详见定义弹窗中的内容 --><el-button type="primary" style="margin-top: 10px;margin-bottom: 10px" @click="cronDialogVisible=true">cron 生成</el-button><!-- 时间预测-- 与预览 (当前页面中) --><el-button type="primary" style="margin-top: 10px;margin-bottom: 10px" @click="temporalPrediction(this.obj.cron,this.obj.startTime,this.obj.endTime) ">时间预测</el-button><div v-for="(item,index) in cronTimes">{{ item }}</div>
</template>
2.3、定义弹窗 (添加/编辑页中)
<!-- cron 是弹窗 --><el-dialog title="cron 生成" v-if="cronDialogVisible" v-model="cronDialogVisible"><!-- 选择 --><Vue3Cron v-model:value="this.obj.cron"/><!-- 时间预测-- 与预览 (弹窗中) --><el-button type="primary" style="margin-top: 10px;margin-bottom: 10px" @click="temporalPrediction(this.obj.cron) ">获取最近运行时间</el-button><div v-for="(item,index) in cronTimes">{{ item }}</div></el-dialog>
后端部分 ( java)
pom.xml
<!-- quartz --><dependency><groupId>org.quartz-scheduler</groupId><artifactId>quartz</artifactId><version>2.2.1</version></dependency><dependency><groupId>org.quartz-scheduler</groupId><artifactId>quartz-jobs</artifactId><version>2.2.1</version></dependency><dependency><groupId>com.cronutils</groupId><artifactId>cron-utils</artifactId><version>9.1.0</version></dependency>
接口 controller
@GetMapping(value = "/temporalPrediction")@ApiOperation(value = "时间预测")public Result<List<String>> temporalPrediction(@RequestParam String cron,String startTime,String endTime) {return Result.success(baseService.temporalPrediction( cron, startTime, endTime));}
接口 service
/*** 时间预测** @param cron cron* @param startTime 开始时间* @param endTime 结束时间* @return {@link List}<{@link String}>*/List<String> temporalPrediction(String cron, String startTime, String endTime);@Overridepublic List<String> temporalPrediction(String cron, String startTime, String endTime) {List<String> nextExecuteTimes = null;try {if (StringUtils.isBlank(startTime) && StringUtils.isBlank(endTime)) {nextExecuteTimes = QuartzUtil.getNextExecuteTimes(cron, 10);} else {nextExecuteTimes = QuartzUtil.getNextExecuteTimesByTimeRange(cron, startTime, endTime, 10);}} catch (Exception e) {throw new ErrorException("cron 表达式解析失败");}return nextExecuteTimes;}
QuartzUtil 工具
/*** 根据时间范围获取新的cron表达式** @param cronExpression 现有的 cron 表达式* @param startTimeStr 开始时间* @param startTimeStr 结束时间* @return {@link String} 时间范围*/public static String getNewCronByTimeRange(String cronExpression, String startTimeStr, String endTimeStr) {LocalDateTime startTime = LocalDateTimeUtil.parse("2021-01-01 " + startTimeStr);LocalDateTime endTime = LocalDateTimeUtil.parse("2021-01-01 " + endTimeStr);if (LocalDateTimeUtil.isAfter(startTime, endTime)) {throw new ErrorException("开始时间不能小于结束时间");}long minutes = LocalDateTimeUtil.betweenTwoTime(startTime, endTime, ChronoUnit.MINUTES);if (minutes == 0) {// 差值为0, cron 表达式结果不变return cronExpression;}// 随机分钟数long randomLong = RandomUtil.randomLong(minutes);// 随机秒数long second = RandomUtil.randomLong(59);LocalDateTime time = LocalDateTimeUtil.plus(startTime, randomLong, ChronoUnit.MINUTES);// System.out.println("随机数:" + randomLong+ " 随机时间:" + time.getHour() +":" + time.getMinute() +":"+ second);String[] cronExpressionArray = cronExpression.split(" ");cronExpressionArray[0] = second + "";cronExpressionArray[1] = time.getMinute() + "";cronExpressionArray[2] = time.getHour() + "";return String.join(" ", cronExpressionArray);}/*** 列出接下来的 n次 执行时间 (根据 cron 表达式)** @param cronExpression cron表达式* @param num 预测次数* @throws ParseException 解析异常 (cron 表达式不规范需返回异常,调用方需主动处理该错误)*/public static List<String> getNextExecuteTimes(String cronExpression, int num) {List<String> dates = new ArrayList<>();CronExpression cron = null;try {cron = new CronExpression(cronExpression);} catch (ParseException e) {throw new RuntimeException(e);}Date currentDate = new Date();for (int i = 0; i < num; i++) {Date nextFireTime = cron.getNextValidTimeAfter(currentDate);currentDate = nextFireTime;dates.add(formatDate(nextFireTime));}return dates;}/*** 列出接下来的 n次 执行时间 (根据 cron 表达式+ 时间范围)** @param cronExpression cron表达式* @param num 预测次数* @throws ParseException 解析异常 (cron 表达式不规范需返回异常,调用方需主动处理该错误)*/public static List<String> getNextExecuteTimesByTimeRange(String cronExpression, String startTimeStr, String endTimeStr, int num) {List<String> dates = new ArrayList<>();for (int i = 0; i < num; i++) {String newCron = getNewCronByTimeRange(cronExpression, startTimeStr, endTimeStr);CronExpression cron = null;try {// 每次获取为 i 的最后一次的执行来保证 年月日 单位的执行日期, 时分秒日期每次 i 循环时修改获得cron = new CronExpression(newCron);} catch (ParseException e) {throw new RuntimeException(e);}Date currentDate = new Date();for (int j = 0; j <= i; j++) {Date nextFireTime = cron.getNextValidTimeAfter(currentDate);currentDate = nextFireTime;if (j == i) {dates.add(formatDate(nextFireTime));}}}return dates;}/*** 格式化时间日期** @param date 日期* @return {@link String}*/private static String formatDate(Date date) {SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");return sdf.format(date);}
相关文章:

vue3 添加编辑页使用 cron 表达式生成
示例效果图 1、添加组件 <template><div class"v3c"><ul class"v3c-tab"><li class"v3c-tab-item" :class"{ v3c-active: tabActive 1 }" click"onHandleTab(1)">秒</li><li class&qu…...

洛谷P1722 矩阵Ⅱ——卡特兰数
传送门: P1722 矩阵 II - 洛谷 | 计算机科学教育新生态 (luogu.com.cn)https://www.luogu.com.cn/problem/P1722 用不需要除任何数的公式来求。 #define _CRT_SECURE_NO_WARNINGS #include<iostream> #include<cstdio> #include<cmath> #includ…...

Unity | Shader基础知识(第六集:语法<如何加入外部颜色资源>)
目录 一、本节介绍 1 上集回顾 2 本节介绍 二、语法结构 1 复习 2 理论知识 3 Shader里声明的写法 4 Properties和SubShader毕竟不是一家人 三、 片元着色器中使用资源 四、代码实现 五、全部代码 六、下集介绍 相关阅读 Unity - Manual: Writing Surface Shaders…...

使用opencv的Laplacian算子实现图像边缘检测
1 边缘检测介绍 图像边缘检测技术是图像处理和计算机视觉等领域最基本的问题,也是经典的技术难题之一。如何快速、精确地提取图像边缘信息,一直是国内外的研究热点,同时边缘的检测也是图像处理中的一个难题。早期的经典算法包括边缘算子方法…...

5. PyTorch——数据处理模块
1.数据加载 在PyTorch中,数据加载可通过自定义的数据集对象。数据集对象被抽象为Dataset类,实现自定义的数据集需要继承Dataset,并实现两个Python魔法方法: __getitem__:返回一条数据,或一个样本。obj[in…...

Android 移动端编译 cityhash动态库
最近做项目, 硬件端 需要 用 cityhash 编译一个 动态库 提供给移动端使用,l 记录一下 编译过程 city .cpp // // Created by Administrator on 2023/12/12. // // Copyright (c) 2011 Google, Inc. // // Permission is hereby granted, free of charg…...

IO流学习
IO流:存储和读取数据的解决方案 import java.io.FileOutputStream; import java.io.IOException;public class Test {public static void main(String[] args) throws IOException {//1.创建对象//写出 输入流 OutputStream//本地文件fileFileOutputStream fos new FileOutputS…...

新手HTML和CSS的常见知识点
目录 1.HTML标题标签(到)用于定义网页中的标题,并按照重要性递减排列。例如: 2.HTML段落标签()用于定义网页中的段落。例如: 3.HTML链接标签()用于创建链接…...

RocketMQ系统性学习-RocketMQ领域模型及Linux下单机安装
MQ 之间的对比 三种常用的 MQ 对比,ActiveMQ、Kafka、RocketMQ 性能方面: 三种 MQ 吞吐量级别为:万,百万,十万消息发送时延:毫秒,毫秒,微秒可用性:主从,分…...

微服务架构之争:Quarkus VS Spring Boot
在容器时代(“Docker时代”),无论如何,Java仍然活着。Java在性能方面一直很有名,主要是因为代码和真实机器之间的抽象层,多平台的成本(一次编写,随处运行——还记得吗?&a…...

如何使用ArcGIS Pro拼接影像
为了方便数据的存储和传输,我们在网上获取到的影像一般都是分块的,正式使用之前需要对这些影像进行拼接,这里为大家介绍一下ArcGIS Pro中拼接影像的方法,希望能对你有所帮助。 数据来源 本教程所使用的数据是从水经微图中下载的…...

[论文笔记] chatgpt系列 SparseMOE—GPT4的MOE结构
SparseMOE: 稀疏激活的MOE Swtich MOE,所有token要在K个专家网络中,选择一个专家网络。 显存增加。 Experts Choice:路由MOE: 由专家选择token。这样不同的专家都选择到某个token,也可以不选择该token。 由于FFN层的时间复杂度和attention层不同,FFN层的时…...

C# WPF上位机开发(键盘绘图控制)
【 声明:版权所有,欢迎转载,请勿用于商业用途。 联系信箱:feixiaoxing 163.com】 在软件开发中,如果存在canvas图像的话,一般有几种控制方法。一种是鼠标控制;一种是键盘控制;还有一…...

《地理信息系统原理》笔记/期末复习资料(10. 空间数据挖掘与空间决策支持系统)
目录 10. 空间数据挖掘与空间决策支持系统 10.1. 空间数据挖掘 10.1.1. 空间数据挖掘的概念 10.1.2. 空间数据挖掘的方法与过程 10.1.3. 空间数据挖掘的应用 10.2. 空间决策支持系统 10.2.1. 空间决策支持系统的概念 10.2.2. 空间决策支持系统的结构 10.2.3. 空间决策…...

uniapp播放 m3u8格式视频 兼容pc和移动端
支持全自动播放、设置参数 自己摸索出来的,花了一天时间,给点订阅支持下,订阅后,不懂的地方可以私聊我。 代码实现 代码实现 1.安装dplayer组件 npm i dplayer2. static/index.html下引入 hls 引入hls.min.js 可以存放在static项目hls下面<script src="/static…...

产品经理之Axure的元件库使用详细案例
⭐⭐ 产品经理专栏:产品专栏 ⭐⭐ 个人主页:个人主页 目录 前言 一.Axure的元件库的使用 1.1 元件介绍 1.2 基本元件的使用 1.2.1 矩形、按钮、标题的使用 1.2.2 图片及热区的使用 1.3 表单元件及表格元件的使用 1.3.1表单元件的使用 1.3.…...

数字化转型对企业有什么好处?
引言 数字化转型已经成为当今商业领域中的一股强大力量,它不仅仅是简单的技术更新,更是企业发展的重要战略转变。随着科技的迅猛发展和全球化竞争的加剧,企业们正在积极探索如何将数字化的力量融入到他们的运营和战略中。 数字化转型不仅是传…...
微信小程序:按钮禁用,避免按钮重复提交
wxml <view class"modal-buttons"><view class"one_btn" bindtap"submit">确认</view><view class"two_btn" bindtap"cancel">取消</view> </view> wxss /* 按钮 */ .modal-buttons…...

JAVA 异常分类及处理
JAVA 异常分类及处理 概念 如果某个方法不能按照正常的途径完成任务,就可以通过另一种路径退出方法。在这种情况下会抛出一个封装了错误信息的对象。此时,这个方法会立刻退出同时不返回任何值。另外,调用这个方法的其他代码也无法继续执行&…...

C语言--求数组的最大值和最小值【两种方法】
🍗方法一:用for循环遍历数组,找出最大值与最小值 🍗方法二:用qsort排序,让数组成为升序的有序数组,第一个值就是最小值,最后一个是最大值 完整代码: 方法一: …...

Vue3 + Element Plus + TypeScript中el-transfer穿梭框组件使用详解及示例
使用详解 Element Plus 的 el-transfer 组件是一个强大的穿梭框组件,常用于在两个集合之间进行数据转移,如权限分配、数据选择等场景。下面我将详细介绍其用法并提供一个完整示例。 核心特性与用法 基本属性 v-model:绑定右侧列表的值&…...

抖音增长新引擎:品融电商,一站式全案代运营领跑者
抖音增长新引擎:品融电商,一站式全案代运营领跑者 在抖音这个日活超7亿的流量汪洋中,品牌如何破浪前行?自建团队成本高、效果难控;碎片化运营又难成合力——这正是许多企业面临的增长困局。品融电商以「抖音全案代运营…...

ServerTrust 并非唯一
NSURLAuthenticationMethodServerTrust 只是 authenticationMethod 的冰山一角 要理解 NSURLAuthenticationMethodServerTrust, 首先要明白它只是 authenticationMethod 的选项之一, 并非唯一 1 先厘清概念 点说明authenticationMethodURLAuthenticationChallenge.protectionS…...
代理篇12|深入理解 Vite中的Proxy接口代理配置
在前端开发中,常常会遇到 跨域请求接口 的情况。为了解决这个问题,Vite 和 Webpack 都提供了 proxy 代理功能,用于将本地开发请求转发到后端服务器。 什么是代理(proxy)? 代理是在开发过程中,前端项目通过开发服务器,将指定的请求“转发”到真实的后端服务器,从而绕…...
iOS性能调优实战:借助克魔(KeyMob)与常用工具深度洞察App瓶颈
在日常iOS开发过程中,性能问题往往是最令人头疼的一类Bug。尤其是在App上线前的压测阶段或是处理用户反馈的高发期,开发者往往需要面对卡顿、崩溃、能耗异常、日志混乱等一系列问题。这些问题表面上看似偶发,但背后往往隐藏着系统资源调度不当…...
Go 语言并发编程基础:无缓冲与有缓冲通道
在上一章节中,我们了解了 Channel 的基本用法。本章将重点分析 Go 中通道的两种类型 —— 无缓冲通道与有缓冲通道,它们在并发编程中各具特点和应用场景。 一、通道的基本分类 类型定义形式特点无缓冲通道make(chan T)发送和接收都必须准备好࿰…...

C++:多态机制详解
目录 一. 多态的概念 1.静态多态(编译时多态) 二.动态多态的定义及实现 1.多态的构成条件 2.虚函数 3.虚函数的重写/覆盖 4.虚函数重写的一些其他问题 1).协变 2).析构函数的重写 5.override 和 final关键字 1&#…...
【Nginx】使用 Nginx+Lua 实现基于 IP 的访问频率限制
使用 NginxLua 实现基于 IP 的访问频率限制 在高并发场景下,限制某个 IP 的访问频率是非常重要的,可以有效防止恶意攻击或错误配置导致的服务宕机。以下是一个详细的实现方案,使用 Nginx 和 Lua 脚本结合 Redis 来实现基于 IP 的访问频率限制…...
Python 训练营打卡 Day 47
注意力热力图可视化 在day 46代码的基础上,对比不同卷积层热力图可视化的结果 import torch import torch.nn as nn import torch.optim as optim from torchvision import datasets, transforms from torch.utils.data import DataLoader import matplotlib.pypl…...
webpack面试题
面试题:webpack介绍和简单使用 一、webpack(模块化打包工具)1. webpack是把项目当作一个整体,通过给定的一个主文件,webpack将从这个主文件开始找到你项目当中的所有依赖文件,使用loaders来处理它们&#x…...