Skip to content

Commit

Permalink
crontab 表达式生成 #237
Browse files Browse the repository at this point in the history
  • Loading branch information
baiy committed May 3, 2023
1 parent e872dff commit 8c94665
Show file tree
Hide file tree
Showing 11 changed files with 333 additions and 30 deletions.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file not shown.
Binary file not shown.
85 changes: 55 additions & 30 deletions packages/ctool-core/src/tools/crontab/Crontab.vue
Original file line number Diff line number Diff line change
@@ -1,6 +1,13 @@
<template>
<Align direction="vertical">
<Input v-model="action.current.input" class="ctool-crontab-input" :label="$t('crontab_expression')"/>
<Input v-model="action.current.input" class="ctool-crontab-input" :label="$t('crontab_expression')">
<template #suffix>
<Align>
<HelpTip link="https://www.npmjs.com/package/cron-parser"/>
<Button size="small" type="primary" :text="$t(`crontab_generate`)" @click="isGenerate = !isGenerate"/>
</Align>
</template>
</Input>
<HeightResize v-slot="{height}" :append="['.ctool-crontab-input']" v-row="'10-14'" :reduce="5">
<Textarea :model-value="output" :height="height" :placeholder="$t('crontab_execute_time')"/>
<Tabs
Expand All @@ -12,21 +19,22 @@
]"
:height="height"
padding="0"
v-if="!isGenerate"
>
<Table
:columns="[{key:'exp',title:$t(`crontab_example`),width:150},{key:'text',title:$t(`crontab_description`)}]"
:lists="example.map((item)=>{return {exp:item,text:conversion(item)}})"
/>
<Align horizontal="center" vertical="center">
<img v-if="locale === 'zh_CN'" src="@/statics/tools/crontab/crontab_cn.png" style="max-width: 80%;max-height: 80%">
<img v-else src="@/statics/tools/crontab/crontab_en.png" style="max-width: 80%;max-height: 80%">
</Align>
<Link href="https://www.npmjs.com/package/cron-parser" style="height: 100%;display: flex;justify-content: center;align-items: center">
<img src="@/statics/tools/crontab/crontab.png" style="max-width: 95%;max-height: 95%" alt="crontab">
</Link>
<Table
:height="height-40"
:columns="[{key:'name',title:$t('crontab_symbol'),width:100},{key:'text',title:$t(`crontab_description`)}]"
:lists="symbol"
/>
</Tabs>
<Generate v-else :height="height" v-model="action.current.input"/>
</HeightResize>
</Align>
</template>
Expand All @@ -37,29 +45,51 @@ import cronstrue from 'cronstrue/i18n';
import parser from 'cron-parser';
import {watch} from 'vue';
import dayjs from 'dayjs';
import Generate from './generate/Generate.vue';
import HeightResize from "@/components/HeightResize.vue";
import Align from "@/components/Align.vue";
import Link from "@/components/ui/Link.vue";
import Input from "@/components/ui/Input.vue";
import Tabs from "@/components/ui/Tabs.vue";
import Textarea from "@/components/ui/Textarea.vue";
import Table from "@/components/ui/Table.vue";
import Button from "@/components/ui/Button.vue";
const locale = $computed(() => $t('main_locale'))
const action = useAction(await initialize({
input: "2,3 */5 * * 2-5",
}, {
paste: (str) => [4, 5].includes(str.trim().match("/ /g")?.length || 0)
paste: (str) => [5, 6].includes(str.trim().split(" ").length),
}))
let isGenerate = $ref(false)
let output = $ref("")
const conversion = (exp: string) => {
return cronstrue.toString(exp, {locale, use24HourTimeFormat: true})
}
watch(() => action.current.input, (input) => {
watch(() => {
return {
input: action.current.input
}
}, ({input}) => {
output = ""
input = input.trim()
if (input === "") {
output = ""
return;
}
let list: string[] = [];
try {
list.push(conversion(input));
list.push(`\n${$t('crontab_execute_time_list')}`);
const msg = conversion(input);
if (input.includes("L")) {
list.push($t('crontab_l_prompt'), "")
}
if (input.split(" ").length > 5) {
list.push($t('crontab_second_prompt'), "")
}
list.push(msg);
list.push("", $t('crontab_execute_time_list'));
let interval = parser.parseExpression(input);
for (let i = 1; i <= 10; i++) {
list.push($t('crontab_no', [i, dayjs(interval.next().toString()).format("YYYY-MM-DD HH:mm:ss")]))
Expand All @@ -72,26 +102,21 @@ watch(() => action.current.input, (input) => {
}, {immediate: true})
const example = [
"*/1 * * * *",
"* * * * *",
"*/5 * * * *",
"0 * * * *",
"0 */1 * * *",
"0 7 * * *",
"10 7 * * *",
"0 0 * * *",
"0 0 * * 0",
"0 0 1 * *",
"0 0 1 1 *",
"5 * * * *",
"30 5 * * *",
"30 7 8 * *",
"30 5 8 6 *",
"30 6 * * 0",
"30 3 10,20 * *",
"25 8-11 * * *",
"*/15 * * * *",
"30 6 */10 * *"
"*/5 */5 * * *",
"* 10/5 * * *",
"1 1-10 * * *",
"0 1,2 * * *",
"1 2 3 4,6,10 *",
"0 * L * *",
"0 1 * * 0",
"0 1 * * 7",
"0 1 * * 1",
"0 1 * * 2-5",
"0 1 * * 2,5",
"0 1 * * 1L",
"0 1 * * 1L,2L",
"*/5 1-10,17,22 L 1-2,3/2 1L,2L",
]
const symbol = $computed(() => {
return [
Expand Down
60 changes: 60 additions & 0 deletions packages/ctool-core/src/tools/crontab/generate/Generate.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
<template>
<Tabs
v-model="current"
padding="20px 10px 0 20px"
:lists="[
{name:`second`,label:$t('crontab_generate_second')},
{name:`minute`,label:$t('crontab_generate_minute')},
{name:`hour`,label:$t('crontab_generate_hour')},
{name:`day`,label:$t('crontab_generate_day')},
{name:`month`,label:$t('crontab_generate_month')},
{name:`week`,label:$t('crontab_generate_week')},
]"
>
<Item type="second" v-model="items.second"/>
<Item type="minute" v-model="items.minute"/>
<Item type="hour" v-model="items.hour"/>
<Item type="day" v-model="items.day"/>
<Item type="month" v-model="items.month"/>
<Item type="week" v-model="items.week"/>
</Tabs>
</template>

<script lang="ts" setup>
import './style.css'
import Tabs from "@/components/ui/Tabs.vue";
import Item from "./Item.vue";
import {watch} from 'vue';
import {ItemType} from "./type";
const props = defineProps({
modelValue: {
type: String,
default: ""
}
})
const emit = defineEmits<{ (e: 'update:modelValue', value: string): void }>()
let current = $ref<ItemType>('minute')
let items = $ref({
second: "",
minute: "*",
hour: "*",
day: "*",
month: "*",
week: "*"
})
watch(() => {
return {items}
}, ({items}) => {
emit('update:modelValue', [
...(items.second !== "" ? [items.second] : []),
items.minute,
items.hour,
items.day,
items.month,
items.week
].join(" "))
}, {immediate: true, deep: true})
</script>
137 changes: 137 additions & 0 deletions packages/ctool-core/src/tools/crontab/generate/Item.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,137 @@
<template>
<Align direction="vertical" gap="large">
<Option v-model="optionType" v-if="type==='second'" name="ignore" @select="(value)=>select(value)"/>
<Option v-model="optionType" name="any" @select="(value)=>select(value)">
<code>*</code>
</Option>
<Option v-model="optionType" name="scope" @select="(value)=>select(value)">
<Align>
<InputNumber size="small" v-model="optionValue.scope.start" :min="scope[0]" :max="scope[1]" :step="1"/>
<code>-</code>
<InputNumber size="small" v-model="optionValue.scope.end" :min="scope[0]" :max="scope[1]" :step="1"/>
</Align>
</Option>
<Option v-model="optionType" name="interval" @select="(value)=>select(value)">
<Align>
<Input size="small" v-model="optionValue.interval.start"/>
<code>/</code>
<InputNumber size="small" v-model="optionValue.interval.step" :min="interval[0]" :max="interval[1]" :step="1"/>
</Align>
</Option>
<Option v-model="optionType" name="list" @select="(value)=>select(value)">
<Checkbox :options="list" v-model="optionValue.list"/>
</Option>
</Align>
</template>

<script lang="ts" setup>
import {watch, PropType} from 'vue';
import {range} from 'lodash';
import Align from "@/components/Align.vue";
import Option from "./Option.vue";
import Checkbox from "@/components/ui/Checkbox.vue";
import Input from "@/components/ui/Input.vue";
import InputNumber from "@/components/ui/InputNumber.vue";
import {ItemType, OptionType, OptionValue} from "./type";
const props = defineProps({
modelValue: {
type: String,
default: ""
},
type: {
type: String as PropType<ItemType>,
default: "minute"
}
})
const emit = defineEmits<{ (e: 'update:modelValue', value: string): void }>()
let optionType = $ref<OptionType>(props.type === "second" ? "ignore" : "any")
let optionValue = $ref<OptionValue>({
any: "*",
scope: {
start: 2,
end: 5,
},
interval: {
start: "*",
step: 5
},
list: []
})
const select = (value: any) => {
optionType = value
}
const scope = $computed<[number, number]>(() => {
if (props.type === "hour") {
return [0, 23]
}
if (props.type === "day") {
return [1, 31]
}
if (props.type === "month") {
return [1, 12]
}
if (props.type === "week") {
return [0, 7]
}
return [0, 59]
})
const interval = $computed<[number, number]>(() => {
if (props.type === "hour") {
return [2, 23]
}
if (props.type === "day") {
return [2, 31]
}
if (props.type === "month") {
return [2, 12]
}
if (props.type === "week") {
return [2, 7]
}
return [0, 59]
})
const list = $computed(() => {
if (props.type === "hour") {
return range(0, 24)
}
if (props.type === "day") {
return [...range(1, 32), 'L']
}
if (props.type === "month") {
return range(1, 13)
}
if (props.type === "week") {
return [...range(0, 8), ...range(1, 8).map(i => `${i}L`)]
}
return range(0, 60)
})
watch(() => {
return {
type: optionType,
value: optionValue
}
}, ({type, value}) => {
if (type === "ignore") {
return emit("update:modelValue", "")
}
if (type === "scope") {
return emit("update:modelValue", `${value.scope.start}-${value.scope.end}`)
}
if (type === "interval") {
let start = value.interval.start.trim()
return emit("update:modelValue", `${["0", "*", ""].includes(start) ? "*" : value.interval.start}/${value.interval.step}`)
}
if (type === "list" && value.list.length > 0) {
return emit("update:modelValue", value.list.join(","))
}
emit("update:modelValue", "*")
}, {deep: true})
</script>
24 changes: 24 additions & 0 deletions packages/ctool-core/src/tools/crontab/generate/Option.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
<template>
<div class="ctool-crontab-generate-layout" @click="emit('select',name)">
<Icon hover v-if="modelValue === name" :size="14" color="var(--primary)" name="checked"/>
<Icon hover v-else :size="14" color="var(--ctool-border-color)" name="unchecked"/>
<span>{{ $t(`crontab_generate_${name}`) }}</span>
<div><slot></slot></div>
</div>
</template>

<script lang="ts" setup>
const props = defineProps({
modelValue: {
type: String,
default: ""
},
name: {
type: String,
default: ""
}
})
const emit = defineEmits<{ (e: 'select', value: string): void }>()
</script>
15 changes: 15 additions & 0 deletions packages/ctool-core/src/tools/crontab/generate/style.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
.ctool-crontab-generate-layout {
display: grid;
grid-template-columns: auto auto minmax(0, 1fr);
gap: 20px 10px;
align-items: center;
}
.ctool-crontab-generate-layout .ctool-input input {
text-align: center;
}
.ctool-crontab-generate-layout .ctool-input {
width: 60px;
}
.ctool-crontab-generate-layout .ctool-checkbox .ctool-bool{
height: 24px;
}
14 changes: 14 additions & 0 deletions packages/ctool-core/src/tools/crontab/generate/type.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
export type ItemType = "second" | "minute" | "hour" | "day" | "month" | "week"
export type OptionType = "any" | "scope" | "interval" | "list" | "ignore"
export type OptionValue = {
any: "*",
scope: {
start: number,
end: number,
},
interval: {
start: string,
step: number
},
list: (string | number)[]
}
Loading

0 comments on commit 8c94665

Please sign in to comment.