diff --git a/package.json b/package.json
index 83456b48..2269bac6 100644
--- a/package.json
+++ b/package.json
@@ -28,6 +28,7 @@
"animate.css": "^4.1.1",
"axios": "^0.27.2",
"codemirror": "^6.0.1",
+ "cron-parser": "^4.9.0",
"crypto-js": "^4.2.0",
"dayjs": "^1.11.4",
"echarts": "^5.4.2",
diff --git a/src/components/GenCron/CronForm/component/day-form.vue b/src/components/GenCron/CronForm/component/day-form.vue
new file mode 100644
index 00000000..32c4237c
--- /dev/null
+++ b/src/components/GenCron/CronForm/component/day-form.vue
@@ -0,0 +1,88 @@
+
+
+
+
+
+
+
+
循环
+
从
+
+
日开始, 间隔
+
+
日
+
+
+
+
指定
+
+
+
+
+
+ {{ i }}
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/components/GenCron/CronForm/component/hour-form.vue b/src/components/GenCron/CronForm/component/hour-form.vue
new file mode 100644
index 00000000..9c361622
--- /dev/null
+++ b/src/components/GenCron/CronForm/component/hour-form.vue
@@ -0,0 +1,60 @@
+
+
+
+
+
+
+
循环
+
从
+
+
时开始, 间隔
+
+
时
+
+
+
指定
+
+
+
+
+
+ {{ i }}
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/components/GenCron/CronForm/component/minute-form.vue b/src/components/GenCron/CronForm/component/minute-form.vue
new file mode 100644
index 00000000..9d18ca9a
--- /dev/null
+++ b/src/components/GenCron/CronForm/component/minute-form.vue
@@ -0,0 +1,60 @@
+
+
+
+
+
+
+
循环
+
从
+
+
分开始, 间隔
+
+
分
+
+
+
指定
+
+
+
+
+
+ {{ i }}
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/components/GenCron/CronForm/component/month-form.vue b/src/components/GenCron/CronForm/component/month-form.vue
new file mode 100644
index 00000000..6a74bc6b
--- /dev/null
+++ b/src/components/GenCron/CronForm/component/month-form.vue
@@ -0,0 +1,60 @@
+
+
+
+
+
+
+
循环
+
从
+
+
月开始, 间隔
+
+
月
+
+
+
指定
+
+
+
+
+
+ {{ i }}
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/components/GenCron/CronForm/component/second-form.vue b/src/components/GenCron/CronForm/component/second-form.vue
new file mode 100644
index 00000000..ce363e3d
--- /dev/null
+++ b/src/components/GenCron/CronForm/component/second-form.vue
@@ -0,0 +1,60 @@
+
+
+
+
+
+
+
循环
+
从
+
+
秒开始, 间隔
+
+
秒
+
+
+
指定
+
+
+
+
+
+ {{ i }}
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/components/GenCron/CronForm/component/use-mixin.ts b/src/components/GenCron/CronForm/component/use-mixin.ts
new file mode 100644
index 00000000..2fb84578
--- /dev/null
+++ b/src/components/GenCron/CronForm/component/use-mixin.ts
@@ -0,0 +1,220 @@
+import { computed, reactive, ref, unref, watch } from 'vue'
+
+// 类型定义
+export enum TypeEnum {
+ unset = 'UNSET',
+ every = 'EVERY',
+ range = 'RANGE',
+ loop = 'LOOP',
+ work = 'WORK',
+ last = 'LAST',
+ specify = 'SPECIFY'
+}
+
+// 周定义
+export const WEEK_MAP: any = {
+ 1: '周日',
+ 2: '周一',
+ 3: '周二',
+ 4: '周三',
+ 5: '周四',
+ 6: '周五',
+ 7: '周六'
+}
+
+// use 公共 props
+export function useFormProps(options: any) {
+ const defaultValue = options?.defaultValue ?? '?'
+ return {
+ modelValue: {
+ type: String,
+ default: defaultValue
+ },
+ disabled: {
+ type: Boolean,
+ default: false
+ },
+ ...options?.props
+ }
+}
+
+// use 公共 emits
+export function useFromEmits() {
+ return ['change', 'update:modelValue']
+}
+
+// use 公共 setup
+export function useFormSetup(props: any, context: any, options: any) {
+ const { emit } = context
+ const defaultValue = ref(options?.defaultValue ?? '?')
+ // 类型
+ const type = ref(options.defaultType ?? TypeEnum.every)
+ const valueList = ref([])
+ // 对于不同的类型, 所定义的值也有所不同
+ const valueRange = reactive(options.valueRange)
+ const valueLoop = reactive(options.valueLoop)
+ const valueWork = ref(options.valueWork)
+ const maxValue = ref(options.maxValue)
+ const minValue = ref(options.minValue)
+
+ // 根据不同的类型计算出的 value
+ const computeValue = computed(() => {
+ const valueArray: any[] = []
+ switch (type.value) {
+ case TypeEnum.unset:
+ valueArray.push('?')
+ break
+ case TypeEnum.every:
+ valueArray.push('*')
+ break
+ case TypeEnum.range:
+ valueArray.push(`${valueRange.start}-${valueRange.end}`)
+ break
+ case TypeEnum.loop:
+ valueArray.push(`${valueLoop.start}/${valueLoop.interval}`)
+ break
+ case TypeEnum.work:
+ valueArray.push(`${valueWork.value}W`)
+ break
+ case TypeEnum.last:
+ valueArray.push('L')
+ break
+ case TypeEnum.specify:
+ if (valueList.value.length === 0) {
+ valueList.value.push(minValue.value)
+ }
+ valueArray.push(valueList.value.join(','))
+ break
+ default:
+ valueArray.push(defaultValue.value)
+ break
+ }
+ return valueArray.length > 0 ? valueArray.join('') : defaultValue.value
+ })
+
+ // 指定值范围区间, 介于最小值和最大值之间
+ const specifyRange = computed(() => {
+ const range: number[] = []
+ if (maxValue.value != null) {
+ for (let i = minValue.value; i <= maxValue.value; i++) {
+ range.push(i)
+ }
+ }
+ return range
+ })
+
+ // 更新值
+ const updateValue = (value: any) => {
+ emit('change', value)
+ emit('update:modelValue', value)
+ }
+
+ // 解析值
+ const parseValue = (value: any) => {
+ if (value === computeValue.value) {
+ return
+ }
+ try {
+ if (!value || value === defaultValue.value) {
+ type.value = TypeEnum.every
+ } else if (value.includes('?')) {
+ type.value = TypeEnum.unset
+ } else if (value.includes('-')) {
+ type.value = TypeEnum.range
+ const values = value.split('-')
+ if (values.length >= 2) {
+ valueRange.start = Number.parseInt(values[0])
+ valueRange.end = Number.parseInt(values[1])
+ }
+ } else if (value.includes('/')) {
+ type.value = TypeEnum.loop
+ const values = value.split('/')
+ if (values.length >= 2) {
+ valueLoop.start = value[0] === '*' ? 0 : Number.parseInt(values[0])
+ valueLoop.interval = Number.parseInt(values[1])
+ }
+ } else if (value.includes('W')) {
+ type.value = TypeEnum.work
+ const values = value.split('W')
+ if (!values[0] && !Number.isNaN(values[0])) {
+ valueWork.value = Number.parseInt(values[0])
+ }
+ } else if (value.includes('L')) {
+ type.value = TypeEnum.last
+ } else if (value.includes(',') || !Number.isNaN(value)) {
+ type.value = TypeEnum.specify
+ valueList.value = value.split(',').map((item: any) => Number.parseInt(item))
+ } else {
+ type.value = TypeEnum.every
+ }
+ } catch (e) {
+ type.value = TypeEnum.every
+ }
+ }
+
+ // 更新值
+ watch(() => props.modelValue, (val) => {
+ if (val !== computeValue.value) {
+ parseValue(val)
+ }
+ }, { immediate: true })
+
+ // 更新值
+ watch(computeValue, (v) => updateValue(v))
+
+ // 单选框属性
+ const beforeRadioAttrs = computed(() => ({
+ class: ['choice'],
+ disabled: props.disabled || unref(options.disabled),
+ size: 'small'
+ }))
+
+ // 输入框属性
+ const inputNumberAttrs = computed(() => ({
+ max: maxValue.value,
+ min: minValue.value,
+ precision: 0,
+ size: 'small',
+ hideButton: true,
+ class: 'w60'
+ }))
+
+ // 区间属性
+ const typeRangeAttrs = computed(() => ({
+ disabled: type.value !== TypeEnum.range || props.disabled || unref(options.disabled),
+ ...inputNumberAttrs.value
+ }))
+
+ // 间隔属性
+ const typeLoopAttrs = computed(() => ({
+ disabled: type.value !== TypeEnum.loop || props.disabled || unref(options.disabled),
+ ...inputNumberAttrs.value
+ }))
+
+ // 指定属性
+ const typeSpecifyAttrs = computed(() => ({
+ disabled: type.value !== TypeEnum.specify || props.disabled || unref(options.disabled),
+ class: ['list-check-item'],
+ size: 'small'
+ }))
+
+ return {
+ type,
+ TypeEnum,
+ defaultValue,
+ valueRange,
+ valueLoop,
+ valueList,
+ valueWork,
+ maxValue,
+ minValue,
+ computeValue,
+ specifyRange,
+ updateValue,
+ beforeRadioAttrs,
+ inputNumberAttrs,
+ typeRangeAttrs,
+ typeLoopAttrs,
+ typeSpecifyAttrs
+ }
+}
diff --git a/src/components/GenCron/CronForm/component/week-form.vue b/src/components/GenCron/CronForm/component/week-form.vue
new file mode 100644
index 00000000..98404010
--- /dev/null
+++ b/src/components/GenCron/CronForm/component/week-form.vue
@@ -0,0 +1,108 @@
+
+
+
+
+
+
区间
+
从
+
+
+
+
至
+
+
+
+
+
+
循环
+
从
+
+
+
+
开始, 间隔
+
+
天
+
+
+
指定
+
+
+
+
+ {{ opt.label }}
+
+
+
+
+
+
+
+
+
+
diff --git a/src/components/GenCron/CronForm/component/year-form.vue b/src/components/GenCron/CronForm/component/year-form.vue
new file mode 100644
index 00000000..4782ae6a
--- /dev/null
+++ b/src/components/GenCron/CronForm/component/year-form.vue
@@ -0,0 +1,46 @@
+
+
+
+
+
+
+
循环
+
从
+
+
年开始, 间隔
+
+
年
+
+
+
+
+
+
diff --git a/src/components/GenCron/CronForm/index.vue b/src/components/GenCron/CronForm/index.vue
new file mode 100644
index 00000000..2b27032d
--- /dev/null
+++ b/src/components/GenCron/CronForm/index.vue
@@ -0,0 +1,378 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 秒
+
+
+
+
+
+
+
+ 分
+
+
+
+
+
+
+
+ 时
+
+
+
+
+
+
+
+ 日
+
+
+
+
+
+
+
+ 月
+
+
+
+
+
+
+
+ 周
+
+
+
+
+
+
+
+ 年
+
+
+
+
+
+
+
+ 表达式
+
+
+
+
+
+
+
+ 近五次执行时间 (不解析年)
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/components/GenCron/CronForm/type.ts b/src/components/GenCron/CronForm/type.ts
new file mode 100644
index 00000000..4ee28fc1
--- /dev/null
+++ b/src/components/GenCron/CronForm/type.ts
@@ -0,0 +1,9 @@
+// cron 参数类型
+export interface CronPropType {
+ modelValue: string
+ disabled: boolean
+ hideSecond: boolean
+ hideYear: boolean
+ placeholder: string
+ callback: (expression: string, timestamp: number, validated: boolean) => void
+}
diff --git a/src/components/GenCron/CronModel/index.vue b/src/components/GenCron/CronModel/index.vue
new file mode 100644
index 00000000..145b10a9
--- /dev/null
+++ b/src/components/GenCron/CronModel/index.vue
@@ -0,0 +1,67 @@
+
+
+
+
+
+
+ 关闭
+
+ 确定
+
+
+
+
+
+
+
+
+
+
diff --git a/src/components/GiCell/GiCellTag.vue b/src/components/GiCell/GiCellTag.vue
index dae70dc5..1b10c34d 100644
--- a/src/components/GiCell/GiCellTag.vue
+++ b/src/components/GiCell/GiCellTag.vue
@@ -11,18 +11,15 @@
diff --git a/src/views/schedule/job/index.vue b/src/views/schedule/job/index.vue
index e0ccc53d..becf5052 100644
--- a/src/views/schedule/job/index.vue
+++ b/src/views/schedule/job/index.vue
@@ -36,7 +36,14 @@
:
{{ record.triggerInterval }} 秒
- {{ record.triggerInterval }}
+
+
+
+
+
+ {{ record.triggerInterval }}
+
+
@@ -77,7 +84,7 @@ import { type JobQuery, type JobResp, deleteJob, listGroup, listJob, triggerJob,
import type { TableInstanceColumns } from '@/components/GiTable/type'
import { useTable } from '@/hooks'
import { useDict } from '@/hooks/app'
-import { isMobile } from '@/utils'
+import { isMobile, parseCron } from '@/utils'
import has from '@/utils/has'
defineOptions({ name: 'ScheduleJob' })
@@ -180,13 +187,11 @@ const JobDetailDrawerRef = ref>()
const onDetail = (record: JobResp) => {
JobDetailDrawerRef.value?.onDetail(record)
}
-
const router = useRouter()
// 日志
const onLog = (record: JobResp) => {
router.push({ path: '/schedule/log', query: { jobId: record.id, jobName: record.jobName, groupName: record.groupName } })
}
-
onMounted(() => {
getGroupList()
})