From 2a4928aed9112863b841e8573ab5e33a9d142a66 Mon Sep 17 00:00:00 2001 From: tjq Date: Sun, 20 Jun 2021 14:26:56 +0800 Subject: [PATCH 1/2] feat: support DispatchStrategy --- src/components/views/JobManager.vue | 15 ++++++++++++++- src/i18n/langs/cn.js | 3 +++ src/i18n/langs/en.js | 5 ++++- 3 files changed, 21 insertions(+), 2 deletions(-) diff --git a/src/components/views/JobManager.vue b/src/components/views/JobManager.vue index db49a9d..7f03f65 100644 --- a/src/components/views/JobManager.vue +++ b/src/components/views/JobManager.vue @@ -202,7 +202,7 @@ - + @@ -212,6 +212,16 @@ + + + + + + @@ -272,6 +282,7 @@ enable: true, designatedWorkers: "", maxWorkerCount: 0, + dispatchStrategy: "", notifyUserIds: [] }, @@ -295,6 +306,8 @@ processorTypeOptions: [{key: "BUILT_IN", label: this.$t('message.builtIn')}, {key: "EXTERNAL", label: this.$t('message.external')}], // {key: "SHELL", label: "SHELL"}, {key: "PYTHON", label: "PYTHON"} // 执行方式类型 executeTypeOptions: [{key: "STANDALONE", label: this.$t('message.standalone')}, {key: "BROADCAST", label: this.$t('message.broadcast')}, {key: "MAP", label: this.$t('message.map')}, {key: "MAP_REDUCE", label: this.$t('message.mapReduce')}], + // 派发策略 + dispatchStrategyOptions: [{key: "PERFORMANCE_FIRST", label: this.$t('message.dps_performance_first')}, {key: "RANDOM", label: this.$t('message.dps_random')}], // 用户列表 userList: [], // 时间表达式校验窗口 diff --git a/src/i18n/langs/cn.js b/src/i18n/langs/cn.js index 3598499..ba98d13 100644 --- a/src/i18n/langs/cn.js +++ b/src/i18n/langs/cn.js @@ -119,6 +119,9 @@ const cn = { 'reRun': '重试', 'builtIn': '内建', 'external': '外置(动态加载)', + 'dps_title': '派发策略', + 'dps_performance_first': '性能优先', + 'dps_random': '随机', // 任务实例管理 'wfInstanceId': '工作流实例 ID', diff --git a/src/i18n/langs/en.js b/src/i18n/langs/en.js index 52a24c9..0205fb5 100644 --- a/src/i18n/langs/en.js +++ b/src/i18n/langs/en.js @@ -115,7 +115,10 @@ const en = { 'runHistory': 'History', 'reRun': 'Retry', 'builtIn': 'BUILT_IN', - 'External': 'EXTERNAL', + 'external': 'EXTERNAL', + 'dps_title': 'DispatchStrategy', + 'dps_performance_first': 'PERFORMANCE_FIRST', + 'dps_random': 'RANDOM', // JobInstance 'wfInstanceId': 'WorkflowInstanceId', From 5c948cd93ec0c3d6d2ef437c19c041851d0d348e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=9D=A8=E7=A6=8F=E7=85=A7?= Date: Fri, 5 Nov 2021 17:07:39 +0800 Subject: [PATCH 2/2] =?UTF-8?q?=E5=89=8D=E7=AB=AF=E5=BC=95=E5=85=A5cron?= =?UTF-8?q?=E7=BC=96=E8=BE=91=E5=B7=A5=E5=85=B7?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- package.json | 3 + src/components/easy-cron/format-date.js | 37 + src/components/easy-cron/index.vue | 269 +++++++ src/components/easy-cron/input-cron.vue | 122 +++ src/components/easy-cron/tabs/const.js | 21 + src/components/easy-cron/tabs/day.vue | 138 ++++ src/components/easy-cron/tabs/hour.vue | 101 +++ src/components/easy-cron/tabs/minute.vue | 94 +++ src/components/easy-cron/tabs/mixin.js | 151 ++++ src/components/easy-cron/tabs/month.vue | 101 +++ src/components/easy-cron/tabs/second.vue | 103 +++ src/components/easy-cron/tabs/week.vue | 148 ++++ src/components/easy-cron/tabs/year.vue | 95 +++ src/components/easy-cron/validator.js | 51 ++ src/components/views/JobManager.vue | 920 ++++++++++++----------- 15 files changed, 1926 insertions(+), 428 deletions(-) create mode 100644 src/components/easy-cron/format-date.js create mode 100644 src/components/easy-cron/index.vue create mode 100644 src/components/easy-cron/input-cron.vue create mode 100644 src/components/easy-cron/tabs/const.js create mode 100644 src/components/easy-cron/tabs/day.vue create mode 100644 src/components/easy-cron/tabs/hour.vue create mode 100644 src/components/easy-cron/tabs/minute.vue create mode 100644 src/components/easy-cron/tabs/mixin.js create mode 100644 src/components/easy-cron/tabs/month.vue create mode 100644 src/components/easy-cron/tabs/second.vue create mode 100644 src/components/easy-cron/tabs/week.vue create mode 100644 src/components/easy-cron/tabs/year.vue create mode 100644 src/components/easy-cron/validator.js diff --git a/package.json b/package.json index 180afd5..caf1f10 100644 --- a/package.json +++ b/package.json @@ -15,6 +15,9 @@ "dagre-d3": "^0.6.4", "element-ui": "^2.13.2", "flyio": "^0.6.14", + "cron-parser": "^2.10.0", + "debounce": "^1.2.0", + "node-gyp": "^8.3.0", "power-workflow": "^0.0.11", "vue": "^2.6.11", "vue-axios": "^2.1.5", diff --git a/src/components/easy-cron/format-date.js b/src/components/easy-cron/format-date.js new file mode 100644 index 0000000..8a55974 --- /dev/null +++ b/src/components/easy-cron/format-date.js @@ -0,0 +1,37 @@ +const dateFormat = (date, block) => { + if (!date) { + return '' + } + + let format = block || 'yyyy-MM-dd' + + date = new Date(date) + + const map = { + 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() // 毫秒 + } + + format = format.replace(/([yMdhmsqS])+/g, (all, t) => { + let v = map[t] + if (v !== undefined) { + if (all.length > 1) { + v = `0${v}` + v = v.substr(v.length - 2) + } + return v + } else if (t === 'y') { + return (date.getFullYear().toString()).substr(4 - all.length) + } + return all + }) + + return format +} + +export default dateFormat diff --git a/src/components/easy-cron/index.vue b/src/components/easy-cron/index.vue new file mode 100644 index 0000000..1f36249 --- /dev/null +++ b/src/components/easy-cron/index.vue @@ -0,0 +1,269 @@ + + + + + diff --git a/src/components/easy-cron/input-cron.vue b/src/components/easy-cron/input-cron.vue new file mode 100644 index 0000000..2372cc4 --- /dev/null +++ b/src/components/easy-cron/input-cron.vue @@ -0,0 +1,122 @@ + + + + + diff --git a/src/components/easy-cron/tabs/const.js b/src/components/easy-cron/tabs/const.js new file mode 100644 index 0000000..e076ee1 --- /dev/null +++ b/src/components/easy-cron/tabs/const.js @@ -0,0 +1,21 @@ +export const WEEK_MAP_EN = { + 'SUN': '0', + 'MON': '1', + 'TUE': '2', + 'WED': '3', + 'THU': '4', + 'FRI': '5', + 'SAT': '6' +} + +export const replaceWeekName = (c) => { + // console.info('after: ' + c) + if (c) { + Object.keys(WEEK_MAP_EN).forEach(k => { + c = c.replace(new RegExp(k, 'g'), WEEK_MAP_EN[k]) + }) + c = c.replace(new RegExp('7', 'g'), '0') + } + // console.info('after: ' + c) + return c +} diff --git a/src/components/easy-cron/tabs/day.vue b/src/components/easy-cron/tabs/day.vue new file mode 100644 index 0000000..dd45b70 --- /dev/null +++ b/src/components/easy-cron/tabs/day.vue @@ -0,0 +1,138 @@ + + + + + diff --git a/src/components/easy-cron/tabs/hour.vue b/src/components/easy-cron/tabs/hour.vue new file mode 100644 index 0000000..9b6304e --- /dev/null +++ b/src/components/easy-cron/tabs/hour.vue @@ -0,0 +1,101 @@ + + + + + diff --git a/src/components/easy-cron/tabs/minute.vue b/src/components/easy-cron/tabs/minute.vue new file mode 100644 index 0000000..dd83e95 --- /dev/null +++ b/src/components/easy-cron/tabs/minute.vue @@ -0,0 +1,94 @@ + + + + + diff --git a/src/components/easy-cron/tabs/mixin.js b/src/components/easy-cron/tabs/mixin.js new file mode 100644 index 0000000..47504bb --- /dev/null +++ b/src/components/easy-cron/tabs/mixin.js @@ -0,0 +1,151 @@ +// 主要用于日和星期的互斥使用 +const TYPE_NOT_SET = 'TYPE_NOT_SET' +const TYPE_EVERY = 'TYPE_EVERY' +const TYPE_RANGE = 'TYPE_RANGE' +const TYPE_LOOP = 'TYPE_LOOP' +const TYPE_WORK = 'TYPE_WORK' +const TYPE_LAST = 'TYPE_LAST' +const TYPE_SPECIFY = 'TYPE_SPECIFY' + +const DEFAULT_VALUE = '?' + +export default { + model: { + prop: 'prop', + event: 'change' + }, + props: { + prop: { + type: String, + default: DEFAULT_VALUE + }, + disabled: { + type: Boolean, + default: false + } + }, + data () { + const type = TYPE_EVERY + return { + DEFAULT_VALUE, + // 类型 + type, + // 启用日或者星期互斥用 + TYPE_NOT_SET, + TYPE_EVERY, + TYPE_RANGE, + TYPE_LOOP, + TYPE_WORK, + TYPE_LAST, + TYPE_SPECIFY, + // 对于不同的类型,所定义的值也有所不同 + valueRange: { + start: 0, + end: 0 + }, + valueLoop: { + start: 0, + interval: 1 + }, + valueWeek: { + start: 0, + end: 0 + }, + valueList: [], + valueWork: 1, + maxValue: 0, + minValue: 0 + } + }, + watch: { + prop (newVal) { + if (newVal === this.value_c) { + // console.info('skip ' + newVal) + return + } + this.parseProp(newVal) + } + }, + computed: { + value_c () { + let result = [] + switch (this.type) { + case TYPE_NOT_SET: + result.push('?') + break + case TYPE_EVERY: + result.push('*') + break + case TYPE_RANGE: + result.push(`${this.valueRange.start}-${this.valueRange.end}`) + break + case TYPE_LOOP: + result.push(`${this.valueLoop.start}/${this.valueLoop.interval}`) + break + case TYPE_WORK: + result.push(`${this.valueWork}W`) + break + case TYPE_LAST: + result.push('L') + break + case TYPE_SPECIFY: + result.push(this.valueList.join(',')) + break + default: + result.push(this.DEFAULT_VALUE) + break + } + return result.length > 0 ? result.join('') : this.DEFAULT_VALUE + } + }, + methods: { + parseProp (value) { + if (value === this.value_c) { + // console.info('same ' + value) + return + } + if (typeof (this.preProcessProp) === 'function') { + value = this.preProcessProp(value) + } + try { + if (!value || value === this.DEFAULT_VALUE) { + this.type = TYPE_EVERY + } else if (value.indexOf('?') >= 0) { + this.type = TYPE_NOT_SET + } else if (value.indexOf('-') >= 0) { + this.type = TYPE_RANGE + const values = value.split('-') + if (values.length >= 2) { + this.valueRange.start = parseInt(values[0]) + this.valueRange.end = parseInt(values[1]) + } + } else if (value.indexOf('/') >= 0) { + this.type = TYPE_LOOP + const values = value.split('/') + if (values.length >= 2) { + this.valueLoop.start = value[0] === '*' ? 0 : parseInt(values[0]) + this.valueLoop.interval = parseInt(values[1]) + } + } else if (value.indexOf('W') >= 0) { + this.type = TYPE_WORK + const values = value.split('W') + if (!values[0] && !isNaN(values[0])) { + this.valueWork = parseInt(values[0]) + } + } else if (value.indexOf('L') >= 0) { + this.type = TYPE_LAST + const values = value.split('L') + this.valueLast = parseInt(values[0]) + } else if (value.indexOf(',') >= 0 || !isNaN(value)) { + this.type = TYPE_SPECIFY + this.valueList = value.split(',').map(item => parseInt(item)) + } else { + this.type = TYPE_EVERY + } + } catch (e) { + // console.info(e) + this.type = TYPE_EVERY + } + } + } +} diff --git a/src/components/easy-cron/tabs/month.vue b/src/components/easy-cron/tabs/month.vue new file mode 100644 index 0000000..56abbb3 --- /dev/null +++ b/src/components/easy-cron/tabs/month.vue @@ -0,0 +1,101 @@ + + + + + diff --git a/src/components/easy-cron/tabs/second.vue b/src/components/easy-cron/tabs/second.vue new file mode 100644 index 0000000..9580c9f --- /dev/null +++ b/src/components/easy-cron/tabs/second.vue @@ -0,0 +1,103 @@ + + + + + diff --git a/src/components/easy-cron/tabs/week.vue b/src/components/easy-cron/tabs/week.vue new file mode 100644 index 0000000..d98b146 --- /dev/null +++ b/src/components/easy-cron/tabs/week.vue @@ -0,0 +1,148 @@ + + + + + diff --git a/src/components/easy-cron/tabs/year.vue b/src/components/easy-cron/tabs/year.vue new file mode 100644 index 0000000..0117f1f --- /dev/null +++ b/src/components/easy-cron/tabs/year.vue @@ -0,0 +1,95 @@ + + + + + diff --git a/src/components/easy-cron/validator.js b/src/components/easy-cron/validator.js new file mode 100644 index 0000000..80d7890 --- /dev/null +++ b/src/components/easy-cron/validator.js @@ -0,0 +1,51 @@ +import CronParser from 'cron-parser' +import { replaceWeekName } from './tabs/const' + +export default (rule, value, callback) => { + // 没填写就不校验 + if (!value) { + callback() + return true + } + const values = value.split(' ').filter(item => !!item) + if (values.length > 7) { + callback(new Error('cron表达式最多7项')) + return false + } + // 检查第7项 + let e = value + if (values.length === 7) { + const year = replaceWeekName(values[6]) + if (year !== '*' && year !== '?') { + let yearValues = [] + if (year.indexOf('-') >= 0) { + yearValues = year.split('-') + } else if (year.indexOf('/')) { + yearValues = year.split('/') + } else { + yearValues = [year] + } + // console.info(yearValues) + // 判断是否都是数字 + const checkYear = yearValues.some(item => isNaN(item)) + if (checkYear) { + callback(new Error('cron表达式参数[年]错误:' + year)) + return false + } + } + // 取其中的前六项 + e = values.slice(0, 6).join(' ') + } + // 6位 没有年 + // 5位没有秒、年 + let result = true + try { + const iter = CronParser.parseExpression(e) + iter.next() + callback() + } catch (e) { + callback(new Error('cron表达式错误:' + e)) + result = false + } + return result +} diff --git a/src/components/views/JobManager.vue b/src/components/views/JobManager.vue index 7f03f65..36b9ca9 100644 --- a/src/components/views/JobManager.vue +++ b/src/components/views/JobManager.vue @@ -1,454 +1,518 @@