editJobTrigger.vue 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281
  1. <template>
  2. <div class="sys-jobTrigger-container">
  3. <el-dialog v-model="state.isShowDialog" draggable :close-on-click-modal="false" width="700px">
  4. <template #header>
  5. <div style="color: #fff">
  6. <el-icon size="16" style="margin-right: 3px; display: inline; vertical-align: middle"> <ele-Edit /> </el-icon>
  7. <span> {{ props.title }} </span>
  8. </div>
  9. </template>
  10. <el-form :model="state.ruleForm" ref="ruleFormRef" label-width="115px">
  11. <el-row :gutter="35">
  12. <el-col :xs="24" :sm="12" :md="12" :lg="12" :xl="12" class="mb20">
  13. <el-form-item label="触发器编号" prop="triggerId" :rules="[{ required: true, message: '触发器编号不能为空', trigger: 'blur' }]">
  14. <el-input v-model="state.ruleForm.triggerId" placeholder="触发器编号" clearable />
  15. </el-form-item>
  16. </el-col>
  17. <el-col :xs="24" :sm="12" :md="12" :lg="12" :xl="12" class="mb20">
  18. <el-form-item label="触发器类型">
  19. <el-select v-model="state.ruleForm.triggerType" style="width: 100%">
  20. <el-option value="Furion.Schedule.PeriodTrigger" label="间隔"></el-option>
  21. <el-option value="Furion.Schedule.CronTrigger" label="Cron表达式"></el-option>
  22. </el-select>
  23. </el-form-item>
  24. </el-col>
  25. <el-col :xs="24" :sm="12" :md="12" :lg="12" :xl="12" class="mb20" v-if="state.ruleForm.triggerType == 'Furion.Schedule.PeriodTrigger'">
  26. <el-form-item label="间隔时间(ms)">
  27. <el-input-number v-model="periodValue" placeholder="间隔" :min="1000" :step="1000" class="w100" />
  28. </el-form-item>
  29. </el-col>
  30. <el-col :xs="24" :sm="24" :md="24" :lg="24" :xl="24" class="mb20" v-else>
  31. <el-form-item label="Cron表达式">
  32. <el-input v-model="cronValue" placeholder="Cron表达式">
  33. <template #append>
  34. <el-dropdown style="color: inherit" trigger="click" @command="macroDropDownCommand">
  35. <el-button style="margin: 0px -10px 0px -20px; color: inherit"> Macro </el-button>
  36. <template #dropdown>
  37. <el-dropdown-menu>
  38. <el-dropdown-item v-for="(item, index) in macroData" :key="index" :command="item">
  39. <el-row style="width: 240px">
  40. <el-col :span="9">{{ item.key }}</el-col>
  41. <el-col :span="15">{{ item.description }}</el-col>
  42. </el-row>
  43. </el-dropdown-item>
  44. </el-dropdown-menu>
  45. </template>
  46. </el-dropdown>
  47. <el-button style="margin: 0px -20px 0px -10px" @click="state.showCronDialog = true">Cron表达式</el-button>
  48. </template>
  49. </el-input>
  50. </el-form-item>
  51. </el-col>
  52. <el-col :xs="24" :sm="12" :md="12" :lg="12" :xl="12" class="mb20">
  53. <el-form-item label="起始时间">
  54. <el-date-picker v-model="state.ruleForm.startTime" type="datetime" placeholder="起始时间" style="width: 100%" format="YYYY-MM-DD HH:mm:ss" value-format="YYYY-MM-DD HH:mm:ss" />
  55. </el-form-item>
  56. </el-col>
  57. <el-col :xs="24" :sm="12" :md="12" :lg="12" :xl="12" class="mb20">
  58. <el-form-item label="结束时间">
  59. <el-date-picker v-model="state.ruleForm.endTime" type="datetime" placeholder="结束时间" style="width: 100%" format="YYYY-MM-DD HH:mm:ss" value-format="YYYY-MM-DD HH:mm:ss" />
  60. </el-form-item>
  61. </el-col>
  62. <el-col :xs="24" :sm="12" :md="12" :lg="12" :xl="12" class="mb20">
  63. <el-form-item label="最大触发次数">
  64. <el-input-number v-model="state.ruleForm.maxNumberOfRuns" placeholder="最大触发次数" class="w100" />
  65. </el-form-item>
  66. </el-col>
  67. <el-col :xs="24" :sm="12" :md="12" :lg="12" :xl="12" class="mb20">
  68. <el-form-item label="最大出错次数">
  69. <el-input-number v-model="state.ruleForm.maxNumberOfErrors" placeholder="最大出错次数" class="w100" />
  70. </el-form-item>
  71. </el-col>
  72. <el-col :xs="24" :sm="12" :md="12" :lg="12" :xl="12" class="mb20">
  73. <el-form-item label="重试次数">
  74. <el-input-number v-model="state.ruleForm.numRetries" placeholder="重试次数" class="w100" />
  75. </el-form-item>
  76. </el-col>
  77. <el-col :xs="24" :sm="12" :md="12" :lg="12" :xl="12" class="mb20">
  78. <el-form-item label="重试间隔(ms)">
  79. <el-input-number v-model="state.ruleForm.retryTimeout" placeholder="重试间隔ms" class="w100" />
  80. </el-form-item>
  81. </el-col>
  82. <el-col :xs="24" :sm="12" :md="12" :lg="12" :xl="12" class="mb20">
  83. <el-form-item label="立即启动">
  84. <el-radio-group v-model="state.ruleForm.startNow">
  85. <el-radio :label="true">是</el-radio>
  86. <el-radio :label="false">否</el-radio>
  87. </el-radio-group>
  88. </el-form-item>
  89. </el-col>
  90. <el-col :xs="24" :sm="12" :md="12" :lg="12" :xl="12" class="mb20">
  91. <el-form-item label="启动时执行一次">
  92. <el-radio-group v-model="state.ruleForm.runOnStart">
  93. <el-radio :label="true">是</el-radio>
  94. <el-radio :label="false">否</el-radio>
  95. </el-radio-group>
  96. </el-form-item>
  97. </el-col>
  98. <el-col :xs="24" :sm="24" :md="24" :lg="24" :xl="24" class="mb20">
  99. <el-form-item>
  100. <template v-slot:label>
  101. <div>
  102. 重置触发次数
  103. <el-tooltip raw-content content="是否在启动时重置最大触发次数等于一次的作业<br/>解决因持久化数据已完成一次触发但启动时不再执行的问题" placement="top">
  104. <SvgIcon name="fa fa-question-circle-o" :size="16" style="vertical-align: middle" />
  105. </el-tooltip>
  106. </div>
  107. </template>
  108. <el-radio-group v-model="state.ruleForm.resetOnlyOnce">
  109. <el-radio :label="true">是</el-radio>
  110. <el-radio :label="false">否</el-radio>
  111. </el-radio-group>
  112. </el-form-item>
  113. </el-col>
  114. <el-col :xs="24" :sm="24" :md="24" :lg="24" :xl="24" class="mb20">
  115. <el-form-item label="描述信息" prop="description">
  116. <el-input v-model="state.ruleForm.description" placeholder="描述信息" clearable type="textarea" />
  117. </el-form-item>
  118. </el-col>
  119. </el-row>
  120. </el-form>
  121. <template #footer>
  122. <span class="dialog-footer">
  123. <el-button @click="cancel">取 消</el-button>
  124. <el-button type="primary" @click="submit">确 定</el-button>
  125. </span>
  126. </template>
  127. </el-dialog>
  128. <el-dialog v-model="state.showCronDialog" draggable :close-on-click-modal="false" class="scrollbar">
  129. <template #header>
  130. <div style="color: #fff">
  131. <el-icon size="16" style="margin-right: 3px; display: inline; vertical-align: middle"> <ele-Edit /> </el-icon>
  132. <span> Cron表达式生成器 </span>
  133. </div>
  134. </template>
  135. <cronTab @hide="state.showCronDialog = false" @fill="crontabFill" :expression="cronValue"></cronTab>
  136. </el-dialog>
  137. </div>
  138. </template>
  139. <script lang="ts" setup name="sysEditJobTrigger">
  140. import { reactive, ref, computed } from 'vue';
  141. import type { WritableComputedRef } from 'vue';
  142. import { ElMessage } from 'element-plus';
  143. import cronTab from './cronTab/index.vue';
  144. import { getAPI } from '/@/utils/axios-utils';
  145. import { SysJobApi } from '/@/api-services/api';
  146. import { UpdateJobTriggerInput } from '/@/api-services/models';
  147. // Macro 标识符数据结构
  148. interface MacroData {
  149. key: string;
  150. description: string;
  151. }
  152. const props = defineProps({
  153. title: String,
  154. });
  155. const emits = defineEmits(['handleQuery']);
  156. const ruleFormRef = ref();
  157. const state = reactive({
  158. isShowDialog: false,
  159. ruleForm: {} as UpdateJobTriggerInput,
  160. showCronDialog: false,
  161. });
  162. const macroData: MacroData[] = reactive([
  163. { key: '@secondly', description: '每秒 .0000000' },
  164. { key: '@minutely', description: '每分钟 00' },
  165. { key: '@hourly', description: '每小时 00:00' },
  166. { key: '@daily', description: '每天 00:00:00' },
  167. { key: '@monthly', description: '每月 1 号 00:00:00' },
  168. { key: '@weekly', description: '每周日 00:00:00' },
  169. { key: '@yearly', description: '每年 1 月 1 号 00:00:00' },
  170. { key: '@workday', description: '每周一至周五 00:00:00' },
  171. ]);
  172. // 间隔周期值
  173. const periodValue: WritableComputedRef<number | undefined> = computed({
  174. get() {
  175. const defaultValue: number | undefined = undefined;
  176. // 触发器周期不是周期,返回默认值
  177. if (state.ruleForm.triggerType != 'Furion.Schedule.PeriodTrigger') return defaultValue;
  178. if (!state.ruleForm.args) return defaultValue;
  179. const value: number | undefined = Number(state.ruleForm.args);
  180. if (Number.isNaN(value)) return defaultValue;
  181. return value;
  182. },
  183. set(value: number | undefined) {
  184. state.ruleForm.args = String(value);
  185. },
  186. });
  187. // cron 表达式值
  188. const cronValue: WritableComputedRef<string> = computed({
  189. get() {
  190. const defaultValue = '';
  191. // 触发器周期不是周期,返回默认值
  192. if (state.ruleForm.triggerType != 'Furion.Schedule.CronTrigger') return defaultValue;
  193. if (!state.ruleForm.args) return defaultValue;
  194. // Furion 的 cron 表达式有2个入参
  195. const value = String(state.ruleForm.args);
  196. const parameters = value.split(',');
  197. if (parameters.length != 2) return defaultValue;
  198. const cron = parameters[0].replace(new RegExp('"', 'gm'), '').trim();
  199. return cron;
  200. },
  201. set(value: string) {
  202. if (state.ruleForm.args == value) return;
  203. const newValue = value.trim();
  204. // 第二个参数值参阅 https://furion.baiqian.ltd/docs/cron#2624-cronstringformat-%E6%A0%BC%E5%BC%8F%E5%8C%96
  205. let cronStringFormatValue = -1;
  206. // 如果是 Macro 标识符,使用默认格式
  207. if (newValue.startsWith('@')) cronStringFormatValue = 0; // 默认格式,书写顺序:分 时 天 月 周
  208. else {
  209. if (newValue.split(' ').length == 6) cronStringFormatValue = 2; // 带秒格式,书写顺序:秒 分 时 天 月 周
  210. else cronStringFormatValue = 3; // 带秒和年格式,书写顺序:秒 分 时 天 月 周 年
  211. }
  212. state.ruleForm.args = `"${newValue}",${cronStringFormatValue}`;
  213. },
  214. });
  215. // 打开弹窗
  216. const openDialog = (row: any) => {
  217. state.ruleForm = JSON.parse(JSON.stringify(row));
  218. state.isShowDialog = true;
  219. };
  220. // 关闭弹窗
  221. const closeDialog = () => {
  222. emits('handleQuery');
  223. state.isShowDialog = false;
  224. };
  225. // 取消
  226. const cancel = () => {
  227. state.isShowDialog = false;
  228. };
  229. // 提交
  230. const submit = () => {
  231. ruleFormRef.value.validate(async (valid: boolean) => {
  232. if (!valid) return;
  233. if (state.ruleForm.triggerType == 'Furion.Schedule.PeriodTrigger' && !periodValue.value) {
  234. ElMessage.warning('间隔时间不能为空');
  235. return;
  236. } else if (state.ruleForm.triggerType == 'Furion.Schedule.CronTrigger' && !cronValue.value) {
  237. ElMessage.warning('Cron表达式不能为空');
  238. return;
  239. }
  240. if (state.ruleForm.id != undefined && state.ruleForm.id > 0) {
  241. await getAPI(SysJobApi).apiSysJobUpdateJobTriggerPost(state.ruleForm);
  242. } else {
  243. await getAPI(SysJobApi).apiSysJobAddJobTriggerPost(state.ruleForm);
  244. }
  245. closeDialog();
  246. });
  247. };
  248. // cron窗体确定后值
  249. const crontabFill = (value: string | null | undefined) => {
  250. cronValue.value = value == null || value == undefined ? '' : value;
  251. };
  252. // macro 下拉选中回调
  253. const macroDropDownCommand = (item: MacroData) => {
  254. cronValue.value = item.key;
  255. };
  256. // 导出对象
  257. defineExpose({ openDialog });
  258. </script>