From ba5b8c77c3071722d00f9d1e1878cb8a4efd06d7 Mon Sep 17 00:00:00 2001 From: RuoYi <yzz_ivy@163.com> Date: 星期五, 28 十月 2022 20:35:50 +0800 Subject: [PATCH] 定时任务支持在线生成cron表达式 --- src/components/Crontab/year.vue | 149 ++++ src/components/Crontab/month.vue | 141 ++++ src/views/monitor/job/index.vue | 12 src/components/Crontab/hour.vue | 127 +++ src/components/Crontab/result.vue | 540 ++++++++++++++++ src/components/Crontab/week.vue | 197 ++++++ src/components/Crontab/day.vue | 174 +++++ src/components/Crontab/min.vue | 126 +++ src/components/Crontab/index.vue | 310 +++++++++ src/components/Crontab/second.vue | 128 ++++ 10 files changed, 1,900 insertions(+), 4 deletions(-) diff --git a/src/components/Crontab/day.vue b/src/components/Crontab/day.vue new file mode 100644 index 0000000..e9a28ae --- /dev/null +++ b/src/components/Crontab/day.vue @@ -0,0 +1,174 @@ +<template> + <el-form size="small"> + <el-form-item> + <el-radio v-model='radioValue' :label="1"> + 鏃ワ紝鍏佽鐨勯�氶厤绗, - * ? / L W] + </el-radio> + </el-form-item> + + <el-form-item> + <el-radio v-model='radioValue' :label="2"> + 涓嶆寚瀹� + </el-radio> + </el-form-item> + + <el-form-item> + <el-radio v-model='radioValue' :label="3"> + 鍛ㄦ湡浠� + <el-input-number v-model='cycle01' :min="1" :max="30" /> - + <el-input-number v-model='cycle02' :min="cycle01 + 1" :max="31" /> 鏃� + </el-radio> + </el-form-item> + + <el-form-item> + <el-radio v-model='radioValue' :label="4"> + 浠� + <el-input-number v-model='average01' :min="1" :max="30" /> 鍙峰紑濮嬶紝姣� + <el-input-number v-model='average02' :min="1" :max="31 - average01" /> 鏃ユ墽琛屼竴娆� + </el-radio> + </el-form-item> + + <el-form-item> + <el-radio v-model='radioValue' :label="5"> + 姣忔湀 + <el-input-number v-model='workday' :min="1" :max="31" /> 鍙锋渶杩戠殑閭d釜宸ヤ綔鏃� + </el-radio> + </el-form-item> + + <el-form-item> + <el-radio v-model='radioValue' :label="6"> + 鏈湀鏈�鍚庝竴澶� + </el-radio> + </el-form-item> + + <el-form-item> + <el-radio v-model='radioValue' :label="7"> + 鎸囧畾 + <el-select clearable v-model="checkboxList" placeholder="鍙閫�" multiple :multiple-limit="10"> + <el-option v-for="item in 31" :key="item" :label="item" :value="item" /> + </el-select> + </el-radio> + </el-form-item> + </el-form> +</template> +<script setup> +const emit = defineEmits(['update']) +const props = defineProps({ + cron: { + type: Object, + default: { + second: "*", + min: "*", + hour: "*", + day: "*", + month: "*", + week: "?", + year: "", + } + }, + check: { + type: Function, + default: () => { + } + } +}) +const radioValue = ref(1) +const cycle01 = ref(1) +const cycle02 = ref(2) +const average01 = ref(1) +const average02 = ref(1) +const workday = ref(1) +const checkboxList = ref([]) +const checkCopy = ref([1]) +const cycleTotal = computed(() => { + cycle01.value = props.check(cycle01.value, 1, 30) + cycle02.value = props.check(cycle02.value, cycle01.value + 1, 31) + return cycle01.value + '-' + cycle02.value +}) +const averageTotal = computed(() => { + average01.value = props.check(average01.value, 1, 30) + average02.value = props.check(average02.value, 1, 31 - average01.value) + return average01.value + '/' + average02.value +}) +const workdayTotal = computed(() => { + workday.value = props.check(workday.value, 1, 31) + return workday.value + 'W' +}) +const checkboxString = computed(() => { + return checkboxList.value.join(',') +}) +watch(() => props.cron.day, value => changeRadioValue(value)) +watch([radioValue, cycleTotal, averageTotal, workdayTotal, checkboxString], () => onRadioChange()) +function changeRadioValue(value) { + if (value === "*") { + radioValue.value = 1 + } else if (value === "?") { + radioValue.value = 2 + } else if (value.indexOf("-") > -1) { + const indexArr = value.split('-') + cycle01.value = Number(indexArr[0]) + cycle02.value = Number(indexArr[1]) + radioValue.value = 3 + } else if (value.indexOf("/") > -1) { + const indexArr = value.split('/') + average01.value = Number(indexArr[0]) + average02.value = Number(indexArr[1]) + radioValue.value = 4 + } else if (value.indexOf("W") > -1) { + const indexArr = value.split("W") + workday.value = Number(indexArr[0]) + radioValue.value = 5 + } else if (value === "L") { + radioValue.value = 6 + } else { + checkboxList.value = [...new Set(value.split(',').map(item => Number(item)))] + radioValue.value = 7 + } +} +// 鍗曢�夋寜閽�煎彉鍖栨椂 +function onRadioChange() { + if (radioValue.value === 2 && props.cron.week === '?') { + emit('update', 'week', '*', 'day') + } + if (radioValue.value !== 2 && props.cron.week !== '?') { + emit('update', 'week', '?', 'day') + } + switch (radioValue.value) { + case 1: + emit('update', 'day', '*', 'day') + break + case 2: + emit('update', 'day', '?', 'day') + break + case 3: + emit('update', 'day', cycleTotal.value, 'day') + break + case 4: + emit('update', 'day', averageTotal.value, 'day') + break + case 5: + emit('update', 'day', workdayTotal.value, 'day') + break + case 6: + emit('update', 'day', 'L', 'day') + break + case 7: + if (checkboxList.value.length === 0) { + checkboxList.value.push(checkCopy.value[0]) + } else { + checkCopy.value = checkboxList.value + } + emit('update', 'day', checkboxString.value, 'day') + break + } +} +</script> + +<style lang="scss" scoped> +.el-input-number--small, .el-input-number--small, .el-select, .el-select--small { + margin: 0 0.2rem; +} +.el-select, .el-select--small { + width: 18.8rem; +} +</style> \ No newline at end of file diff --git a/src/components/Crontab/hour.vue b/src/components/Crontab/hour.vue new file mode 100644 index 0000000..5b22a6e --- /dev/null +++ b/src/components/Crontab/hour.vue @@ -0,0 +1,127 @@ +<template> + <el-form size="small"> + <el-form-item> + <el-radio v-model='radioValue' :label="1"> + 灏忔椂锛屽厑璁哥殑閫氶厤绗, - * /] + </el-radio> + </el-form-item> + + <el-form-item> + <el-radio v-model='radioValue' :label="2"> + 鍛ㄦ湡浠� + <el-input-number v-model='cycle01' :min="0" :max="22" /> - + <el-input-number v-model='cycle02' :min="cycle01 + 1" :max="23" /> 鏃� + </el-radio> + </el-form-item> + + <el-form-item> + <el-radio v-model='radioValue' :label="3"> + 浠� + <el-input-number v-model='average01' :min="0" :max="22" /> 鏃跺紑濮嬶紝姣� + <el-input-number v-model='average02' :min="1" :max="23 - average01" /> 灏忔椂鎵ц涓�娆� + </el-radio> + </el-form-item> + + <el-form-item> + <el-radio v-model='radioValue' :label="4"> + 鎸囧畾 + <el-select clearable v-model="checkboxList" placeholder="鍙閫�" multiple :multiple-limit="10"> + <el-option v-for="item in 24" :key="item" :label="item - 1" :value="item - 1" /> + </el-select> + </el-radio> + </el-form-item> + </el-form> +</template> + +<script setup> +const emit = defineEmits(['update']) +const props = defineProps({ + cron: { + type: Object, + default: { + second: "*", + min: "*", + hour: "*", + day: "*", + month: "*", + week: "?", + year: "", + } + }, + check: { + type: Function, + default: () => { + } + } +}) +const radioValue = ref(1) +const cycle01 = ref(0) +const cycle02 = ref(1) +const average01 = ref(0) +const average02 = ref(1) +const checkboxList = ref([]) +const checkCopy = ref([0]) +const cycleTotal = computed(() => { + cycle01.value = props.check(cycle01.value, 0, 22) + cycle02.value = props.check(cycle02.value, cycle01.value + 1, 23) + return cycle01.value + '-' + cycle02.value +}) +const averageTotal = computed(() => { + average01.value = props.check(average01.value, 0, 22) + average02.value = props.check(average02.value, 1, 23 - average01.value) + return average01.value + '/' + average02.value +}) +const checkboxString = computed(() => { + return checkboxList.value.join(',') +}) +watch(() => props.cron.hour, value => changeRadioValue(value)) +watch([radioValue, cycleTotal, averageTotal, checkboxString], () => onRadioChange()) +function changeRadioValue(value) { + if (value === '*') { + radioValue.value = 1 + } else if (value.indexOf('-') > -1) { + const indexArr = value.split('-') + cycle01.value = Number(indexArr[0]) + cycle02.value = Number(indexArr[1]) + radioValue.value = 2 + } else if (value.indexOf('/') > -1) { + const indexArr = value.split('/') + average01.value = Number(indexArr[0]) + average02.value = Number(indexArr[1]) + radioValue.value = 3 + } else { + checkboxList.value = [...new Set(value.split(',').map(item => Number(item)))] + radioValue.value = 4 + } +} +function onRadioChange() { + switch (radioValue.value) { + case 1: + emit('update', 'hour', '*', 'hour') + break + case 2: + emit('update', 'hour', cycleTotal.value, 'hour') + break + case 3: + emit('update', 'hour', averageTotal.value, 'hour') + break + case 4: + if (checkboxList.value.length === 0) { + checkboxList.value.push(checkCopy.value[0]) + } else { + checkCopy.value = checkboxList.value + } + emit('update', 'hour', checkboxString.value, 'hour') + break + } +} +</script> + +<style lang="scss" scoped> +.el-input-number--small, .el-input-number--small, .el-select, .el-select--small { + margin: 0 0.2rem; +} +.el-select, .el-select--small { + width: 18.8rem; +} +</style> \ No newline at end of file diff --git a/src/components/Crontab/index.vue b/src/components/Crontab/index.vue new file mode 100644 index 0000000..f1d7201 --- /dev/null +++ b/src/components/Crontab/index.vue @@ -0,0 +1,310 @@ +<template> + <div> + <el-tabs type="border-card"> + <el-tab-pane label="绉�" v-if="shouldHide('second')"> + <CrontabSecond + @update="updateCrontabValue" + :check="checkNumber" + :cron="crontabValueObj" + ref="cronsecond" + /> + </el-tab-pane> + + <el-tab-pane label="鍒嗛挓" v-if="shouldHide('min')"> + <CrontabMin + @update="updateCrontabValue" + :check="checkNumber" + :cron="crontabValueObj" + ref="cronmin" + /> + </el-tab-pane> + + <el-tab-pane label="灏忔椂" v-if="shouldHide('hour')"> + <CrontabHour + @update="updateCrontabValue" + :check="checkNumber" + :cron="crontabValueObj" + ref="cronhour" + /> + </el-tab-pane> + + <el-tab-pane label="鏃�" v-if="shouldHide('day')"> + <CrontabDay + @update="updateCrontabValue" + :check="checkNumber" + :cron="crontabValueObj" + ref="cronday" + /> + </el-tab-pane> + + <el-tab-pane label="鏈�" v-if="shouldHide('month')"> + <CrontabMonth + @update="updateCrontabValue" + :check="checkNumber" + :cron="crontabValueObj" + ref="cronmonth" + /> + </el-tab-pane> + + <el-tab-pane label="鍛�" v-if="shouldHide('week')"> + <CrontabWeek + @update="updateCrontabValue" + :check="checkNumber" + :cron="crontabValueObj" + ref="cronweek" + /> + </el-tab-pane> + + <el-tab-pane label="骞�" v-if="shouldHide('year')"> + <CrontabYear + @update="updateCrontabValue" + :check="checkNumber" + :cron="crontabValueObj" + ref="cronyear" + /> + </el-tab-pane> + </el-tabs> + + <div class="popup-main"> + <div class="popup-result"> + <p class="title">鏃堕棿琛ㄨ揪寮�</p> + <table> + <thead> + <th v-for="item of tabTitles" :key="item">{{item}}</th> + <th>Cron 琛ㄨ揪寮�</th> + </thead> + <tbody> + <td> + <span v-if="crontabValueObj.second.length < 10">{{crontabValueObj.second}}</span> + <el-tooltip v-else :content="crontabValueObj.second" placement="top"><span>{{crontabValueObj.second}}</span></el-tooltip> + </td> + <td> + <span v-if="crontabValueObj.min.length < 10">{{crontabValueObj.min}}</span> + <el-tooltip v-else :content="crontabValueObj.min" placement="top"><span>{{crontabValueObj.min}}</span></el-tooltip> + </td> + <td> + <span v-if="crontabValueObj.hour.length < 10">{{crontabValueObj.hour}}</span> + <el-tooltip v-else :content="crontabValueObj.hour" placement="top"><span>{{crontabValueObj.hour}}</span></el-tooltip> + </td> + <td> + <span v-if="crontabValueObj.day.length < 10">{{crontabValueObj.day}}</span> + <el-tooltip v-else :content="crontabValueObj.day" placement="top"><span>{{crontabValueObj.day}}</span></el-tooltip> + </td> + <td> + <span v-if="crontabValueObj.month.length < 10">{{crontabValueObj.month}}</span> + <el-tooltip v-else :content="crontabValueObj.month" placement="top"><span>{{crontabValueObj.month}}</span></el-tooltip> + </td> + <td> + <span v-if="crontabValueObj.week.length < 10">{{crontabValueObj.week}}</span> + <el-tooltip v-else :content="crontabValueObj.week" placement="top"><span>{{crontabValueObj.week}}</span></el-tooltip> + </td> + <td> + <span v-if="crontabValueObj.year.length < 10">{{crontabValueObj.year}}</span> + <el-tooltip v-else :content="crontabValueObj.year" placement="top"><span>{{crontabValueObj.year}}</span></el-tooltip> + </td> + <td class="result"> + <span v-if="crontabValueString.length < 90">{{crontabValueString}}</span> + <el-tooltip v-else :content="crontabValueString" placement="top"><span>{{crontabValueString}}</span></el-tooltip> + </td> + </tbody> + </table> + </div> + <CrontabResult :ex="crontabValueString"></CrontabResult> + + <div class="pop_btn"> + <el-button type="primary" @click="submitFill">纭畾</el-button> + <el-button type="warning" @click="clearCron">閲嶇疆</el-button> + <el-button @click="hidePopup">鍙栨秷</el-button> + </div> + </div> + </div> +</template> + +<script setup> +import CrontabSecond from "./second.vue" +import CrontabMin from "./min.vue" +import CrontabHour from "./hour.vue" +import CrontabDay from "./day.vue" +import CrontabMonth from "./month.vue" +import CrontabWeek from "./week.vue" +import CrontabYear from "./year.vue" +import CrontabResult from "./result.vue" +const { proxy } = getCurrentInstance() +const emit = defineEmits(['hide', 'fill']) +const props = defineProps({ + hideComponent: { + type: Array, + default: () => [], + }, + expression: { + type: String, + default: "" + } +}) +const tabTitles = ref(["绉�", "鍒嗛挓", "灏忔椂", "鏃�", "鏈�", "鍛�", "骞�"]) +const tabActive = ref(0) +const hideComponent = ref([]) +const expression = ref('') +const crontabValueObj = ref({ + second: "*", + min: "*", + hour: "*", + day: "*", + month: "*", + week: "?", + year: "", +}) +const crontabValueString = computed(() => { + const obj = crontabValueObj.value + return obj.second + + " " + + obj.min + + " " + + obj.hour + + " " + + obj.day + + " " + + obj.month + + " " + + obj.week + + (obj.year === "" ? "" : " " + obj.year) +}) +watch(expression, () => resolveExp()) +function shouldHide(key) { + return !(hideComponent.value && hideComponent.value.includes(key)) +} +function resolveExp() { + // 鍙嶈В鏋� 琛ㄨ揪寮� + if (expression.value) { + const arr = expression.value.split(/\s+/) + if (arr.length >= 6) { + //6 浣嶄互涓婃槸鍚堟硶琛ㄨ揪寮� + let obj = { + second: arr[0], + min: arr[1], + hour: arr[2], + day: arr[3], + month: arr[4], + week: arr[5], + year: arr[6] ? arr[6] : "" + } + crontabValueObj.value = { + ...obj, + } + } + } else { + // 娌℃湁浼犲叆鐨勮〃杈惧紡 鍒欒繕鍘� + clearCron() + } +} +// tab鍒囨崲鍊� +function tabCheck(index) { + tabActive.value = index +} +// 鐢卞瓙缁勪欢瑙﹀彂锛屾洿鏀硅〃杈惧紡缁勬垚鐨勫瓧娈靛�� +function updateCrontabValue(name, value, from) { + crontabValueObj.value[name] = value +} +// 琛ㄥ崟閫夐」鐨勫瓙缁勪欢鏍¢獙鏁板瓧鏍煎紡锛堥�氳繃-props浼犻�掞級 +function checkNumber(value, minLimit, maxLimit) { + // 妫�鏌ュ繀椤讳负鏁存暟 + value = Math.floor(value) + if (value < minLimit) { + value = minLimit + } else if (value > maxLimit) { + value = maxLimit + } + return value +} +// 闅愯棌寮圭獥 +function hidePopup() { + emit("hide") +} +// 濉厖琛ㄨ揪寮� +function submitFill() { + emit("fill", crontabValueString.value) + hidePopup() +} +function clearCron() { + // 杩樺師閫夋嫨椤� + crontabValueObj.value = { + second: "*", + min: "*", + hour: "*", + day: "*", + month: "*", + week: "?", + year: "", + } +} +onMounted(() => { + expression.value = props.expression + hideComponent.value = props.hideComponent +}) +</script> + +<style lang="scss" scoped> +.pop_btn { + text-align: center; + margin-top: 20px; +} +.popup-main { + position: relative; + margin: 10px auto; + background: #fff; + border-radius: 5px; + font-size: 12px; + overflow: hidden; +} +.popup-title { + overflow: hidden; + line-height: 34px; + padding-top: 6px; + background: #f2f2f2; +} +.popup-result { + box-sizing: border-box; + line-height: 24px; + margin: 25px auto; + padding: 15px 10px 10px; + border: 1px solid #ccc; + position: relative; +} +.popup-result .title { + position: absolute; + top: -28px; + left: 50%; + width: 140px; + font-size: 14px; + margin-left: -70px; + text-align: center; + line-height: 30px; + background: #fff; +} +.popup-result table { + text-align: center; + width: 100%; + margin: 0 auto; +} +.popup-result table td:not(.result) { + width: 3.5rem; + min-width: 3.5rem; + max-width: 3.5rem; +} +.popup-result table span { + display: block; + width: 100%; + font-family: arial; + line-height: 30px; + height: 30px; + white-space: nowrap; + overflow: hidden; + border: 1px solid #e8e8e8; +} +.popup-result-scroll { + font-size: 12px; + line-height: 24px; + height: 10em; + overflow-y: auto; +} +</style> \ No newline at end of file diff --git a/src/components/Crontab/min.vue b/src/components/Crontab/min.vue new file mode 100644 index 0000000..eacfabe --- /dev/null +++ b/src/components/Crontab/min.vue @@ -0,0 +1,126 @@ +<template> + <el-form size="small"> + <el-form-item> + <el-radio v-model='radioValue' :label="1"> + 鍒嗛挓锛屽厑璁哥殑閫氶厤绗, - * /] + </el-radio> + </el-form-item> + + <el-form-item> + <el-radio v-model='radioValue' :label="2"> + 鍛ㄦ湡浠� + <el-input-number v-model='cycle01' :min="0" :max="58" /> - + <el-input-number v-model='cycle02' :min="cycle01 + 1" :max="59" /> 鍒嗛挓 + </el-radio> + </el-form-item> + + <el-form-item> + <el-radio v-model='radioValue' :label="3"> + 浠� + <el-input-number v-model='average01' :min="0" :max="58" /> 鍒嗛挓寮�濮嬶紝 姣� + <el-input-number v-model='average02' :min="1" :max="59 - average01" /> 鍒嗛挓鎵ц涓�娆� + </el-radio> + </el-form-item> + + <el-form-item> + <el-radio v-model='radioValue' :label="4"> + 鎸囧畾 + <el-select clearable v-model="checkboxList" placeholder="鍙閫�" multiple :multiple-limit="10"> + <el-option v-for="item in 60" :key="item" :label="item - 1" :value="item - 1" /> + </el-select> + </el-radio> + </el-form-item> + </el-form> +</template> +<script setup> +const emit = defineEmits(['update']) +const props = defineProps({ + cron: { + type: Object, + default: { + second: "*", + min: "*", + hour: "*", + day: "*", + month: "*", + week: "?", + year: "", + } + }, + check: { + type: Function, + default: () => { + } + } +}) +const radioValue = ref(1) +const cycle01 = ref(0) +const cycle02 = ref(1) +const average01 = ref(0) +const average02 = ref(1) +const checkboxList = ref([]) +const checkCopy = ref([0]) +const cycleTotal = computed(() => { + cycle01.value = props.check(cycle01.value, 0, 58) + cycle02.value = props.check(cycle02.value, cycle01.value + 1, 59) + return cycle01.value + '-' + cycle02.value +}) +const averageTotal = computed(() => { + average01.value = props.check(average01.value, 0, 58) + average02.value = props.check(average02.value, 1, 59 - average01.value) + return average01.value + '/' + average02.value +}) +const checkboxString = computed(() => { + return checkboxList.value.join(',') +}) +watch(() => props.cron.min, value => changeRadioValue(value)) +watch([radioValue, cycleTotal, averageTotal, checkboxString], () => onRadioChange()) +function changeRadioValue(value) { + if (value === '*') { + radioValue.value = 1 + } else if (value.indexOf('-') > -1) { + const indexArr = value.split('-') + cycle01.value = Number(indexArr[0]) + cycle02.value = Number(indexArr[1]) + radioValue.value = 2 + } else if (value.indexOf('/') > -1) { + const indexArr = value.split('/') + average01.value = Number(indexArr[0]) + average02.value = Number(indexArr[1]) + radioValue.value = 3 + } else { + checkboxList.value = [...new Set(value.split(',').map(item => Number(item)))] + radioValue.value = 4 + } +} +function onRadioChange() { + switch (radioValue.value) { + case 1: + emit('update', 'min', '*', 'min') + break + case 2: + emit('update', 'min', cycleTotal.value, 'min') + break + case 3: + emit('update', 'min', averageTotal.value, 'min') + break + case 4: + if (checkboxList.value.length === 0) { + checkboxList.value.push(checkCopy.value[0]) + } else { + checkCopy.value = checkboxList.value + } + emit('update', 'min', checkboxString.value, 'min') + break + } +} +</script> + +<style lang="scss" scoped> +.el-input-number--small, .el-input-number--small, .el-select, .el-select--small { + margin: 0 0.2rem; +} +.el-select, .el-select--small { + width: 19.8rem; +} +</style> \ No newline at end of file diff --git a/src/components/Crontab/month.vue b/src/components/Crontab/month.vue new file mode 100644 index 0000000..a883c2b --- /dev/null +++ b/src/components/Crontab/month.vue @@ -0,0 +1,141 @@ +<template> + <el-form size='small'> + <el-form-item> + <el-radio v-model='radioValue' :label="1"> + 鏈堬紝鍏佽鐨勯�氶厤绗, - * /] + </el-radio> + </el-form-item> + + <el-form-item> + <el-radio v-model='radioValue' :label="2"> + 鍛ㄦ湡浠� + <el-input-number v-model='cycle01' :min="1" :max="11" /> - + <el-input-number v-model='cycle02' :min="cycle01 + 1" :max="12" /> 鏈� + </el-radio> + </el-form-item> + + <el-form-item> + <el-radio v-model='radioValue' :label="3"> + 浠� + <el-input-number v-model='average01' :min="1" :max="11" /> 鏈堝紑濮嬶紝姣� + <el-input-number v-model='average02' :min="1" :max="12 - average01" /> 鏈堟湀鎵ц涓�娆� + </el-radio> + </el-form-item> + + <el-form-item> + <el-radio v-model='radioValue' :label="4"> + 鎸囧畾 + <el-select clearable v-model="checkboxList" placeholder="鍙閫�" multiple :multiple-limit="8"> + <el-option v-for="item in monthList" :key="item.key" :label="item.value" :value="item.key" /> + </el-select> + </el-radio> + </el-form-item> + </el-form> +</template> + +<script setup> +const emit = defineEmits(['update']) +const props = defineProps({ + cron: { + type: Object, + default: { + second: "*", + min: "*", + hour: "*", + day: "*", + month: "*", + week: "?", + year: "", + } + }, + check: { + type: Function, + default: () => { + } + } +}) +const radioValue = ref(1) +const cycle01 = ref(1) +const cycle02 = ref(2) +const average01 = ref(1) +const average02 = ref(1) +const checkboxList = ref([]) +const checkCopy = ref([1]) +const monthList = ref([ + {key: 1, value: '涓�鏈�'}, + {key: 2, value: '浜屾湀'}, + {key: 3, value: '涓夋湀'}, + {key: 4, value: '鍥涙湀'}, + {key: 5, value: '浜旀湀'}, + {key: 6, value: '鍏湀'}, + {key: 7, value: '涓冩湀'}, + {key: 8, value: '鍏湀'}, + {key: 9, value: '涔濇湀'}, + {key: 10, value: '鍗佹湀'}, + {key: 11, value: '鍗佷竴鏈�'}, + {key: 12, value: '鍗佷簩鏈�'} +]) +const cycleTotal = computed(() => { + cycle01.value = props.check(cycle01.value, 1, 11) + cycle02.value = props.check(cycle02.value, cycle01.value + 1, 12) + return cycle01.value + '-' + cycle02.value +}) +const averageTotal = computed(() => { + average01.value = props.check(average01.value, 1, 11) + average02.value = props.check(average02.value, 1, 12 - average01.value) + return average01.value + '/' + average02.value +}) +const checkboxString = computed(() => { + return checkboxList.value.join(',') +}) +watch(() => props.cron.month, value => changeRadioValue(value)) +watch([radioValue, cycleTotal, averageTotal, checkboxString], () => onRadioChange()) +function changeRadioValue(value) { + if (value === '*') { + radioValue.value = 1 + } else if (value.indexOf('-') > -1) { + const indexArr = value.split('-') + cycle01.value = Number(indexArr[0]) + cycle02.value = Number(indexArr[1]) + radioValue.value = 2 + } else if (value.indexOf('/') > -1) { + const indexArr = value.split('/') + average01.value = Number(indexArr[0]) + average02.value = Number(indexArr[1]) + radioValue.value = 3 + } else { + checkboxList.value = [...new Set(value.split(',').map(item => Number(item)))] + radioValue.value = 4 + } +} +function onRadioChange() { + switch (radioValue.value) { + case 1: + emit('update', 'month', '*', 'month') + break + case 2: + emit('update', 'month', cycleTotal.value, 'month') + break + case 3: + emit('update', 'month', averageTotal.value, 'month') + break + case 4: + if (checkboxList.value.length === 0) { + checkboxList.value.push(checkCopy.value[0]) + } else { + checkCopy.value = checkboxList.value + } + emit('update', 'month', checkboxString.value, 'month') + break + } +} +</script> + +<style lang="scss" scoped> +.el-input-number--small, .el-input-number--small, .el-select, .el-select--small { + margin: 0 0.2rem; +} +.el-select, .el-select--small { + width: 18.8rem; +} +</style> \ No newline at end of file diff --git a/src/components/Crontab/result.vue b/src/components/Crontab/result.vue new file mode 100644 index 0000000..271577e --- /dev/null +++ b/src/components/Crontab/result.vue @@ -0,0 +1,540 @@ +<template> + <div class="popup-result"> + <p class="title">鏈�杩�5娆¤繍琛屾椂闂�</p> + <ul class="popup-result-scroll"> + <template v-if='isShow'> + <li v-for='item in resultList' :key="item">{{item}}</li> + </template> + <li v-else>璁$畻缁撴灉涓�...</li> + </ul> + </div> +</template> + +<script setup> +const props = defineProps({ + ex: { + type: String, + default: '' + } +}) +const dayRule = ref('') +const dayRuleSup = ref('') +const dateArr = ref([]) +const resultList = ref([]) +const isShow = ref(false) +watch(() => props.ex, () => expressionChange()) +// 琛ㄨ揪寮忓�煎彉鍖栨椂锛屽紑濮嬪幓璁$畻缁撴灉 +function expressionChange() { + // 璁$畻寮�濮�-闅愯棌缁撴灉 + isShow.value = false; + // 鑾峰彇瑙勫垯鏁扮粍[0绉掋��1鍒嗐��2鏃躲��3鏃ャ��4鏈堛��5鏄熸湡銆�6骞碷 + let ruleArr = props.ex.split(' '); + // 鐢ㄤ簬璁板綍杩涘叆寰幆鐨勬鏁� + let nums = 0; + // 鐢ㄤ簬鏆傛椂瀛樼鍙锋椂闂磋鍒欑粨鏋滅殑鏁扮粍 + let resultArr = []; + // 鑾峰彇褰撳墠鏃堕棿绮剧‘鑷砙骞淬�佹湀銆佹棩銆佹椂銆佸垎銆佺] + let nTime = new Date(); + let nYear = nTime.getFullYear(); + let nMonth = nTime.getMonth() + 1; + let nDay = nTime.getDate(); + let nHour = nTime.getHours(); + let nMin = nTime.getMinutes(); + let nSecond = nTime.getSeconds(); + // 鏍规嵁瑙勫垯鑾峰彇鍒拌繎100骞村彲鑳藉勾鏁扮粍銆佹湀鏁扮粍绛夌瓑 + getSecondArr(ruleArr[0]); + getMinArr(ruleArr[1]); + getHourArr(ruleArr[2]); + getDayArr(ruleArr[3]); + getMonthArr(ruleArr[4]); + getWeekArr(ruleArr[5]); + getYearArr(ruleArr[6], nYear); + // 灏嗚幏鍙栧埌鐨勬暟缁勮祴鍊�-鏂逛究浣跨敤 + let sDate = dateArr.value[0]; + let mDate = dateArr.value[1]; + let hDate = dateArr.value[2]; + let DDate = dateArr.value[3]; + let MDate = dateArr.value[4]; + let YDate = dateArr.value[5]; + // 鑾峰彇褰撳墠鏃堕棿鍦ㄦ暟缁勪腑鐨勭储寮� + let sIdx = getIndex(sDate, nSecond); + let mIdx = getIndex(mDate, nMin); + let hIdx = getIndex(hDate, nHour); + let DIdx = getIndex(DDate, nDay); + let MIdx = getIndex(MDate, nMonth); + let YIdx = getIndex(YDate, nYear); + // 閲嶇疆鏈堟棩鏃跺垎绉掔殑鍑芥暟(鍚庨潰鐢ㄧ殑姣旇緝澶�) + const resetSecond = function () { + sIdx = 0; + nSecond = sDate[sIdx] + } + const resetMin = function () { + mIdx = 0; + nMin = mDate[mIdx] + resetSecond(); + } + const resetHour = function () { + hIdx = 0; + nHour = hDate[hIdx] + resetMin(); + } + const resetDay = function () { + DIdx = 0; + nDay = DDate[DIdx] + resetHour(); + } + const resetMonth = function () { + MIdx = 0; + nMonth = MDate[MIdx] + resetDay(); + } + // 濡傛灉褰撳墠骞翠唤涓嶄负鏁扮粍涓綋鍓嶅�� + if (nYear !== YDate[YIdx]) { + resetMonth(); + } + // 濡傛灉褰撳墠鏈堜唤涓嶄负鏁扮粍涓綋鍓嶅�� + if (nMonth !== MDate[MIdx]) { + resetDay(); + } + // 濡傛灉褰撳墠鈥滄棩鈥濅笉涓烘暟缁勪腑褰撳墠鍊� + if (nDay !== DDate[DIdx]) { + resetHour(); + } + // 濡傛灉褰撳墠鈥滄椂鈥濅笉涓烘暟缁勪腑褰撳墠鍊� + if (nHour !== hDate[hIdx]) { + resetMin(); + } + // 濡傛灉褰撳墠鈥滃垎鈥濅笉涓烘暟缁勪腑褰撳墠鍊� + if (nMin !== mDate[mIdx]) { + resetSecond(); + } + // 寰幆骞翠唤鏁扮粍 + goYear: for (let Yi = YIdx; Yi < YDate.length; Yi++) { + let YY = YDate[Yi]; + // 濡傛灉鍒拌揪鏈�澶у�兼椂 + if (nMonth > MDate[MDate.length - 1]) { + resetMonth(); + continue; + } + // 寰幆鏈堜唤鏁扮粍 + goMonth: for (let Mi = MIdx; Mi < MDate.length; Mi++) { + // 璧嬪�笺�佹柟渚垮悗闈㈣繍绠� + let MM = MDate[Mi]; + MM = MM < 10 ? '0' + MM : MM; + // 濡傛灉鍒拌揪鏈�澶у�兼椂 + if (nDay > DDate[DDate.length - 1]) { + resetDay(); + if (Mi === MDate.length - 1) { + resetMonth(); + continue goYear; + } + continue; + } + // 寰幆鏃ユ湡鏁扮粍 + goDay: for (let Di = DIdx; Di < DDate.length; Di++) { + // 璧嬪�笺�佹柟渚垮悗闈㈣繍绠� + let DD = DDate[Di]; + let thisDD = DD < 10 ? '0' + DD : DD; + // 濡傛灉鍒拌揪鏈�澶у�兼椂 + if (nHour > hDate[hDate.length - 1]) { + resetHour(); + if (Di === DDate.length - 1) { + resetDay(); + if (Mi === MDate.length - 1) { + resetMonth(); + continue goYear; + } + continue goMonth; + } + continue; + } + // 鍒ゆ柇鏃ユ湡鐨勫悎娉曟�э紝涓嶅悎娉曠殑璇濅篃鏄烦鍑哄綋鍓嶅惊鐜� + if (checkDate(YY + '-' + MM + '-' + thisDD + ' 00:00:00') !== true && dayRule.value !== 'workDay' && dayRule.value !== 'lastWeek' && dayRule.value !== 'lastDay') { + resetDay(); + continue goMonth; + } + // 濡傛灉鏃ユ湡瑙勫垯涓湁鍊兼椂 + if (dayRule.value === 'lastDay') { + // 濡傛灉涓嶆槸鍚堟硶鏃ユ湡鍒欓渶瑕佸皢鍓嶅皢鏃ユ湡璋冨埌鍚堟硶鏃ユ湡鍗虫湀鏈渶鍚庝竴澶� + if (checkDate(YY + '-' + MM + '-' + thisDD + ' 00:00:00') !== true) { + while (DD > 0 && checkDate(YY + '-' + MM + '-' + thisDD + ' 00:00:00') !== true) { + DD--; + thisDD = DD < 10 ? '0' + DD : DD; + } + } + } else if (dayRule.value === 'workDay') { + // 鏍¢獙骞惰皟鏁村鏋滄槸2鏈�30鍙疯繖绉嶆棩鏈熶紶杩涙潵鏃堕渶璋冩暣鑷虫甯告湀搴� + if (checkDate(YY + '-' + MM + '-' + thisDD + ' 00:00:00') !== true) { + while (DD > 0 && checkDate(YY + '-' + MM + '-' + thisDD + ' 00:00:00') !== true) { + DD--; + thisDD = DD < 10 ? '0' + DD : DD; + } + } + // 鑾峰彇杈惧埌鏉′欢鐨勬棩鏈熸槸鏄熸湡X + let thisWeek = formatDate(new Date(YY + '-' + MM + '-' + thisDD + ' 00:00:00'), 'week'); + // 褰撴槦鏈熸棩鏃� + if (thisWeek === 1) { + // 鍏堟壘涓嬩竴涓棩锛屽苟鍒ゆ柇鏄惁涓烘湀搴� + DD++; + thisDD = DD < 10 ? '0' + DD : DD; + // 鍒ゆ柇涓嬩竴鏃ュ凡缁忎笉鏄悎娉曟棩鏈� + if (checkDate(YY + '-' + MM + '-' + thisDD + ' 00:00:00') !== true) { + DD -= 3; + } + } else if (thisWeek === 7) { + // 褰撴槦鏈�6鏃跺彧闇�鍒ゆ柇涓嶆槸1鍙峰氨鍙繘琛屾搷浣� + if (dayRuleSup.value !== 1) { + DD--; + } else { + DD += 2; + } + } + } else if (dayRule.value === 'weekDay') { + // 濡傛灉鎸囧畾浜嗘槸鏄熸湡鍑� + // 鑾峰彇褰撳墠鏃ユ湡鏄睘浜庢槦鏈熷嚑 + let thisWeek = formatDate(new Date(YY + '-' + MM + '-' + DD + ' 00:00:00'), 'week'); + // 鏍¢獙褰撳墠鏄熸湡鏄惁鍦ㄦ槦鏈熸睜锛坉ayRuleSup锛変腑 + if (dayRuleSup.value.indexOf(thisWeek) < 0) { + // 濡傛灉鍒拌揪鏈�澶у�兼椂 + if (Di === DDate.length - 1) { + resetDay(); + if (Mi === MDate.length - 1) { + resetMonth(); + continue goYear; + } + continue goMonth; + } + continue; + } + } else if (dayRule.value === 'assWeek') { + // 濡傛灉鎸囧畾浜嗘槸绗嚑鍛ㄧ殑鏄熸湡鍑� + // 鑾峰彇姣忔湀1鍙锋槸灞炰簬鏄熸湡鍑� + let thisWeek = formatDate(new Date(YY + '-' + MM + '-' + DD + ' 00:00:00'), 'week'); + if (dayRuleSup.value[1] >= thisWeek) { + DD = (dayRuleSup.value[0] - 1) * 7 + dayRuleSup.value[1] - thisWeek + 1; + } else { + DD = dayRuleSup.value[0] * 7 + dayRuleSup.value[1] - thisWeek + 1; + } + } else if (dayRule.value === 'lastWeek') { + // 濡傛灉鎸囧畾浜嗘瘡鏈堟渶鍚庝竴涓槦鏈熷嚑 + // 鏍¢獙骞惰皟鏁村鏋滄槸2鏈�30鍙疯繖绉嶆棩鏈熶紶杩涙潵鏃堕渶璋冩暣鑷虫甯告湀搴� + if (checkDate(YY + '-' + MM + '-' + thisDD + ' 00:00:00') !== true) { + while (DD > 0 && checkDate(YY + '-' + MM + '-' + thisDD + ' 00:00:00') !== true) { + DD--; + thisDD = DD < 10 ? '0' + DD : DD; + } + } + // 鑾峰彇鏈堟湯鏈�鍚庝竴澶╂槸鏄熸湡鍑� + let thisWeek = formatDate(new Date(YY + '-' + MM + '-' + thisDD + ' 00:00:00'), 'week'); + // 鎵惧埌瑕佹眰涓渶杩戠殑閭d釜鏄熸湡鍑� + if (dayRuleSup.value < thisWeek) { + DD -= thisWeek - dayRuleSup.value; + } else if (dayRuleSup.value > thisWeek) { + DD -= 7 - (dayRuleSup.value - thisWeek) + } + } + // 鍒ゆ柇鏃堕棿鍊兼槸鍚﹀皬浜�10缃崲鎴愨��05鈥濊繖绉嶆牸寮� + DD = DD < 10 ? '0' + DD : DD; + // 寰幆鈥滄椂鈥濇暟缁� + goHour: for (let hi = hIdx; hi < hDate.length; hi++) { + let hh = hDate[hi] < 10 ? '0' + hDate[hi] : hDate[hi] + // 濡傛灉鍒拌揪鏈�澶у�兼椂 + if (nMin > mDate[mDate.length - 1]) { + resetMin(); + if (hi === hDate.length - 1) { + resetHour(); + if (Di === DDate.length - 1) { + resetDay(); + if (Mi === MDate.length - 1) { + resetMonth(); + continue goYear; + } + continue goMonth; + } + continue goDay; + } + continue; + } + // 寰幆"鍒�"鏁扮粍 + goMin: for (let mi = mIdx; mi < mDate.length; mi++) { + let mm = mDate[mi] < 10 ? '0' + mDate[mi] : mDate[mi]; + // 濡傛灉鍒拌揪鏈�澶у�兼椂 + if (nSecond > sDate[sDate.length - 1]) { + resetSecond(); + if (mi === mDate.length - 1) { + resetMin(); + if (hi === hDate.length - 1) { + resetHour(); + if (Di === DDate.length - 1) { + resetDay(); + if (Mi === MDate.length - 1) { + resetMonth(); + continue goYear; + } + continue goMonth; + } + continue goDay; + } + continue goHour; + } + continue; + } + // 寰幆"绉�"鏁扮粍 + goSecond: for (let si = sIdx; si <= sDate.length - 1; si++) { + let ss = sDate[si] < 10 ? '0' + sDate[si] : sDate[si]; + // 娣诲姞褰撳墠鏃堕棿锛堟椂闂村悎娉曟�у湪鏃ユ湡寰幆鏃跺凡缁忓垽鏂級 + if (MM !== '00' && DD !== '00') { + resultArr.push(YY + '-' + MM + '-' + DD + ' ' + hh + ':' + mm + ':' + ss) + nums++; + } + // 濡傛灉鏉℃暟婊′簡灏遍��鍑哄惊鐜� + if (nums === 5) break goYear; + // 濡傛灉鍒拌揪鏈�澶у�兼椂 + if (si === sDate.length - 1) { + resetSecond(); + if (mi === mDate.length - 1) { + resetMin(); + if (hi === hDate.length - 1) { + resetHour(); + if (Di === DDate.length - 1) { + resetDay(); + if (Mi === MDate.length - 1) { + resetMonth(); + continue goYear; + } + continue goMonth; + } + continue goDay; + } + continue goHour; + } + continue goMin; + } + } //goSecond + } //goMin + }//goHour + }//goDay + }//goMonth + } + // 鍒ゆ柇100骞村唴鐨勭粨鏋滄潯鏁� + if (resultArr.length === 0) { + resultList.value = ['娌℃湁杈惧埌鏉′欢鐨勭粨鏋滐紒']; + } else { + resultList.value = resultArr; + if (resultArr.length !== 5) { + resultList.value.push('鏈�杩�100骞村唴鍙湁涓婇潰' + resultArr.length + '鏉$粨鏋滐紒') + } + } + // 璁$畻瀹屾垚-鏄剧ず缁撴灉 + isShow.value = true; +} +// 鐢ㄤ簬璁$畻鏌愪綅鏁板瓧鍦ㄦ暟缁勪腑鐨勭储寮� +function getIndex(arr, value) { + if (value <= arr[0] || value > arr[arr.length - 1]) { + return 0; + } else { + for (let i = 0; i < arr.length - 1; i++) { + if (value > arr[i] && value <= arr[i + 1]) { + return i + 1; + } + } + } +} +// 鑾峰彇"骞�"鏁扮粍 +function getYearArr(rule, year) { + dateArr.value[5] = getOrderArr(year, year + 100); + if (rule !== undefined) { + if (rule.indexOf('-') >= 0) { + dateArr.value[5] = getCycleArr(rule, year + 100, false) + } else if (rule.indexOf('/') >= 0) { + dateArr.value[5] = getAverageArr(rule, year + 100) + } else if (rule !== '*') { + dateArr.value[5] = getAssignArr(rule) + } + } +} +// 鑾峰彇"鏈�"鏁扮粍 +function getMonthArr(rule) { + dateArr.value[4] = getOrderArr(1, 12); + if (rule.indexOf('-') >= 0) { + dateArr.value[4] = getCycleArr(rule, 12, false) + } else if (rule.indexOf('/') >= 0) { + dateArr.value[4] = getAverageArr(rule, 12) + } else if (rule !== '*') { + dateArr.value[4] = getAssignArr(rule) + } +} +// 鑾峰彇"鏃�"鏁扮粍-涓昏涓烘棩鏈熻鍒� +function getWeekArr(rule) { + // 鍙湁褰撴棩鏈熻鍒欑殑涓や釜鍊煎潎涓衡�溾�濇椂鍒欒〃杈炬棩鏈熸槸鏈夐�夐」鐨� + if (dayRule.value === '' && dayRuleSup.value === '') { + if (rule.indexOf('-') >= 0) { + dayRule.value = 'weekDay'; + dayRuleSup.value = getCycleArr(rule, 7, false) + } else if (rule.indexOf('#') >= 0) { + dayRule.value = 'assWeek'; + let matchRule = rule.match(/[0-9]{1}/g); + dayRuleSup.value = [Number(matchRule[1]), Number(matchRule[0])]; + dateArr.value[3] = [1]; + if (dayRuleSup.value[1] === 7) { + dayRuleSup.value[1] = 0; + } + } else if (rule.indexOf('L') >= 0) { + dayRule.value = 'lastWeek'; + dayRuleSup.value = Number(rule.match(/[0-9]{1,2}/g)[0]); + dateArr.value[3] = [31]; + if (dayRuleSup.value === 7) { + dayRuleSup.value = 0; + } + } else if (rule !== '*' && rule !== '?') { + dayRule.value = 'weekDay'; + dayRuleSup.value = getAssignArr(rule) + } + } +} +// 鑾峰彇"鏃�"鏁扮粍-灏戦噺涓烘棩鏈熻鍒� +function getDayArr(rule) { + dateArr.value[3] = getOrderArr(1, 31); + dayRule.value = ''; + dayRuleSup.value = ''; + if (rule.indexOf('-') >= 0) { + dateArr.value[3] = getCycleArr(rule, 31, false) + dayRuleSup.value = 'null'; + } else if (rule.indexOf('/') >= 0) { + dateArr.value[3] = getAverageArr(rule, 31) + dayRuleSup.value = 'null'; + } else if (rule.indexOf('W') >= 0) { + dayRule.value = 'workDay'; + dayRuleSup.value = Number(rule.match(/[0-9]{1,2}/g)[0]); + dateArr.value[3] = [dayRuleSup.value]; + } else if (rule.indexOf('L') >= 0) { + dayRule.value = 'lastDay'; + dayRuleSup.value = 'null'; + dateArr.value[3] = [31]; + } else if (rule !== '*' && rule !== '?') { + dateArr.value[3] = getAssignArr(rule) + dayRuleSup.value = 'null'; + } else if (rule === '*') { + dayRuleSup.value = 'null'; + } +} +// 鑾峰彇"鏃�"鏁扮粍 +function getHourArr(rule) { + dateArr.value[2] = getOrderArr(0, 23); + if (rule.indexOf('-') >= 0) { + dateArr.value[2] = getCycleArr(rule, 24, true) + } else if (rule.indexOf('/') >= 0) { + dateArr.value[2] = getAverageArr(rule, 23) + } else if (rule !== '*') { + dateArr.value[2] = getAssignArr(rule) + } +} +// 鑾峰彇"鍒�"鏁扮粍 +function getMinArr(rule) { + dateArr.value[1] = getOrderArr(0, 59); + if (rule.indexOf('-') >= 0) { + dateArr.value[1] = getCycleArr(rule, 60, true) + } else if (rule.indexOf('/') >= 0) { + dateArr.value[1] = getAverageArr(rule, 59) + } else if (rule !== '*') { + dateArr.value[1] = getAssignArr(rule) + } +} +// 鑾峰彇"绉�"鏁扮粍 +function getSecondArr(rule) { + dateArr.value[0] = getOrderArr(0, 59); + if (rule.indexOf('-') >= 0) { + dateArr.value[0] = getCycleArr(rule, 60, true) + } else if (rule.indexOf('/') >= 0) { + dateArr.value[0] = getAverageArr(rule, 59) + } else if (rule !== '*') { + dateArr.value[0] = getAssignArr(rule) + } +} +// 鏍规嵁浼犺繘鏉ョ殑min-max杩斿洖涓�涓『搴忕殑鏁扮粍 +function getOrderArr(min, max) { + let arr = []; + for (let i = min; i <= max; i++) { + arr.push(i); + } + return arr; +} +// 鏍规嵁瑙勫垯涓寚瀹氱殑闆舵暎鍊艰繑鍥炰竴涓暟缁� +function getAssignArr(rule) { + let arr = []; + let assiginArr = rule.split(','); + for (let i = 0; i < assiginArr.length; i++) { + arr[i] = Number(assiginArr[i]) + } + arr.sort(compare) + return arr; +} +// 鏍规嵁涓�瀹氱畻鏈鍒欒绠楄繑鍥炰竴涓暟缁� +function getAverageArr(rule, limit) { + let arr = []; + let agArr = rule.split('/'); + let min = Number(agArr[0]); + let step = Number(agArr[1]); + while (min <= limit) { + arr.push(min); + min += step; + } + return arr; +} +// 鏍规嵁瑙勫垯杩斿洖涓�涓叿鏈夊懆鏈熸�х殑鏁扮粍 +function getCycleArr(rule, limit, status) { + // status--琛ㄧず鏄惁浠�0寮�濮嬶紙鍒欎粠1寮�濮嬶級 + let arr = []; + let cycleArr = rule.split('-'); + let min = Number(cycleArr[0]); + let max = Number(cycleArr[1]); + if (min > max) { + max += limit; + } + for (let i = min; i <= max; i++) { + let add = 0; + if (status === false && i % limit === 0) { + add = limit; + } + arr.push(Math.round(i % limit + add)) + } + arr.sort(compare) + return arr; +} +// 姣旇緝鏁板瓧澶у皬锛堢敤浜嶢rray.sort锛� +function compare(value1, value2) { + if (value2 - value1 > 0) { + return -1; + } else { + return 1; + } +} +// 鏍煎紡鍖栨棩鏈熸牸寮忓锛�2017-9-19 18:04:33 +function formatDate(value, type) { + // 璁$畻鏃ユ湡鐩稿叧鍊� + let time = typeof value == 'number' ? new Date(value) : value; + let Y = time.getFullYear(); + let M = time.getMonth() + 1; + let D = time.getDate(); + let h = time.getHours(); + let m = time.getMinutes(); + let s = time.getSeconds(); + let week = time.getDay(); + // 濡傛灉浼犻�掍簡type鐨勮瘽 + if (type === undefined) { + return Y + '-' + (M < 10 ? '0' + M : M) + '-' + (D < 10 ? '0' + D : D) + ' ' + (h < 10 ? '0' + h : h) + ':' + (m < 10 ? '0' + m : m) + ':' + (s < 10 ? '0' + s : s); + } else if (type === 'week') { + // 鍦╭uartz涓� 1涓烘槦鏈熸棩 + return week + 1; + } +} +// 妫�鏌ユ棩鏈熸槸鍚﹀瓨鍦� +function checkDate(value) { + let time = new Date(value); + let format = formatDate(time) + return value === format; +} +onMounted(() => { + expressionChange() +}) +</script> \ No newline at end of file diff --git a/src/components/Crontab/second.vue b/src/components/Crontab/second.vue new file mode 100644 index 0000000..84b32b5 --- /dev/null +++ b/src/components/Crontab/second.vue @@ -0,0 +1,128 @@ +<template> + <el-form size="small"> + <el-form-item> + <el-radio v-model='radioValue' :label="1"> + 绉掞紝鍏佽鐨勯�氶厤绗, - * /] + </el-radio> + </el-form-item> + + <el-form-item> + <el-radio v-model='radioValue' :label="2"> + 鍛ㄦ湡浠� + <el-input-number v-model='cycle01' :min="0" :max="58" /> - + <el-input-number v-model='cycle02' :min="cycle01 + 1" :max="59" /> 绉� + </el-radio> + </el-form-item> + + <el-form-item> + <el-radio v-model='radioValue' :label="3"> + 浠� + <el-input-number v-model='average01' :min="0" :max="58" /> 绉掑紑濮嬶紝姣� + <el-input-number v-model='average02' :min="1" :max="59 - average01" /> 绉掓墽琛屼竴娆� + </el-radio> + </el-form-item> + + <el-form-item> + <el-radio v-model='radioValue' :label="4"> + 鎸囧畾 + <el-select clearable v-model="checkboxList" placeholder="鍙閫�" multiple :multiple-limit="10"> + <el-option v-for="item in 60" :key="item" :label="item - 1" :value="item - 1" /> + </el-select> + </el-radio> + </el-form-item> + </el-form> +</template> + +<script setup> +const emit = defineEmits(['update']) +const props = defineProps({ + cron: { + type: Object, + default: { + second: "*", + min: "*", + hour: "*", + day: "*", + month: "*", + week: "?", + year: "", + } + }, + check: { + type: Function, + default: () => { + } + } +}) +const radioValue = ref(1) +const cycle01 = ref(0) +const cycle02 = ref(1) +const average01 = ref(0) +const average02 = ref(1) +const checkboxList = ref([]) +const checkCopy = ref([0]) +const cycleTotal = computed(() => { + cycle01.value = props.check(cycle01.value, 0, 58) + cycle02.value = props.check(cycle02.value, cycle01.value + 1, 59) + return cycle01.value + '-' + cycle02.value +}) +const averageTotal = computed(() => { + average01.value = props.check(average01.value, 0, 58) + average02.value = props.check(average02.value, 1, 59 - average01.value) + return average01.value + '/' + average02.value +}) +const checkboxString = computed(() => { + return checkboxList.value.join(',') +}) +watch(() => props.cron.second, value => changeRadioValue(value)) +watch([radioValue, cycleTotal, averageTotal, checkboxString], () => onRadioChange()) +function changeRadioValue(value) { + if (value === '*') { + radioValue.value = 1 + } else if (value.indexOf('-') > -1) { + const indexArr = value.split('-') + cycle01.value = Number(indexArr[0]) + cycle02.value = Number(indexArr[1]) + radioValue.value = 2 + } else if (value.indexOf('/') > -1) { + const indexArr = value.split('/') + average01.value = Number(indexArr[0]) + average02.value = Number(indexArr[1]) + radioValue.value = 3 + } else { + checkboxList.value = [...new Set(value.split(',').map(item => Number(item)))] + radioValue.value = 4 + } +} +// 鍗曢�夋寜閽�煎彉鍖栨椂 +function onRadioChange() { + switch (radioValue.value) { + case 1: + emit('update', 'second', '*', 'second') + break + case 2: + emit('update', 'second', cycleTotal.value, 'second') + break + case 3: + emit('update', 'second', averageTotal.value, 'second') + break + case 4: + if (checkboxList.value.length === 0) { + checkboxList.value.push(checkCopy.value[0]) + } else { + checkCopy.value = checkboxList.value + } + emit('update', 'second', checkboxString.value, 'second') + break + } +} +</script> + +<style lang="scss" scoped> +.el-input-number--small, .el-input-number--small, .el-select, .el-select--small { + margin: 0 0.2rem; +} +.el-select, .el-select--small { + width: 18.8rem; +} +</style> \ No newline at end of file diff --git a/src/components/Crontab/week.vue b/src/components/Crontab/week.vue new file mode 100644 index 0000000..cc570b2 --- /dev/null +++ b/src/components/Crontab/week.vue @@ -0,0 +1,197 @@ +<template> + <el-form size='small'> + <el-form-item> + <el-radio v-model='radioValue' :label="1"> + 鍛紝鍏佽鐨勯�氶厤绗, - * ? / L #] + </el-radio> + </el-form-item> + + <el-form-item> + <el-radio v-model='radioValue' :label="2"> + 涓嶆寚瀹� + </el-radio> + </el-form-item> + + <el-form-item> + <el-radio v-model='radioValue' :label="3"> + 鍛ㄦ湡浠� + <el-select clearable v-model="cycle01"> + <el-option + v-for="(item,index) of weekList" + :key="index" + :label="item.value" + :value="item.key" + :disabled="item.key === 7" + >{{item.value}}</el-option> + </el-select> + - + <el-select clearable v-model="cycle02"> + <el-option + v-for="(item,index) of weekList" + :key="index" + :label="item.value" + :value="item.key" + :disabled="item.key <= cycle01" + >{{item.value}}</el-option> + </el-select> + </el-radio> + </el-form-item> + + <el-form-item> + <el-radio v-model='radioValue' :label="4"> + 绗� + <el-input-number v-model='average01' :min="1" :max="4" /> 鍛ㄧ殑 + <el-select clearable v-model="average02"> + <el-option v-for="item in weekList" :key="item.key" :label="item.value" :value="item.key" /> + </el-select> + </el-radio> + </el-form-item> + + <el-form-item> + <el-radio v-model='radioValue' :label="5"> + 鏈湀鏈�鍚庝竴涓� + <el-select clearable v-model="weekday"> + <el-option v-for="item in weekList" :key="item.key" :label="item.value" :value="item.key" /> + </el-select> + </el-radio> + </el-form-item> + + <el-form-item> + <el-radio v-model='radioValue' :label="6"> + 鎸囧畾 + <el-select class="multiselect" clearable v-model="checkboxList" placeholder="鍙閫�" multiple :multiple-limit="6"> + <el-option v-for="item in weekList" :key="item.key" :label="item.value" :value="item.key" /> + </el-select> + </el-radio> + </el-form-item> + + </el-form> +</template> + +<script setup> +const emit = defineEmits(['update']) +const props = defineProps({ + cron: { + type: Object, + default: { + second: "*", + min: "*", + hour: "*", + day: "*", + month: "*", + week: "?", + year: "" + } + }, + check: { + type: Function, + default: () => { + } + } +}) +const radioValue = ref(2) +const cycle01 = ref(2) +const cycle02 = ref(3) +const average01 = ref(1) +const average02 = ref(2) +const weekday = ref(2) +const checkboxList = ref([]) +const checkCopy = ref([2]) +const weekList = ref([ + {key: 1, value: '鏄熸湡鏃�'}, + {key: 2, value: '鏄熸湡涓�'}, + {key: 3, value: '鏄熸湡浜�'}, + {key: 4, value: '鏄熸湡涓�'}, + {key: 5, value: '鏄熸湡鍥�'}, + {key: 6, value: '鏄熸湡浜�'}, + {key: 7, value: '鏄熸湡鍏�'} +]) +const cycleTotal = computed(() => { + cycle01.value = props.check(cycle01.value, 1, 6) + cycle02.value = props.check(cycle02.value, cycle01.value + 1, 7) + return cycle01.value + '-' + cycle02.value +}) +const averageTotal = computed(() => { + average01.value = props.check(average01.value, 1, 4) + average02.value = props.check(average02.value, 1, 7) + return average02.value + '#' + average01.value +}) +const weekdayTotal = computed(() => { + weekday.value = props.check(weekday.value, 1, 7) + return weekday.value + 'L' +}) +const checkboxString = computed(() => { + return checkboxList.value.join(',') +}) +watch(() => props.cron.week, value => changeRadioValue(value)) +watch([radioValue, cycleTotal, averageTotal, weekdayTotal, checkboxString], () => onRadioChange()) +function changeRadioValue(value) { + if (value === "*") { + radioValue.value = 1 + } else if (value === "?") { + radioValue.value = 2 + } else if (value.indexOf("-") > -1) { + const indexArr = value.split('-') + cycle01.value = Number(indexArr[0]) + cycle02.value = Number(indexArr[1]) + radioValue.value = 3 + } else if (value.indexOf("#") > -1) { + const indexArr = value.split('#') + average01.value = Number(indexArr[1]) + average02.value = Number(indexArr[0]) + radioValue.value = 4 + } else if (value.indexOf("L") > -1) { + const indexArr = value.split("L") + weekday.value = Number(indexArr[0]) + radioValue.value = 5 + } else { + checkboxList.value = [...new Set(value.split(',').map(item => Number(item)))] + radioValue.value = 6 + } +} +function onRadioChange() { + if (radioValue.value === 2 && props.cron.day === '?') { + emit('update', 'day', '*', 'week') + } + if (radioValue.value !== 2 && props.cron.day !== '?') { + emit('update', 'day', '?', 'week') + } + switch (radioValue.value) { + case 1: + emit('update', 'week', '*', 'week') + break + case 2: + emit('update', 'week', '?', 'week') + break + case 3: + emit('update', 'week', cycleTotal.value, 'week') + break + case 4: + emit('update', 'week', averageTotal.value, 'week') + break + case 5: + emit('update', 'week', weekdayTotal.value, 'week') + break + case 6: + if (checkboxList.value.length === 0) { + checkboxList.value.push(checkCopy.value[0]) + } else { + checkCopy.value = checkboxList.value + } + emit('update', 'week', checkboxString.value, 'week') + break + } +} +</script> + +<style lang="scss" scoped> +.el-input-number--small, .el-input-number--small, .el-select, .el-select--small { + margin: 0 0.5rem; +} +.el-select, .el-select--small { + width: 8rem; +} +.el-select.multiselect, .el-select--small.multiselect { + width: 17.8rem; +} +</style> \ No newline at end of file diff --git a/src/components/Crontab/year.vue b/src/components/Crontab/year.vue new file mode 100644 index 0000000..cbdb95c --- /dev/null +++ b/src/components/Crontab/year.vue @@ -0,0 +1,149 @@ +<template> + <el-form size="small"> + <el-form-item> + <el-radio :label="1" v-model='radioValue'> + 涓嶅~锛屽厑璁哥殑閫氶厤绗, - * /] + </el-radio> + </el-form-item> + + <el-form-item> + <el-radio :label="2" v-model='radioValue'> + 姣忓勾 + </el-radio> + </el-form-item> + + <el-form-item> + <el-radio :label="3" v-model='radioValue'> + 鍛ㄦ湡浠� + <el-input-number v-model='cycle01' :min='fullYear' :max="maxFullYear - 1" /> - + <el-input-number v-model='cycle02' :min="cycle01 + 1" :max="maxFullYear" /> + </el-radio> + </el-form-item> + + <el-form-item> + <el-radio :label="4" v-model='radioValue'> + 浠� + <el-input-number v-model='average01' :min='fullYear' :max="maxFullYear - 1"/> 骞村紑濮嬶紝姣� + <el-input-number v-model='average02' :min="1" :max="10" /> 骞存墽琛屼竴娆� + </el-radio> + + </el-form-item> + + <el-form-item> + <el-radio :label="5" v-model='radioValue'> + 鎸囧畾 + <el-select clearable v-model="checkboxList" placeholder="鍙閫�" multiple :multiple-limit="8"> + <el-option v-for="item in 9" :key="item" :value="item - 1 + fullYear" :label="item -1 + fullYear" /> + </el-select> + </el-radio> + </el-form-item> + </el-form> +</template> + +<script setup> +const emit = defineEmits(['update']) +const props = defineProps({ + cron: { + type: Object, + default: { + second: "*", + min: "*", + hour: "*", + day: "*", + month: "*", + week: "?", + year: "" + } + }, + check: { + type: Function, + default: () => { + } + } +}) +const fullYear = ref(0) +const maxFullYear = ref(0) +const radioValue = ref(1) +const cycle01 = ref(0) +const cycle02 = ref(0) +const average01 = ref(0) +const average02 = ref(1) +const checkboxList = ref([]) +const checkCopy = ref([]) +const cycleTotal = computed(() => { + cycle01.value = props.check(cycle01.value, fullYear.value, maxFullYear.value - 1) + cycle02.value = props.check(cycle02.value, cycle01.value + 1, maxFullYear.value) + return cycle01.value + '-' + cycle02.value +}) +const averageTotal = computed(() => { + average01.value = props.check(average01.value, fullYear.value, maxFullYear.value - 1) + average02.value = props.check(average02.value, 1, 10) + return average01.value + '/' + average02.value +}) +const checkboxString = computed(() => { + return checkboxList.value.join(',') +}) +watch(() => props.cron.year, value => changeRadioValue(value)) +watch([radioValue, cycleTotal, averageTotal, checkboxString], () => onRadioChange()) +function changeRadioValue(value) { + if (value === '') { + radioValue.value = 1 + } else if (value === "*") { + radioValue.value = 2 + } else if (value.indexOf("-") > -1) { + const indexArr = value.split('-') + cycle01.value = Number(indexArr[0]) + cycle02.value = Number(indexArr[1]) + radioValue.value = 3 + } else if (value.indexOf("/") > -1) { + const indexArr = value.split('#') + average01.value = Number(indexArr[1]) + average02.value = Number(indexArr[0]) + radioValue.value = 4 + } else { + checkboxList.value = [...new Set(value.split(',').map(item => Number(item)))] + radioValue.value = 5 + } +} +function onRadioChange() { + switch (radioValue.value) { + case 1: + emit('update', 'year', '', 'year') + break + case 2: + emit('update', 'year', '*', 'year') + break + case 3: + emit('update', 'year', cycleTotal.value, 'year') + break + case 4: + emit('update', 'year', averageTotal.value, 'year') + break + case 5: + if (checkboxList.value.length === 0) { + checkboxList.value.push(checkCopy.value[0]) + } else { + checkCopy.value = checkboxList.value + } + emit('update', 'year', checkboxString.value, 'year') + break + } +} +onMounted(() => { + fullYear.value = Number(new Date().getFullYear()) + maxFullYear.value = fullYear.value + 10 + cycle01.value = fullYear.value + cycle02.value = cycle01.value + 1 + average01.value = fullYear.value + checkCopy.value = [fullYear.value] +}) +</script> + +<style lang="scss" scoped> +.el-input-number--small, .el-input-number--small, .el-select, .el-select--small { + margin: 0 0.2rem; +} +.el-select, .el-select--small { + width: 18.8rem; +} +</style> \ No newline at end of file diff --git a/src/views/monitor/job/index.vue b/src/views/monitor/job/index.vue index da67945..aab4ba4 100644 --- a/src/views/monitor/job/index.vue +++ b/src/views/monitor/job/index.vue @@ -204,7 +204,7 @@ </el-col> <el-col :span="24"> <el-form-item label="cron琛ㄨ揪寮�" prop="cronExpression"> - <el-input v-model="form.cronExpression" placeholder="璇疯緭鍏ron鎵ц琛ㄨ揪寮�"> + <el-input v-model="form.cronExpression" placeholder="璇疯緭鍏ron鎵ц琛ㄨ揪寮�" readonly> <template #append> <el-button type="primary" @click="handleShowCron"> 鐢熸垚琛ㄨ揪寮� @@ -251,7 +251,11 @@ </div> </template> </el-dialog> - + + <el-dialog title="Cron琛ㄨ揪寮忕敓鎴愬櫒" v-model="openCron" append-to-body destroy-on-close> + <crontab ref="crontabRef" @hide="openCron=false" @fill="crontabFill" :expression="expression"></crontab> + </el-dialog> + <!-- 浠诲姟鏃ュ織璇︾粏 --> <el-dialog title="浠诲姟璇︾粏" v-model="openView" width="700px" append-to-body> <el-form :model="form" label-width="120px"> @@ -306,7 +310,7 @@ <script setup name="Job"> import { listJob, getJob, delJob, addJob, updateJob, runJob, changeJobStatus } from "@/api/monitor/job"; - +import Crontab from '@/components/Crontab' const router = useRouter(); const { proxy } = getCurrentInstance(); const { sys_job_group, sys_job_status } = proxy.useDict("sys_job_group", "sys_job_status"); @@ -336,7 +340,7 @@ rules: { jobName: [{ required: true, message: "浠诲姟鍚嶇О涓嶈兘涓虹┖", trigger: "blur" }], invokeTarget: [{ required: true, message: "璋冪敤鐩爣瀛楃涓蹭笉鑳戒负绌�", trigger: "blur" }], - cronExpression: [{ required: true, message: "cron鎵ц琛ㄨ揪寮忎笉鑳戒负绌�", trigger: "blur" }] + cronExpression: [{ required: true, message: "cron鎵ц琛ㄨ揪寮忎笉鑳戒负绌�", trigger: "change" }] } }); -- Gitblit v1.9.3