This commit is contained in:
ktianc 2023-07-16 21:44:26 +08:00
commit a6e17c2dae
10 changed files with 1346 additions and 17 deletions

View File

@ -9,12 +9,14 @@ const slots = useSlots()
const props = defineProps({
modelValue: propTypes.bool.def(false),
title: propTypes.string.def('Dialog'),
top: propTypes.string.def('8vh'),
fullscreen: propTypes.bool.def(true),
maxHeight: propTypes.oneOfType([String, Number]).def('500px')
height: propTypes.oneOfType([String, Number]).def('500px'),
width: propTypes.oneOfType([String, Number]).def('700px')
})
const getBindValue = computed(() => {
const delArr: string[] = ['fullscreen', 'title', 'maxHeight']
const delArr: string[] = ['fullscreen', 'title', 'height', 'top', 'width']
const attrs = useAttrs()
const obj = { ...attrs, ...props }
for (const key in obj) {
@ -31,7 +33,7 @@ const toggleFull = () => {
isFullscreen.value = !unref(isFullscreen)
}
const dialogHeight = ref(isNumber(props.maxHeight) ? `${props.maxHeight}px` : props.maxHeight)
const dialogHeight = ref(isNumber(props.height) ? `${props.height}px` : props.height)
watch(
() => isFullscreen.value,
@ -41,7 +43,7 @@ watch(
const windowHeight = document.documentElement.offsetHeight
dialogHeight.value = `${windowHeight - 55 - 60 - (slots.footer ? 63 : 0)}px`
} else {
dialogHeight.value = isNumber(props.maxHeight) ? `${props.maxHeight}px` : props.maxHeight
dialogHeight.value = isNumber(props.height) ? `${props.height}px` : props.height
}
},
{
@ -64,6 +66,8 @@ const dialogStyle = computed(() => {
lock-scroll
draggable
:close-on-click-modal="false"
:top="top"
:width="width"
>
<template #header>
<div class="flex justify-between">

View File

@ -196,7 +196,7 @@ watch(
</template>
</Table>
<Dialog v-model="dialogVisible" :title="dialogTitle" width="700px" maxHeight="600px">
<Dialog v-model="dialogVisible" :title="dialogTitle" width="700px" height="600px">
<Write
ref="writeRef"
:current-row="tableObject.currentRow"

View File

@ -367,12 +367,7 @@ const handleCommand = (command: string) => {
</template>
</Dialog>
<Dialog
v-model="importDialogVisible"
:title="importDialogTitle"
width="750px"
maxHeight="550px"
>
<Dialog v-model="importDialogVisible" :title="importDialogTitle" width="750px" height="550px">
<Import @get-list="getList" />
</Dialog>
@ -380,7 +375,7 @@ const handleCommand = (command: string) => {
v-model="passwordDialogVisible"
:title="passwordDialogTitle"
width="1000px"
maxHeight="550px"
height="550px"
>
<PasswordSendSMS
v-if="passwordDialogType === 'sms'"

View File

@ -0,0 +1,77 @@
<script setup>
import { ElDescriptions, ElDescriptionsItem } from 'element-plus'
import { ref } from 'vue'
const cronExample = ref([
{
value: '0 0 10,15,16 * * ?',
label: '每天上午10点下午3点4点'
},
{
value: '0 0/30 9-17 * * ?',
label: '朝九晚五工作时间内每半小时'
},
{
value: '0 0 12 ? * WED ',
label: '表示每个星期三中午12点'
},
{
value: '0 0 12 * * ?',
label: '每天中午12点触发'
},
{
value: '0 15 10 ? * *',
label: '每天上午10:15触发'
},
{
value: '0 15 10 * * ?',
label: '每天上午10:15触发 (跟上面的一样)'
},
{
value: '0 15 10 * * ? 2005',
label: '2005年的每天上午10:15触发'
},
{
value: '0 * 14 * * ?',
label: '在每天下午2点到下午2:59期间的每1分钟触发'
},
{
value: '0 0/5 14 * * ?',
label: '在每天下午2点到下午2:55期间的每5分钟触发'
},
{
value: '0 0/5 14,18 * * ?',
label: '在每天下午2点到2:55期间和下午6点到6:55期间的每5分钟触发'
},
{
value: '0 0-5 14 * * ?',
label: '在每天下午2点到下午2:05期间的每1分钟触发'
},
{
value: '0 10,44 14 ? 3 WED',
label: '每年三月的星期三的下午2:10和2:44触发'
},
{
value: '0 15 10 ? * MON-FRI',
label: '周一至周五的上午10:15触发'
},
{
value: '0 15 10 15 * ?',
label: '每月15日上午10:15触发'
},
{
value: '0 15 10 L * ?',
label: '每月最后一日的上午10:15触发'
}
])
</script>
<template>
<ElDescriptions :column="1" :border="true">
<ElDescriptionsItem v-for="(item, index) in cronExample" :key="index" :label="item.value">{{
item.label
}}</ElDescriptionsItem>
</ElDescriptions>
</template>
<style lang="scss" scoped></style>

View File

@ -0,0 +1,681 @@
<script setup lang="ts">
import { ref } from 'vue'
import {
ElTabs,
ElTabPane,
ElRadioGroup,
ElRadio,
ElInput,
ElCheckboxGroup,
ElCheckbox,
ElTable,
ElTableColumn,
ElButton,
ElMessage
} from 'element-plus'
import type { TableColumnCtx } from 'element-plus'
import cron from 'cron-validate'
import { Dialog } from '@/components/Dialog'
import RunDatetimeList from './RunDatetimeList.vue'
import CronExample from './CronExample.vue'
const activeName = ref('seconds')
const tableData = ref([
{
title: '表达式字段',
seconds: '*',
minutes: '*',
hour: '*',
day: '*',
month: '*',
week: '?',
year: ''
},
{
title: 'Cron 表达式',
seconds: '* * * * * ?',
minutes: '',
hour: '',
day: '',
month: '',
week: '',
year: ''
}
])
const seconds = ref('0')
const seconds1_1 = ref()
const seconds1_2 = ref()
const seconds2_1 = ref()
const seconds2_2 = ref()
const seconds3_1 = ref()
//
const secondsChange = () => {
const secondsValue = ref('*')
if (seconds.value === '1' && seconds1_1.value && seconds1_2.value) {
secondsValue.value = `${seconds1_1.value}-${seconds1_2.value}`
} else if (seconds.value === '2' && seconds2_1.value && seconds2_2.value) {
secondsValue.value = `${seconds2_1.value}/${seconds2_2.value}`
} else if (seconds.value === '3' && seconds3_1.value) {
secondsValue.value = seconds3_1.value.join(',')
}
tableData.value[0].seconds = secondsValue.value
fieldToCron(tableData.value[0])
}
const minutes = ref('0')
const minutes1_1 = ref()
const minutes1_2 = ref()
const minutes2_1 = ref()
const minutes2_2 = ref()
const minutes3_1 = ref()
//
const minutesChange = () => {
const minutesValue = ref('*')
if (minutes.value === '1' && minutes1_1.value && minutes1_2.value) {
minutesValue.value = `${minutes1_1.value}-${minutes1_2.value}`
} else if (minutes.value === '2' && minutes2_1.value && minutes2_2.value) {
minutesValue.value = `${minutes2_1.value}/${minutes2_2.value}`
} else if (minutes.value === '3' && minutes3_1.value) {
minutesValue.value = minutes3_1.value.join(',')
}
tableData.value[0].minutes = minutesValue.value
fieldToCron(tableData.value[0])
}
const hour = ref('0')
const hour1_1 = ref()
const hour1_2 = ref()
const hour2_1 = ref()
const hour2_2 = ref()
const hour3_1 = ref()
//
const hourChange = () => {
const hourValue = ref('*')
if (hour.value === '1' && hour1_1.value && hour1_2.value) {
hourValue.value = `${hour1_1.value}-${hour1_2.value}`
} else if (hour.value === '2' && hour2_1.value && hour2_2.value) {
hourValue.value = `${hour2_1.value}/${hour2_2.value}`
} else if (hour.value === '3' && hour3_1.value) {
hourValue.value = hour3_1.value.join(',')
}
tableData.value[0].hour = hourValue.value
fieldToCron(tableData.value[0])
}
const day = ref('0')
const day1_1 = ref()
const day1_2 = ref()
const day2_1 = ref()
const day2_2 = ref()
const day3_1 = ref()
const day4_1 = ref()
//
const dayChange = () => {
const dayValue = ref('*')
if (day.value === '1') {
dayValue.value = '?'
} else if (day.value === '2') {
dayValue.value = 'L'
} else if (day.value === '3' && day1_1.value && day1_2.value) {
dayValue.value = `${day1_1.value}-${day1_2.value}`
} else if (day.value === '4' && day2_1.value && day2_2.value) {
dayValue.value = `${day2_1.value}/${day2_2.value}`
} else if (day.value === '5' && day3_1.value) {
dayValue.value = `${day3_1.value}W`
} else if (day.value === '6' && day4_1.value) {
dayValue.value = day4_1.value.join(',')
}
tableData.value[0].day = dayValue.value
if (day.value !== '1') {
week.value = '1'
tableData.value[0].week = '?'
}
fieldToCron(tableData.value[0])
}
const month = ref('0')
const month1_1 = ref()
const month1_2 = ref()
const month2_1 = ref()
const month2_2 = ref()
const month3_1 = ref()
//
const monthChange = () => {
const monthValue = ref('*')
if (month.value === '1' && month1_1.value && month1_2.value) {
monthValue.value = `${month1_1.value}-${month1_2.value}`
} else if (month.value === '2' && month2_1.value && month2_2.value) {
monthValue.value = `${month2_1.value}/${month2_2.value}`
} else if (month.value === '3' && month3_1.value) {
monthValue.value = month3_1.value.join(',')
}
tableData.value[0].month = monthValue.value
fieldToCron(tableData.value[0])
}
const weekListData = ref(['星期一', '星期二', '星期三', '星期四', '星期五', '星期六', '星期日'])
const week = ref('1')
const week1_1 = ref()
const week1_2 = ref()
const week2_1 = ref()
const week2_2 = ref()
const week3_1 = ref()
const week4_1 = ref()
//
const weekChange = () => {
const weekValue = ref('*')
if (week.value === '1') {
weekValue.value = '?'
} else if (week.value === '2' && week1_1.value && week1_2.value) {
weekValue.value = `${week1_1.value}-${week1_2.value}`
} else if (week.value === '3' && week2_1.value && week2_2.value) {
weekValue.value = `${week2_1.value}#${week2_2.value}`
} else if (week.value === '4' && week3_1.value) {
weekValue.value = `${week3_1.value}L`
} else if (week.value === '5' && week4_1.value) {
weekValue.value = week4_1.value.join(',')
}
tableData.value[0].week = weekValue.value
if (week.value !== '1') {
day.value = '1'
tableData.value[0].day = '?'
}
fieldToCron(tableData.value[0])
}
const year = ref()
const year1_1 = ref()
const year1_2 = ref()
//
const yearChange = () => {
const yearValue = ref('')
if (year.value === '0') {
yearValue.value = '*'
} else if (year.value === '1') {
yearValue.value = ''
} else if (year.value === '2' && year1_1.value && year1_2.value) {
yearValue.value = `${year1_1.value}-${year1_2.value}`
}
tableData.value[0].year = yearValue.value
fieldToCron(tableData.value[0])
}
interface SpanMethodProps {
row: any
column: TableColumnCtx<any>
rowIndex: number
columnIndex: number
}
//
const arraySpanMethod = ({ row, column, rowIndex, columnIndex }: SpanMethodProps) => {
if (rowIndex === 1 && columnIndex >= 1 && columnIndex <= 6) {
return {
rowspan: 1,
colspan: 6
}
}
}
// Cron
const fieldToCron = (row) => {
const seconds = row.seconds !== '' ? row.seconds : '*'
const minutes = row.minutes !== '' ? row.minutes : '*'
const hour = row.hour !== '' ? row.hour : '*'
const day = row.day !== '' ? row.day : '*'
const month = row.month !== '' ? row.month : '*'
const week = row.week !== '' ? row.week : '?'
const year = row.year !== '' ? row.year : ''
const cron = ref(`${seconds} ${minutes} ${hour} ${day} ${month} ${week} ${year}`)
tableData.value[1].seconds = cron.value.trim()
}
// Cron
const cronToField = () => {
const cronExpression = tableData.value[1].seconds // Cron
const [seconds, minutes, hour, day, month, week, year] = cronExpression.split(' ')
//
tableData.value[0].seconds = seconds
tableData.value[0].minutes = minutes
tableData.value[0].hour = hour
tableData.value[0].day = day
tableData.value[0].month = month
tableData.value[0].week = week
tableData.value[0].year = year
}
// cron
// https://blog.csdn.net/a2524289/article/details/109177764
// https://cron.qqe2.com/
// cron https://github.com/bradymholt/cRonstrue
// cronjs ()https://github.com/datasert/cronjs
// cron https://github.com/Airfooox/cron-validate
const validate = () => {
const cronExpression = tableData.value[1].seconds // Cron
const fields = cronExpression.split(' ')
const [seconds, minutes, hour, day, month, week, year] = fields
let useYears = true
if (fields.length === 6) {
useYears = false
} else if (fields.length === 7) {
useYears = true
} else {
ElMessage.error('验证失败')
return
}
//
if (!seconds || !minutes || !hour || !day || !month || !week) {
ElMessage.error('验证失败')
return
}
//
if (day === '?' && week === '?') {
ElMessage.error('验证失败')
return
}
const result = cron(cronExpression, {
override: {
useSeconds: true,
useYears: useYears,
useBlankDay: true,
useLastDayOfMonth: true,
useLastDayOfWeek: true,
useNearestWeekday: true,
useNthWeekdayOfMonth: true
}
}).isValid()
if (result) {
ElMessage.success('验证成功')
} else {
ElMessage.error('验证失败')
}
}
const dialogVisible1 = ref(false)
const dialogVisible2 = ref(false)
//
const getRunDatetime = () => {
dialogVisible1.value = true
}
// Cron
const getCronExample = () => {
dialogVisible2.value = true
}
</script>
<template>
<div class="cron-expression-box">
<ElTabs v-model="activeName" type="border-card">
<ElTabPane label="秒" name="seconds">
<ElRadioGroup class="!block" v-model="seconds" @change="secondsChange">
<div class="list-item">
<ElRadio label="0">每秒</ElRadio>
</div>
<div class="list-item mt-2">
<ElRadio label="1">
<ElInput v-model="seconds1_1" @change="secondsChange" class="!w-[300px]">
<template #prepend> 周期从 </template>
</ElInput>
<ElInput v-model="seconds1_2" @change="secondsChange" class="!w-[300px]">
<template #prepend> - </template>
<template #append></template>
</ElInput>
</ElRadio>
</div>
<div class="list-item mt-4">
<ElRadio label="2">
<ElInput v-model="seconds2_1" @change="secondsChange" class="!w-[200px]">
<template #prepend> </template>
</ElInput>
<ElInput v-model="seconds2_2" @change="secondsChange" class="!w-[400px]">
<template #prepend> 秒开始 </template>
<template #append>秒执行一次</template>
</ElInput>
</ElRadio>
</div>
<div class="list-item mt-2">
<ElRadio class="!whitespace-normal !inline" label="3">
<span>指定</span>
<ElCheckboxGroup class="ml-6" v-model="seconds3_1" @change="secondsChange">
<ElCheckbox v-for="(num, index) in 60" :key="num" :label="index.toString()" />
</ElCheckboxGroup>
</ElRadio>
</div>
</ElRadioGroup>
</ElTabPane>
<ElTabPane label="分" name="minutes">
<ElRadioGroup class="!block" v-model="minutes" @change="minutesChange">
<div class="list-item">
<ElRadio label="0">每分</ElRadio>
</div>
<div class="list-item mt-2">
<ElRadio label="1">
<ElInput v-model="minutes1_1" @change="minutesChange" class="!w-[300px]">
<template #prepend> 周期从 </template>
</ElInput>
<ElInput v-model="minutes1_2" @change="minutesChange" class="!w-[300px]">
<template #prepend> - </template>
<template #append></template>
</ElInput>
</ElRadio>
</div>
<div class="list-item mt-4">
<ElRadio label="2">
<ElInput v-model="minutes2_1" @change="minutesChange" class="!w-[200px]">
<template #prepend> </template>
</ElInput>
<ElInput v-model="minutes2_2" @change="minutesChange" class="!w-[400px]">
<template #prepend> 分开始 </template>
<template #append>分执行一次</template>
</ElInput>
</ElRadio>
</div>
<div class="list-item mt-2">
<ElRadio class="!whitespace-normal !inline" label="3">
<span>指定</span>
<ElCheckboxGroup class="ml-6" v-model="minutes3_1" @change="minutesChange">
<ElCheckbox v-for="(num, index) in 60" :key="num" :label="index.toString()" />
</ElCheckboxGroup>
</ElRadio>
</div>
</ElRadioGroup>
</ElTabPane>
<ElTabPane label="时" name="hour">
<ElRadioGroup class="!block" v-model="hour" @change="hourChange">
<div class="list-item">
<ElRadio label="0">每小时</ElRadio>
</div>
<div class="list-item mt-2">
<ElRadio label="1">
<ElInput v-model="hour1_1" @change="hourChange" class="!w-[300px]">
<template #prepend> 周期从 </template>
</ElInput>
<ElInput v-model="hour1_2" @change="hourChange" class="!w-[300px]">
<template #prepend> - </template>
<template #append>小时</template>
</ElInput>
</ElRadio>
</div>
<div class="list-item mt-4">
<ElRadio label="2">
<ElInput v-model="hour2_1" @change="hourChange" class="!w-[200px]">
<template #prepend> </template>
</ElInput>
<ElInput v-model="hour2_2" @change="hourChange" class="!w-[400px]">
<template #prepend> 小时开始 </template>
<template #append>小时执行一次</template>
</ElInput>
</ElRadio>
</div>
<div class="list-item mt-2">
<ElRadio class="!whitespace-normal !inline" label="3">
<span>指定</span>
<ElCheckboxGroup class="ml-6" v-model="hour3_1" @change="hourChange">
<ElCheckbox v-for="(num, index) in 24" :key="num" :label="index.toString()" />
</ElCheckboxGroup>
</ElRadio>
</div>
</ElRadioGroup>
</ElTabPane>
<ElTabPane label="日" name="day">
<ElRadioGroup class="!block" v-model="day" @change="dayChange">
<div class="list-item">
<ElRadio label="0">每天</ElRadio>
</div>
<div class="list-item">
<ElRadio label="1">不指定</ElRadio>
</div>
<div class="list-item">
<ElRadio label="2">月最后一天</ElRadio>
</div>
<div class="list-item mt-2">
<ElRadio label="3">
<ElInput v-model="day1_1" @change="dayChange" class="!w-[300px]">
<template #prepend> 周期从 </template>
</ElInput>
<ElInput v-model="day1_2" @change="dayChange" class="!w-[300px]">
<template #prepend> - </template>
<template #append></template>
</ElInput>
</ElRadio>
</div>
<div class="list-item mt-4">
<ElRadio label="4">
<ElInput v-model="day2_1" @change="dayChange" class="!w-[200px]">
<template #prepend> </template>
</ElInput>
<ElInput v-model="day2_2" @change="dayChange" class="!w-[400px]">
<template #prepend> 日开始 </template>
<template #append>日执行一次</template>
</ElInput>
</ElRadio>
</div>
<div class="list-item mt-4">
<ElRadio label="5">
<ElInput v-model="day3_1" @change="dayChange" class="!w-[600px]">
<template #prepend> 每月 </template>
<template #append>号最近的那个工作日</template>
</ElInput>
</ElRadio>
</div>
<div class="list-item mt-2">
<ElRadio class="!whitespace-normal !inline" label="6">
<span>指定</span>
<ElCheckboxGroup class="ml-6" v-model="day4_1" @change="dayChange">
<ElCheckbox v-for="num in 31" :key="num" :label="num" />
</ElCheckboxGroup>
</ElRadio>
</div>
</ElRadioGroup>
</ElTabPane>
<ElTabPane label="月" name="month">
<ElRadioGroup class="!block" v-model="month" @change="monthChange">
<div class="list-item">
<ElRadio label="0">每月</ElRadio>
</div>
<div class="list-item mt-2">
<ElRadio label="1">
<ElInput v-model="month1_1" @change="monthChange" class="!w-[300px]">
<template #prepend> 周期从 </template>
</ElInput>
<ElInput v-model="month1_2" @change="monthChange" class="!w-[300px]">
<template #prepend> - </template>
<template #append></template>
</ElInput>
</ElRadio>
</div>
<div class="list-item mt-4">
<ElRadio label="2">
<ElInput v-model="month2_1" @change="monthChange" class="!w-[200px]">
<template #prepend> </template>
</ElInput>
<ElInput v-model="month2_2" @change="monthChange" class="!w-[400px]">
<template #prepend> 月开始 </template>
<template #append>月执行一次</template>
</ElInput>
</ElRadio>
</div>
<div class="list-item mt-2">
<ElRadio class="!whitespace-normal !inline" label="3">
<span>指定</span>
<ElCheckboxGroup class="ml-6" v-model="month3_1" @change="monthChange">
<ElCheckbox v-for="num in 12" :key="num" :label="num" />
</ElCheckboxGroup>
</ElRadio>
</div>
</ElRadioGroup>
</ElTabPane>
<ElTabPane label="周" name="week">
<ElRadioGroup class="!block" v-model="week" @change="weekChange">
<div class="list-item">
<ElRadio label="0">每周</ElRadio>
</div>
<div class="list-item">
<ElRadio label="1">不指定</ElRadio>
</div>
<div class="list-item mt-2">
<ElRadio label="2">
<ElInput v-model="week1_1" @change="weekChange" class="!w-[300px]">
<template #prepend> 周期从星期 </template>
</ElInput>
<ElInput v-model="week1_2" @change="weekChange" class="!w-[300px]">
<template #prepend> - 星期</template>
</ElInput>
</ElRadio>
</div>
<div class="list-item mt-4">
<ElRadio label="3">
<ElInput v-model="week2_1" @change="weekChange" class="!w-[300px]">
<template #prepend> </template>
</ElInput>
<ElInput v-model="week2_2" @change="weekChange" class="!w-[300px]">
<template #prepend> 星期的星期 </template>
</ElInput>
</ElRadio>
</div>
<div class="list-item mt-4">
<ElRadio label="4">
<ElInput v-model="week3_1" @change="weekChange" class="!w-[600px]">
<template #prepend> 本月最后一个星期 </template>
</ElInput>
</ElRadio>
</div>
<div class="list-item mt-2">
<ElRadio class="!whitespace-normal !inline" label="5">
<span>指定</span>
<ElCheckboxGroup class="ml-6" v-model="week4_1" @change="weekChange">
<ElCheckbox v-for="(item, index) in weekListData" :key="index" :label="index">{{
item
}}</ElCheckbox>
</ElCheckboxGroup>
</ElRadio>
</div>
</ElRadioGroup>
</ElTabPane>
<ElTabPane label="年" name="year">
<ElRadioGroup class="!block" v-model="year" @change="yearChange">
<div class="list-item">
<ElRadio label="0">每年</ElRadio>
</div>
<div class="list-item">
<ElRadio label="1">不指定</ElRadio>
</div>
<div class="list-item mt-2">
<ElRadio label="2">
<ElInput v-model="year1_1" @change="yearChange" class="!w-[300px]">
<template #prepend> 周期从 </template>
</ElInput>
<ElInput v-model="year1_2" @change="yearChange" class="!w-[300px]">
<template #prepend> - </template>
<template #append> </template>
</ElInput>
</ElRadio>
</div>
</ElRadioGroup>
</ElTabPane>
</ElTabs>
<div class="mt-5">
<span class="text-[17px]">生成表达式</span>
<ElTable
:data="tableData"
style="width: 100%"
:span-method="arraySpanMethod"
class="mt-2"
:border="true"
>
<ElTableColumn prop="title" label="" />
<ElTableColumn prop="seconds" label="秒" align="center">
<template #default="{ row, $index }">
<ElInput v-if="$index === 0" v-model="row.seconds" @change="fieldToCron(row)" />
<ElInput v-if="$index === 1" v-model="row.seconds" />
</template>
</ElTableColumn>
<ElTableColumn prop="minutes" label="分钟" align="center">
<template #default="{ row, $index }">
<ElInput v-if="$index === 0" v-model="row.minutes" @change="fieldToCron(row)" />
<ElButton v-if="$index === 1" type="primary" link @click="cronToField">
解析为字段
</ElButton>
</template>
</ElTableColumn>
<ElTableColumn prop="hour" label="小时" align="center">
<template #default="{ row, $index }">
<ElInput v-if="$index === 0" v-model="row.hour" @change="fieldToCron(row)" />
</template>
</ElTableColumn>
<ElTableColumn prop="day" label="日" align="center">
<template #default="{ row, $index }">
<ElInput v-if="$index === 0" v-model="row.day" @change="fieldToCron(row)" />
</template>
</ElTableColumn>
<ElTableColumn prop="month" label="月" align="center">
<template #default="{ row, $index }">
<ElInput v-if="$index === 0" v-model="row.month" @change="fieldToCron(row)" />
</template>
</ElTableColumn>
<ElTableColumn prop="week" label="星期" align="center">
<template #default="{ row, $index }">
<ElInput v-if="$index === 0" v-model="row.week" @change="fieldToCron(row)" />
</template>
</ElTableColumn>
<ElTableColumn prop="year" label="年" align="center">
<template #default="{ row, $index }">
<ElInput v-if="$index === 0" v-model="row.year" @change="fieldToCron(row)" />
</template>
</ElTableColumn>
</ElTable>
<div class="mt-3 text-center">
<ElButton type="primary" @click="getRunDatetime">获取最近十次运行时间</ElButton>
<ElButton type="primary" @click="validate">Cron 表达式验证</ElButton>
<ElButton type="primary" @click="getCronExample">Cron 表达式示例</ElButton>
</div>
</div>
</div>
<Dialog
v-model="dialogVisible1"
title="获取最近十次运行时间"
width="600px"
height="400px"
top="13vh"
>
<RunDatetimeList :expression="tableData[1].seconds" />
</Dialog>
<Dialog v-model="dialogVisible2" title="Cron 表达式示例" width="700px" height="620px" top="12vh">
<CronExample />
</Dialog>
</template>
<style lang="less">
.cron-expression-box {
.el-input {
.el-input-group__prepend {
border-radius: 0;
}
.el-input-group__append {
border-radius: 0;
}
.el-input__wrapper {
border-radius: 0;
}
}
}
</style>

View File

@ -0,0 +1,565 @@
<script setup>
import { ref } from 'vue'
import { propTypes } from '@/utils/propTypes'
const props = defineProps({
expression: propTypes.string.def('')
})
let dateArr = []
let isShow = false
let dayRule = ''
let dayRuleSup = ''
let resultList = []
/**
* 计算 Cron 表达式最近五次运行时间结果
* 感谢若依http://vue.ruoyi.vip/monitor/job
*/
const expressionChange = (expression) => {
// -
isShow = false
// [0123456]
let ruleArr = expression.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[0]
let mDate = dateArr[1]
let hDate = dateArr[2]
let DDate = dateArr[3]
let MDate = dateArr[4]
let YDate = dateArr[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 !== 'workDay' &&
dayRule !== 'lastWeek' &&
dayRule !== 'lastDay'
) {
resetDay()
continue goMonth
}
//
if (dayRule == '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 == 'workDay') {
// 230
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) {
// 61
if (dayRuleSup !== 1) {
DD--
} else {
DD += 2
}
}
} else if (dayRule == 'weekDay') {
//
//
let thisWeek = formatDate(new Date(YY + '-' + MM + '-' + DD + ' 00:00:00'), 'week')
// dayRuleSup
if (dayRuleSup.indexOf(thisWeek) < 0) {
//
if (Di == DDate.length - 1) {
resetDay()
if (Mi == MDate.length - 1) {
resetMonth()
continue goYear
}
continue goMonth
}
continue
}
} else if (dayRule == 'assWeek') {
//
// 1
let thisWeek = formatDate(new Date(YY + '-' + MM + '-' + DD + ' 00:00:00'), 'week')
if (dayRuleSup[1] >= thisWeek) {
DD = (dayRuleSup[0] - 1) * 7 + dayRuleSup[1] - thisWeek + 1
} else {
DD = dayRuleSup[0] * 7 + dayRuleSup[1] - thisWeek + 1
}
} else if (dayRule == 'lastWeek') {
//
// 230
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')
//
if (dayRuleSup < thisWeek) {
DD -= thisWeek - dayRuleSup
} else if (dayRuleSup > thisWeek) {
DD -= 7 - (dayRuleSup - thisWeek)
}
}
// 1005
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 = ['没有达到条件的结果!']
} else {
resultList = resultArr
if (resultArr.length !== 5) {
resultList.push('最近100年内只有上面' + resultArr.length + '条结果!')
}
}
// -
isShow = true
}
//
const 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
}
}
}
}
// ""
const getYearArr = (rule, year) => {
dateArr[5] = getOrderArr(year, year + 100)
if (rule !== undefined) {
if (rule.indexOf('-') >= 0) {
dateArr[5] = getCycleArr(rule, year + 100, false)
} else if (rule.indexOf('/') >= 0) {
dateArr[5] = getAverageArr(rule, year + 100)
} else if (rule !== '*') {
dateArr[5] = getAssignArr(rule)
}
}
}
// ""
const getMonthArr = (rule) => {
dateArr[4] = getOrderArr(1, 12)
if (rule.indexOf('-') >= 0) {
dateArr[4] = getCycleArr(rule, 12, false)
} else if (rule.indexOf('/') >= 0) {
dateArr[4] = getAverageArr(rule, 12)
} else if (rule !== '*') {
dateArr[4] = getAssignArr(rule)
}
}
// ""-
const getWeekArr = (rule) => {
//
if (dayRule == '' && dayRuleSup == '') {
if (rule.indexOf('-') >= 0) {
dayRule = 'weekDay'
dayRuleSup = getCycleArr(rule, 7, false)
} else if (rule.indexOf('#') >= 0) {
dayRule = 'assWeek'
let matchRule = rule.match(/[0-9]{1}/g)
dayRuleSup = [Number(matchRule[1]), Number(matchRule[0])]
dateArr[3] = [1]
if (dayRuleSup[1] == 7) {
dayRuleSup[1] = 0
}
} else if (rule.indexOf('L') >= 0) {
dayRule = 'lastWeek'
dayRuleSup = Number(rule.match(/[0-9]{1,2}/g)[0])
dateArr[3] = [31]
if (dayRuleSup == 7) {
dayRuleSup = 0
}
} else if (rule !== '*' && rule !== '?') {
dayRule = 'weekDay'
dayRuleSup = getAssignArr(rule)
}
}
}
// ""-
const getDayArr = (rule) => {
dateArr[3] = getOrderArr(1, 31)
dayRule = ''
dayRuleSup = ''
if (rule.indexOf('-') >= 0) {
dateArr[3] = getCycleArr(rule, 31, false)
dayRuleSup = 'null'
} else if (rule.indexOf('/') >= 0) {
dateArr[3] = getAverageArr(rule, 31)
dayRuleSup = 'null'
} else if (rule.indexOf('W') >= 0) {
dayRule = 'workDay'
dayRuleSup = Number(rule.match(/[0-9]{1,2}/g)[0])
dateArr[3] = [dayRuleSup]
} else if (rule.indexOf('L') >= 0) {
dayRule = 'lastDay'
dayRuleSup = 'null'
dateArr[3] = [31]
} else if (rule !== '*' && rule !== '?') {
dateArr[3] = getAssignArr(rule)
dayRuleSup = 'null'
} else if (rule == '*') {
dayRuleSup = 'null'
}
}
// ""
const getHourArr = (rule) => {
dateArr[2] = getOrderArr(0, 23)
if (rule.indexOf('-') >= 0) {
dateArr[2] = getCycleArr(rule, 24, true)
} else if (rule.indexOf('/') >= 0) {
dateArr[2] = getAverageArr(rule, 23)
} else if (rule !== '*') {
dateArr[2] = getAssignArr(rule)
}
}
// ""
const getMinArr = (rule) => {
dateArr[1] = getOrderArr(0, 59)
if (rule.indexOf('-') >= 0) {
dateArr[1] = getCycleArr(rule, 60, true)
} else if (rule.indexOf('/') >= 0) {
dateArr[1] = getAverageArr(rule, 59)
} else if (rule !== '*') {
dateArr[1] = getAssignArr(rule)
}
}
// ""
const getSecondArr = (rule) => {
dateArr[0] = getOrderArr(0, 59)
if (rule.indexOf('-') >= 0) {
dateArr[0] = getCycleArr(rule, 60, true)
} else if (rule.indexOf('/') >= 0) {
dateArr[0] = getAverageArr(rule, 59)
} else if (rule !== '*') {
dateArr[0] = getAssignArr(rule)
}
}
// min-max
const getOrderArr = (min, max) => {
let arr = []
for (let i = min; i <= max; i++) {
arr.push(i)
}
return arr
}
//
const 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
}
//
const 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
}
//
const getCycleArr = (rule, limit, status) => {
// status--01
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
}
// Array.sort
const compare = (value1, value2) => {
if (value2 - value1 > 0) {
return -1
} else {
return 1
}
}
// 2017-9-19 18:04:33
const 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') {
// quartz 1
return week + 1
}
}
//
const checkDate = (value) => {
let time = new Date(value)
let format = formatDate(time)
return value === format
}
//
expressionChange(props.expression)
</script>
<template>
<ul class="text-center">
<li v-for="(item, index) in resultList" :key="index" class="leading-9"> {{ item }}</li>
</ul>
</template>
<style lang="scss" scoped></style>

