From 6bb3688279ffe9ad348f4d0b34d446b090008ddd Mon Sep 17 00:00:00 2001
From: lzq <2495532633@qq.com>
Date: Wed, 24 Dec 2025 01:19:09 +0800
Subject: [PATCH] 1
---
src/common/app/app-user-store.ts | 4 +-
src/common/utils/strings.ts | 25 +-
src/pages/sys/task/TaskForm.vue | 31 +-
src/pages/sys/task/cron/Cron.vue | 31 +-
src/pages/sys/task/cron/cron-expression.ts | 241 ++++++++++
.../sys/task/cron/cron-panel/CronPanel.vue | 69 +--
.../sys/task/cron/cron-panel/DayPanel.vue | 99 +---
.../sys/task/cron/cron-panel/HourPanel.vue | 76 +--
.../sys/task/cron/cron-panel/MinutePanel.vue | 77 +--
.../sys/task/cron/cron-panel/MonthPanel.vue | 99 +---
.../sys/task/cron/cron-panel/SecondPanel.vue | 76 +--
.../sys/task/cron/cron-panel/WeekPanel.vue | 109 +----
.../sys/task/cron/cron-panel/YearPanel.vue | 57 +--
src/pages/sys/task/cron/cron-store.ts | 453 ++++++++++++++++++
14 files changed, 864 insertions(+), 583 deletions(-)
create mode 100644 src/pages/sys/task/cron/cron-expression.ts
create mode 100644 src/pages/sys/task/cron/cron-store.ts
diff --git a/src/common/app/app-user-store.ts b/src/common/app/app-user-store.ts
index 9bc2e6f..3cc1257 100644
--- a/src/common/app/app-user-store.ts
+++ b/src/common/app/app-user-store.ts
@@ -3,7 +3,7 @@ import {
computed,
ref,
} from 'vue'
-import { isEmpty } from '@/common/utils/strings.ts'
+import Strings from '@/common/utils/strings.ts'
import Evt from '@/common/utils/evt.ts'
export const useAppUserStore = defineStore('AppUser', () => {
@@ -15,7 +15,7 @@ export const useAppUserStore = defineStore('AppUser', () => {
const token = ref(null)
const tenantId = ref(null)
const tenantName = ref(null)
- const isAuthenticated = computed(() => !isEmpty(token.value))
+ const isAuthenticated = computed(() => !Strings.isEmpty(token.value))
function $reset() {
userId.value = null
diff --git a/src/common/utils/strings.ts b/src/common/utils/strings.ts
index f254d36..0da4d1b 100644
--- a/src/common/utils/strings.ts
+++ b/src/common/utils/strings.ts
@@ -3,7 +3,7 @@
*
* @param str 待测字符串
*/
-export function isBlank(str?: string | null) {
+function isBlank(str?: string | null) {
return str == null || str.trim().length === 0
}
@@ -12,7 +12,7 @@ export function isBlank(str?: string | null) {
*
* @param str 待测字符串
*/
-export function isEmpty(str?: string | null) {
+function isEmpty(str?: string | null) {
return str == null || str === ''
}
@@ -21,7 +21,7 @@ export function isEmpty(str?: string | null) {
*
* @param str 待处理字符串
*/
-export function capitalize(str?: string | null) {
+function capitalize(str?: string | null) {
if (isBlank(str)) return ''
return str!.charAt(0).toUpperCase() + str!.slice(1)
}
@@ -34,7 +34,7 @@ let splitCharPattern = /[^\x00-\x2f\x3a-\x40\x5b-\x60\x7b-\x7f]+/g
* @param str 待处理字符串
* @param joiner 连接符处理函数 (当前单词, 单词索引)=>单词处理后的结果
*/
-export function processWords(str: string | null | undefined, joiner: (word: string, index: number) => string) {
+function processWords(str: string | null | undefined, joiner: (word: string, index: number) => string) {
if (isBlank(str)) return ''
return (str!
@@ -49,7 +49,7 @@ export function processWords(str: string | null | undefined, joiner: (word: stri
*
* @param str 待处理字符串
*/
-export function camelCase(str?: string) {
+function camelCase(str?: string) {
return processWords(str,
(word, index) => index !== 0 ? capitalize(word) : word,
)
@@ -60,7 +60,7 @@ export function camelCase(str?: string) {
*
* @param str 待处理字符串
*/
-export function pascalCase(str?: string) {
+function pascalCase(str?: string) {
return capitalize(camelCase(str))
}
@@ -69,7 +69,7 @@ export function pascalCase(str?: string) {
*
* @param str 待处理字符串
*/
-export function snakeCase(str?: string) {
+function snakeCase(str?: string) {
return processWords(str,
(word, index) => index !== 0 ? '_' + word : word,
)
@@ -80,12 +80,20 @@ export function snakeCase(str?: string) {
*
* @param str 待处理字符串
*/
-export function kebabCase(str?: string) {
+function kebabCase(str?: string) {
return processWords(str,
(word, index) => index !== 0 ? '-' + word : word,
)
}
+function isNumStr(str: any) {
+ if (typeof str !== 'string') {
+ return false
+ }
+ const reg = /^[+-]?\d+(\.\d+)?$/
+ return reg.test(str)
+}
+
export default {
isBlank,
isEmpty,
@@ -95,4 +103,5 @@ export default {
kebabCase,
pascalCase,
processWords,
+ isNumStr,
}
diff --git a/src/pages/sys/task/TaskForm.vue b/src/pages/sys/task/TaskForm.vue
index 5ba4460..64225de 100644
--- a/src/pages/sys/task/TaskForm.vue
+++ b/src/pages/sys/task/TaskForm.vue
@@ -17,22 +17,17 @@
-
+
-
+
-
+
+
+
+ 周期(秒)
+
@@ -76,7 +71,19 @@ function dialogCloseHandler() {
taskFormData.value = {}
}
+function scheduleTypeChange(val: string) {
+ if (val === 'Manually') {
+ taskFormData.value.scheduleConf = ''
+ } else if (val === 'Fixed') {
+ taskFormData.value.scheduleConf = '1'
+ } else if (val === 'Cron') {
+ taskFormData.value.scheduleConf = '* * * * * ? ?'
+ }
+}
+
function submitHandler() {
+ console.log(taskFormData.value.scheduleConf)
+ return
if (status.value === 'view') return
submiting.value = true
if (taskFormData.value.id != null) {
diff --git a/src/pages/sys/task/cron/Cron.vue b/src/pages/sys/task/cron/Cron.vue
index 17dfc43..1c95547 100644
--- a/src/pages/sys/task/cron/Cron.vue
+++ b/src/pages/sys/task/cron/Cron.vue
@@ -1,21 +1,43 @@
- emits('update:modelValue',val)">
+
@@ -23,10 +45,7 @@ function openPanel() {
- emits('update:modelValue',val)"/>
-
- {{ modelValue }}
-
+
diff --git a/src/pages/sys/task/cron/cron-expression.ts b/src/pages/sys/task/cron/cron-expression.ts
new file mode 100644
index 0000000..3d210bc
--- /dev/null
+++ b/src/pages/sys/task/cron/cron-expression.ts
@@ -0,0 +1,241 @@
+/**
+ * 提供类 Unix 风格 Cron 表达式的解析器和求值器。Cron 表达式能够指定复杂的时间组合,
+ * 例如“每周一至周五的上午 8:00”或“每月最后一个周五的凌晨 1:30”。
+ *
+ *
+ * Cron 表达式由 6 个必填字段和 1 个可选字段组成,字段之间通过空格分隔。各字段的详细说明如下:
+ *
+ *
+ *
+ * | 字段名称 |
+ * |
+ * 允许值 |
+ * |
+ * 允许的特殊字符 |
+ *
+ *
+ * 秒(Seconds) |
+ *
+ * | 0-59 |
+ *
+ * | , - * / |
+ *
+ *
+ * 分(Minutes) |
+ *
+ * | 0-59 |
+ *
+ * | , - * / |
+ *
+ *
+ * 时(Hours) |
+ *
+ * | 0-23 |
+ *
+ * | , - * / |
+ *
+ *
+ * 日(Day-of-month) |
+ *
+ * | 1-31 |
+ *
+ * | , - * ? / L W |
+ *
+ *
+ * 月(Month) |
+ *
+ * | 0-11 或 JAN-DEC(一月至十二月) |
+ *
+ * | , - * / |
+ *
+ *
+ * 周(Day-of-Week) |
+ *
+ * | 1-7 或 SUN-SAT(周日至周六) |
+ *
+ * | , - * ? / L # |
+ *
+ *
+ * 年(Year,可选) |
+ *
+ * | 空值、1970-2199 |
+ *
+ * | , - * / |
+ *
+ *
+ *
+ * '*' 字符用于指定“所有可能的值”。例如,在“分钟”字段中使用 "*" 表示“每分钟”。
+ *
+ * '?' 字符仅允许在“日(Day-of-month)”和“周(Day-of-Week)”字段中使用,
+ * 用于表示“不指定具体值”。当你需要在这两个字段中的一个指定条件,而另一个无需指定时,该字符非常有用。
+ *
+ * '-' 字符用于指定时间范围。例如,在“小时”字段中使用 "10-12" 表示“上午 10 点、11 点和中午 12 点”。
+ *
+ * ',' 字符用于指定多个额外的取值。例如,在“周”字段中使用 "MON,WED,FRI" 表示“周一、周三和周五”。
+ *
+ * '/' 字符用于指定递增步长。例如,在“秒”字段中使用 "0/15" 表示“第 0 秒、15 秒、30 秒和 45 秒”;
+ * 而 "5/15" 在“秒”字段中表示“第 5 秒、20 秒、35 秒和 50 秒”。在 '/' 前指定 '*' 等价于以 0 作为起始值。
+ * 本质上,表达式的每个字段都对应一组可启用或禁用的数字集合:秒和分的范围是 0-59,小时是 0-23,
+ * 日是 1-31,月是 0-11(对应 JAN 至 DEC)。'/' 字符的作用是在指定集合中启用“每隔 n 个”的取值。
+ * 需要注意一个细节:在“月”字段中使用 "7/6" 仅会启用“7 月”,并不表示“每 6 个月”。
+ *
+ * 'L' 字符仅允许在“日(Day-of-month)”和“周(Day-of-Week)”字段中使用,是“last(最后一个)”的缩写,
+ * 但在两个字段中的含义有所不同。例如,在“日”字段中使用 "L" 表示“当月的最后一天”——1 月对应 31 日,
+ * 非闰年的 2 月对应 28 日。若在“周”字段中单独使用 "L",仅表示“7”或“SAT(周六)”;
+ * 若在其他值后使用,则表示“当月最后一个指定星期几”,例如 "6L" 表示“当月最后一个周五”。
+ * 你也可以指定相对于月末的偏移量,例如 "L-3" 表示“当月的倒数第三天”。
+ * 注意:使用 'L' 选项时,请勿同时指定多个取值(列表)或范围,否则会得到混乱或不符合预期的结果。
+ *
+ * 'W' 字符仅允许在“日(Day-of-month)”字段中使用,用于指定“距离给定日期最近的工作日(周一至周五)”。
+ * 例如,若在“日”字段中指定 "15W",含义是“当月 15 日附近最近的工作日”:如果 15 日是周六,
+ * 则触发时间为 14 日(周五);如果 15 日是周日,则触发时间为 16 日(周一);如果 15 日是周二,
+ * 则触发时间为 15 日(周二)本身。若指定 "1W" 且 1 日是周六,触发时间会是 3 日(周一),
+ * 因为该字符不会“跨越”月份的边界。'W' 字符仅能在“日”字段指定单个日期时使用,
+ * 不能用于日期范围或多个日期列表。
+ *
+ * 'L' 和 'W' 字符可在“日(Day-of-month)”字段中组合使用,形成 "LW",含义是“当月的最后一个工作日”。
+ *
+ * '#' 字符仅允许在“周(Day-of-Week)”字段中使用,用于指定“当月第 n 个星期几”。
+ * 例如,在“周”字段中使用 "6#3" 表示“当月第三个周五”(6 对应周五,"#3" 对应第三个)。
+ * 其他示例:"2#1" 表示“当月第一个周一”,"4#5" 表示“当月第五个周三”。
+ * 注意:若指定了 "#5" 但当月不存在对应的第 5 个星期几,则该月不会触发任务。
+ * 此外,使用 '#' 字符时,“周”字段中只能包含一个表达式(例如 "3#1,6#3" 是无效的,因为它包含两个表达式)。
+ *
+ *
+ *
+ * 所有合法字符以及月份、星期几的名称均不区分大小写。
+ *
+ *
+ * 注意事项:
+ *
+ * - 同时指定“日(Day-of-month)”和“周(Day-of-Week)”字段的功能尚未完善,
+ * 你需要在其中一个字段中使用 '?' 字符。
+ * - 支持“溢出范围”(即范围左侧的数字大于右侧):例如,你可以使用 22-2 表示“晚上 10 点至次日凌晨 2 点”,
+ * 或使用 NOV-FEB 表示“11 月至次年 2 月”。但需要特别注意,过度使用溢出范围会产生无意义的时间范围,
+ * 且 CronExpression 不会对这些无效范围进行特殊判断和处理(例如表达式 "0 0 14-6 ? * FRI-MON" 就是一个无意义的溢出范围示例)。
+ *
+ *
+ *
+ * @author 沙拉达·詹布拉、詹姆斯·豪斯
+ * @author 贡献者:麦兹·亨德森
+ * @author 重构者:艾伦·克雷文(将功能从 CronTrigger 重构为 CronExpression)
+ *
+ * 源自 Quartz 框架 v2.3.1 版本
+ */
+
+export class CronExpression {
+ // 公共常量:最大年份(当前年份+100,与 Java 保持一致)
+ public static readonly MAX_YEAR: number = new Date().getFullYear() + 100
+
+ // 私有静态常量:字段索引(秒、分、时、日、月、周、年)
+ private static readonly SECOND: number = 0
+ private static readonly MINUTE: number = 1
+ private static readonly HOUR: number = 2
+ private static readonly DAY_OF_MONTH: number = 3
+ private static readonly MONTH: number = 4
+ private static readonly DAY_OF_WEEK: number = 5
+ private static readonly YEAR: number = 6
+
+ // 私有静态常量:特殊标记值(* 对应 99,? 对应 98)
+ private static readonly ALL_SPEC_INT: number = 99 // 对应 '*'
+ private static readonly NO_SPEC_INT: number = 98 // 对应 '?'
+ private static readonly ALL_SPEC: number = CronExpression.ALL_SPEC_INT
+ private static readonly NO_SPEC: number = CronExpression.NO_SPEC_INT
+
+ // 私有静态映射:月份英文缩写 -> 数字(0-11,与 Java 一致)
+ private static readonly monthMap: Record = {JAN: 0, FEB: 1, MAR: 2, APR: 3, MAY: 4, JUN: 5, JUL: 6, AUG: 7, SEP: 8, OCT: 9, NOV: 10, DEC: 11}
+ // 私有静态映射:星期英文缩写 -> 数字(1-7,与 Java 一致)
+ private static readonly dayMap: Record = {SUN: 1, MON: 2, TUE: 3, WED: 4, THU: 5, FRI: 6, SAT: 7}
+
+ // 实例属性
+ private readonly cronExpression: string
+ private seconds?: number[] // TS 中无原生TreeSet,后续可实现或用SortedSet替代
+ private minutes?: number[]
+ private hours?: number[]
+ private daysOfMonth?: number[]
+ private months?: number[]
+ private daysOfWeek?: number[]
+ private years?: number[]
+
+ private lastdayOfWeek: boolean = false
+ private nthdayOfWeek: number = 0
+ private lastdayOfMonth: boolean = false
+ private nearestWeekday: boolean = false
+ private lastdayOffset: number = 0
+ private expressionParsed: boolean = false
+ private timeZone?: TimeZone // 对应 Java TimeZone,TS 中可用 string 或原生 TimeZone 替代
+
+
+ /**
+ * 根据指定的参数构造一个新的 CronExpression 实例。
+ *
+ * @param cronExpression 字符串形式的 Cron 表达式,该表达式将由新建的对象进行承载
+ * @throws Error 如果传入的字符串表达式无法被解析为一个有效的 CronExpression 实例,则抛出该异常
+ */
+ constructor(cronExpression: string) {
+ if (cronExpression == null) {
+ throw new Error('表达式不能为空')
+ }
+
+ this.cronExpression = cronExpression.toUpperCase(Locale.US)
+
+ buildExpression(this.cronExpression)
+ }
+
+ /**
+ * 复制现有实例,创建新的 Cron 表达式
+ */
+ constructor(expression: CronExpression) {
+ this.cronExpression = expression.getCronExpression()
+ buildExpression(cronExpression)
+ if (expression.getTimeZone() != null) {
+ setTimeZone(expression.getTimeZone().clone())
+ }
+ }
+
+ /**
+ * 检查指定的 Cron 表达式是否能够被解析为一个有效的 Cron 表达式
+ * @param cronExpression 待校验的 Cron 表达式字符串
+ * @returns 布尔值:如果传入的表达式是有效的 Cron 表达式,返回 true;否则返回 false
+ */
+ public static isValidExpression(cronExpression: string): boolean {
+ try {
+ new CronExpression(cronExpression)
+ } catch (e: Error) {
+ return false
+ }
+ return true
+ }
+
+ /**
+ * 判断指定的日期是否满足当前 Cron 表达式的规则。
+ * 注意:该方法会忽略日期的毫秒部分,因此在同一秒钟内不同毫秒数的两个日期,在此方法中会返回相同的结果。
+ * @param date 待校验的日期对象
+ * @returns 布尔值:如果指定日期满足 Cron 表达式规则,返回 true;否则返回 false
+ */
+ public isSatisfiedBy(date: Date): boolean {
+ const testDateCal = new Date(date.getTime())
+ testDateCal.setMilliseconds(0)
+
+ const originalDate = new Date(testDateCal.getTime())
+
+ testDateCal.setSeconds(date.getSeconds() - 1)
+
+ const timeAfter = this.getTimeAfter(testDateCal)
+
+ return timeAfter !== null && timeAfter.getTime() === originalDate.getTime()
+ }
+}
+
+
+/**
+ * 时间区类型(简化版,对应 Java TimeZone)
+ * 可根据实际需求扩展,此处保持与原代码结构一致
+ */
+type TimeZone = {
+ id: string;
+ offset: number;
+} | null;
diff --git a/src/pages/sys/task/cron/cron-panel/CronPanel.vue b/src/pages/sys/task/cron/cron-panel/CronPanel.vue
index 5ea5b39..3ff9fb3 100644
--- a/src/pages/sys/task/cron/cron-panel/CronPanel.vue
+++ b/src/pages/sys/task/cron/cron-panel/CronPanel.vue
@@ -6,63 +6,42 @@ import DayPanel from '@/pages/sys/task/cron/cron-panel/DayPanel.vue'
import MonthPanel from '@/pages/sys/task/cron/cron-panel/MonthPanel.vue'
import WeekPanel from '@/pages/sys/task/cron/cron-panel/WeekPanel.vue'
import YearPanel from '@/pages/sys/task/cron/cron-panel/YearPanel.vue'
+import { useCronStore } from '@/pages/sys/task/cron/cron-store.ts'
-const props = defineProps<{
- modelValue: string
-}>()
-
-const emits = defineEmits([ 'update:modelValue' ])
-
-const cronKernels = reactive({
- second: '*',
- minute: '*',
- hour: '*',
- day: '*',
- month: '*',
- week: '?',
- year: '?',
-})
-
-watch(cronKernels, newVal => {
- console.log(`${newVal.second} ${newVal.minute} ${newVal.hour} ${newVal.day} ${newVal.month} ${newVal.week} ${newVal.year}`)
- emits('update:modelValue', `${newVal.second} ${newVal.minute} ${newVal.hour} ${newVal.day} ${newVal.month} ${newVal.week} ${newVal.year}`)
-})
-onMounted(() => {
- let sections = props.modelValue.split(' ')
- cronKernels.second = sections[0]
- cronKernels.minute = sections[1]
- cronKernels.hour = sections[2]
- cronKernels.day = sections[3]
- cronKernels.month = sections[4]
- cronKernels.week = sections[5]
- cronKernels.year = sections[6]
-})
-
+const {
+ secondVal,
+ minuteVal,
+ hourVal,
+ dayVal,
+ monthVal,
+ weekVal,
+ yearVal,
+} = toRefs(useCronStore())
-
-
+
+
-
-
+
+
-
-
+
+
-
-
+
+
-
-
+
+
-
-
+
+
-
-
+
+
diff --git a/src/pages/sys/task/cron/cron-panel/DayPanel.vue b/src/pages/sys/task/cron/cron-panel/DayPanel.vue
index 7079307..47d0d38 100644
--- a/src/pages/sys/task/cron/cron-panel/DayPanel.vue
+++ b/src/pages/sys/task/cron/cron-panel/DayPanel.vue
@@ -1,112 +1,36 @@
-
+
不指定
每天
每月最后一天
每月
-
+
号最近的那个工作日
周期,从
-
- 到
-
+
+ 号到
+
+ 号
周期,从第
-
+
天开始,每
-
+
天执行一次
指定
-
+
@@ -119,7 +43,6 @@ onMounted(() => {
align-items start
gap 10px
width 100%;
- //overflow auto
flex-wrap nowrap
:deep(.el-input-number) {
diff --git a/src/pages/sys/task/cron/cron-panel/HourPanel.vue b/src/pages/sys/task/cron/cron-panel/HourPanel.vue
index 0dbd51b..bef27e7 100644
--- a/src/pages/sys/task/cron/cron-panel/HourPanel.vue
+++ b/src/pages/sys/task/cron/cron-panel/HourPanel.vue
@@ -1,88 +1,29 @@
-
+
每小时
周期,从
-
+
时到
-
+
时
周期,从第
-
+
时开始,每
-
+
小时执行一次
指定
-
+
@@ -95,7 +36,6 @@ onMounted(() => {
align-items start
gap 10px
width 100%;
- //overflow auto
flex-wrap nowrap
:deep(.el-input-number) {
diff --git a/src/pages/sys/task/cron/cron-panel/MinutePanel.vue b/src/pages/sys/task/cron/cron-panel/MinutePanel.vue
index 251bb65..829bd00 100644
--- a/src/pages/sys/task/cron/cron-panel/MinutePanel.vue
+++ b/src/pages/sys/task/cron/cron-panel/MinutePanel.vue
@@ -1,88 +1,29 @@
-
+
每分钟
周期,从
-
+
到
-
+
周期,从第
-
+
分钟开始,每
-
+
分钟执行一次
指定
-
+
@@ -95,8 +36,6 @@ onMounted(() => {
align-items start
gap 10px
width 100%;
- //width 600px;
- //overflow auto
flex-wrap nowrap
:deep(.el-input-number) {
diff --git a/src/pages/sys/task/cron/cron-panel/MonthPanel.vue b/src/pages/sys/task/cron/cron-panel/MonthPanel.vue
index 223924d..559c955 100644
--- a/src/pages/sys/task/cron/cron-panel/MonthPanel.vue
+++ b/src/pages/sys/task/cron/cron-panel/MonthPanel.vue
@@ -1,94 +1,39 @@
-
+
不指定
每月
周期,从
-
+
+
+
+
到
-
+
+
+
+
- 周期,从第
-
- 月开始,每
-
- 月执行一次
+ 周期,从
+
+
+
+ 开始,每
+
+ 个月执行一次
指定
-
-
+
+
@@ -100,8 +45,6 @@ onMounted(() => {
align-items start
gap 10px
width 100%;
- //width 600px;
- //overflow auto
flex-wrap nowrap
:deep(.el-input-number) {
diff --git a/src/pages/sys/task/cron/cron-panel/SecondPanel.vue b/src/pages/sys/task/cron/cron-panel/SecondPanel.vue
index c222948..b46688c 100644
--- a/src/pages/sys/task/cron/cron-panel/SecondPanel.vue
+++ b/src/pages/sys/task/cron/cron-panel/SecondPanel.vue
@@ -1,88 +1,29 @@
-
+
每秒
周期,从
-
+
到
-
+
周期,从第
-
+
秒开始,每
-
+
秒执行一次
指定
-
+
@@ -95,7 +36,6 @@ onMounted(() => {
align-items start
gap 10px
width 100%;
- //overflow auto
flex-wrap nowrap
:deep(.el-input-number) {
diff --git a/src/pages/sys/task/cron/cron-panel/WeekPanel.vue b/src/pages/sys/task/cron/cron-panel/WeekPanel.vue
index 71afec2..bbe1ec8 100644
--- a/src/pages/sys/task/cron/cron-panel/WeekPanel.vue
+++ b/src/pages/sys/task/cron/cron-panel/WeekPanel.vue
@@ -1,107 +1,42 @@
-
+
不指定
每周
- 每月最后一个星期
-
+ 每月最后一个
+
+
+
周期,从
-
+
+
+
到
-
+
+
+
每月第
-
- 周的星期
-
+
+ 个星期的星期
+
+
+
指定
-
-
+
+
@@ -113,8 +48,6 @@ onMounted(() => {
align-items start
gap 10px
width 100%;
- //width 600px;
- //overflow auto
flex-wrap nowrap
:deep(.el-input-number) {
diff --git a/src/pages/sys/task/cron/cron-panel/YearPanel.vue b/src/pages/sys/task/cron/cron-panel/YearPanel.vue
index 86e3c0f..0128c63 100644
--- a/src/pages/sys/task/cron/cron-panel/YearPanel.vue
+++ b/src/pages/sys/task/cron/cron-panel/YearPanel.vue
@@ -1,63 +1,20 @@
-
+
不指定
每年
周期,从
-
+
到
-
+
@@ -68,8 +25,6 @@ onMounted(() => {
align-items start
gap 10px
width 100%;
- //width 600px;
- //overflow auto
flex-wrap nowrap
:deep(.el-input-number) {
diff --git a/src/pages/sys/task/cron/cron-store.ts b/src/pages/sys/task/cron/cron-store.ts
new file mode 100644
index 0000000..fdecb63
--- /dev/null
+++ b/src/pages/sys/task/cron/cron-store.ts
@@ -0,0 +1,453 @@
+import { defineStore } from 'pinia'
+import Strings from '@/common/utils/strings.ts'
+
+export interface SMHState {
+ mode: 'every' | 'period1' | 'period2' | 'specify'
+ period1: {
+ start: number
+ end: number
+ }
+ period2: {
+ start: number
+ end: number
+ }
+ specify: number[]
+}
+
+export interface DState {
+ mode: 'none' | 'every' | 'last' | 'near' | 'period1' | 'period2' | 'specify'
+ period1: {
+ start: number
+ end: number
+ }
+ period2: {
+ start: number
+ end: number
+ }
+ specify: number[]
+ near: number
+}
+
+export interface MState {
+ mode: 'none' | 'every' | 'period1' | 'period2' | 'specify'
+ period1: {
+ start: number
+ end: number
+ }
+ period2: {
+ start: number
+ end: number
+ }
+ specify: number[]
+}
+
+export interface WState {
+ mode: 'none' | 'every' | 'last' | 'period1' | 'period2' | 'specify'
+ period1: {
+ start: number
+ end: number
+ }
+ period2: {
+ start: number
+ end: number
+ }
+ specify: number[]
+ last: number
+}
+
+export interface YState {
+ mode: 'none' | 'every' | 'period1'
+ period1: {
+ start: number
+ end: number
+ }
+}
+
+export const useCronStore = defineStore('Cron', () => {
+ const secondState = reactive({
+ mode: 'every',
+ period1: {
+ start: 1,
+ end: 2,
+ },
+ period2: {
+ start: 1,
+ end: 1,
+ },
+ specify: [],
+ })
+ const secondVal = computed(() => {
+ let data: string = '*'
+ if (secondState.mode === 'period1') {
+ data = `${secondState.period1.start}-${secondState.period1.end}`
+ } else if (secondState.mode === 'period2') {
+ data = `${secondState.period2.start}/${secondState.period2.end}`
+ } else if (secondState.mode === 'specify') {
+ data = secondState.specify.sort((a, b) => a - b).join(',')
+ }
+ return data
+ })
+ const setSecondVal = (val: string) => {
+ if (val.includes('-')) {
+ const s = val.split('-')
+ secondState.period1.start = +s[0]
+ secondState.period1.end = +s[1]
+ secondState.mode = 'period1'
+ } else if (val.includes('/')) {
+ const s = val.split('/')
+ secondState.period2.start = +s[0]
+ secondState.period2.end = +s[1]
+ secondState.mode = 'period2'
+ } else if (val.includes(',')) {
+ secondState.specify = val.split(',').map(it => +it)
+ secondState.mode = 'specify'
+ } else if (Strings.isNumStr(val)) {
+ secondState.specify = [ +val ]
+ secondState.mode = 'specify'
+ } else {
+ secondState.mode = 'every'
+ }
+ }
+
+ const minuteState = reactive({
+ mode: 'every',
+ period1: {
+ start: 1,
+ end: 2,
+ },
+ period2: {
+ start: 1,
+ end: 1,
+ },
+ specify: [],
+ })
+ const minuteVal = computed(() => {
+ let data: string = '*'
+ if (minuteState.mode === 'period1') {
+ data = `${minuteState.period1.start}-${minuteState.period1.end}`
+ } else if (minuteState.mode === 'period2') {
+ data = `${minuteState.period2.start}/${minuteState.period2.end}`
+ } else if (minuteState.mode === 'specify') {
+ data = minuteState.specify.sort((a, b) => a - b).join(',')
+ }
+ return data
+ })
+ const setMinuteVal = (val: string) => {
+ if (val.includes('-')) {
+ const s = val.split('-')
+ minuteState.period1.start = +s[0]
+ minuteState.period1.end = +s[1]
+ minuteState.mode = 'period1'
+ } else if (val.includes('/')) {
+ const s = val.split('/')
+ minuteState.period2.start = +s[0]
+ minuteState.period2.end = +s[1]
+ minuteState.mode = 'period2'
+ } else if (val.includes(',')) {
+ minuteState.specify = val.split(',').map(it => +it)
+ minuteState.mode = 'specify'
+ } else if (Strings.isNumStr(val)) {
+ minuteState.specify = [ +val ]
+ minuteState.mode = 'specify'
+ } else {
+ minuteState.mode = 'every'
+ }
+ }
+
+ const hourState = reactive({
+ mode: 'every',
+ period1: {
+ start: 1,
+ end: 2,
+ },
+ period2: {
+ start: 1,
+ end: 1,
+ },
+ specify: [],
+ })
+ const hourVal = computed(() => {
+ let data: string = '*'
+ if (hourState.mode === 'period1') {
+ data = `${hourState.period1.start}-${hourState.period1.end}`
+ } else if (hourState.mode === 'period2') {
+ data = `${hourState.period2.start}/${hourState.period2.end}`
+ } else if (hourState.mode === 'specify') {
+ data = hourState.specify.sort((a, b) => a - b).join(',')
+ }
+ return data
+ })
+ const setHourVal = (val: string) => {
+ if (val.includes('-')) {
+ const s = val.split('-')
+ hourState.period1.start = +s[0]
+ hourState.period1.end = +s[1]
+ hourState.mode = 'period1'
+ } else if (val.includes('/')) {
+ const s = val.split('/')
+ hourState.period2.start = +s[0]
+ hourState.period2.end = +s[1]
+ hourState.mode = 'period2'
+ } else if (val.includes(',')) {
+ hourState.specify = val.split(',').map(it => +it)
+ hourState.mode = 'specify'
+ } else if (Strings.isNumStr(val)) {
+ hourState.specify = [ +val ]
+ hourState.mode = 'specify'
+ } else {
+ hourState.mode = 'every'
+ }
+ }
+
+ const dayState = reactive({
+ mode: 'every',
+ period1: {
+ start: 1,
+ end: 2,
+ },
+ period2: {
+ start: 1,
+ end: 1,
+ },
+ specify: [],
+ near: 1,
+ })
+ const dayVal = computed(() => {
+ let data: string = '*'
+ if (dayState.mode === 'none') {
+ data = '?'
+ } else if (dayState.mode === 'last') {
+ data = 'L'
+ } else if (dayState.mode === 'near') {
+ data = `${dayState.near}W`
+ } else if (dayState.mode === 'period1') {
+ data = `${dayState.period1.start}-${dayState.period1.end}`
+ } else if (dayState.mode === 'period2') {
+ data = `${dayState.period2.start}/${dayState.period2.end}`
+ } else if (dayState.mode === 'specify') {
+ data = dayState.specify.sort((a, b) => a - b).join(',')
+ }
+ return data
+ })
+ const setDayVal = (val: string) => {
+ if (val === '?') {
+ dayState.mode = 'none'
+ } else if (val === 'L') {
+ dayState.mode = 'last'
+ } else if (val.includes('W')) {
+ const s = val.split('W')
+ dayState.near = +s[0]
+ dayState.mode = 'near'
+ } else if (val.includes('-')) {
+ const s = val.split('-')
+ dayState.period1.start = +s[0]
+ dayState.period1.end = +s[1]
+ dayState.mode = 'period1'
+ } else if (val.includes('/')) {
+ const s = val.split('/')
+ dayState.period2.start = +s[0]
+ dayState.period2.end = +s[1]
+ dayState.mode = 'period2'
+ } else if (val.includes(',')) {
+ dayState.specify = val.split(',').map(it => +it)
+ dayState.mode = 'specify'
+ } else if (Strings.isNumStr(val)) {
+ dayState.specify = [ +val ]
+ dayState.mode = 'specify'
+ } else {
+ dayState.mode = 'every'
+ }
+ }
+
+ const monthState = reactive({
+ mode: 'every',
+ period1: {
+ start: 1,
+ end: 2,
+ },
+ period2: {
+ start: 1,
+ end: 1,
+ },
+ specify: [],
+ })
+ const monthVal = computed(() => {
+ let data: string = '*'
+ if (monthState.mode === 'none') {
+ data = '?'
+ } else if (monthState.mode === 'period1') {
+ data = `${monthState.period1.start}-${monthState.period1.end}`
+ } else if (monthState.mode === 'period2') {
+ data = `${monthState.period2.start}/${monthState.period2.end}`
+ } else if (monthState.mode === 'specify') {
+ data = monthState.specify.sort((a, b) => a - b).join(',')
+ }
+ return data
+ })
+ const setMonthVal = (val: string) => {
+ if (val === '?') {
+ monthState.mode = 'none'
+ } else if (val.includes('-')) {
+ const s = val.split('-')
+ monthState.period1.start = +s[0]
+ monthState.period1.end = +s[1]
+ monthState.mode = 'period1'
+ } else if (val.includes('/')) {
+ const s = val.split('/')
+ monthState.period2.start = +s[0]
+ monthState.period2.end = +s[1]
+ monthState.mode = 'period2'
+ } else if (val.includes(',')) {
+ monthState.specify = val.split(',').map(it => +it)
+ monthState.mode = 'specify'
+ } else if (Strings.isNumStr(val)) {
+ monthState.specify = [ +val ]
+ monthState.mode = 'specify'
+ } else {
+ monthState.mode = 'every'
+ }
+ }
+
+ const weekState = reactive({
+ mode: 'none',
+ period1: {
+ start: 1,
+ end: 2,
+ },
+ period2: {
+ start: 1,
+ end: 1,
+ },
+ specify: [],
+ last: 1,
+ })
+ const weekVal = computed(() => {
+ let data: string = '*'
+ if (weekState.mode === 'none') {
+ data = '?'
+ } else if (weekState.mode === 'last') {
+ data = `${(weekState.last + 1) % 7}L`
+ } else if (weekState.mode === 'period1') {
+ data = `${(weekState.period1.start + 1) % 7}-${(weekState.period1.end + 1) % 7}`
+ } else if (weekState.mode === 'period2') {
+ data = `${(weekState.period2.start + 1) % 7}#${(weekState.period2.end + 1) % 7}`
+ } else if (weekState.mode === 'specify') {
+ data = weekState.specify.map(it => (it + 1) % 7).sort((a, b) => a - b).join(',')
+ }
+ return data
+ })
+ const setWeekVal = (val: string) => {
+ if (val === '?') {
+ weekState.mode = 'none'
+ } else if (val.includes('L')) {
+ const s = val.split('L')
+ let s0 = +s[0]
+ weekState.last = s0 === 0 ? 6 : (s0 - 1)
+ weekState.mode = 'last'
+ } else if (val.includes('-')) {
+ const s = val.split('-')
+ let s0 = +s[0]
+ let s1 = +s[1]
+ secondState.period1.start = s0 === 0 ? 6 : (s0 - 1)
+ secondState.period1.end = s1 === 0 ? 6 : (s1 - 1)
+ secondState.mode = 'period1'
+ } else if (val.includes('#')) {
+ const s = val.split('#')
+ let s0 = +s[0]
+ let s1 = +s[1]
+ weekState.period2.start = s0 === 0 ? 6 : (s0 - 1)
+ weekState.period2.end = s1 === 0 ? 6 : (s1 - 1)
+ weekState.mode = 'period2'
+ } else if (val.includes(',')) {
+ weekState.specify = val.split(',').map(it => {
+ let num = +it
+ return num === 0 ? 6 : (num - 1)
+ })
+ weekState.mode = 'specify'
+ } else if (Strings.isNumStr(val)) {
+ let num = +val
+ weekState.specify = [ num === 0 ? 6 : (num - 1) ]
+ weekState.mode = 'specify'
+ } else {
+ weekState.mode = 'every'
+ }
+ }
+
+ let year = new Date().getFullYear()
+ const yearState = reactive({
+ mode: 'none',
+ period1: {
+ start: year,
+ end: year + 10,
+ },
+
+ })
+ const yearVal = computed(() => {
+ let data: string = '*'
+ if (yearState.mode === 'none') {
+ data = '?'
+ } else if (yearState.mode === 'period1') {
+ data = `${yearState.period1.start}-${yearState.period1.end}`
+ }
+ return data
+ })
+ const setYearVal = (val: string) => {
+ if (val === '?') {
+ yearState.mode = 'none'
+ } else if (val.includes('-')) {
+ const s = val.split('-')
+ yearState.period1.start = +s[0]
+ yearState.period1.end = +s[1]
+ yearState.mode = 'period1'
+ } else {
+ yearState.mode = 'every'
+ }
+ }
+ const cronVal = computed({
+ get() {
+ console.log('获取值', `${secondVal.value} ${minuteVal.value} ${hourVal.value} ${dayVal.value} ${monthVal.value} ${weekVal.value} ${yearVal.value}`)
+ return `${secondVal.value} ${minuteVal.value} ${hourVal.value} ${dayVal.value} ${monthVal.value} ${weekVal.value} ${yearVal.value}`
+ },
+ set(val: string = '* * * * * ? ?') {
+
+ let sections = val.split(' ')
+ if (sections.length != 7) {
+ sections = '* * * * * ? ?'.split(' ')
+ }
+ console.log('设置值', val, sections)
+ setSecondVal(sections[0])
+ setMinuteVal(sections[1])
+ setHourVal(sections[2])
+ setDayVal(sections[3])
+ setMonthVal(sections[4])
+ setWeekVal(sections[5])
+ setYearVal(sections[6])
+ },
+ })
+
+ return {
+ secondState,
+ secondVal,
+ setSecondVal,
+ minuteState,
+ minuteVal,
+ setMinuteVal,
+ hourState,
+ hourVal,
+ setHourVal,
+ dayState,
+ dayVal,
+ setDayVal,
+ monthState,
+ monthVal,
+ setMonthVal,
+ weekState,
+ weekVal,
+ setWeekVal,
+ yearState,
+ yearVal,
+ setYearVal,
+ cronVal,
+ // setCronVal,
+ }
+})