mirror of
				https://github.com/continew-org/continew-admin-ui.git
				synced 2025-11-04 10:57:08 +08:00 
			
		
		
		
	feat(job): 支持可视化生成 CRON 表达式
This commit is contained in:
		@@ -28,6 +28,7 @@
 | 
				
			|||||||
    "animate.css": "^4.1.1",
 | 
					    "animate.css": "^4.1.1",
 | 
				
			||||||
    "axios": "^0.27.2",
 | 
					    "axios": "^0.27.2",
 | 
				
			||||||
    "codemirror": "^6.0.1",
 | 
					    "codemirror": "^6.0.1",
 | 
				
			||||||
 | 
					    "cron-parser": "^4.9.0",
 | 
				
			||||||
    "crypto-js": "^4.2.0",
 | 
					    "crypto-js": "^4.2.0",
 | 
				
			||||||
    "dayjs": "^1.11.4",
 | 
					    "dayjs": "^1.11.4",
 | 
				
			||||||
    "echarts": "^5.4.2",
 | 
					    "echarts": "^5.4.2",
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										88
									
								
								src/components/GenCron/CronForm/component/day-form.vue
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										88
									
								
								src/components/GenCron/CronForm/component/day-form.vue
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,88 @@
 | 
				
			|||||||
 | 
					<template>
 | 
				
			||||||
 | 
					  <div class="cron-inner-config-list">
 | 
				
			||||||
 | 
					    <a-radio-group v-model="type">
 | 
				
			||||||
 | 
					      <div class="item">
 | 
				
			||||||
 | 
					        <a-radio :value="TypeEnum.unset" v-bind="beforeRadioAttrs">不设置</a-radio>
 | 
				
			||||||
 | 
					        <span class="tip-info">日和周只能设置其中之一</span>
 | 
				
			||||||
 | 
					      </div>
 | 
				
			||||||
 | 
					      <div class="item">
 | 
				
			||||||
 | 
					        <a-radio :value="TypeEnum.every" v-bind="beforeRadioAttrs">每日</a-radio>
 | 
				
			||||||
 | 
					      </div>
 | 
				
			||||||
 | 
					      <div class="item">
 | 
				
			||||||
 | 
					        <a-radio :value="TypeEnum.range" v-bind="beforeRadioAttrs">区间</a-radio>
 | 
				
			||||||
 | 
					        <span>从</span>
 | 
				
			||||||
 | 
					        <a-input-number v-model="valueRange.start" v-bind="inputNumberAttrs" />
 | 
				
			||||||
 | 
					        <span>日 至</span>
 | 
				
			||||||
 | 
					        <a-input-number v-model="valueRange.end" v-bind="inputNumberAttrs" />
 | 
				
			||||||
 | 
					        <span>日</span>
 | 
				
			||||||
 | 
					      </div>
 | 
				
			||||||
 | 
					      <div class="item">
 | 
				
			||||||
 | 
					        <a-radio :value="TypeEnum.loop" v-bind="beforeRadioAttrs">循环</a-radio>
 | 
				
			||||||
 | 
					        <span>从</span>
 | 
				
			||||||
 | 
					        <a-input-number v-model="valueLoop.start" v-bind="typeLoopAttrs" />
 | 
				
			||||||
 | 
					        <span>日开始, 间隔</span>
 | 
				
			||||||
 | 
					        <a-input-number v-model="valueLoop.interval" v-bind="typeLoopAttrs" />
 | 
				
			||||||
 | 
					        <span>日</span>
 | 
				
			||||||
 | 
					      </div>
 | 
				
			||||||
 | 
					      <div class="item">
 | 
				
			||||||
 | 
					        <a-radio :value="TypeEnum.last" v-bind="beforeRadioAttrs">最后一日</a-radio>
 | 
				
			||||||
 | 
					      </div>
 | 
				
			||||||
 | 
					      <div class="item">
 | 
				
			||||||
 | 
					        <a-radio :value="TypeEnum.specify" v-bind="beforeRadioAttrs">指定</a-radio>
 | 
				
			||||||
 | 
					        <div class="list">
 | 
				
			||||||
 | 
					          <a-checkbox-group v-model="valueList">
 | 
				
			||||||
 | 
					            <a-grid :cols="11">
 | 
				
			||||||
 | 
					              <a-grid-item v-for="i in specifyRange" :key="i">
 | 
				
			||||||
 | 
					                <a-checkbox :value="i" v-bind="typeSpecifyAttrs">
 | 
				
			||||||
 | 
					                  {{ i }}
 | 
				
			||||||
 | 
					                </a-checkbox>
 | 
				
			||||||
 | 
					              </a-grid-item>
 | 
				
			||||||
 | 
					            </a-grid>
 | 
				
			||||||
 | 
					          </a-checkbox-group>
 | 
				
			||||||
 | 
					        </div>
 | 
				
			||||||
 | 
					      </div>
 | 
				
			||||||
 | 
					    </a-radio-group>
 | 
				
			||||||
 | 
					  </div>
 | 
				
			||||||
 | 
					</template>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					<script lang="ts">
 | 
				
			||||||
 | 
					import { TypeEnum, useFormProps, useFormSetup, useFromEmits } from './use-mixin'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export default defineComponent({
 | 
				
			||||||
 | 
					  name: 'DayForm',
 | 
				
			||||||
 | 
					  props: useFormProps({
 | 
				
			||||||
 | 
					    defaultValue: '*',
 | 
				
			||||||
 | 
					    props: {
 | 
				
			||||||
 | 
					      week: { type: String, default: '?' }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					  }),
 | 
				
			||||||
 | 
					  emits: useFromEmits(),
 | 
				
			||||||
 | 
					  setup(props, context) {
 | 
				
			||||||
 | 
					    const isDisabled = computed(() => {
 | 
				
			||||||
 | 
					      return (props.week && props.week !== '?') || props.disabled
 | 
				
			||||||
 | 
					    })
 | 
				
			||||||
 | 
					    const setup = useFormSetup(props, context, {
 | 
				
			||||||
 | 
					      defaultValue: '*',
 | 
				
			||||||
 | 
					      valueWork: 1,
 | 
				
			||||||
 | 
					      minValue: 1,
 | 
				
			||||||
 | 
					      maxValue: 31,
 | 
				
			||||||
 | 
					      valueRange: { start: 1, end: 31 },
 | 
				
			||||||
 | 
					      valueLoop: { start: 1, interval: 1 },
 | 
				
			||||||
 | 
					      disabled: isDisabled
 | 
				
			||||||
 | 
					    })
 | 
				
			||||||
 | 
					    const typeWorkAttrs = computed(() => ({
 | 
				
			||||||
 | 
					      disabled: setup.type.value !== TypeEnum.work || props.disabled || isDisabled.value,
 | 
				
			||||||
 | 
					      ...setup.inputNumberAttrs.value
 | 
				
			||||||
 | 
					    }))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    watch(
 | 
				
			||||||
 | 
					      () => props.week,
 | 
				
			||||||
 | 
					      () => {
 | 
				
			||||||
 | 
					        setup.updateValue(isDisabled.value ? '?' : setup.computeValue.value)
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					    )
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    return { ...setup, typeWorkAttrs }
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					})
 | 
				
			||||||
 | 
					</script>
 | 
				
			||||||
							
								
								
									
										60
									
								
								src/components/GenCron/CronForm/component/hour-form.vue
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										60
									
								
								src/components/GenCron/CronForm/component/hour-form.vue
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,60 @@
 | 
				
			|||||||
 | 
					<template>
 | 
				
			||||||
 | 
					  <div class="cron-inner-config-list">
 | 
				
			||||||
 | 
					    <a-radio-group v-model="type">
 | 
				
			||||||
 | 
					      <div class="item">
 | 
				
			||||||
 | 
					        <a-radio :value="TypeEnum.every" v-bind="beforeRadioAttrs">每时</a-radio>
 | 
				
			||||||
 | 
					      </div>
 | 
				
			||||||
 | 
					      <div class="item">
 | 
				
			||||||
 | 
					        <a-radio :value="TypeEnum.range" v-bind="beforeRadioAttrs">区间</a-radio>
 | 
				
			||||||
 | 
					        <span>从</span>
 | 
				
			||||||
 | 
					        <a-input-number v-model="valueRange.start" v-bind="typeRangeAttrs" />
 | 
				
			||||||
 | 
					        <span>时 至</span>
 | 
				
			||||||
 | 
					        <a-input-number v-model="valueRange.end" v-bind="typeRangeAttrs" />
 | 
				
			||||||
 | 
					        <span>时</span>
 | 
				
			||||||
 | 
					      </div>
 | 
				
			||||||
 | 
					      <div class="item">
 | 
				
			||||||
 | 
					        <a-radio :value="TypeEnum.loop" v-bind="beforeRadioAttrs">循环</a-radio>
 | 
				
			||||||
 | 
					        <span>从</span>
 | 
				
			||||||
 | 
					        <a-input-number v-model="valueLoop.start" v-bind="typeLoopAttrs" />
 | 
				
			||||||
 | 
					        <span>时开始, 间隔</span>
 | 
				
			||||||
 | 
					        <a-input-number v-model="valueLoop.interval" v-bind="typeLoopAttrs" />
 | 
				
			||||||
 | 
					        <span>时</span>
 | 
				
			||||||
 | 
					      </div>
 | 
				
			||||||
 | 
					      <div class="item">
 | 
				
			||||||
 | 
					        <a-radio :value="TypeEnum.specify" v-bind="beforeRadioAttrs">指定</a-radio>
 | 
				
			||||||
 | 
					        <div class="list">
 | 
				
			||||||
 | 
					          <a-checkbox-group v-model="valueList">
 | 
				
			||||||
 | 
					            <a-grid :cols="12">
 | 
				
			||||||
 | 
					              <a-grid-item v-for="i in specifyRange" :key="i">
 | 
				
			||||||
 | 
					                <a-checkbox :value="i" v-bind="typeSpecifyAttrs">
 | 
				
			||||||
 | 
					                  {{ i }}
 | 
				
			||||||
 | 
					                </a-checkbox>
 | 
				
			||||||
 | 
					              </a-grid-item>
 | 
				
			||||||
 | 
					            </a-grid>
 | 
				
			||||||
 | 
					          </a-checkbox-group>
 | 
				
			||||||
 | 
					        </div>
 | 
				
			||||||
 | 
					      </div>
 | 
				
			||||||
 | 
					    </a-radio-group>
 | 
				
			||||||
 | 
					  </div>
 | 
				
			||||||
 | 
					</template>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					<script lang="ts">
 | 
				
			||||||
 | 
					import { useFormProps, useFormSetup, useFromEmits } from './use-mixin'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export default defineComponent({
 | 
				
			||||||
 | 
					  name: 'HourForm',
 | 
				
			||||||
 | 
					  props: useFormProps({
 | 
				
			||||||
 | 
					    defaultValue: '*'
 | 
				
			||||||
 | 
					  }),
 | 
				
			||||||
 | 
					  emits: useFromEmits(),
 | 
				
			||||||
 | 
					  setup(props, context) {
 | 
				
			||||||
 | 
					    return useFormSetup(props, context, {
 | 
				
			||||||
 | 
					      defaultValue: '*',
 | 
				
			||||||
 | 
					      minValue: 0,
 | 
				
			||||||
 | 
					      maxValue: 23,
 | 
				
			||||||
 | 
					      valueRange: { start: 0, end: 23 },
 | 
				
			||||||
 | 
					      valueLoop: { start: 0, interval: 1 }
 | 
				
			||||||
 | 
					    })
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					})
 | 
				
			||||||
 | 
					</script>
 | 
				
			||||||
							
								
								
									
										60
									
								
								src/components/GenCron/CronForm/component/minute-form.vue
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										60
									
								
								src/components/GenCron/CronForm/component/minute-form.vue
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,60 @@
 | 
				
			|||||||
 | 
					<template>
 | 
				
			||||||
 | 
					  <div class="cron-inner-config-list">
 | 
				
			||||||
 | 
					    <a-radio-group v-model="type">
 | 
				
			||||||
 | 
					      <div class="item">
 | 
				
			||||||
 | 
					        <a-radio :value="TypeEnum.every" v-bind="beforeRadioAttrs">每分</a-radio>
 | 
				
			||||||
 | 
					      </div>
 | 
				
			||||||
 | 
					      <div class="item">
 | 
				
			||||||
 | 
					        <a-radio :value="TypeEnum.range" v-bind="beforeRadioAttrs">区间</a-radio>
 | 
				
			||||||
 | 
					        <span>从</span>
 | 
				
			||||||
 | 
					        <a-input-number v-model="valueRange.start" v-bind="typeRangeAttrs" />
 | 
				
			||||||
 | 
					        <span>分 至</span>
 | 
				
			||||||
 | 
					        <a-input-number v-model="valueRange.end" v-bind="typeRangeAttrs" />
 | 
				
			||||||
 | 
					        <span>分</span>
 | 
				
			||||||
 | 
					      </div>
 | 
				
			||||||
 | 
					      <div class="item">
 | 
				
			||||||
 | 
					        <a-radio :value="TypeEnum.loop" v-bind="beforeRadioAttrs">循环</a-radio>
 | 
				
			||||||
 | 
					        <span>从</span>
 | 
				
			||||||
 | 
					        <a-input-number v-model="valueLoop.start" v-bind="typeLoopAttrs" />
 | 
				
			||||||
 | 
					        <span>分开始, 间隔</span>
 | 
				
			||||||
 | 
					        <a-input-number v-model="valueLoop.interval" v-bind="typeLoopAttrs" />
 | 
				
			||||||
 | 
					        <span>分</span>
 | 
				
			||||||
 | 
					      </div>
 | 
				
			||||||
 | 
					      <div class="item">
 | 
				
			||||||
 | 
					        <a-radio :value="TypeEnum.specify" v-bind="beforeRadioAttrs">指定</a-radio>
 | 
				
			||||||
 | 
					        <div class="list">
 | 
				
			||||||
 | 
					          <a-checkbox-group v-model="valueList">
 | 
				
			||||||
 | 
					            <a-grid :cols="10">
 | 
				
			||||||
 | 
					              <a-grid-item v-for="i in specifyRange" :key="i">
 | 
				
			||||||
 | 
					                <a-checkbox :value="i" v-bind="typeSpecifyAttrs">
 | 
				
			||||||
 | 
					                  {{ i }}
 | 
				
			||||||
 | 
					                </a-checkbox>
 | 
				
			||||||
 | 
					              </a-grid-item>
 | 
				
			||||||
 | 
					            </a-grid>
 | 
				
			||||||
 | 
					          </a-checkbox-group>
 | 
				
			||||||
 | 
					        </div>
 | 
				
			||||||
 | 
					      </div>
 | 
				
			||||||
 | 
					    </a-radio-group>
 | 
				
			||||||
 | 
					  </div>
 | 
				
			||||||
 | 
					</template>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					<script lang="ts">
 | 
				
			||||||
 | 
					import { useFormProps, useFormSetup, useFromEmits } from './use-mixin'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export default defineComponent({
 | 
				
			||||||
 | 
					  name: 'MinuteForm',
 | 
				
			||||||
 | 
					  props: useFormProps({
 | 
				
			||||||
 | 
					    defaultValue: '*'
 | 
				
			||||||
 | 
					  }),
 | 
				
			||||||
 | 
					  emits: useFromEmits(),
 | 
				
			||||||
 | 
					  setup(props, context) {
 | 
				
			||||||
 | 
					    return useFormSetup(props, context, {
 | 
				
			||||||
 | 
					      defaultValue: '*',
 | 
				
			||||||
 | 
					      minValue: 0,
 | 
				
			||||||
 | 
					      maxValue: 59,
 | 
				
			||||||
 | 
					      valueRange: { start: 0, end: 59 },
 | 
				
			||||||
 | 
					      valueLoop: { start: 0, interval: 1 }
 | 
				
			||||||
 | 
					    })
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					})
 | 
				
			||||||
 | 
					</script>
 | 
				
			||||||
							
								
								
									
										60
									
								
								src/components/GenCron/CronForm/component/month-form.vue
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										60
									
								
								src/components/GenCron/CronForm/component/month-form.vue
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,60 @@
 | 
				
			|||||||
 | 
					<template>
 | 
				
			||||||
 | 
					  <div class="cron-inner-config-list">
 | 
				
			||||||
 | 
					    <a-radio-group v-model="type">
 | 
				
			||||||
 | 
					      <div class="item">
 | 
				
			||||||
 | 
					        <a-radio :value="TypeEnum.every" v-bind="beforeRadioAttrs">每月</a-radio>
 | 
				
			||||||
 | 
					      </div>
 | 
				
			||||||
 | 
					      <div class="item">
 | 
				
			||||||
 | 
					        <a-radio :value="TypeEnum.range" v-bind="beforeRadioAttrs">区间</a-radio>
 | 
				
			||||||
 | 
					        <span>从</span>
 | 
				
			||||||
 | 
					        <a-input-number v-model="valueRange.start" v-bind="typeRangeAttrs" />
 | 
				
			||||||
 | 
					        <span>月 至</span>
 | 
				
			||||||
 | 
					        <a-input-number v-model="valueRange.end" v-bind="typeRangeAttrs" />
 | 
				
			||||||
 | 
					        <span>月</span>
 | 
				
			||||||
 | 
					      </div>
 | 
				
			||||||
 | 
					      <div class="item">
 | 
				
			||||||
 | 
					        <a-radio :value="TypeEnum.loop" v-bind="beforeRadioAttrs">循环</a-radio>
 | 
				
			||||||
 | 
					        <span>从</span>
 | 
				
			||||||
 | 
					        <a-input-number v-model="valueLoop.start" v-bind="typeLoopAttrs" />
 | 
				
			||||||
 | 
					        <span>月开始, 间隔</span>
 | 
				
			||||||
 | 
					        <a-input-number v-model="valueLoop.interval" v-bind="typeLoopAttrs" />
 | 
				
			||||||
 | 
					        <span>月</span>
 | 
				
			||||||
 | 
					      </div>
 | 
				
			||||||
 | 
					      <div class="item">
 | 
				
			||||||
 | 
					        <a-radio :value="TypeEnum.specify" v-bind="beforeRadioAttrs">指定</a-radio>
 | 
				
			||||||
 | 
					        <div class="list">
 | 
				
			||||||
 | 
					          <a-checkbox-group v-model="valueList">
 | 
				
			||||||
 | 
					            <a-grid :cols="12">
 | 
				
			||||||
 | 
					              <a-grid-item v-for="i in specifyRange" :key="i">
 | 
				
			||||||
 | 
					                <a-checkbox :value="i" v-bind="typeSpecifyAttrs">
 | 
				
			||||||
 | 
					                  {{ i }}
 | 
				
			||||||
 | 
					                </a-checkbox>
 | 
				
			||||||
 | 
					              </a-grid-item>
 | 
				
			||||||
 | 
					            </a-grid>
 | 
				
			||||||
 | 
					          </a-checkbox-group>
 | 
				
			||||||
 | 
					        </div>
 | 
				
			||||||
 | 
					      </div>
 | 
				
			||||||
 | 
					    </a-radio-group>
 | 
				
			||||||
 | 
					  </div>
 | 
				
			||||||
 | 
					</template>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					<script lang="ts">
 | 
				
			||||||
 | 
					import { useFormProps, useFormSetup, useFromEmits } from './use-mixin'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export default defineComponent({
 | 
				
			||||||
 | 
					  name: 'MonthForm',
 | 
				
			||||||
 | 
					  props: useFormProps({
 | 
				
			||||||
 | 
					    defaultValue: '*'
 | 
				
			||||||
 | 
					  }),
 | 
				
			||||||
 | 
					  emits: useFromEmits(),
 | 
				
			||||||
 | 
					  setup(props, context) {
 | 
				
			||||||
 | 
					    return useFormSetup(props, context, {
 | 
				
			||||||
 | 
					      defaultValue: '*',
 | 
				
			||||||
 | 
					      minValue: 1,
 | 
				
			||||||
 | 
					      maxValue: 12,
 | 
				
			||||||
 | 
					      valueRange: { start: 1, end: 12 },
 | 
				
			||||||
 | 
					      valueLoop: { start: 1, interval: 1 }
 | 
				
			||||||
 | 
					    })
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					})
 | 
				
			||||||
 | 
					</script>
 | 
				
			||||||
							
								
								
									
										60
									
								
								src/components/GenCron/CronForm/component/second-form.vue
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										60
									
								
								src/components/GenCron/CronForm/component/second-form.vue
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,60 @@
 | 
				
			|||||||
 | 
					<template>
 | 
				
			||||||
 | 
					  <div class="cron-inner-config-list">
 | 
				
			||||||
 | 
					    <a-radio-group v-model="type">
 | 
				
			||||||
 | 
					      <div class="item">
 | 
				
			||||||
 | 
					        <a-radio :value="TypeEnum.every" v-bind="beforeRadioAttrs">每秒</a-radio>
 | 
				
			||||||
 | 
					      </div>
 | 
				
			||||||
 | 
					      <div class="item">
 | 
				
			||||||
 | 
					        <a-radio :value="TypeEnum.range" v-bind="beforeRadioAttrs">区间</a-radio>
 | 
				
			||||||
 | 
					        <span>从</span>
 | 
				
			||||||
 | 
					        <a-input-number v-model="valueRange.start" v-bind="typeRangeAttrs" />
 | 
				
			||||||
 | 
					        <span>秒 至</span>
 | 
				
			||||||
 | 
					        <a-input-number v-model="valueRange.end" v-bind="typeRangeAttrs" />
 | 
				
			||||||
 | 
					        <span>秒</span>
 | 
				
			||||||
 | 
					      </div>
 | 
				
			||||||
 | 
					      <div class="item">
 | 
				
			||||||
 | 
					        <a-radio :value="TypeEnum.loop" v-bind="beforeRadioAttrs">循环</a-radio>
 | 
				
			||||||
 | 
					        <span>从</span>
 | 
				
			||||||
 | 
					        <a-input-number v-model="valueLoop.start" v-bind="typeLoopAttrs" />
 | 
				
			||||||
 | 
					        <span>秒开始, 间隔</span>
 | 
				
			||||||
 | 
					        <a-input-number v-model="valueLoop.interval" v-bind="typeLoopAttrs" />
 | 
				
			||||||
 | 
					        <span>秒</span>
 | 
				
			||||||
 | 
					      </div>
 | 
				
			||||||
 | 
					      <div class="item">
 | 
				
			||||||
 | 
					        <a-radio :value="TypeEnum.specify" v-bind="beforeRadioAttrs">指定</a-radio>
 | 
				
			||||||
 | 
					        <div class="list">
 | 
				
			||||||
 | 
					          <a-checkbox-group v-model="valueList">
 | 
				
			||||||
 | 
					            <a-grid :cols="10">
 | 
				
			||||||
 | 
					              <a-grid-item v-for="i in specifyRange" :key="i">
 | 
				
			||||||
 | 
					                <a-checkbox :value="i" v-bind="typeSpecifyAttrs">
 | 
				
			||||||
 | 
					                  {{ i }}
 | 
				
			||||||
 | 
					                </a-checkbox>
 | 
				
			||||||
 | 
					              </a-grid-item>
 | 
				
			||||||
 | 
					            </a-grid>
 | 
				
			||||||
 | 
					          </a-checkbox-group>
 | 
				
			||||||
 | 
					        </div>
 | 
				
			||||||
 | 
					      </div>
 | 
				
			||||||
 | 
					    </a-radio-group>
 | 
				
			||||||
 | 
					  </div>
 | 
				
			||||||
 | 
					</template>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					<script lang="ts">
 | 
				
			||||||
 | 
					import { useFormProps, useFormSetup, useFromEmits } from './use-mixin'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export default defineComponent({
 | 
				
			||||||
 | 
					  name: 'SecondForm',
 | 
				
			||||||
 | 
					  props: useFormProps({
 | 
				
			||||||
 | 
					    defaultValue: '*'
 | 
				
			||||||
 | 
					  }),
 | 
				
			||||||
 | 
					  emits: useFromEmits(),
 | 
				
			||||||
 | 
					  setup(props, context) {
 | 
				
			||||||
 | 
					    return useFormSetup(props, context, {
 | 
				
			||||||
 | 
					      defaultValue: '*',
 | 
				
			||||||
 | 
					      minValue: 0,
 | 
				
			||||||
 | 
					      maxValue: 59,
 | 
				
			||||||
 | 
					      valueRange: { start: 0, end: 59 },
 | 
				
			||||||
 | 
					      valueLoop: { start: 0, interval: 1 }
 | 
				
			||||||
 | 
					    })
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					})
 | 
				
			||||||
 | 
					</script>
 | 
				
			||||||
							
								
								
									
										220
									
								
								src/components/GenCron/CronForm/component/use-mixin.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										220
									
								
								src/components/GenCron/CronForm/component/use-mixin.ts
									
									
									
									
									
										Normal file
									
								
							@@ -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<any[]>([])
 | 
				
			||||||
 | 
					  // 对于不同的类型, 所定义的值也有所不同
 | 
				
			||||||
 | 
					  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
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										108
									
								
								src/components/GenCron/CronForm/component/week-form.vue
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										108
									
								
								src/components/GenCron/CronForm/component/week-form.vue
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,108 @@
 | 
				
			|||||||
 | 
					<template>
 | 
				
			||||||
 | 
					  <div class="cron-inner-config-list">
 | 
				
			||||||
 | 
					    <a-radio-group v-model="type">
 | 
				
			||||||
 | 
					      <div class="item">
 | 
				
			||||||
 | 
					        <a-radio :value="TypeEnum.unset" v-bind="beforeRadioAttrs">不设置</a-radio>
 | 
				
			||||||
 | 
					        <span class="tip-info">日和周只能设置其中之一</span>
 | 
				
			||||||
 | 
					      </div>
 | 
				
			||||||
 | 
					      <div class="item">
 | 
				
			||||||
 | 
					        <a-radio :value="TypeEnum.range" v-bind="beforeRadioAttrs">区间</a-radio>
 | 
				
			||||||
 | 
					        <span>从</span>
 | 
				
			||||||
 | 
					        <a-select v-model="valueRange.start" v-bind="typeRangeSelectAttrs">
 | 
				
			||||||
 | 
					          <a-option v-for="item in weekOptions" :key="item.value" :label="item.label" :value="item.value" />
 | 
				
			||||||
 | 
					        </a-select>
 | 
				
			||||||
 | 
					        <span>至</span>
 | 
				
			||||||
 | 
					        <a-select v-model="valueRange.end" v-bind="typeRangeSelectAttrs">
 | 
				
			||||||
 | 
					          <a-option v-for="item in weekOptions" :key="item.value" :label="item.label" :value="item.value" />
 | 
				
			||||||
 | 
					        </a-select>
 | 
				
			||||||
 | 
					      </div>
 | 
				
			||||||
 | 
					      <div class="item">
 | 
				
			||||||
 | 
					        <a-radio :value="TypeEnum.loop" v-bind="beforeRadioAttrs">循环</a-radio>
 | 
				
			||||||
 | 
					        <span>从</span>
 | 
				
			||||||
 | 
					        <a-select v-model="valueLoop.start" v-bind="typeLoopSelectAttrs">
 | 
				
			||||||
 | 
					          <a-option v-for="item in weekOptions" :key="item.value" :label="item.label" :value="item.value" />
 | 
				
			||||||
 | 
					        </a-select>
 | 
				
			||||||
 | 
					        <span>开始, 间隔</span>
 | 
				
			||||||
 | 
					        <a-input-number v-model="valueLoop.interval" v-bind="typeLoopAttrs" />
 | 
				
			||||||
 | 
					        <span>天</span>
 | 
				
			||||||
 | 
					      </div>
 | 
				
			||||||
 | 
					      <div class="item">
 | 
				
			||||||
 | 
					        <a-radio :value="TypeEnum.specify" v-bind="beforeRadioAttrs">指定</a-radio>
 | 
				
			||||||
 | 
					        <div class="list list-cn">
 | 
				
			||||||
 | 
					          <a-checkbox-group v-model="valueList">
 | 
				
			||||||
 | 
					            <template v-for="opt in weekOptions" :key="opt">
 | 
				
			||||||
 | 
					              <a-checkbox :value="opt.value" v-bind="typeSpecifyAttrs">
 | 
				
			||||||
 | 
					                {{ opt.label }}
 | 
				
			||||||
 | 
					              </a-checkbox>
 | 
				
			||||||
 | 
					            </template>
 | 
				
			||||||
 | 
					          </a-checkbox-group>
 | 
				
			||||||
 | 
					        </div>
 | 
				
			||||||
 | 
					      </div>
 | 
				
			||||||
 | 
					    </a-radio-group>
 | 
				
			||||||
 | 
					  </div>
 | 
				
			||||||
 | 
					</template>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					<script lang="ts">
 | 
				
			||||||
 | 
					import { TypeEnum, WEEK_MAP, useFormProps, useFormSetup, useFromEmits } from './use-mixin'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export default defineComponent({
 | 
				
			||||||
 | 
					  name: 'WeekForm',
 | 
				
			||||||
 | 
					  props: useFormProps({
 | 
				
			||||||
 | 
					    defaultValue: '?',
 | 
				
			||||||
 | 
					    props: {
 | 
				
			||||||
 | 
					      day: { type: String, default: '*' }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					  }),
 | 
				
			||||||
 | 
					  emits: useFromEmits(),
 | 
				
			||||||
 | 
					  setup(props, context) {
 | 
				
			||||||
 | 
					    const disabledChoice = computed(() => {
 | 
				
			||||||
 | 
					      return (props.day && props.day !== '?') || props.disabled
 | 
				
			||||||
 | 
					    })
 | 
				
			||||||
 | 
					    const setup = useFormSetup(props, context, {
 | 
				
			||||||
 | 
					      defaultType: TypeEnum.unset,
 | 
				
			||||||
 | 
					      defaultValue: '?',
 | 
				
			||||||
 | 
					      minValue: 1,
 | 
				
			||||||
 | 
					      maxValue: 7,
 | 
				
			||||||
 | 
					      // 0,7表示周日 1表示周一
 | 
				
			||||||
 | 
					      valueRange: { start: 1, end: 7 },
 | 
				
			||||||
 | 
					      valueLoop: { start: 2, interval: 1 },
 | 
				
			||||||
 | 
					      disabled: disabledChoice
 | 
				
			||||||
 | 
					    })
 | 
				
			||||||
 | 
					    const weekOptions = computed(() => {
 | 
				
			||||||
 | 
					      const options: { label: string, value: number }[] = []
 | 
				
			||||||
 | 
					      for (const weekKey of Object.keys(WEEK_MAP)) {
 | 
				
			||||||
 | 
					        const weekName: string = WEEK_MAP[weekKey]
 | 
				
			||||||
 | 
					        options.push({
 | 
				
			||||||
 | 
					          value: Number.parseInt(weekKey),
 | 
				
			||||||
 | 
					          label: weekName
 | 
				
			||||||
 | 
					        })
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					      return options
 | 
				
			||||||
 | 
					    })
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    const typeRangeSelectAttrs = computed(() => ({
 | 
				
			||||||
 | 
					      disabled: setup.typeRangeAttrs.value.disabled,
 | 
				
			||||||
 | 
					      size: 'small',
 | 
				
			||||||
 | 
					      class: ['w80']
 | 
				
			||||||
 | 
					    }))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    const typeLoopSelectAttrs = computed(() => ({
 | 
				
			||||||
 | 
					      disabled: setup.typeLoopAttrs.value.disabled,
 | 
				
			||||||
 | 
					      size: 'small',
 | 
				
			||||||
 | 
					      class: ['w80']
 | 
				
			||||||
 | 
					    }))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    watch(() => props.day, () => {
 | 
				
			||||||
 | 
					      setup.updateValue(disabledChoice.value ? '?' : setup.computeValue.value)
 | 
				
			||||||
 | 
					    })
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    return {
 | 
				
			||||||
 | 
					      ...setup,
 | 
				
			||||||
 | 
					      weekOptions,
 | 
				
			||||||
 | 
					      typeLoopSelectAttrs,
 | 
				
			||||||
 | 
					      typeRangeSelectAttrs,
 | 
				
			||||||
 | 
					      WEEK_MAP
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					})
 | 
				
			||||||
 | 
					</script>
 | 
				
			||||||
							
								
								
									
										46
									
								
								src/components/GenCron/CronForm/component/year-form.vue
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										46
									
								
								src/components/GenCron/CronForm/component/year-form.vue
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,46 @@
 | 
				
			|||||||
 | 
					<template>
 | 
				
			||||||
 | 
					  <div class="cron-inner-config-list">
 | 
				
			||||||
 | 
					    <a-radio-group v-model="type">
 | 
				
			||||||
 | 
					      <div class="item">
 | 
				
			||||||
 | 
					        <a-radio :value="TypeEnum.every" v-bind="beforeRadioAttrs">每年</a-radio>
 | 
				
			||||||
 | 
					      </div>
 | 
				
			||||||
 | 
					      <div class="item">
 | 
				
			||||||
 | 
					        <a-radio :value="TypeEnum.range" v-bind="beforeRadioAttrs">区间</a-radio>
 | 
				
			||||||
 | 
					        <span>从</span>
 | 
				
			||||||
 | 
					        <a-input-number v-model="valueRange.start" v-bind="typeRangeAttrs" />
 | 
				
			||||||
 | 
					        <span>年 至</span>
 | 
				
			||||||
 | 
					        <a-input-number v-model="valueRange.end" v-bind="typeRangeAttrs" />
 | 
				
			||||||
 | 
					        <span>年</span>
 | 
				
			||||||
 | 
					      </div>
 | 
				
			||||||
 | 
					      <div class="item">
 | 
				
			||||||
 | 
					        <a-radio :value="TypeEnum.loop" v-bind="beforeRadioAttrs">循环</a-radio>
 | 
				
			||||||
 | 
					        <span>从</span>
 | 
				
			||||||
 | 
					        <a-input-number v-model="valueLoop.start" v-bind="typeLoopAttrs" />
 | 
				
			||||||
 | 
					        <span>年开始, 间隔</span>
 | 
				
			||||||
 | 
					        <a-input-number v-model="valueLoop.interval" v-bind="typeLoopAttrs" />
 | 
				
			||||||
 | 
					        <span>年</span>
 | 
				
			||||||
 | 
					      </div>
 | 
				
			||||||
 | 
					    </a-radio-group>
 | 
				
			||||||
 | 
					  </div>
 | 
				
			||||||
 | 
					</template>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					<script lang="ts">
 | 
				
			||||||
 | 
					import { useFormProps, useFormSetup, useFromEmits } from './use-mixin'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export default defineComponent({
 | 
				
			||||||
 | 
					  name: 'YearForm',
 | 
				
			||||||
 | 
					  props: useFormProps({
 | 
				
			||||||
 | 
					    defaultValue: '*'
 | 
				
			||||||
 | 
					  }),
 | 
				
			||||||
 | 
					  emits: useFromEmits(),
 | 
				
			||||||
 | 
					  setup(props, context) {
 | 
				
			||||||
 | 
					    const nowYear = new Date().getFullYear()
 | 
				
			||||||
 | 
					    return useFormSetup(props, context, {
 | 
				
			||||||
 | 
					      defaultValue: '*',
 | 
				
			||||||
 | 
					      minValue: 0,
 | 
				
			||||||
 | 
					      valueRange: { start: nowYear, end: nowYear + 100 },
 | 
				
			||||||
 | 
					      valueLoop: { start: nowYear, interval: 1 }
 | 
				
			||||||
 | 
					    })
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					})
 | 
				
			||||||
 | 
					</script>
 | 
				
			||||||
							
								
								
									
										378
									
								
								src/components/GenCron/CronForm/index.vue
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										378
									
								
								src/components/GenCron/CronForm/index.vue
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,378 @@
 | 
				
			|||||||
 | 
					<template>
 | 
				
			||||||
 | 
					  <div class="cron-inner">
 | 
				
			||||||
 | 
					    <div class="content">
 | 
				
			||||||
 | 
					      <!-- 设置表单 -->
 | 
				
			||||||
 | 
					      <a-tabs v-model:active-key="activeKey" size="small">
 | 
				
			||||||
 | 
					        <!-- 秒 -->
 | 
				
			||||||
 | 
					        <a-tab-pane v-if="!hideSecond" key="second" title="秒">
 | 
				
			||||||
 | 
					          <SecondForm v-model="second" :disabled="disabled" />
 | 
				
			||||||
 | 
					        </a-tab-pane>
 | 
				
			||||||
 | 
					        <!-- 分 -->
 | 
				
			||||||
 | 
					        <a-tab-pane key="minute" title="分">
 | 
				
			||||||
 | 
					          <MinuteForm v-model="minute" :disabled="disabled" />
 | 
				
			||||||
 | 
					        </a-tab-pane>
 | 
				
			||||||
 | 
					        <!-- 时 -->
 | 
				
			||||||
 | 
					        <a-tab-pane key="hour" title="时">
 | 
				
			||||||
 | 
					          <HourForm v-model="hour" :disabled="disabled" />
 | 
				
			||||||
 | 
					        </a-tab-pane>
 | 
				
			||||||
 | 
					        <!-- 日 -->
 | 
				
			||||||
 | 
					        <a-tab-pane key="day" title="日">
 | 
				
			||||||
 | 
					          <DayForm v-model="day" :week="week" :disabled="disabled" />
 | 
				
			||||||
 | 
					        </a-tab-pane>
 | 
				
			||||||
 | 
					        <!-- 月 -->
 | 
				
			||||||
 | 
					        <a-tab-pane key="month" title="月">
 | 
				
			||||||
 | 
					          <MonthForm v-model="month" :disabled="disabled" />
 | 
				
			||||||
 | 
					        </a-tab-pane>
 | 
				
			||||||
 | 
					        <!-- 周 -->
 | 
				
			||||||
 | 
					        <a-tab-pane key="week" title="周">
 | 
				
			||||||
 | 
					          <WeekForm v-model="week" :day="day" :disabled="disabled" />
 | 
				
			||||||
 | 
					        </a-tab-pane>
 | 
				
			||||||
 | 
					        <!-- 年 -->
 | 
				
			||||||
 | 
					        <a-tab-pane v-if="!hideYear && !hideSecond" key="year" title="年">
 | 
				
			||||||
 | 
					          <YearForm v-model="year" :disabled="disabled" />
 | 
				
			||||||
 | 
					        </a-tab-pane>
 | 
				
			||||||
 | 
					      </a-tabs>
 | 
				
			||||||
 | 
					      <!-- 执行时间预览 -->
 | 
				
			||||||
 | 
					      <a-row :gutter="8">
 | 
				
			||||||
 | 
					        <!-- 快捷修改 -->
 | 
				
			||||||
 | 
					        <a-col :span="18" style="margin-top: 28px">
 | 
				
			||||||
 | 
					          <a-row :gutter="[12, 12]">
 | 
				
			||||||
 | 
					            <!-- 秒 -->
 | 
				
			||||||
 | 
					            <a-col :span="8">
 | 
				
			||||||
 | 
					              <a-input v-model="cronInputs.second" @change="onInputChange">
 | 
				
			||||||
 | 
					                <template #prepend>
 | 
				
			||||||
 | 
					                  <span class="allow-click" @click="activeKey = 'second'">秒</span>
 | 
				
			||||||
 | 
					                </template>
 | 
				
			||||||
 | 
					              </a-input>
 | 
				
			||||||
 | 
					            </a-col>
 | 
				
			||||||
 | 
					            <!-- 分 -->
 | 
				
			||||||
 | 
					            <a-col :span="8">
 | 
				
			||||||
 | 
					              <a-input v-model="cronInputs.minute" @change="onInputChange">
 | 
				
			||||||
 | 
					                <template #prepend>
 | 
				
			||||||
 | 
					                  <span class="allow-click" @click="activeKey = 'minute'">分</span>
 | 
				
			||||||
 | 
					                </template>
 | 
				
			||||||
 | 
					              </a-input>
 | 
				
			||||||
 | 
					            </a-col>
 | 
				
			||||||
 | 
					            <!-- 时 -->
 | 
				
			||||||
 | 
					            <a-col :span="8">
 | 
				
			||||||
 | 
					              <a-input v-model="cronInputs.hour" @change="onInputChange">
 | 
				
			||||||
 | 
					                <template #prepend>
 | 
				
			||||||
 | 
					                  <span class="allow-click" @click="activeKey = 'hour'">时</span>
 | 
				
			||||||
 | 
					                </template>
 | 
				
			||||||
 | 
					              </a-input>
 | 
				
			||||||
 | 
					            </a-col>
 | 
				
			||||||
 | 
					            <!-- 日 -->
 | 
				
			||||||
 | 
					            <a-col :span="8">
 | 
				
			||||||
 | 
					              <a-input v-model="cronInputs.day" @change="onInputChange">
 | 
				
			||||||
 | 
					                <template #prepend>
 | 
				
			||||||
 | 
					                  <span class="allow-click" @click="activeKey = 'day'">日</span>
 | 
				
			||||||
 | 
					                </template>
 | 
				
			||||||
 | 
					              </a-input>
 | 
				
			||||||
 | 
					            </a-col>
 | 
				
			||||||
 | 
					            <!-- 月 -->
 | 
				
			||||||
 | 
					            <a-col :span="8">
 | 
				
			||||||
 | 
					              <a-input v-model="cronInputs.month" @change="onInputChange">
 | 
				
			||||||
 | 
					                <template #prepend>
 | 
				
			||||||
 | 
					                  <span class="allow-click" @click="activeKey = 'month'">月</span>
 | 
				
			||||||
 | 
					                </template>
 | 
				
			||||||
 | 
					              </a-input>
 | 
				
			||||||
 | 
					            </a-col>
 | 
				
			||||||
 | 
					            <!-- 周 -->
 | 
				
			||||||
 | 
					            <a-col :span="8">
 | 
				
			||||||
 | 
					              <a-input v-model="cronInputs.week" @change="onInputChange">
 | 
				
			||||||
 | 
					                <template #prepend>
 | 
				
			||||||
 | 
					                  <span class="allow-click" @click="activeKey = 'week'">周</span>
 | 
				
			||||||
 | 
					                </template>
 | 
				
			||||||
 | 
					              </a-input>
 | 
				
			||||||
 | 
					            </a-col>
 | 
				
			||||||
 | 
					            <!-- 年 -->
 | 
				
			||||||
 | 
					            <a-col :span="8">
 | 
				
			||||||
 | 
					              <a-input v-model="cronInputs.year" @change="onInputChange">
 | 
				
			||||||
 | 
					                <template #prepend>
 | 
				
			||||||
 | 
					                  <span class="allow-click" @click="activeKey = 'year'">年</span>
 | 
				
			||||||
 | 
					                </template>
 | 
				
			||||||
 | 
					              </a-input>
 | 
				
			||||||
 | 
					            </a-col>
 | 
				
			||||||
 | 
					            <!-- 表达式 -->
 | 
				
			||||||
 | 
					            <a-col :span="16">
 | 
				
			||||||
 | 
					              <a-input v-model="cronInputs.cron"
 | 
				
			||||||
 | 
					                       :placeholder="placeholder"
 | 
				
			||||||
 | 
					                       @change="onInputCronChange">
 | 
				
			||||||
 | 
					                <template #prepend>
 | 
				
			||||||
 | 
					                  <span class="allow-click">表达式</span>
 | 
				
			||||||
 | 
					                </template>
 | 
				
			||||||
 | 
					              </a-input>
 | 
				
			||||||
 | 
					            </a-col>
 | 
				
			||||||
 | 
					          </a-row>
 | 
				
			||||||
 | 
					        </a-col>
 | 
				
			||||||
 | 
					        <!-- 执行时间 -->
 | 
				
			||||||
 | 
					        <a-col :span="6">
 | 
				
			||||||
 | 
					          <div class="preview-times usn">近五次执行时间 (不解析年)</div>
 | 
				
			||||||
 | 
					          <a-textarea v-model="previewTimes" :auto-size="{ minRows: 5, maxRows: 5 }" />
 | 
				
			||||||
 | 
					        </a-col>
 | 
				
			||||||
 | 
					      </a-row>
 | 
				
			||||||
 | 
					    </div>
 | 
				
			||||||
 | 
					  </div>
 | 
				
			||||||
 | 
					</template>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					<script lang="ts">
 | 
				
			||||||
 | 
					</script>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					<script lang="ts" setup>
 | 
				
			||||||
 | 
					import { useDebounceFn } from '@vueuse/core'
 | 
				
			||||||
 | 
					import CronParser from 'cron-parser'
 | 
				
			||||||
 | 
					import SecondForm from '@/components/GenCron/CronForm/component/second-form.vue'
 | 
				
			||||||
 | 
					import MinuteForm from '@/components/GenCron/CronForm/component/minute-form.vue'
 | 
				
			||||||
 | 
					import HourForm from '@/components/GenCron/CronForm/component/hour-form.vue'
 | 
				
			||||||
 | 
					import DayForm from '@/components/GenCron/CronForm/component/day-form.vue'
 | 
				
			||||||
 | 
					import MonthForm from '@/components/GenCron/CronForm/component/month-form.vue'
 | 
				
			||||||
 | 
					import WeekForm from '@/components/GenCron/CronForm/component/week-form.vue'
 | 
				
			||||||
 | 
					import YearForm from '@/components/GenCron/CronForm/component/year-form.vue'
 | 
				
			||||||
 | 
					import { dateFormat } from '@/utils'
 | 
				
			||||||
 | 
					import type { CronPropType } from '@/components/GenCron/CronForm/type'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					const props = withDefaults(defineProps<Partial<CronPropType>>(), {
 | 
				
			||||||
 | 
					  disabled: false,
 | 
				
			||||||
 | 
					  hideSecond: false,
 | 
				
			||||||
 | 
					  hideYear: false,
 | 
				
			||||||
 | 
					  placeholder: '请输入 Cron 表达式'
 | 
				
			||||||
 | 
					})
 | 
				
			||||||
 | 
					const emit = defineEmits(['change', 'update:modelValue'])
 | 
				
			||||||
 | 
					const activeKey = ref(props.hideSecond ? 'minute' : 'second')
 | 
				
			||||||
 | 
					const second = ref('*')
 | 
				
			||||||
 | 
					const minute = ref('*')
 | 
				
			||||||
 | 
					const hour = ref('*')
 | 
				
			||||||
 | 
					const day = ref('*')
 | 
				
			||||||
 | 
					const month = ref('*')
 | 
				
			||||||
 | 
					const week = ref('?')
 | 
				
			||||||
 | 
					const year = ref('*')
 | 
				
			||||||
 | 
					const cronInputs = reactive({
 | 
				
			||||||
 | 
					  second: '',
 | 
				
			||||||
 | 
					  minute: '',
 | 
				
			||||||
 | 
					  hour: '',
 | 
				
			||||||
 | 
					  day: '',
 | 
				
			||||||
 | 
					  month: '',
 | 
				
			||||||
 | 
					  week: '',
 | 
				
			||||||
 | 
					  year: '',
 | 
				
			||||||
 | 
					  cron: ''
 | 
				
			||||||
 | 
					})
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					const previewTimes = ref('执行预览')
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// cron 表达式
 | 
				
			||||||
 | 
					const cronExpression = computed(() => {
 | 
				
			||||||
 | 
					  const result: string[] = []
 | 
				
			||||||
 | 
					  if (!props.hideSecond) {
 | 
				
			||||||
 | 
					    result.push(second.value ? second.value : '*')
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					  result.push(minute.value ? minute.value : '*')
 | 
				
			||||||
 | 
					  result.push(hour.value ? hour.value : '*')
 | 
				
			||||||
 | 
					  result.push(day.value ? day.value : '*')
 | 
				
			||||||
 | 
					  result.push(month.value ? month.value : '*')
 | 
				
			||||||
 | 
					  result.push(week.value ? week.value : '?')
 | 
				
			||||||
 | 
					  if (!props.hideYear && !props.hideSecond) {
 | 
				
			||||||
 | 
					    result.push(year.value ? year.value : '*')
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					  return result.join(' ')
 | 
				
			||||||
 | 
					})
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// 不含年的 cron 表达式
 | 
				
			||||||
 | 
					const expressionNoYear = (corn: string) => {
 | 
				
			||||||
 | 
					  if (props.hideYear || props.hideSecond) return corn
 | 
				
			||||||
 | 
					  const vs = corn.split(' ')
 | 
				
			||||||
 | 
					  return vs.slice(0, vs.length - 1).join(' ')
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// 计算触发时间
 | 
				
			||||||
 | 
					const calculateNextExecutionTimes = (corn: string = cronExpression.value) => {
 | 
				
			||||||
 | 
					  try {
 | 
				
			||||||
 | 
					    const parse = expressionNoYear(corn)
 | 
				
			||||||
 | 
					    // 解析表达式
 | 
				
			||||||
 | 
					    const date = dateFormat(new Date())
 | 
				
			||||||
 | 
					    const iter = CronParser.parseExpression(parse, {
 | 
				
			||||||
 | 
					      currentDate: date
 | 
				
			||||||
 | 
					    })
 | 
				
			||||||
 | 
					    const result: string[] = []
 | 
				
			||||||
 | 
					    for (let i = 1; i <= 5; i++) {
 | 
				
			||||||
 | 
					      result.push(dateFormat(new Date(iter.next() as any)))
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    previewTimes.value = result.length > 0 ? result.join('\n') : '无执行时间'
 | 
				
			||||||
 | 
					    // 回调
 | 
				
			||||||
 | 
					    if (props.callback) {
 | 
				
			||||||
 | 
					      props.callback(cronExpression.value, +new Date(), true)
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					  } catch (e) {
 | 
				
			||||||
 | 
					    previewTimes.value = '表达式错误'
 | 
				
			||||||
 | 
					    // 回调
 | 
				
			||||||
 | 
					    if (props.callback) {
 | 
				
			||||||
 | 
					      props.callback(cronExpression.value, +new Date(), false)
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					const calcTriggerTimeList = useDebounceFn(calculateNextExecutionTimes, 500)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// 监听 cron 修改
 | 
				
			||||||
 | 
					watch(() => props.modelValue, (newVal) => {
 | 
				
			||||||
 | 
					  if (newVal === cronExpression.value) {
 | 
				
			||||||
 | 
					    return
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					  parseCron()
 | 
				
			||||||
 | 
					})
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// 监听 cron 修改
 | 
				
			||||||
 | 
					watch(cronExpression, (newValue) => {
 | 
				
			||||||
 | 
					  calcTriggerTimeList()
 | 
				
			||||||
 | 
					  emitValue(newValue)
 | 
				
			||||||
 | 
					  assignInput()
 | 
				
			||||||
 | 
					})
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// 根据 cron 解析
 | 
				
			||||||
 | 
					const parseCron = () => {
 | 
				
			||||||
 | 
					  // 计算执行时间
 | 
				
			||||||
 | 
					  calcTriggerTimeList()
 | 
				
			||||||
 | 
					  if (!props.modelValue) {
 | 
				
			||||||
 | 
					    return
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					  const values = props.modelValue.split(' ').filter((item) => !!item)
 | 
				
			||||||
 | 
					  if (!values || values.length <= 0) {
 | 
				
			||||||
 | 
					    return
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					  let i = 0
 | 
				
			||||||
 | 
					  if (!props.hideSecond) second.value = values[i++]
 | 
				
			||||||
 | 
					  if (values.length > i) minute.value = values[i++]
 | 
				
			||||||
 | 
					  if (values.length > i) hour.value = values[i++]
 | 
				
			||||||
 | 
					  if (values.length > i) day.value = values[i++]
 | 
				
			||||||
 | 
					  if (values.length > i) month.value = values[i++]
 | 
				
			||||||
 | 
					  if (values.length > i) week.value = values[i++]
 | 
				
			||||||
 | 
					  if (values.length > i) year.value = values[i]
 | 
				
			||||||
 | 
					  // 重新分配
 | 
				
			||||||
 | 
					  assignInput()
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// 重新分配
 | 
				
			||||||
 | 
					const assignInput = () => {
 | 
				
			||||||
 | 
					  cronInputs.second = second.value
 | 
				
			||||||
 | 
					  cronInputs.minute = minute.value
 | 
				
			||||||
 | 
					  cronInputs.hour = hour.value
 | 
				
			||||||
 | 
					  cronInputs.day = day.value
 | 
				
			||||||
 | 
					  cronInputs.month = month.value
 | 
				
			||||||
 | 
					  cronInputs.week = week.value
 | 
				
			||||||
 | 
					  cronInputs.year = year.value
 | 
				
			||||||
 | 
					  cronInputs.cron = cronExpression.value
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// 修改 cron 解析内容
 | 
				
			||||||
 | 
					const onInputChange = () => {
 | 
				
			||||||
 | 
					  second.value = cronInputs.second
 | 
				
			||||||
 | 
					  minute.value = cronInputs.minute
 | 
				
			||||||
 | 
					  hour.value = cronInputs.hour
 | 
				
			||||||
 | 
					  day.value = cronInputs.day
 | 
				
			||||||
 | 
					  month.value = cronInputs.month
 | 
				
			||||||
 | 
					  week.value = cronInputs.week
 | 
				
			||||||
 | 
					  year.value = cronInputs.year
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// 修改 cron 输入框
 | 
				
			||||||
 | 
					const onInputCronChange = (value: string) => {
 | 
				
			||||||
 | 
					  emitValue(value)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// 修改 cron
 | 
				
			||||||
 | 
					const emitValue = (value: string) => {
 | 
				
			||||||
 | 
					  emit('change', value)
 | 
				
			||||||
 | 
					  emit('update:modelValue', value)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					onMounted(() => {
 | 
				
			||||||
 | 
					  assignInput()
 | 
				
			||||||
 | 
					  parseCron()
 | 
				
			||||||
 | 
					  // 如果 modelValue 没有值则更新为 cronExpression
 | 
				
			||||||
 | 
					  if (!props.modelValue) {
 | 
				
			||||||
 | 
					    emitValue(cronExpression.value)
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					})
 | 
				
			||||||
 | 
					const checkCron = () => {
 | 
				
			||||||
 | 
					  return (day.value === '?' && week.value === '?')
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					defineExpose({ checkCron })
 | 
				
			||||||
 | 
					</script>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					<style lang="less" scoped>
 | 
				
			||||||
 | 
					.cron-inner {
 | 
				
			||||||
 | 
					  user-select: none;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  :deep(.arco-tabs-content) {
 | 
				
			||||||
 | 
					    padding-top: 6px;
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  :deep(.cron-inner-config-list) {
 | 
				
			||||||
 | 
					    text-align: left;
 | 
				
			||||||
 | 
					    margin: 0 12px 4px 12px;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    .item {
 | 
				
			||||||
 | 
					      margin-top: 6px;
 | 
				
			||||||
 | 
					      font-size: 14px;
 | 
				
			||||||
 | 
					      width: 100%;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    .choice {
 | 
				
			||||||
 | 
					      padding: 4px 8px 4px 0;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    .w60 {
 | 
				
			||||||
 | 
					      margin: 0 8px !important;
 | 
				
			||||||
 | 
					      padding: 0 8px !important;
 | 
				
			||||||
 | 
					      width: 60px !important;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    .w80 {
 | 
				
			||||||
 | 
					      margin: 0 8px !important;
 | 
				
			||||||
 | 
					      padding: 0 8px !important;
 | 
				
			||||||
 | 
					      width: 80px !important;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    .list {
 | 
				
			||||||
 | 
					      margin: 0 20px;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    .list-check-item {
 | 
				
			||||||
 | 
					      padding: 1px 3px;
 | 
				
			||||||
 | 
					      width: 4em;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    .list-cn .list-check-item {
 | 
				
			||||||
 | 
					      width: 5em;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    .tip-info {
 | 
				
			||||||
 | 
					      color: var(--color-text-3);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					:deep(.arco-input-prepend) {
 | 
				
			||||||
 | 
					  padding: 0 !important;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					:deep(.arco-input-append) {
 | 
				
			||||||
 | 
					  padding: 0 !important;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					.preview-times {
 | 
				
			||||||
 | 
					  color: var(--color-text-3);
 | 
				
			||||||
 | 
					  margin: 2px 0 4px 0;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					.allow-click {
 | 
				
			||||||
 | 
					  width: 100%;
 | 
				
			||||||
 | 
					  height: 100%;
 | 
				
			||||||
 | 
					  padding: 0 12px;
 | 
				
			||||||
 | 
					  display: flex;
 | 
				
			||||||
 | 
					  align-items: center;
 | 
				
			||||||
 | 
					  justify-content: center;
 | 
				
			||||||
 | 
					  cursor: pointer;
 | 
				
			||||||
 | 
					  user-select: none;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					</style>
 | 
				
			||||||
							
								
								
									
										9
									
								
								src/components/GenCron/CronForm/type.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										9
									
								
								src/components/GenCron/CronForm/type.ts
									
									
									
									
									
										Normal file
									
								
							@@ -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
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										67
									
								
								src/components/GenCron/CronModel/index.vue
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										67
									
								
								src/components/GenCron/CronModel/index.vue
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,67 @@
 | 
				
			|||||||
 | 
					<template>
 | 
				
			||||||
 | 
					  <a-modal v-model:visible="visible"
 | 
				
			||||||
 | 
					           modal-class="modal-form-small"
 | 
				
			||||||
 | 
					           title-align="start"
 | 
				
			||||||
 | 
					           title="CRON 生成器"
 | 
				
			||||||
 | 
					           :top="32"
 | 
				
			||||||
 | 
					           :width="780"
 | 
				
			||||||
 | 
					           :align-center="false"
 | 
				
			||||||
 | 
					           :draggable="true"
 | 
				
			||||||
 | 
					           :mask-closable="false"
 | 
				
			||||||
 | 
					           :unmount-on-close="true"
 | 
				
			||||||
 | 
					           :body-style="{ padding: '4px 16px 8px 16px' }">
 | 
				
			||||||
 | 
					    <!-- cron 输入框 -->
 | 
				
			||||||
 | 
					    <CronGeneratorInput ref="cronInputRef" v-model="cronExpression" />
 | 
				
			||||||
 | 
					    <!-- 页脚 -->
 | 
				
			||||||
 | 
					    <template #footer>
 | 
				
			||||||
 | 
					      <a-button size="small" @click="handlerClose">关闭</a-button>
 | 
				
			||||||
 | 
					      <a-button size="small"
 | 
				
			||||||
 | 
					                type="primary"
 | 
				
			||||||
 | 
					                @click="handlerOk">
 | 
				
			||||||
 | 
					        确定
 | 
				
			||||||
 | 
					      </a-button>
 | 
				
			||||||
 | 
					    </template>
 | 
				
			||||||
 | 
					  </a-modal>
 | 
				
			||||||
 | 
					</template>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					<script lang="ts">
 | 
				
			||||||
 | 
					</script>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					<script lang="ts" setup>
 | 
				
			||||||
 | 
					import { Message } from '@arco-design/web-vue'
 | 
				
			||||||
 | 
					import CronGeneratorInput from '@/components/GenCron/CronForm/index.vue'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					const emits = defineEmits(['ok'])
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					const visible = ref<boolean>(false)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					const cronInputRef = ref<InstanceType<typeof CronGeneratorInput>>()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					const cronExpression = ref('')
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// 打开新增
 | 
				
			||||||
 | 
					const open = (cron: string = '') => {
 | 
				
			||||||
 | 
					  cronExpression.value = cron
 | 
				
			||||||
 | 
					  visible.value = true
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					defineExpose({ open })
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// 确定
 | 
				
			||||||
 | 
					const handlerOk = () => {
 | 
				
			||||||
 | 
					  if (cronInputRef.value?.checkCron()) {
 | 
				
			||||||
 | 
					    Message.error('日和周只能有一个为 [不设置]')
 | 
				
			||||||
 | 
					    return
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					  visible.value = false
 | 
				
			||||||
 | 
					  emits('ok', cronExpression.value)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// 关闭
 | 
				
			||||||
 | 
					const handlerClose = () => {
 | 
				
			||||||
 | 
					  visible.value = false
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					</script>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					<style lang="less" scoped>
 | 
				
			||||||
 | 
					</style>
 | 
				
			||||||
@@ -11,18 +11,15 @@
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
<script lang="ts" setup>
 | 
					<script lang="ts" setup>
 | 
				
			||||||
import type { LabelValueState } from '@/types/global'
 | 
					import type { LabelValueState } from '@/types/global'
 | 
				
			||||||
 | 
					import type { GiCellTagType } from '@/components/GiCell/type'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
defineOptions({ name: 'GiCellTag' })
 | 
					defineOptions({ name: 'GiCellTag' })
 | 
				
			||||||
 | 
					const props = withDefaults(defineProps<Partial<GiCellTagType>>(), {
 | 
				
			||||||
const props = defineProps({
 | 
					  dict: [{
 | 
				
			||||||
  dict: {
 | 
					    label: '',
 | 
				
			||||||
    type: Array<LabelValueState>,
 | 
					    value: ''
 | 
				
			||||||
    required: true
 | 
					  }],
 | 
				
			||||||
  },
 | 
					  value: ''
 | 
				
			||||||
  value: {
 | 
					 | 
				
			||||||
    type: [Number, String],
 | 
					 | 
				
			||||||
    required: true
 | 
					 | 
				
			||||||
  }
 | 
					 | 
				
			||||||
})
 | 
					})
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const dictItem = computed((): LabelValueState => {
 | 
					const dictItem = computed((): LabelValueState => {
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										6
									
								
								src/components/GiCell/type.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										6
									
								
								src/components/GiCell/type.ts
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,6 @@
 | 
				
			|||||||
 | 
					import type { LabelValueState } from '@/types/global'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export interface GiCellTagType {
 | 
				
			||||||
 | 
					  dict: LabelValueState[] | any[]
 | 
				
			||||||
 | 
					  value: number | string
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@@ -1,6 +1,7 @@
 | 
				
			|||||||
import { browse, mapTree } from 'xe-utils'
 | 
					import { browse, mapTree } from 'xe-utils'
 | 
				
			||||||
import { camelCase, upperFirst } from 'lodash-es'
 | 
					import { camelCase, upperFirst } from 'lodash-es'
 | 
				
			||||||
import { Message } from '@arco-design/web-vue'
 | 
					import { Message } from '@arco-design/web-vue'
 | 
				
			||||||
 | 
					import CronParser from 'cron-parser'
 | 
				
			||||||
import { isExternal } from '@/utils/validate'
 | 
					import { isExternal } from '@/utils/validate'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export function getProperty<T, K extends keyof T>(obj: T, key: K): T[K] {
 | 
					export function getProperty<T, K extends keyof T>(obj: T, key: K): T[K] {
 | 
				
			||||||
@@ -279,3 +280,75 @@ export const fileToBase64 = (file: File): Promise<string> => {
 | 
				
			|||||||
    reader.readAsDataURL(file)
 | 
					    reader.readAsDataURL(file)
 | 
				
			||||||
  })
 | 
					  })
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					export const YMD_HMS = 'yyyy-MM-dd HH:mm:ss'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/**
 | 
				
			||||||
 | 
					 * 格式化时间
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					export function dateFormat(date = new Date(), pattern = YMD_HMS) {
 | 
				
			||||||
 | 
					  if (!date) {
 | 
				
			||||||
 | 
					    return ''
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  const o = {
 | 
				
			||||||
 | 
					    'M+': date.getMonth() + 1,
 | 
				
			||||||
 | 
					    'd+': date.getDate(),
 | 
				
			||||||
 | 
					    'H+': date.getHours(),
 | 
				
			||||||
 | 
					    'm+': date.getMinutes(),
 | 
				
			||||||
 | 
					    's+': date.getSeconds(),
 | 
				
			||||||
 | 
					    'q+': Math.floor((date.getMonth() + 3) / 3),
 | 
				
			||||||
 | 
					    'S+': date.getMilliseconds()
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  let formattedDate = pattern // Start with the pattern
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  // Year Handling
 | 
				
			||||||
 | 
					  const yearMatch = formattedDate.match(/(y+)/)
 | 
				
			||||||
 | 
					  if (yearMatch) {
 | 
				
			||||||
 | 
					    formattedDate = formattedDate.replace(yearMatch[0], (`${date.getFullYear()}`).substring(4 - yearMatch[0].length))
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  // Other Formatters
 | 
				
			||||||
 | 
					  for (const k in o) {
 | 
				
			||||||
 | 
					    const reg = new RegExp(`(${k})`)
 | 
				
			||||||
 | 
					    const match = formattedDate.match(reg)
 | 
				
			||||||
 | 
					    if (match) {
 | 
				
			||||||
 | 
					      formattedDate = formattedDate.replace(match[0], (match[0].length === 1) ? o[k] : (`00${o[k]}`).substring((`${o[k]}`).length))
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  return formattedDate
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/**
 | 
				
			||||||
 | 
					 * 不含年的 cron 表达式
 | 
				
			||||||
 | 
					 * @param cron
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					const expressionNoYear = (cron: string) => {
 | 
				
			||||||
 | 
					  const vs = cron.split(' ')
 | 
				
			||||||
 | 
					  return vs.slice(0, vs.length - 1).join(' ')
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/**
 | 
				
			||||||
 | 
					 * 解析cron表达式预计未来运行时间
 | 
				
			||||||
 | 
					 * @param cron cron表达式
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					export function parseCron(cron: string) {
 | 
				
			||||||
 | 
					  try {
 | 
				
			||||||
 | 
					    const parse = expressionNoYear(cron)
 | 
				
			||||||
 | 
					    const iter = CronParser.parseExpression(parse, {
 | 
				
			||||||
 | 
					      currentDate: dateFormat(new Date())
 | 
				
			||||||
 | 
					    })
 | 
				
			||||||
 | 
					    const result: string[] = []
 | 
				
			||||||
 | 
					    for (let i = 1; i <= 5; i++) {
 | 
				
			||||||
 | 
					      const nextDate = iter.next()
 | 
				
			||||||
 | 
					      if (nextDate) {
 | 
				
			||||||
 | 
					        result.push(dateFormat(new Date(nextDate as any)))
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    return result.length > 0 ? result.join('\n') : '无执行时间'
 | 
				
			||||||
 | 
					  } catch (e) {
 | 
				
			||||||
 | 
					    return '表达式错误'
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -58,11 +58,17 @@
 | 
				
			|||||||
              >
 | 
					              >
 | 
				
			||||||
                <template #suffix>秒</template>
 | 
					                <template #suffix>秒</template>
 | 
				
			||||||
              </a-input-number>
 | 
					              </a-input-number>
 | 
				
			||||||
              <a-input
 | 
					              <div v-else style="display: flex;">
 | 
				
			||||||
                v-else
 | 
					                <a-input
 | 
				
			||||||
                v-model="form.triggerInterval"
 | 
					                    v-model="form.triggerInterval"
 | 
				
			||||||
                placeholder="请输入CRON表达式"
 | 
					                    placeholder="请输入CRON表达式"
 | 
				
			||||||
              />
 | 
					                />
 | 
				
			||||||
 | 
					                <a-button @click="openGeneratorCron(form.triggerInterval)">
 | 
				
			||||||
 | 
					                  <template #icon>
 | 
				
			||||||
 | 
					                    <icon-history />
 | 
				
			||||||
 | 
					                  </template>
 | 
				
			||||||
 | 
					                </a-button>
 | 
				
			||||||
 | 
					              </div>
 | 
				
			||||||
            </a-form-item>
 | 
					            </a-form-item>
 | 
				
			||||||
          </a-col>
 | 
					          </a-col>
 | 
				
			||||||
        </a-row>
 | 
					        </a-row>
 | 
				
			||||||
@@ -150,6 +156,7 @@
 | 
				
			|||||||
        </a-row>
 | 
					        </a-row>
 | 
				
			||||||
      </fieldset>
 | 
					      </fieldset>
 | 
				
			||||||
    </a-form>
 | 
					    </a-form>
 | 
				
			||||||
 | 
					    <CronGeneratorModal ref="genModal" @ok="(e) => form.triggerInterval = e" />
 | 
				
			||||||
  </a-modal>
 | 
					  </a-modal>
 | 
				
			||||||
</template>
 | 
					</template>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -159,6 +166,7 @@ import { useWindowSize } from '@vueuse/core'
 | 
				
			|||||||
import { addJob, listGroup, updateJob } from '@/apis/schedule'
 | 
					import { addJob, listGroup, updateJob } from '@/apis/schedule'
 | 
				
			||||||
import { useForm } from '@/hooks'
 | 
					import { useForm } from '@/hooks'
 | 
				
			||||||
import { useDict } from '@/hooks/app'
 | 
					import { useDict } from '@/hooks/app'
 | 
				
			||||||
 | 
					import CronGeneratorModal from '@/components/GenCron/CronModel/index.vue'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const emit = defineEmits<{
 | 
					const emit = defineEmits<{
 | 
				
			||||||
  (e: 'save-success'): void
 | 
					  (e: 'save-success'): void
 | 
				
			||||||
@@ -176,7 +184,7 @@ const dataId = ref()
 | 
				
			|||||||
const isUpdate = computed(() => !!dataId.value)
 | 
					const isUpdate = computed(() => !!dataId.value)
 | 
				
			||||||
const title = computed(() => (isUpdate.value ? '修改任务' : '新增任务'))
 | 
					const title = computed(() => (isUpdate.value ? '修改任务' : '新增任务'))
 | 
				
			||||||
const formRef = ref<FormInstance>()
 | 
					const formRef = ref<FormInstance>()
 | 
				
			||||||
 | 
					const genModal = ref()
 | 
				
			||||||
const rules: FormInstance['rules'] = {
 | 
					const rules: FormInstance['rules'] = {
 | 
				
			||||||
  groupName: [{ required: true, message: '请选择任务组' }],
 | 
					  groupName: [{ required: true, message: '请选择任务组' }],
 | 
				
			||||||
  jobName: [{ required: true, message: '请输入任务名称' }],
 | 
					  jobName: [{ required: true, message: '请输入任务名称' }],
 | 
				
			||||||
@@ -302,6 +310,11 @@ const onDeleteArgs = (index) => {
 | 
				
			|||||||
  args.value.splice(index, 1)
 | 
					  args.value.splice(index, 1)
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// 打开生成表达式
 | 
				
			||||||
 | 
					const openGeneratorCron = (cron: string) => {
 | 
				
			||||||
 | 
					  genModal.value.open(cron)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
defineExpose({ onAdd, onUpdate })
 | 
					defineExpose({ onAdd, onUpdate })
 | 
				
			||||||
</script>
 | 
					</script>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -36,7 +36,14 @@
 | 
				
			|||||||
      <template #triggerType="{ record }">
 | 
					      <template #triggerType="{ record }">
 | 
				
			||||||
        <GiCellTag :value="record.triggerType" :dict="job_trigger_type_enum" />: 
 | 
					        <GiCellTag :value="record.triggerType" :dict="job_trigger_type_enum" />: 
 | 
				
			||||||
        <span v-if="record.triggerType === 2">{{ record.triggerInterval }} 秒</span>
 | 
					        <span v-if="record.triggerType === 2">{{ record.triggerInterval }} 秒</span>
 | 
				
			||||||
        <span v-else>{{ record.triggerInterval }}</span>
 | 
					        <span v-else>
 | 
				
			||||||
 | 
					          <a-popover title="最近5次运行时间" position="bottom">
 | 
				
			||||||
 | 
					            <template #content>
 | 
				
			||||||
 | 
					              <a-textarea :model-value="parseCron(record.triggerInterval)" :auto-size="true" style="margin-top: 10px" />
 | 
				
			||||||
 | 
					            </template>
 | 
				
			||||||
 | 
					             <a-link>{{ record.triggerInterval }}</a-link>
 | 
				
			||||||
 | 
					          </a-popover>
 | 
				
			||||||
 | 
					         </span>
 | 
				
			||||||
      </template>
 | 
					      </template>
 | 
				
			||||||
      <template #taskType="{ record }">
 | 
					      <template #taskType="{ record }">
 | 
				
			||||||
        <GiCellTag :value="record.taskType" :dict="job_task_type_enum" />
 | 
					        <GiCellTag :value="record.taskType" :dict="job_task_type_enum" />
 | 
				
			||||||
@@ -77,7 +84,7 @@ import { type JobQuery, type JobResp, deleteJob, listGroup, listJob, triggerJob,
 | 
				
			|||||||
import type { TableInstanceColumns } from '@/components/GiTable/type'
 | 
					import type { TableInstanceColumns } from '@/components/GiTable/type'
 | 
				
			||||||
import { useTable } from '@/hooks'
 | 
					import { useTable } from '@/hooks'
 | 
				
			||||||
import { useDict } from '@/hooks/app'
 | 
					import { useDict } from '@/hooks/app'
 | 
				
			||||||
import { isMobile } from '@/utils'
 | 
					import { isMobile, parseCron } from '@/utils'
 | 
				
			||||||
import has from '@/utils/has'
 | 
					import has from '@/utils/has'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
defineOptions({ name: 'ScheduleJob' })
 | 
					defineOptions({ name: 'ScheduleJob' })
 | 
				
			||||||
@@ -180,13 +187,11 @@ const JobDetailDrawerRef = ref<InstanceType<typeof JobDetailDrawer>>()
 | 
				
			|||||||
const onDetail = (record: JobResp) => {
 | 
					const onDetail = (record: JobResp) => {
 | 
				
			||||||
  JobDetailDrawerRef.value?.onDetail(record)
 | 
					  JobDetailDrawerRef.value?.onDetail(record)
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					 | 
				
			||||||
const router = useRouter()
 | 
					const router = useRouter()
 | 
				
			||||||
// 日志
 | 
					// 日志
 | 
				
			||||||
const onLog = (record: JobResp) => {
 | 
					const onLog = (record: JobResp) => {
 | 
				
			||||||
  router.push({ path: '/schedule/log', query: { jobId: record.id, jobName: record.jobName, groupName: record.groupName } })
 | 
					  router.push({ path: '/schedule/log', query: { jobId: record.id, jobName: record.jobName, groupName: record.groupName } })
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					 | 
				
			||||||
onMounted(() => {
 | 
					onMounted(() => {
 | 
				
			||||||
  getGroupList()
 | 
					  getGroupList()
 | 
				
			||||||
})
 | 
					})
 | 
				
			||||||
 
 | 
				
			|||||||
		Reference in New Issue
	
	Block a user