diff --git a/dolphinscheduler-ui-next/src/components/crontab/common.ts b/dolphinscheduler-ui-next/src/components/crontab/common.ts new file mode 100644 index 0000000000..5069801d5d --- /dev/null +++ b/dolphinscheduler-ui-next/src/components/crontab/common.ts @@ -0,0 +1,193 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import _ from 'lodash' + +const timeI18n = { + second: { + everyTime: 'crontab.every_second', + every: 'crontab.every', + timeCarriedOut: 'crontab.second_carried_out', + timeStart: 'crontab.second_start', + cycleFrom: 'crontab.cycle_from', + specificTime: 'crontab.specific_second', + specificTimeTip: 'crontab.specific_second_tip', + to: 'crontab.to', + time: 'crontab.second' + }, + minute: { + everyTime: 'crontab.every_minute', + every: 'crontab.every', + timeCarriedOut: 'crontab.minute_carried_out', + timeStart: 'crontab.minute_start', + cycleFrom: 'crontab.cycle_from', + specificTime: 'crontab.specific_minute', + specificTimeTip: 'crontab.specific_minute_tip', + to: 'crontab.to', + time: 'crontab.minute' + }, + hour: { + everyTime: 'crontab.every_hour', + every: 'crontab.every', + timeCarriedOut: 'crontab.hour_carried_out', + timeStart: 'crontab.hour_start', + cycleFrom: 'crontab.cycle_from', + specificTime: 'crontab.specific_hour', + specificTimeTip: 'crontab.specific_hour_tip', + to: 'crontab.to', + time: 'crontab.hour' + }, + month: { + everyTime: 'crontab.every_month', + every: 'crontab.every', + timeCarriedOut: 'crontab.month_carried_out', + timeStart: 'crontab.month_start', + cycleFrom: 'crontab.cycle_from', + specificTime: 'crontab.specific_month', + specificTimeTip: 'crontab.specific_month_tip', + to: 'crontab.to', + time: 'crontab.month' + }, + year: { + everyTime: 'crontab.every_year', + every: 'crontab.every', + timeCarriedOut: 'crontab.year_carried_out', + timeStart: 'crontab.year_start', + cycleFrom: 'crontab.cycle_from', + specificTime: 'crontab.specific_year', + specificTimeTip: 'crontab.specific_year_tip', + to: 'crontab.to', + time: 'crontab.year' + } +} + +const week = [ + { + label: 'crontab.sunday', + value: 1 + }, + { + label: 'crontab.monday', + value: 2 + }, + { + label: 'crontab.tuesday', + value: 3 + }, + { + label: 'crontab.wednesday', + value: 4 + }, + { + label: 'crontab.thursday', + value: 5 + }, + { + label: 'crontab.friday', + value: 6 + }, + { + label: 'crontab.saturday', + value: 7 + } +] + +const specificWeek = [ + { + label: 'SUN', + value: 'SUN' + }, + { + label: 'MON', + value: 'MON' + }, + { + label: 'TUE', + value: 'TUE' + }, + { + label: 'WED', + value: 'WED' + }, + { + label: 'THU', + value: 'THU' + }, + { + label: 'FRI', + value: 'FRI' + }, + { + label: 'SAT', + value: 'SAT' + } +] + +const lastWeeks = [ + { + label: 'crontab.sunday', + value: '?' + }, + { + label: 'crontab.monday', + value: '2L' + }, + { + label: 'crontab.tuesday', + value: '3L' + }, + { + label: 'crontab.wednesday', + value: '4L' + }, + { + label: 'crontab.thursday', + value: '5L' + }, + { + label: 'crontab.friday', + value: '6L' + }, + { + label: 'crontab.saturday', + value: '7L' + } +] + +const isStr = (str: string, v: string) => { + let flag + if (str.indexOf(v) !== -1) { + flag = str.split(v) + } + return flag +} + +const isWeek = (str: string) => { + let flag = false + const data = str.split(',') + const isSpecificWeek = (key: string) => { + return _.findIndex(specificWeek, (v) => v.value === key) !== -1 + } + _.map(data, (v) => { + if (isSpecificWeek(v)) { + flag = true + } + }) + return flag +} + +export { isStr, isWeek, timeI18n, week, specificWeek, lastWeeks } diff --git a/dolphinscheduler-ui-next/src/components/crontab/index.module.scss b/dolphinscheduler-ui-next/src/components/crontab/index.module.scss new file mode 100644 index 0000000000..8c3fb30c89 --- /dev/null +++ b/dolphinscheduler-ui-next/src/components/crontab/index.module.scss @@ -0,0 +1,29 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + + .crontab-list { + display: flex; + .crontab-list-item { + display: flex; + vertical-align: middle; + align-items: center; + width: 460px; + > div { + margin: 5px; + } + } + } \ No newline at end of file diff --git a/dolphinscheduler-ui-next/src/components/crontab/index.tsx b/dolphinscheduler-ui-next/src/components/crontab/index.tsx new file mode 100644 index 0000000000..d74623f841 --- /dev/null +++ b/dolphinscheduler-ui-next/src/components/crontab/index.tsx @@ -0,0 +1,131 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { computed, defineComponent, ref, watch, PropType } from 'vue' +import { NTabPane, NTabs } from 'naive-ui' +import { useI18n } from 'vue-i18n' +import CrontabTime from './modules/time' +import CrontabDay from './modules/day' +import { timeI18n } from './common' + +const props = { + value: { + type: String as PropType, + default: '* * * * * ? *' + } +} + +export default defineComponent({ + name: 'Crontab', + props, + emits: ['update:value'], + setup(props, ctx) { + const cron = props.value.split(' ') + const secondRef = ref(cron[0]) + const minuteRef = ref(cron[1]) + const hourRef = ref(cron[2]) + const dayRef = ref(cron[3]) + const monthRef = ref(cron[4]) + const weekRef = ref(cron[5]) + const yearRef = ref(cron[6]) + + const crontabValue = computed( + () => + `${secondRef.value} ${minuteRef.value} ${hourRef.value} ${dayRef.value} ${monthRef.value} ${weekRef.value} ${yearRef.value}` + ) + + const reset = () => { + const cron = props.value.split(' ') + secondRef.value = cron[0] + minuteRef.value = cron[1] + hourRef.value = cron[2] + dayRef.value = cron[3] + monthRef.value = cron[4] + weekRef.value = cron[5] + yearRef.value = cron[6] + } + + watch( + () => crontabValue.value, + () => { + ctx.emit('update:value', crontabValue.value) + } + ) + + watch( + () => props.value, + () => { + reset() + } + ) + + return { + secondRef, + minuteRef, + hourRef, + dayRef, + weekRef, + monthRef, + yearRef, + crontabValue + } + }, + render() { + const { t } = useI18n() + + return ( + + + + + + + + + + + + + + + + + + + + + ) + } +}) diff --git a/dolphinscheduler-ui-next/src/components/crontab/modules/day.tsx b/dolphinscheduler-ui-next/src/components/crontab/modules/day.tsx new file mode 100644 index 0000000000..148c6b62cd --- /dev/null +++ b/dolphinscheduler-ui-next/src/components/crontab/modules/day.tsx @@ -0,0 +1,640 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { defineComponent, onMounted, PropType, ref, watch } from 'vue' +import { NInputNumber, NRadio, NRadioGroup, NSelect } from 'naive-ui' +import { useI18n } from 'vue-i18n' +import { isStr, isWeek, week, specificWeek, lastWeeks } from '../common' +import styles from '../index.module.scss' + +const props = { + dayValue: { + type: String as PropType, + default: '*' + }, + weekValue: { + type: String as PropType, + default: '?' + } +} + +export default defineComponent({ + name: 'CrontabDay', + props, + emits: ['update:dayValue', 'update:weekValue'], + setup(props, ctx) { + const { t } = useI18n() + + const options = Array.from({ length: 60 }, (x, i) => ({ + label: i.toString(), + value: i + })) + + const weekOptions = week.map((v) => ({ + label: t(v.label), + value: v.value + })) + + const lastWeekOptions = lastWeeks.map((v) => ({ + label: t(v.label), + value: v.value + })) + + const radioRef = ref() + const dayRef = ref() + const weekRef = ref() + const WkintervalWeekStartRef = ref(2) + const WkintervalWeekPerformRef = ref(2) + const intervalDayStartRef = ref(1) + const intervalDayPerformRef = ref(1) + const WkspecificDayRef = ref>([]) + const WkspecificWeekRef = ref>([]) + const monthLastDaysRef = ref('L') + const monthLastWorkingDaysRef = ref('LW') + const monthLastWeeksRef = ref('?') + const monthTailBeforeRef = ref(1) + const recentlyWorkingDaysMonthRef = ref(1) + const WkmonthNumWeeksDayRef = ref(1) + const WkmonthNumWeeksWeekRef = ref(1) + + /** + * Parse parameter value + */ + const analyticalValue = () => { + const $dayVal = props.dayValue + const $weekVal = props.weekValue + const isWeek1 = $weekVal.indexOf('/') !== -1 + const isWeek2 = $weekVal.indexOf('#') !== -1 + + // Initialization + if ($dayVal === '*' && $weekVal === '?') { + radioRef.value = 'everyDay' + return + } + + // week + if (isWeek1 || isWeek2 || isWeek($weekVal)) { + dayRef.value = '?' + + /** + * Processing by sequence number (excluding days) + * @param [ + * WkintervalWeek=>(/), + * WkspecificWeek=>(TUE,WED), + * WkmonthNumWeeks=>(#) + * ] + */ + const hanleWeekOne = () => { + const a = isStr($weekVal, '/') as string[] + WkintervalWeekStartRef.value = parseInt(a[0]) + WkintervalWeekPerformRef.value = parseInt(a[1]) + dayRef.value = '?' + weekRef.value = `${WkintervalWeekPerformRef.value}/${WkintervalWeekStartRef.value}` + radioRef.value = 'WkintervalWeek' + } + + const hanleWeekTwo = () => { + WkspecificWeekRef.value = $weekVal + .split(',') + .map((item) => parseInt(item)) + radioRef.value = 'WkspecificWeek' + } + + const hanleWeekThree = () => { + const a = isStr($weekVal, '#') as string[] + WkmonthNumWeeksDayRef.value = parseInt(a[0]) + WkmonthNumWeeksDayRef.value = parseInt(a[1]) + radioRef.value = 'WkmonthNumWeeks' + } + + // Processing week + if (isStr($weekVal, '/')) { + hanleWeekOne() + } else if (isStr($weekVal, '#')) { + hanleWeekThree() + } else if (isWeek($weekVal)) { + hanleWeekTwo() + } + } else { + weekRef.value = '?' + + /** + * Processing by sequence number (excluding week) + * @param [ + * everyDay=>(*), + * intervalDay=>(1/1), + * specificDay=>(1,2,5,3,4), + * monthLastDays=>(L), + * monthLastWorkingDays=>(LW), + * monthLastWeeks=>(3L), + * monthTailBefore=>(L-4), + * recentlyWorkingDaysMonth=>(6W) + * ] + */ + const hanleDayOne = () => { + radioRef.value = 'everyDay' + } + + const hanleDayTwo = () => { + const a = isStr($dayVal, '/') as string[] + intervalDayStartRef.value = parseInt(a[0]) + intervalDayPerformRef.value = parseInt(a[1]) + radioRef.value = 'intervalDay' + } + + const hanleDayThree = () => { + WkspecificDayRef.value = $dayVal + .split(',') + .map((item) => parseInt(item)) + radioRef.value = 'specificDay' + } + + const hanleDayFour = () => { + radioRef.value = 'monthLastDays' + } + + const hanleDayFive = () => { + radioRef.value = 'monthLastWorkingDays' + } + + const hanleDaySix = () => { + monthLastWeeksRef.value = $dayVal + radioRef.value = 'monthLastWeeks' + } + + const hanleDaySeven = () => { + const a = isStr($dayVal, '-') as string[] + monthTailBeforeRef.value = parseInt(a[1]) + radioRef.value = 'monthTailBefore' + } + + const hanleDayEight = () => { + recentlyWorkingDaysMonthRef.value = parseInt( + $dayVal.slice(0, $dayVal.length - 1) + ) + radioRef.value = 'recentlyWorkingDaysMonth' + } + + if ($dayVal === '*') { + hanleDayOne() + } else if (isStr($dayVal, '/')) { + hanleDayTwo() + } else if ($dayVal === 'L') { + hanleDayFour() + } else if ($dayVal === 'LW') { + hanleDayFive() + } else if ($dayVal.charAt($dayVal.length - 1) === 'L') { + hanleDaySix() + } else if (isStr($dayVal, '-')) { + hanleDaySeven() + } else if ($dayVal.charAt($dayVal.length - 1) === 'W') { + hanleDayEight() + } else { + hanleDayThree() + } + } + } + + // Every few weeks + const onWkintervalWeekPerform = (value: number | null) => { + WkintervalWeekPerformRef.value = value || 0 + if (radioRef.value === 'WkintervalWeek') { + dayRef.value = '?' + weekRef.value = `${WkintervalWeekStartRef.value}/${WkintervalWeekPerformRef.value}` + } + } + + // Every few weeks + const onWkintervalWeekStart = (value: number | null) => { + WkintervalWeekStartRef.value = value || 0 + if (radioRef.value === 'WkintervalWeek') { + dayRef.value = '?' + weekRef.value = `${WkintervalWeekStartRef.value}/${WkintervalWeekPerformRef.value}` + } + } + + // Interval start time(1) + const onIntervalDayStart = (value: number | null) => { + intervalDayStartRef.value = value || 0 + if (radioRef.value === 'intervalDay') { + intervalDaySet() + } + } + + // Interval execution time(2) + const onIntervalDayPerform = (value: number | null) => { + intervalDayPerformRef.value = value || 0 + if (radioRef.value === 'intervalDay') { + intervalDaySet() + } + } + + // Specific day of the week (multiple choice) + const onWkspecificWeek = (arr: Array) => { + WkspecificWeekRef.value = arr + if (radioRef.value === 'WkspecificWeek') { + dayRef.value = '?' + weekRef.value = arr.join(',') + } + } + + // Specific days (multiple choices) + const onWkspecificDay = (arr: Array) => { + WkspecificDayRef.value = arr + if (radioRef.value === 'specificDay') { + weekRef.value = '?' + dayRef.value = arr.join(',') + } + } + + const onMonthLastWeeks = (value: string | null) => { + monthLastWeeksRef.value = value || '?' + if (radioRef.value === 'monthLastWeeks') { + weekRef.value = value + dayRef.value = '?' + } + } + + // Specific days + const onSpecificDays = (arr: Array) => { + WkspecificDayRef.value = arr + if (radioRef.value === 'specificDay') { + specificSet() + } + } + + // By the end of this month + const onMonthTailBefore = (value: number | null) => { + monthTailBeforeRef.value = value || 0 + if (radioRef.value === 'monthTailBefore') { + dayRef.value = `L-${monthTailBeforeRef.value}` + } + } + + // Last working day + const onRecentlyWorkingDaysMonth = (value: number | null) => { + recentlyWorkingDaysMonthRef.value = value || 0 + if (radioRef.value === 'recentlyWorkingDaysMonth') { + dayRef.value = `${recentlyWorkingDaysMonthRef.value}W` + } + } + + // On the day of this month + const onWkmonthNumWeeksDay = (value: number | null) => { + WkmonthNumWeeksDayRef.value = value || 0 + if (radioRef.value === 'WkmonthNumWeeks') { + weekRef.value = `${WkmonthNumWeeksWeekRef.value}#${WkmonthNumWeeksDayRef.value}` + } + } + + // On the week of this month + const onWkmonthNumWeeksWeek = (value: number | null) => { + if (radioRef.value === 'WkmonthNumWeeks') { + dayRef.value = '?' + weekRef.value = `${value}#${WkmonthNumWeeksDayRef.value}` + } + } + + // Reset every day + const everyDaySet = () => { + dayRef.value = '*' + } + + // Reset interval week starts from * + const WkintervalWeekReset = () => { + weekRef.value = `${WkintervalWeekStartRef.value}/${WkintervalWeekPerformRef.value}` + } + + // Reset interval days + const intervalDaySet = () => { + dayRef.value = `${intervalDayStartRef.value}/${intervalDayPerformRef.value}` + } + + // Specific week (multiple choices) + const WkspecificWeekReset = () => { + weekRef.value = WkspecificWeekRef.value.length + ? WkspecificWeekRef.value.join(',') + : '*' + } + + // Reset specific days + const specificSet = () => { + if (WkspecificDayRef.value.length) { + dayRef.value = WkspecificDayRef.value.join(',') + } else { + dayRef.value = '*' + } + } + + // On the last day of the month + const monthLastDaysReset = () => { + dayRef.value = monthLastDaysRef.value + } + + // On the last working day of the month + const monthLastWorkingDaysReset = () => { + dayRef.value = monthLastWorkingDaysRef.value + } + + // At the end of the month* + const monthLastWeeksReset = () => { + dayRef.value = monthLastWeeksRef.value + } + + // By the end of this month + const monthTailBeforeReset = () => { + dayRef.value = `L-${monthTailBeforeRef.value}` + } + + // Last working day (Monday to Friday) to this month + const recentlyWorkingDaysMonthReset = () => { + dayRef.value = `${recentlyWorkingDaysMonthRef.value}W` + } + + // On the day of this month + const WkmonthNumReset = () => { + weekRef.value = `${WkmonthNumWeeksWeekRef.value}#${WkmonthNumWeeksDayRef.value}` + } + + const updateRadioDay = (value: string) => { + switch (value) { + case 'everyDay': + weekRef.value = '?' + everyDaySet() + break + case 'WkintervalWeek': + dayRef.value = '?' + WkintervalWeekReset() + break + case 'intervalDay': + weekRef.value = '?' + intervalDaySet() + break + case 'WkspecificWeek': + dayRef.value = '?' + WkspecificWeekReset() + break + case 'specificDay': + weekRef.value = '?' + specificSet() + break + case 'monthLastDays': + weekRef.value = '?' + monthLastDaysReset() + break + case 'monthLastWorkingDays': + weekRef.value = '?' + monthLastWorkingDaysReset() + break + case 'monthLastWeeks': + weekRef.value = '1L' + monthLastWeeksReset() + break + case 'monthTailBefore': + weekRef.value = '?' + monthTailBeforeReset() + break + case 'recentlyWorkingDaysMonth': + weekRef.value = '?' + recentlyWorkingDaysMonthReset() + break + case 'WkmonthNumWeeks': + dayRef.value = '?' + WkmonthNumReset() + break + } + } + + watch( + () => dayRef.value, + () => ctx.emit('update:dayValue', dayRef.value.toString()) + ) + + watch( + () => weekRef.value, + () => ctx.emit('update:weekValue', weekRef.value.toString()) + ) + + onMounted(() => analyticalValue()) + + return { + options, + weekOptions, + lastWeekOptions, + radioRef, + WkintervalWeekStartRef, + WkintervalWeekPerformRef, + intervalDayStartRef, + intervalDayPerformRef, + WkspecificWeekRef, + WkspecificDayRef, + monthLastWeeksRef, + monthTailBeforeRef, + recentlyWorkingDaysMonthRef, + WkmonthNumWeeksDayRef, + WkmonthNumWeeksWeekRef, + updateRadioDay, + onWkintervalWeekStart, + onWkintervalWeekPerform, + onIntervalDayStart, + onIntervalDayPerform, + onSpecificDays, + onWkspecificWeek, + onWkspecificDay, + onMonthLastWeeks, + onMonthTailBefore, + onRecentlyWorkingDaysMonth, + onWkmonthNumWeeksDay, + onWkmonthNumWeeksWeek + } + }, + render() { + const { t } = useI18n() + + return ( + + +
+
{t('crontab.every_day')}
+
+
+ +
+
{t('crontab.every')}
+
+ +
+
{t('crontab.day_carried_out')}
+
+ +
+
{t('crontab.start')}
+
+
+ +
+
{t('crontab.every')}
+
+ +
+
{t('crontab.day_carried_out')}
+
+ +
+
{t('crontab.day_start')}
+
+
+ +
+
{t('crontab.specific_week')}
+
+ +
+
+
+ +
+
{t('crontab.specific_day')}
+
+ +
+
+
+ +
+
{t('crontab.last_day_of_month')}
+
+
+ +
+
{t('crontab.last_work_day_of_month')}
+
+
+ +
+
{t('crontab.last_of_month')}
+
+ +
+
+
+ +
+
+ +
+
{t('crontab.before_end_of_month')}
+
+
+ +
+
{t('crontab.recent_business_day_to_month')}
+
+ +
+
{t('crontab.one_day')}
+
+
+ +
+
{t('crontab.in_this_months')}
+
+ +
+
+ +
+
+
+
+ ) + } +}) diff --git a/dolphinscheduler-ui-next/src/components/crontab/modules/time.tsx b/dolphinscheduler-ui-next/src/components/crontab/modules/time.tsx new file mode 100644 index 0000000000..84ed74f8ab --- /dev/null +++ b/dolphinscheduler-ui-next/src/components/crontab/modules/time.tsx @@ -0,0 +1,296 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import _ from 'lodash' +import { defineComponent, onMounted, PropType, ref, toRefs, watch } from 'vue' +import { NInputNumber, NRadio, NRadioGroup, NSelect } from 'naive-ui' +import { useI18n } from 'vue-i18n' +import { ICrontabI18n } from '../types' +import { isStr } from '../common' +import styles from '../index.module.scss' + +const props = { + timeValue: { + type: String as PropType, + default: '*' + }, + timeI18n: { + type: Object as PropType, + require: true + } +} + +export default defineComponent({ + name: 'CrontabTime', + props, + emits: ['update:timeValue'], + setup(props, ctx) { + const options = Array.from({ length: 60 }, (x, i) => ({ + label: i.toString(), + value: i + })) + + const timeRef = ref() + const radioRef = ref() + const intervalStartRef = ref(0) + const intervalPerformRef = ref(0) + const specificTimesRef = ref>([]) + const cycleStartRef = ref(0) + const cycleEndRef = ref(0) + + /** + * Parse parameter value + */ + const analyticalValue = () => { + const $timeVal = props.timeValue + // Interval time + const $interval = isStr($timeVal, '/') + // Specific time + const $specific = isStr($timeVal, ',') + // Cycle time + const $cycle = isStr($timeVal, '-') + + // Every time + if ($timeVal === '*') { + radioRef.value = 'everyTime' + timeRef.value = '*' + return + } + + // Positive integer (times) + if ( + ($timeVal.length === 1 && _.isInteger(parseInt($timeVal))) || + ($timeVal.length === 2 && _.isInteger(parseInt($timeVal))) + ) { + radioRef.value = 'specificTime' + specificTimesRef.value = [parseInt($timeVal)] + return + } + + // Interval times + if ($interval) { + radioRef.value = 'intervalTime' + intervalStartRef.value = parseInt($interval[0]) + intervalPerformRef.value = parseInt($interval[1]) + timeRef.value = `${intervalStartRef.value}/${intervalPerformRef.value}` + return + } + + // Specific times + if ($specific) { + radioRef.value = 'specificTime' + specificTimesRef.value = $specific.map((item) => parseInt(item)) + return + } + + // Cycle time + if ($cycle) { + radioRef.value = 'cycleTime' + cycleStartRef.value = parseInt($cycle[0]) + cycleEndRef.value = parseInt($cycle[1]) + timeRef.value = `${cycleStartRef.value}-${cycleEndRef.value}` + return + } + } + + // Interval start time(1) + const onIntervalStart = (value: number | null) => { + intervalStartRef.value = value || 0 + if (radioRef.value === 'intervalTime') { + timeRef.value = `${intervalStartRef.value}/${intervalPerformRef.value}` + } + } + + // Interval execution time(2) + const onIntervalPerform = (value: number | null) => { + intervalPerformRef.value = value || 0 + if (radioRef.value === 'intervalTime') { + timeRef.value = `${intervalStartRef.value}/${intervalPerformRef.value}` + } + } + + // Specific time + const onSpecificTimes = (arr: Array) => { + specificTimesRef.value = arr + if (radioRef.value === 'specificTime') { + specificReset() + } + } + + // Cycle start value + const onCycleStart = (value: number | null) => { + cycleStartRef.value = value || 0 + if (radioRef.value === 'cycleTime') { + timeRef.value = `${cycleStartRef.value}-${cycleEndRef.value}` + } + } + + // Cycle end value + const onCycleEnd = (value: number | null) => { + cycleEndRef.value = value || 0 + if (radioRef.value === 'cycleTime') { + timeRef.value = `${cycleStartRef.value}-${cycleEndRef.value}` + } + } + + // Reset every time + const everyReset = () => { + timeRef.value = '*' + } + + // Reset interval time + const intervalReset = () => { + timeRef.value = `${intervalStartRef.value}/${intervalPerformRef.value}` + } + + // Reset specific time + const specificReset = () => { + let timeValue = '*' + if (specificTimesRef.value.length) { + timeValue = specificTimesRef.value.join(',') + } + timeRef.value = timeValue + } + + // Reset cycle time + const cycleReset = () => { + timeRef.value = `${cycleStartRef.value}-${cycleEndRef.value}` + } + + const updateRadioTime = (value: string) => { + switch (value) { + case 'everyTime': + everyReset() + break + case 'intervalTime': + intervalReset() + break + case 'specificTime': + specificReset() + break + case 'cycleTime': + cycleReset() + break + } + } + + watch( + () => timeRef.value, + () => ctx.emit('update:timeValue', timeRef.value.toString()) + ) + + onMounted(() => analyticalValue()) + + return { + options, + radioRef, + intervalStartRef, + intervalPerformRef, + specificTimesRef, + cycleStartRef, + cycleEndRef, + updateRadioTime, + onIntervalStart, + onIntervalPerform, + onSpecificTimes, + onCycleStart, + onCycleEnd, + ...toRefs(props) + } + }, + render() { + const { t } = useI18n() + + return ( + + +
+
{t(this.timeI18n!.everyTime)}
+
+
+ +
+
{t(this.timeI18n!.every)}
+
+ +
+
{t(this.timeI18n!.timeCarriedOut)}
+
+ +
+
{t(this.timeI18n!.timeStart)}
+
+
+ +
+
{t(this.timeI18n!.specificTime)}
+
+ +
+
+
+ +
+
{t(this.timeI18n!.cycleFrom)}
+
+ +
+
{t(this.timeI18n!.to)}
+
+ +
+
{t(this.timeI18n!.time)}
+
+
+
+ ) + } +}) diff --git a/dolphinscheduler-ui-next/src/components/crontab/types.ts b/dolphinscheduler-ui-next/src/components/crontab/types.ts new file mode 100644 index 0000000000..77d28676d5 --- /dev/null +++ b/dolphinscheduler-ui-next/src/components/crontab/types.ts @@ -0,0 +1,30 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +interface ICrontabI18n { + everyTime: string + every: string + timeCarriedOut: string + timeStart: string + cycleFrom: string + specificTime: string + specificTimeTip: string + to: string + time: string +} + +export { ICrontabI18n } diff --git a/dolphinscheduler-ui-next/src/locales/modules/en_US.ts b/dolphinscheduler-ui-next/src/locales/modules/en_US.ts index d52c90ba06..1034bc66b0 100644 --- a/dolphinscheduler-ui-next/src/locales/modules/en_US.ts +++ b/dolphinscheduler-ui-next/src/locales/modules/en_US.ts @@ -951,6 +951,68 @@ const data_quality = { } } +const crontab = { + second: 'second', + minute: 'minute', + hour: 'hour', + day: 'day', + month: 'month', + year: 'year', + monday: 'Monday', + tuesday: 'Tuesday', + wednesday: 'Wednesday', + thursday: 'Thursday', + friday: 'Friday', + saturday: 'Saturday', + sunday: 'Sunday', + every_second: 'Every second', + every: 'Every', + second_carried_out: 'second carried out', + second_start: 'Start', + specific_second: 'Specific second(multiple)', + specific_second_tip: 'Please enter a specific second', + cycle_from: 'Cycle from', + to: 'to', + every_minute: 'Every minute', + minute_carried_out: 'minute carried out', + minute_start: 'Start', + specific_minute: 'Specific minute(multiple)', + specific_minute_tip: 'Please enter a specific minute', + every_hour: 'Every hour', + hour_carried_out: 'hour carried out', + hour_start: 'Start', + specific_hour: 'Specific hour(multiple)', + specific_hour_tip: 'Please enter a specific hour', + every_day: 'Every day', + week_carried_out: 'week carried out', + start: 'Start', + day_carried_out: 'day carried out', + day_start: 'Start', + specific_week: 'Specific day of the week(multiple)', + specific_week_tip: 'Please enter a specific week', + specific_day: 'Specific days(multiple)', + specific_day_tip: 'Please enter a days', + last_day_of_month: 'On the last day of the month', + last_work_day_of_month: 'On the last working day of the month', + last_of_month: 'At the last of this month', + before_end_of_month: 'Before the end of this month', + recent_business_day_to_month: + 'The most recent business day (Monday to Friday) to this month', + in_this_months: 'In this months', + every_month: 'Every month', + month_carried_out: 'month carried out', + month_start: 'Start', + specific_month: 'Specific months(multiple)', + specific_month_tip: 'Please enter a months', + every_year: 'Every year', + year_carried_out: 'year carried out', + year_start: 'Start', + specific_year: 'Specific year(multiple)', + specific_year_tip: 'Please enter a year', + one_hour: 'hour', + one_day: 'day' +} + export default { login, modal, @@ -965,5 +1027,6 @@ export default { project, security, datasource, - data_quality + data_quality, + crontab } diff --git a/dolphinscheduler-ui-next/src/locales/modules/zh_CN.ts b/dolphinscheduler-ui-next/src/locales/modules/zh_CN.ts index 4728ca4a33..c17ff86924 100644 --- a/dolphinscheduler-ui-next/src/locales/modules/zh_CN.ts +++ b/dolphinscheduler-ui-next/src/locales/modules/zh_CN.ts @@ -937,6 +937,66 @@ const data_quality = { TargetTableTotalRows: '目标表总行数' } } +const crontab = { + second: '秒', + minute: '分', + hour: '时', + day: '天', + month: '月', + year: '年', + monday: '星期一', + tuesday: '星期二', + wednesday: '星期三', + thursday: '星期四', + friday: '星期五', + saturday: '星期六', + sunday: '星期天', + every_second: '每一秒钟', + every: '每隔', + second_carried_out: '秒执行 从', + second_start: '秒开始', + specific_second: '具体秒数(可多选)', + specific_second_tip: '请选择具体秒数', + cycle_from: '周期从', + to: '到', + every_minute: '每一分钟', + minute_carried_out: '分执行 从', + minute_start: '分开始', + specific_minute: '具体分钟数(可多选)', + specific_minute_tip: '请选择具体分钟数', + every_hour: '每一小时', + hour_carried_out: '小时执行 从', + hour_start: '小时开始', + specific_hour: '具体小时数(可多选)', + specific_hour_tip: '请选择具体小时数', + every_day: '每一天', + week_carried_out: '周执行 从', + start: '开始', + day_carried_out: '天执行 从', + day_start: '天开始', + specific_week: '具体星期几(可多选)', + specific_week_tip: '请选择具体周几', + specific_day: '具体天数(可多选)', + specific_day_tip: '请选择具体天数', + last_day_of_month: '在这个月的最后一天', + last_work_day_of_month: '在这个月的最后一个工作日', + last_of_month: '在这个月的最后一个', + before_end_of_month: '在本月底前', + recent_business_day_to_month: '最近的工作日(周一至周五)至本月', + in_this_months: '在这个月的第', + every_month: '每一月', + month_carried_out: '月执行 从', + month_start: '月开始', + specific_month: '具体月数(可多选)', + specific_month_tip: '请选择具体月数', + every_year: '每一年', + year_carried_out: '年执行 从', + year_start: '年开始', + specific_year: '具体年数(可多选)', + specific_year_tip: '请选择具体年数', + one_hour: '小时', + one_day: '日' +} export default { login, @@ -952,5 +1012,6 @@ export default { project, security, datasource, - data_quality + data_quality, + crontab } diff --git a/dolphinscheduler-ui-next/src/views/projects/workflow/definition/components/timing-modal.tsx b/dolphinscheduler-ui-next/src/views/projects/workflow/definition/components/timing-modal.tsx index d79f79d71f..3ddfe81bb0 100644 --- a/dolphinscheduler-ui-next/src/views/projects/workflow/definition/components/timing-modal.tsx +++ b/dolphinscheduler-ui-next/src/views/projects/workflow/definition/components/timing-modal.tsx @@ -15,7 +15,15 @@ * limitations under the License. */ -import { defineComponent, PropType, toRefs, h, onMounted, ref } from 'vue' +import { + defineComponent, + PropType, + toRefs, + h, + onMounted, + ref, + watch +} from 'vue' import { useI18n } from 'vue-i18n' import Modal from '@/components/modal' import { useForm } from './use-form' @@ -34,10 +42,12 @@ import { NInputGroup, NList, NListItem, - NThing + NThing, + NPopover } from 'naive-ui' import { ArrowDownOutlined, ArrowUpOutlined } from '@vicons/antd' import { timezoneList } from '@/utils/timezone' +import Crontab from '@/components/crontab' const props = { row: { @@ -59,6 +69,7 @@ export default defineComponent({ props, emits: ['update:show', 'update:row', 'updateList'], setup(props, ctx) { + const crontabRef = ref() const parallelismRef = ref(false) const { t } = useI18n() const { timingState } = useForm() @@ -173,8 +184,28 @@ export default defineComponent({ getEnvironmentList() }) + watch( + () => props.row, + () => { + timingState.timingForm.startEndTime = [ + new Date(props.row.startTime), + new Date(props.row.endTime) + ] + timingState.timingForm.crontab = props.row.crontab + timingState.timingForm.timezoneId = props.row.timezoneId + timingState.timingForm.failureStrategy = props.row.failureStrategy + timingState.timingForm.warningType = props.row.warningType + timingState.timingForm.processInstancePriority = + props.row.processInstancePriority + timingState.timingForm.warningGroupId = props.row.warningGroupId + timingState.timingForm.workerGroup = props.row.workerGroup + timingState.timingForm.environmentCode = props.row.environmentCode + } + ) + return { t, + crontabRef, parallelismRef, hideModal, handleTiming, @@ -216,10 +247,25 @@ export default defineComponent({ - + + {{ + trigger: () => ( + + ), + default: () => ( + + ) + }} + {t('project.workflow.execute_time')} diff --git a/dolphinscheduler-ui-next/src/views/projects/workflow/definition/components/use-modal.ts b/dolphinscheduler-ui-next/src/views/projects/workflow/definition/components/use-modal.ts index e43234af11..0a63a97b16 100644 --- a/dolphinscheduler-ui-next/src/views/projects/workflow/definition/components/use-modal.ts +++ b/dolphinscheduler-ui-next/src/views/projects/workflow/definition/components/use-modal.ts @@ -160,7 +160,8 @@ export function useModal( schedule: JSON.stringify({ startTime: start, endTime: end, - crontab: state.timingForm.crontab + crontab: state.timingForm.crontab, + timezoneId: state.timingForm.timezoneId }), failureStrategy: state.timingForm.failureStrategy, warningType: state.timingForm.warningType, @@ -169,7 +170,7 @@ export function useModal( state.timingForm.warningGroupId === '' ? 0 : state.timingForm.warningGroupId, - workerGroup: state.timingForm.workerGroups, + workerGroup: state.timingForm.workerGroup, environmentCode: state.timingForm.environmentCode } return data