View File

@ -174,7 +174,7 @@ export const schema = reactive<FormSchema[]>([
style: {
width: '100%'
},
placeholder: 'cron 表达式,六位或七位,分别表示秒、分钟、小时、天、月、星期几、年'
placeholder: 'cron 表达式,六位或七位,分别表示秒、分钟、小时、天、月、星期几、年(可选)'
},
ifshow: (values) => values.exec_strategy === 'cron'
},

View File

@ -30,6 +30,7 @@ import { Dialog } from '@/components/Dialog'
import { useCache } from '@/hooks/web/useCache'
import { useRouter } from 'vue-router'
import Write from './components/Write.vue'
import CronExpression from './components/CronExpression.vue'
const { wsCache } = useCache()
const { t } = useI18n()
@ -67,6 +68,7 @@ watch(
}
)
const cronDialogVisible = ref(false)
const dialogVisible = ref(false)
const dialogTitle = ref('')
const loading = ref(false)
@ -98,7 +100,6 @@ getOptions()
//
const toRecord = (row: any) => {
if (row) {
console.log(row)
push(`/system/record/task?job_id=${row._id}`)
} else {
push(`/system/record/task`)
@ -205,7 +206,7 @@ const save = async () => {
// cron
const generateCronExpression = () => {
ElMessage.info('下一个版本更新')
cronDialogVisible.value = true
}
getList()
@ -225,7 +226,7 @@ getList()
<ElCol :span="1.5">
<ElButton type="primary" @click="addAction">添加定时任务</ElButton>
<ElButton type="primary" @click="toRecord(null)">调度日志</ElButton>
<ElButton type="primary" @click="generateCronExpression">生成 Cron 表达式</ElButton>
<ElButton type="primary" @click="generateCronExpression">快速生成 Cron 表达式</ElButton>
</ElCol>
</ElRow>
<RightToolbar
@ -285,5 +286,9 @@ getList()
<ElButton @click="dialogVisible = false">{{ t('dialogDemo.close') }}</ElButton>
</template>
</Dialog>
<Dialog v-model="cronDialogVisible" title="快速生成 Cron 表达式" width="920px" height="680px">
<CronExpression />
</Dialog>
</ContentWrap>
</template>

View File

@ -11,7 +11,7 @@ from fastapi.security import OAuth2PasswordBearer
"""
系统版本
"""
VERSION = "1.9.1"
VERSION = "1.9.2"
"""安全警告: 不要在生产中打开调试运行!"""
DEBUG = True

View File

@ -113,6 +113,8 @@ class ScheduledTask:
logger.info(content)
print(content)
getattr(self, operation)(**task)
else:
print("意外", message)
def start_mongo(self) -> None:
"""