2
0
mirror of https://github.com/acepanel/panel.git synced 2026-02-04 03:07:20 +08:00
Files
panel/web/src/components/common/CronSelector.vue

363 lines
9.7 KiB
Vue
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<script setup lang="ts">
import { useGettext } from 'vue3-gettext'
import CronPreview from './CronPreview.vue'
const { $gettext } = useGettext()
// 生成的 Cron 表达式值
const value = defineModel<string>('value', {
type: String,
required: true
})
// 当前选择的周期类型
const selectedOption = ref<string>('every-n-minutes')
// 表单数据
const formData = ref({
// Every N 系列
nMinutes: 30, // 每 N 分钟
nHours: 2, // 每 N 小时
nDays: 3, // 每 N 天
// 时间配置
minute: 30, // 分钟
hour: 1, // 小时
day: 3, // 日期1-31
month: 1, // 月份1-12
weekday: 1, // 星期0-60=周日)
// 自定义表达式
customCron: '* * * * *'
})
// 是否已初始化
const initialized = ref(false)
// 解析 Cron 表达式,反向设置表单数据
const parseCron = (cron: string) => {
if (!cron) return
const parts = cron.split(' ')
if (parts.length !== 5) {
// 无法解析,使用自定义模式
selectedOption.value = 'custom'
formData.value.customCron = cron
return
}
const [minute, hour, day, month, weekday] = parts
// 每 N 分钟:*/N * * * *
if (minute.startsWith('*/') && hour === '*' && day === '*' && month === '*' && weekday === '*') {
selectedOption.value = 'every-n-minutes'
formData.value.nMinutes = parseInt(minute.slice(2)) || 30
return
}
// 每 N 小时M */N * * *
if (hour.startsWith('*/') && day === '*' && month === '*' && weekday === '*') {
selectedOption.value = 'every-n-hours'
formData.value.minute = parseInt(minute) || 0
formData.value.nHours = parseInt(hour.slice(2)) || 2
return
}
// 每 N 天M H */N * *
if (day.startsWith('*/') && month === '*' && weekday === '*') {
selectedOption.value = 'every-n-days'
formData.value.minute = parseInt(minute) || 0
formData.value.hour = parseInt(hour) || 0
formData.value.nDays = parseInt(day.slice(2)) || 3
return
}
// 每小时M * * * *
if (hour === '*' && day === '*' && month === '*' && weekday === '*' && !minute.includes('/')) {
selectedOption.value = 'every-hour'
formData.value.minute = parseInt(minute) || 0
return
}
// 每天M H * * *
if (day === '*' && month === '*' && weekday === '*' && !hour.includes('/')) {
selectedOption.value = 'every-day'
formData.value.minute = parseInt(minute) || 0
formData.value.hour = parseInt(hour) || 0
return
}
// 每周M H * * W
if (day === '*' && month === '*' && weekday !== '*') {
selectedOption.value = 'every-week'
formData.value.minute = parseInt(minute) || 0
formData.value.hour = parseInt(hour) || 0
formData.value.weekday = parseInt(weekday) || 0
return
}
// 每月M H D * *
if (month === '*' && weekday === '*' && day !== '*' && !day.includes('/')) {
selectedOption.value = 'every-month'
formData.value.minute = parseInt(minute) || 0
formData.value.hour = parseInt(hour) || 0
formData.value.day = parseInt(day) || 1
return
}
// 每年M H D Mon *
if (weekday === '*' && month !== '*' && day !== '*') {
selectedOption.value = 'every-year'
formData.value.minute = parseInt(minute) || 0
formData.value.hour = parseInt(hour) || 0
formData.value.day = parseInt(day) || 1
formData.value.month = parseInt(month) || 1
return
}
// 无法匹配,使用自定义模式
selectedOption.value = 'custom'
formData.value.customCron = cron
}
// 周期选项
const options = [
{ label: $gettext('Every N Minutes'), value: 'every-n-minutes' },
{ label: $gettext('Every N Hours'), value: 'every-n-hours' },
{ label: $gettext('Every N Days'), value: 'every-n-days' },
{ label: $gettext('Hourly'), value: 'every-hour' },
{ label: $gettext('Daily'), value: 'every-day' },
{ label: $gettext('Weekly'), value: 'every-week' },
{ label: $gettext('Monthly'), value: 'every-month' },
{ label: $gettext('Yearly'), value: 'every-year' },
{ label: $gettext('Custom'), value: 'custom' }
]
// 星期选项
const weekdayOptions = [
{ label: $gettext('Sunday'), value: 0 },
{ label: $gettext('Monday'), value: 1 },
{ label: $gettext('Tuesday'), value: 2 },
{ label: $gettext('Wednesday'), value: 3 },
{ label: $gettext('Thursday'), value: 4 },
{ label: $gettext('Friday'), value: 5 },
{ label: $gettext('Saturday'), value: 6 }
]
// 月份选项
const monthOptions = Array.from({ length: 12 }, (_, i) => ({
label: $gettext('Month %{month}', { month: String(i + 1) }),
value: i + 1
}))
// 生成 Cron 表达式
const generateCron = (): string => {
const { minute, hour, day, month, weekday, nMinutes, nHours, nDays, customCron } = formData.value
switch (selectedOption.value) {
case 'every-n-minutes':
// 每 N 分钟:*/N * * * *
return `*/${nMinutes} * * * *`
case 'every-n-hours':
// 每 N 小时的第 M 分钟M */N * * *
return `${minute} */${nHours} * * *`
case 'every-n-days':
// 每 N 天的 H 时 M 分M H */N * *
return `${minute} ${hour} */${nDays} * *`
case 'every-hour':
// 每小时的第 M 分钟M * * * *
return `${minute} * * * *`
case 'every-day':
// 每天 H 时 M 分M H * * *
return `${minute} ${hour} * * *`
case 'every-week':
// 每周几的 H 时 M 分M H * * W
return `${minute} ${hour} * * ${weekday}`
case 'every-month':
// 每月 D 日 H 时 M 分M H D * *
return `${minute} ${hour} ${day} * *`
case 'every-year':
// 每年 Mon 月 D 日 H 时 M 分M H D Mon *
return `${minute} ${hour} ${day} ${month} *`
case 'custom':
return customCron
default:
return '* * * * *'
}
}
// 组件挂载时,解析传入的 Cron 表达式
onMounted(() => {
if (value.value) {
parseCron(value.value)
}
// 标记已初始化
nextTick(() => {
initialized.value = true
})
})
// 监听变化,更新 Cron 表达式(仅在初始化后)
watch(
[selectedOption, formData],
() => {
if (initialized.value) {
value.value = generateCron()
}
},
{ deep: true }
)
// 判断是否显示某个输入框
const showMonth = computed(() => selectedOption.value === 'every-year')
const showDay = computed(() =>
['every-n-days', 'every-month', 'every-year'].includes(selectedOption.value)
)
const showWeekday = computed(() => selectedOption.value === 'every-week')
const showHour = computed(() =>
['every-n-days', 'every-day', 'every-week', 'every-month', 'every-year'].includes(
selectedOption.value
)
)
const showMinute = computed(() =>
[
'every-n-minutes',
'every-n-hours',
'every-n-days',
'every-hour',
'every-day',
'every-week',
'every-month',
'every-year'
].includes(selectedOption.value)
)
const showNDays = computed(() => selectedOption.value === 'every-n-days')
const showNHours = computed(() => selectedOption.value === 'every-n-hours')
const showNMinutes = computed(() => selectedOption.value === 'every-n-minutes')
const showCustom = computed(() => selectedOption.value === 'custom')
</script>
<template>
<n-flex vertical :size="12">
<n-flex align="center" :wrap="false">
<!-- 周期类型选择 -->
<n-select
v-model:value="selectedOption"
:options="options"
:style="{ width: '160px', flexShrink: 0 }"
/>
<!-- N 分钟 -->
<n-input-number
v-if="showNMinutes"
v-model:value="formData.nMinutes"
:min="1"
:max="59"
:style="{ width: '140px' }"
>
<template #suffix>{{ $gettext('Minutes') }}</template>
</n-input-number>
<!-- N 小时 -->
<n-input-number
v-if="showNHours"
v-model:value="formData.nHours"
:min="1"
:max="23"
:style="{ width: '140px' }"
>
<template #suffix>{{ $gettext('Hours') }}</template>
</n-input-number>
<!-- N -->
<n-input-number
v-if="showNDays"
v-model:value="formData.nDays"
:min="1"
:max="31"
:style="{ width: '140px' }"
>
<template #suffix>{{ $gettext('Days') }}</template>
</n-input-number>
<!-- 月份选择每年 -->
<n-select
v-if="showMonth"
v-model:value="formData.month"
:options="monthOptions"
:style="{ width: '140px' }"
/>
<!-- 日期选择每月每年 -->
<n-input-number
v-if="showDay && !showNDays"
v-model:value="formData.day"
:min="1"
:max="31"
:style="{ width: '140px' }"
>
<template #suffix>{{ $gettext('Day') }}</template>
</n-input-number>
<!-- 星期选择每周 -->
<n-select
v-if="showWeekday"
v-model:value="formData.weekday"
:options="weekdayOptions"
:style="{ width: '140px' }"
/>
<!-- 小时选择 -->
<n-input-number
v-if="showHour"
v-model:value="formData.hour"
:min="0"
:max="23"
:style="{ width: '140px' }"
>
<template #suffix>{{ $gettext('Hour') }}</template>
</n-input-number>
<!-- 分钟选择 -->
<n-input-number
v-if="showMinute && !showNMinutes"
v-model:value="formData.minute"
:min="0"
:max="59"
:style="{ width: '140px' }"
>
<template #suffix>{{ $gettext('Minute') }}</template>
</n-input-number>
<!-- 自定义 Cron 表达式 -->
<n-input
v-if="showCustom"
v-model:value="formData.customCron"
:placeholder="$gettext('Enter Cron expression')"
:style="{ width: '240px' }"
/>
</n-flex>
<!-- 预览 -->
<n-text depth="3">
<cron-preview :cron="value" />
</n-text>
</n-flex>
</template>
<style scoped lang="scss"></style>