Browse Source

[Feature][UI Next] Add task creation and task editor. (#8393)

3.0.0/version-upgrade
Amy0104 3 years ago committed by GitHub
parent
commit
ca086c037e
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 6
      dolphinscheduler-ui-next/src/components/form/fields/checkbox.ts
  2. 8
      dolphinscheduler-ui-next/src/components/form/fields/radio.ts
  3. 4
      dolphinscheduler-ui-next/src/components/form/fields/select.ts
  4. 4
      dolphinscheduler-ui-next/src/components/form/fields/tree-select.ts
  5. 2
      dolphinscheduler-ui-next/src/components/form/types.ts
  6. 9
      dolphinscheduler-ui-next/src/components/form/use-form.ts
  7. 12
      dolphinscheduler-ui-next/src/components/monaco-editor/index.tsx
  8. 27
      dolphinscheduler-ui-next/src/service/modules/task-definition/index.ts
  9. 9
      dolphinscheduler-ui-next/src/service/modules/task-definition/types.ts
  10. 83
      dolphinscheduler-ui-next/src/views/projects/node/use-data.ts
  11. 33
      dolphinscheduler-ui-next/src/views/projects/node/use-detail.ts
  12. 91
      dolphinscheduler-ui-next/src/views/projects/task/components/node/detail-modal.tsx
  13. 53
      dolphinscheduler-ui-next/src/views/projects/task/components/node/detail.tsx
  14. 3
      dolphinscheduler-ui-next/src/views/projects/task/components/node/fields/index.ts
  15. 6
      dolphinscheduler-ui-next/src/views/projects/task/components/node/fields/use-delay-time.ts
  16. 5
      dolphinscheduler-ui-next/src/views/projects/task/components/node/fields/use-description.ts
  17. 6
      dolphinscheduler-ui-next/src/views/projects/task/components/node/fields/use-environment-name.ts
  18. 13
      dolphinscheduler-ui-next/src/views/projects/task/components/node/fields/use-failed.ts
  19. 3
      dolphinscheduler-ui-next/src/views/projects/task/components/node/fields/use-name.ts
  20. 3
      dolphinscheduler-ui-next/src/views/projects/task/components/node/fields/use-pre-tasks.ts
  21. 62
      dolphinscheduler-ui-next/src/views/projects/task/components/node/fields/use-process-name.ts
  22. 5
      dolphinscheduler-ui-next/src/views/projects/task/components/node/fields/use-run-flag.ts
  23. 5
      dolphinscheduler-ui-next/src/views/projects/task/components/node/fields/use-shell.ts
  24. 3
      dolphinscheduler-ui-next/src/views/projects/task/components/node/fields/use-task-group.ts
  25. 6
      dolphinscheduler-ui-next/src/views/projects/task/components/node/fields/use-task-priority.ts
  26. 48
      dolphinscheduler-ui-next/src/views/projects/task/components/node/fields/use-task-type.ts
  27. 26
      dolphinscheduler-ui-next/src/views/projects/task/components/node/fields/use-timeout-alarm.ts
  28. 3
      dolphinscheduler-ui-next/src/views/projects/task/components/node/fields/use-worker-group.ts
  29. 109
      dolphinscheduler-ui-next/src/views/projects/task/components/node/format-data.ts
  30. 45
      dolphinscheduler-ui-next/src/views/projects/task/components/node/tasks/use-shell.ts
  31. 71
      dolphinscheduler-ui-next/src/views/projects/task/components/node/types.ts
  32. 20
      dolphinscheduler-ui-next/src/views/projects/task/components/node/use-task.ts
  33. 10
      dolphinscheduler-ui-next/src/views/projects/task/definition/index.module.scss
  34. 46
      dolphinscheduler-ui-next/src/views/projects/task/definition/index.tsx
  35. 11
      dolphinscheduler-ui-next/src/views/projects/task/definition/types.ts
  36. 29
      dolphinscheduler-ui-next/src/views/projects/task/definition/use-table.ts
  37. 97
      dolphinscheduler-ui-next/src/views/projects/task/definition/use-task.ts
  38. 4
      dolphinscheduler-ui-next/src/views/projects/workflow/components/dag/index.tsx

6
dolphinscheduler-ui-next/src/components/form/fields/checkbox.ts

@ -15,7 +15,7 @@
* limitations under the License.
*/
import { h } from 'vue'
import { h, unref } from 'vue'
import { NCheckbox, NCheckboxGroup, NSpace } from 'naive-ui'
import type { IJsonItem } from '../types'
@ -24,7 +24,7 @@ export function renderCheckbox(
fields: { [field: string]: any }
) {
const { props, field, options } = item
if (!options || options.length === 0) {
if (!options) {
return h(NCheckbox, {
...props,
value: fields[field],
@ -39,7 +39,7 @@ export function renderCheckbox(
},
() =>
h(NSpace, null, () =>
options.map((option: object) => h(NCheckbox, { ...option }))
unref(options).map((option: object) => h(NCheckbox, { ...option }))
)
)
}

8
dolphinscheduler-ui-next/src/components/form/fields/radio.ts

@ -15,13 +15,13 @@
* limitations under the License.
*/
import { h } from 'vue'
import { h, unref } from 'vue'
import { NRadio, NRadioGroup, NSpace } from 'naive-ui'
import type { IJsonItem, IOption } from '../types'
export function renderRadio(item: IJsonItem, fields: { [field: string]: any }) {
const { props, field, options } = item
if (!options || options.length === 0) {
if (!options) {
return h(NRadio, {
...props,
value: fields[field],
@ -36,7 +36,9 @@ export function renderRadio(item: IJsonItem, fields: { [field: string]: any }) {
},
() =>
h(NSpace, null, () =>
options.map((option: IOption) => h(NRadio, option, () => option.label))
unref(options).map((option: IOption) =>
h(NRadio, option, () => option.label)
)
)
)
}

4
dolphinscheduler-ui-next/src/components/form/fields/select.ts

@ -15,7 +15,7 @@
* limitations under the License.
*/
import { h } from 'vue'
import { h, unref } from 'vue'
import { NSelect } from 'naive-ui'
import type { IJsonItem } from '../types'
@ -28,6 +28,6 @@ export function renderSelect(
...props,
value: fields[field],
onUpdateValue: (value) => void (fields[field] = value),
options
options: unref(options)
})
}

4
dolphinscheduler-ui-next/src/components/form/fields/tree-select.ts

@ -15,7 +15,7 @@
* limitations under the License.
*/
import { h } from 'vue'
import { h, unref } from 'vue'
import { NTreeSelect } from 'naive-ui'
import type { IJsonItem } from '../types'
@ -28,6 +28,6 @@ export function renderTreeSelect(
...props,
value: fields[field],
onUpdateValue: (value) => void (fields[field] = value),
options
options: unref(options)
})
}

2
dolphinscheduler-ui-next/src/components/form/types.ts

@ -60,7 +60,7 @@ interface IJsonItem {
type?: IType
validate?: FormItemRule
value?: any
options?: IOption[]
options?: IOption[] | Ref<IOption[]>
children?: IJsonItem[]
slots?: object
span?: number | Ref<number>

9
dolphinscheduler-ui-next/src/components/form/use-form.ts

@ -22,8 +22,8 @@ export function useForm() {
formRef: ref()
})
const validate = (...args: []) => {
state.formRef.validate(...args)
const validate = async (...args: []) => {
await state.formRef.validate(...args)
}
const setValues = (initialValues: { [field: string]: any }) => {
@ -36,10 +36,15 @@ export function useForm() {
state.formRef.restoreValidation()
}
const getValues = () => {
return state.formRef.model
}
return {
state,
validate,
setValues,
getValues,
restoreValidation
}
}

12
dolphinscheduler-ui-next/src/components/monaco-editor/index.tsx

@ -109,19 +109,13 @@ export default defineComponent({
await nextTick()
const dom = editorRef.value
if (dom) {
editor = monaco.editor.create(
dom,
{
editor = monaco.editor.create(dom, {
...props.options,
readOnly:
formItem.mergedDisabledRef.value || props.options?.readOnly
},
{
readOnly: formItem.mergedDisabledRef.value || props.options?.readOnly,
value: props.defaultValue ?? props.value,
language: props.language,
automaticLayout: true
}
)
})
editor.onDidChangeModelContent(() => {
const { onUpdateValue, 'onUpdate:value': _onUpdateValue } = props
const value = editor?.getValue() || ''

27
dolphinscheduler-ui-next/src/service/modules/task-definition/index.ts

@ -24,7 +24,8 @@ import {
CodeReq,
TaskDefinitionJsonObjReq,
ReleaseStateReq,
VersionReq
VersionReq,
ISingleSaveReq
} from './types'
export function queryTaskDefinitionListPaging(
@ -60,8 +61,8 @@ export function genTaskCodeList(num: number, projectCode: number) {
}
export function queryTaskDefinitionByCode(
code: CodeReq,
projectCode: ProjectCodeReq
code: number,
projectCode: number
): any {
return axios({
url: `/projects/${projectCode}/task-definition/${code}`,
@ -136,3 +137,23 @@ export function deleteVersion(
method: 'delete'
})
}
export function saveSingle(projectCode: number, data: ISingleSaveReq) {
return axios({
url: `/projects/${projectCode}/task-definition/save-single`,
method: 'post',
data
})
}
export function updateWithUpstream(
projectCode: number,
code: number,
data: ISingleSaveReq
) {
return axios({
url: `/projects/${projectCode}/task-definition/${code}/with-upstream`,
method: 'put',
data
})
}

9
dolphinscheduler-ui-next/src/service/modules/task-definition/types.ts

@ -122,6 +122,12 @@ interface TaskDefinitionVersionRes {
start: number
}
interface ISingleSaveReq {
processDefinitionCode: string
upstreamCodes: string
taskDefinitionJsonObj: string
}
export {
PageReq,
ListReq,
@ -135,5 +141,6 @@ export {
TaskDefinitionItem,
TaskDefinitionRes,
TaskDefinitionVersionItem,
TaskDefinitionVersionRes
TaskDefinitionVersionRes,
ISingleSaveReq
}

83
dolphinscheduler-ui-next/src/views/projects/node/use-data.ts

@ -1,83 +0,0 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import { omit } from 'lodash'
import type { IDataNode, ITask } from './types'
export function useData({
nodeData,
type,
taskDefinition
}: {
nodeData: IDataNode
type: string
taskDefinition?: ITask
}) {
const data = {
backfill: {},
isCreate: false
}
if (type === 'task-definition') {
if (taskDefinition) {
data.backfill = formatBackfill(taskDefinition, nodeData.taskType)
data.isCreate = false
}
}
return {
code: nodeData.id
}
}
export function formatBackfill(task: ITask, taskType: string) {
let strategy: string | undefined = task.timeoutNotifyStrategy
if (taskType === 'DEPENDENT' && task.timeoutNotifyStrategy === 'WARNFAILED') {
strategy = 'WARN,FAILED'
}
return {
code: task.code,
conditionResult: task.taskParams.conditionResult,
switchResult: task.taskParams.switchResult,
delayTime: task.delayTime,
dependence: task.taskParams.dependence,
desc: task.description,
id: task.id,
maxRetryTimes: task.failRetryTimes,
name: task.name,
params: omit(task.taskParams, [
'conditionResult',
'dependence',
'waitStartTimeout',
'switchResult'
]),
retryInterval: task.failRetryInterval,
runFlag: task.flag,
taskInstancePriority: task.taskPriority,
timeout: {
interval: task.timeout,
strategy,
enable: task.timeoutFlag === 'OPEN'
},
type: task.taskType,
waitStartTimeout: task.taskParams.waitStartTimeout,
workerGroup: task.workerGroup,
environmentCode: task.environmentCode,
taskGroupId: task.taskGroupId,
taskGroupPriority: task.taskGroupPriority
}
}

33
dolphinscheduler-ui-next/src/views/projects/node/use-detail.ts

@ -1,33 +0,0 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import { reactive, ref } from 'vue'
export function useDetail() {
const state = reactive({
formRef: ref(),
loading: false,
saving: false
})
const onSubmit = async (model: object) => {
await state.formRef.validate()
}
return {
state,
onSubmit
}
}

91
dolphinscheduler-ui-next/src/views/projects/node/detail-modal.tsx → dolphinscheduler-ui-next/src/views/projects/task/components/node/detail-modal.tsx

@ -15,19 +15,28 @@
* limitations under the License.
*/
import { defineComponent, PropType, ref } from 'vue'
import {
defineComponent,
PropType,
ref,
reactive,
toRefs,
watch,
nextTick
} from 'vue'
import { useI18n } from 'vue-i18n'
import Modal from '@/components/modal'
import Detail from './detail'
import type { NodeData } from '@/views/projects/workflow/components/dag/types'
import { formatModel } from './format-data'
import type { ITaskData } from './types'
const props = {
show: {
type: Boolean as PropType<boolean>,
default: false
},
taskDefinition: {
type: Object as PropType<NodeData>,
data: {
type: Object as PropType<ITaskData>,
default: { code: 0, taskType: 'SHELL', name: '' }
},
projectCode: {
@ -37,6 +46,10 @@ const props = {
readonly: {
type: Boolean as PropType<boolean>,
default: false
},
from: {
type: Number as PropType<number>,
default: 0
}
}
@ -46,78 +59,52 @@ const NodeDetailModal = defineComponent({
emits: ['cancel', 'submit'],
setup(props, { emit }) {
const { t } = useI18n()
const detailRef = ref()
// TODO
const mapFormToTaskDefinition = (form: any) => {
return {
// "code": form.code,
name: form.name,
description: form.desc,
taskType: 'SHELL',
taskParams: {
resourceList: [],
localParams: form.localParams,
rawScript: form.shell,
dependence: {},
conditionResult: {
successNode: [],
failedNode: []
},
waitStartTimeout: {},
switchResult: {}
},
flag: form.runFlag,
taskPriority: 'MEDIUM',
workerGroup: form.workerGroup,
failRetryTimes: '0',
failRetryInterval: '1',
timeoutFlag: 'CLOSE',
timeoutNotifyStrategy: '',
timeout: 0,
delayTime: '0',
environmentCode: form.environmentCode
}
}
const onConfirm = () => {
emit('submit', {
formRef: detailRef.value.formRef,
form: mapFormToTaskDefinition(detailRef.value.form)
const state = reactive({
saving: false,
detailRef: ref()
})
const onConfirm = async () => {
await state.detailRef.form.validate()
emit('submit', { data: state.detailRef.form.getValues() })
}
const onCancel = () => {
emit('cancel')
}
watch(
() => props.data,
async () => {
await nextTick()
state.detailRef.form.setValues(formatModel(props.data))
}
)
return {
t,
detailRef,
...toRefs(state),
onConfirm,
onCancel
}
},
render() {
const {
t,
show,
onConfirm,
onCancel,
projectCode,
taskDefinition,
readonly
} = this
const { t, show, onConfirm, onCancel, projectCode, data, readonly, from } =
this
return (
<Modal
show={show}
title={`${t('project.node.current_node_settings')}`}
onConfirm={onConfirm}
confirmLoading={false}
confirmDisabled={readonly}
onCancel={onCancel}
>
<Detail
ref='detailRef'
taskType={taskDefinition.taskType}
data={data}
projectCode={projectCode}
readonly={readonly}
from={from}
/>
</Modal>
)

53
dolphinscheduler-ui-next/src/views/projects/node/detail.tsx → dolphinscheduler-ui-next/src/views/projects/task/components/node/detail.tsx

@ -15,21 +15,31 @@
* limitations under the License.
*/
import { defineComponent, PropType, ref, toRef, toRefs } from 'vue'
import { defineComponent, PropType, ref, watch } from 'vue'
import Form from '@/components/form'
import { useTask } from './use-task'
import { useDetail } from './use-detail'
import type { ITaskType } from './types'
import getElementByJson from '@/components/form/get-elements-by-json'
import type { ITaskData } from './types'
const props = {
projectCode: {
type: Number as PropType<number>
},
taskType: {
type: String as PropType<ITaskType>,
default: 'SHELL',
required: true
data: {
type: Object as PropType<ITaskData>,
default: { taskType: 'SHELL' }
},
readonly: {
type: Boolean as PropType<boolean>,
default: false
},
loading: {
type: Boolean as PropType<boolean>,
default: false
},
from: {
type: Number as PropType<number>,
default: 0
}
}
@ -37,23 +47,34 @@ const NodeDetail = defineComponent({
name: 'NodeDetail',
props,
setup(props, { expose }) {
const { taskType, projectCode } = props
const { data, projectCode, from, readonly } = props
const { json, model } = useTask({ taskType, projectCode })
const { state } = useDetail()
const { json, model } = useTask({
taskType: data.taskType,
projectCode,
from,
readonly
})
const jsonRef = ref(json)
const formRef = ref()
const { rules, elements } = getElementByJson(jsonRef.value, model)
expose({
formRef: toRef(state, 'formRef'),
form: model
form: formRef
})
return { rules, elements, model, ...toRefs(state) }
watch(
() => model.taskType,
(taskType) => {
// TODO: Change task type
}
)
return { rules, elements, model, formRef }
},
render() {
render(props: { readonly: boolean; loading: boolean }) {
const { rules, elements, model } = this
return (
<Form
@ -61,8 +82,10 @@ const NodeDetail = defineComponent({
meta={{
model,
rules,
elements
elements,
disabled: props.readonly
}}
loading={props.loading}
layout={{
xGap: 10
}}

3
dolphinscheduler-ui-next/src/views/projects/node/fields/index.ts → dolphinscheduler-ui-next/src/views/projects/task/components/node/fields/index.ts

@ -26,4 +26,7 @@ export { useFailed } from './use-failed'
export { useDelayTime } from './use-delay-time'
export { useTimeoutAlarm } from './use-timeout-alarm'
export { usePreTasks } from './use-pre-tasks'
export { useTaskType } from './use-task-type'
export { useProcessName } from './use-process-name'
export { useShell } from './use-shell'

6
dolphinscheduler-ui-next/src/views/projects/node/fields/use-delay-time.ts → dolphinscheduler-ui-next/src/views/projects/task/components/node/fields/use-delay-time.ts

@ -16,8 +16,9 @@
*/
import { useI18n } from 'vue-i18n'
import type { IJsonItem } from '../types'
export function useDelayTime() {
export function useDelayTime(): IJsonItem {
const { t } = useI18n()
return {
type: 'input-number',
@ -26,6 +27,7 @@ export function useDelayTime() {
span: 12,
slots: {
suffix: () => t('project.node.minute')
}
},
value: 0
}
}

5
dolphinscheduler-ui-next/src/views/projects/node/fields/use-description.ts → dolphinscheduler-ui-next/src/views/projects/task/components/node/fields/use-description.ts

@ -16,12 +16,13 @@
*/
import { useI18n } from 'vue-i18n'
import type { IJsonItem } from '../types'
export function useDescription() {
export function useDescription(): IJsonItem {
const { t } = useI18n()
return {
type: 'input',
field: 'desc',
field: 'description',
name: t('project.node.description'),
props: {
placeholder: t('project.node.description_tips'),

6
dolphinscheduler-ui-next/src/views/projects/node/fields/use-environment-name.ts → dolphinscheduler-ui-next/src/views/projects/task/components/node/fields/use-environment-name.ts

@ -15,15 +15,15 @@
* limitations under the License.
*/
import { ref, reactive, onMounted, watch } from 'vue'
import { ref, onMounted, watch } from 'vue'
import { useI18n } from 'vue-i18n'
import { queryAllEnvironmentList } from '@/service/modules/environment'
import type { IEnvironmentNameOption } from '../types'
import type { IEnvironmentNameOption, IJsonItem } from '../types'
export function useEnvironmentName(
model: { [field: string]: any },
isCreate: boolean
) {
): IJsonItem {
const { t } = useI18n()
let environmentList = [] as IEnvironmentNameOption[]

13
dolphinscheduler-ui-next/src/views/projects/node/fields/use-failed.ts → dolphinscheduler-ui-next/src/views/projects/task/components/node/fields/use-failed.ts

@ -16,27 +16,30 @@
*/
import { useI18n } from 'vue-i18n'
import type { IJsonItem } from '../types'
export function useFailed() {
export function useFailed(): IJsonItem[] {
const { t } = useI18n()
return [
{
type: 'input-number',
field: 'maxRetryTimes',
field: 'failRetryTimes',
name: t('project.node.number_of_failed_retries'),
span: 12,
slots: {
suffix: () => t('project.node.times')
}
},
value: 0
},
{
type: 'input-number',
field: 'retryInterval',
field: 'failRetryInterval',
name: t('project.node.failed_retry_interval'),
span: 12,
slots: {
suffix: () => t('project.node.minute')
}
},
value: 1
}
]
}

3
dolphinscheduler-ui-next/src/views/projects/node/fields/use-name.ts → dolphinscheduler-ui-next/src/views/projects/task/components/node/fields/use-name.ts

@ -16,8 +16,9 @@
*/
import { useI18n } from 'vue-i18n'
import type { IJsonItem } from '../types'
export function useName() {
export function useName(): IJsonItem {
const { t } = useI18n()
return {
type: 'input',

3
dolphinscheduler-ui-next/src/views/projects/node/fields/use-pre-tasks.ts → dolphinscheduler-ui-next/src/views/projects/task/components/node/fields/use-pre-tasks.ts

@ -17,8 +17,9 @@
import { ref, onMounted } from 'vue'
import { useI18n } from 'vue-i18n'
import type { IJsonItem } from '../types'
export function usePreTasks() {
export function usePreTasks(): IJsonItem {
const { t } = useI18n()
const options = ref([])

62
dolphinscheduler-ui-next/src/views/projects/task/components/node/fields/use-process-name.ts

@ -0,0 +1,62 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import { ref, onMounted } from 'vue'
import { useI18n } from 'vue-i18n'
import { querySimpleList } from '@/service/modules/process-definition'
import type { IJsonItem } from '../types'
export function useProcessName(
projectCode: number,
isCreate: boolean
): IJsonItem {
const { t } = useI18n()
const options = ref([] as { label: string; value: string }[])
const loading = ref(false)
const getProcessList = async () => {
if (loading.value) return
loading.value = true
try {
const res = await querySimpleList(projectCode)
options.value = res.map((option: { name: string; code: number }) => ({
label: option.name,
value: option.code
}))
loading.value = false
} catch (err) {
loading.value = false
}
}
onMounted(() => {
getProcessList()
})
return {
type: 'select',
field: 'processCode',
span: 24,
name: t('project.node.process_name'),
props: {
loading: loading,
disabled: !isCreate
},
options: options
}
}

5
dolphinscheduler-ui-next/src/views/projects/node/fields/use-run-flag.ts → dolphinscheduler-ui-next/src/views/projects/task/components/node/fields/use-run-flag.ts

@ -16,8 +16,9 @@
*/
import { useI18n } from 'vue-i18n'
import type { IJsonItem } from '../types'
export function useRunFlag() {
export function useRunFlag(): IJsonItem {
const { t } = useI18n()
const options = [
{
@ -31,7 +32,7 @@ export function useRunFlag() {
]
return {
type: 'radio',
field: 'runFlag',
field: 'flag',
name: t('project.node.run_flag'),
options: options
}

5
dolphinscheduler-ui-next/src/views/projects/node/fields/use-shell.ts → dolphinscheduler-ui-next/src/views/projects/task/components/node/fields/use-shell.ts

@ -17,8 +17,9 @@
import { ref, onMounted } from 'vue'
import { useI18n } from 'vue-i18n'
import { queryResourceList } from '@/service/modules/resources'
import type { IJsonItem } from '../types'
export function useShell(model: { [field: string]: any }) {
export function useShell(model: { [field: string]: any }): IJsonItem[] {
const { t } = useI18n()
const options = ref([])
@ -44,7 +45,7 @@ export function useShell(model: { [field: string]: any }) {
return [
{
type: 'editor',
field: 'shell',
field: 'rawScript',
name: t('project.node.script'),
validate: {
trigger: ['input', 'trigger'],

3
dolphinscheduler-ui-next/src/views/projects/node/fields/use-task-group.ts → dolphinscheduler-ui-next/src/views/projects/task/components/node/fields/use-task-group.ts

@ -18,11 +18,12 @@
import { ref, watch, computed, onMounted } from 'vue'
import { useI18n } from 'vue-i18n'
import { queryTaskGroupListPagingByProjectCode } from '@/service/modules/task-group'
import type { IJsonItem } from '../types'
export function useTaskGroup(
model: { [field: string]: any },
projectCode: number
) {
): IJsonItem[] {
const { t } = useI18n()
const options = ref([])

6
dolphinscheduler-ui-next/src/views/projects/node/fields/use-task-priority.ts → dolphinscheduler-ui-next/src/views/projects/task/components/node/fields/use-task-priority.ts

@ -19,9 +19,9 @@ import { h, markRaw, VNode, VNodeChild } from 'vue'
import { NIcon } from 'naive-ui'
import { useI18n } from 'vue-i18n'
import { ArrowUpOutlined, ArrowDownOutlined } from '@vicons/antd'
import type { ITaskPriorityOption } from '../types'
import type { ITaskPriorityOption, IJsonItem } from '../types'
export function useTaskPriority() {
export function useTaskPriority(): IJsonItem {
const { t } = useI18n()
const options = markRaw([
{
@ -78,7 +78,7 @@ export function useTaskPriority() {
})
return {
type: 'select',
field: 'taskInstancePriority',
field: 'taskPriority',
name: t('project.node.task_priority'),
options,
validate: {

48
dolphinscheduler-ui-next/src/views/projects/task/components/node/fields/use-task-type.ts

@ -0,0 +1,48 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import { useI18n } from 'vue-i18n'
import { TASK_TYPES_MAP } from '@/views/projects/task/constants/task-type'
import type { IJsonItem } from '../types'
export function useTaskType(
model: { [field: string]: any },
readonly?: boolean
): IJsonItem {
const { t } = useI18n()
const options = Object.keys(TASK_TYPES_MAP).map((option: string) => ({
label: option,
value: option
}))
return {
type: 'select',
field: 'taskType',
span: 24,
name: t('project.node.task_type'),
props: {
disabled: readonly || ['CONDITIONS', 'SWITCH'].includes(model.taskType)
},
options: options,
validate: {
trigger: ['input', 'blur'],
required: true,
message: t('project.node.task_type_tips')
},
value: 'SHELL'
}
}

26
dolphinscheduler-ui-next/src/views/projects/node/fields/use-timeout-alarm.ts → dolphinscheduler-ui-next/src/views/projects/task/components/node/fields/use-timeout-alarm.ts

@ -17,10 +17,11 @@
import { computed, watch } from 'vue'
import { useI18n } from 'vue-i18n'
import type { IJsonItem } from '../types'
export function useTimeoutAlarm(model: { [field: string]: any }) {
export function useTimeoutAlarm(model: { [field: string]: any }): IJsonItem[] {
const { t } = useI18n()
const span = computed(() => (model.enable ? 12 : 0))
const span = computed(() => (model.timeoutFlag ? 12 : 0))
const strategyOptions = [
{
@ -33,29 +34,29 @@ export function useTimeoutAlarm(model: { [field: string]: any }) {
}
]
watch(
() => model.enable,
(enable) => {
model.strategy = enable ? ['WARN'] : []
model.interval = enable ? 30 : null
() => model.timeoutFlag,
(timeoutFlag) => {
model.strategy = timeoutFlag ? ['WARN'] : []
model.interval = timeoutFlag ? 30 : null
}
)
return [
{
type: 'switch',
field: 'enable',
field: 'timeoutFlag',
name: t('project.node.timeout_alarm')
},
{
type: 'checkbox',
field: 'strategy',
field: 'timeoutNotifyStrategy',
name: t('project.node.timeout_strategy'),
options: strategyOptions,
span: span,
validate: {
trigger: ['input'],
validator(validate: any, value: []) {
if (model.enable && !value.length) {
if (model.timeoutFlag && !value.length) {
return new Error(t('project.node.timeout_strategy_tips'))
}
}
@ -64,7 +65,7 @@ export function useTimeoutAlarm(model: { [field: string]: any }) {
},
{
type: 'input-number',
field: 'interval',
field: 'timeout',
name: t('project.node.timeout_period'),
span,
props: {
@ -76,11 +77,12 @@ export function useTimeoutAlarm(model: { [field: string]: any }) {
validate: {
trigger: ['input'],
validator(validate: any, value: number) {
if (model.enable && !/^[1-9]\d*$/.test(String(value))) {
if (model.timeoutFlag && !/^[1-9]\d*$/.test(String(value))) {
return new Error(t('project.node.timeout_period_tips'))
}
}
}
},
value: 30
}
]
}

3
dolphinscheduler-ui-next/src/views/projects/node/fields/use-worker-group.ts → dolphinscheduler-ui-next/src/views/projects/task/components/node/fields/use-worker-group.ts

@ -18,8 +18,9 @@
import { ref, onMounted } from 'vue'
import { useI18n } from 'vue-i18n'
import { queryAllWorkerGroups } from '@/service/modules/worker-groups'
import type { IJsonItem } from '../types'
export function useWorkerGroup() {
export function useWorkerGroup(): IJsonItem {
const { t } = useI18n()
const options = ref([] as { label: string; value: string }[])

109
dolphinscheduler-ui-next/src/views/projects/task/components/node/format-data.ts

@ -0,0 +1,109 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import { omit } from 'lodash'
import type { INodeData, ITaskData } from './types'
export function formatParams(data: INodeData): {
processDefinitionCode: string
upstreamCodes: string
taskDefinitionJsonObj: object
} {
const params = {
processDefinitionCode: data.processCode ? String(data.processCode) : '',
upstreamCodes: '',
taskDefinitionJsonObj: {
...omit(data, [
'processCode',
'delayTime',
'environmentCode',
'failRetryTimes',
'failRetryInterval',
'taskGroupId',
'localParams',
'timeoutFlag',
'timeoutNotifyStrategy',
'resourceList'
]),
code: data.code,
delayTime: data.delayTime ? '0' : String(data.delayTime),
environmentCode: data.environmentCode || -1,
failRetryTimes: data.failRetryTimes ? String(data.failRetryTimes) : '0',
failRetryInterval: data.failRetryTimes
? String(data.failRetryTimes)
: '0',
taskGroupId: data.taskGroupId || '',
taskParams: {
localParams: data.localParams,
rawScript: data.rawScript,
resourceList: data.resourceList?.length
? data.resourceList.map((id: number) => ({ id }))
: []
},
timeoutFlag: data.timeoutFlag ? 'OPEN' : 'CLOSE',
timeoutNotifyStrategy: data.timeoutNotifyStrategy?.join('')
}
} as {
processDefinitionCode: string
upstreamCodes: string
taskDefinitionJsonObj: { timeout: number; timeoutNotifyStrategy: string }
}
if (!data.timeoutFlag) {
params.taskDefinitionJsonObj.timeout = 0
params.taskDefinitionJsonObj.timeoutNotifyStrategy = ''
}
return params
}
export function formatModel(data: ITaskData) {
const params = {
name: data.name,
taskType: data.taskType,
processName: data.processName,
flag: data.flag,
description: data.description,
taskPriority: data.taskPriority,
workerGroup: data.workerGroup,
environmentCode: data.environmentCode === -1 ? null : data.environmentCode,
taskGroupId: data.taskGroupId,
taskGroupPriority: data.taskGroupPriority,
failRetryTimes: data.failRetryTimes,
failRetryInterval: data.failRetryInterval,
delayTime: data.delayTime,
timeoutFlag: data.timeoutFlag === 'OPEN',
timeoutNotifyStrategy: [data.timeoutNotifyStrategy] || [],
resourceList: data.taskParams.resourceList,
timeout: data.timeout,
rawScript: data.taskParams.rawScript,
localParams: data.taskParams.localParams,
preTasks: [],
id: data.id,
code: data.code
} as {
timeoutNotifyStrategy: string[]
resourceList: number[]
}
if (data.timeoutNotifyStrategy === 'WARNFAILED') {
params.timeoutNotifyStrategy = ['WARN', 'FAILED']
}
if (data.taskParams.resourceList) {
params.resourceList = data.taskParams.resourceList.map(
(item: { id: number }) => item.id
)
}
return params
}

45
dolphinscheduler-ui-next/src/views/projects/node/tasks/use-shell.ts → dolphinscheduler-ui-next/src/views/projects/task/components/node/tasks/use-shell.ts

@ -17,42 +17,49 @@
import { reactive } from 'vue'
import * as Fields from '../fields/index'
import { IJsonItem } from '../types'
import type { IJsonItem, INodeData } from '../types'
export function useShell({
isCreate,
projectCode
projectCode,
from = 0,
readonly
}: {
isCreate: boolean
projectCode: number
from?: number
readonly?: boolean
}) {
const model = reactive({
name: '',
runFlag: 'YES',
desc: '',
enable: false,
flag: 'YES',
description: '',
timeoutFlag: false,
localParams: [],
environmentCode: null,
workerGroup: 'default'
} as {
name: string
runFlag: string
desc: string
workerGroup: string
taskGroupId: string
enable: boolean
localParams: []
environmentCode: string | null
})
failRetryInterval: 1,
failRetryTimes: 0,
workerGroup: 'default',
delayTime: 0,
timeout: 30,
rawScript: ''
} as INodeData)
let extra: IJsonItem[] = []
if (from === 1) {
extra = [
Fields.useTaskType(model, readonly),
Fields.useProcessName(projectCode, !model.id)
]
}
return {
json: [
Fields.useName(),
...extra,
Fields.useRunFlag(),
Fields.useDescription(),
Fields.useTaskPriority(),
Fields.useWorkerGroup(),
Fields.useEnvironmentName(model, isCreate),
Fields.useEnvironmentName(model, !model.id),
...Fields.useTaskGroup(model, projectCode),
...Fields.useFailed(),
Fields.useDelayTime(),

71
dolphinscheduler-ui-next/src/views/projects/node/types.ts → dolphinscheduler-ui-next/src/views/projects/task/components/node/types.ts

@ -35,45 +35,48 @@ interface ILocalParam {
type: string
value?: string
}
interface ITaskParams {
conditionResult?: string
switchResult?: string
delayTime?: string
dependence?: string
waitStartTimeout?: string
}
interface ITimeout {
enable: boolean
timeout?: number
strategy?: string
}
type ITaskType = TaskType
interface ITask {
code: number
timeoutNotifyStrategy?: string
taskParams: ITaskParams
description?: string
id: number
interface INodeData {
id?: string
taskType?: ITaskType
processCode?: string
delayTime?: number
failRetryTimes?: number
name: string
params?: object
description?: string
environmentCode?: number | null
failRetryInterval?: number
flag: string
taskPriority?: number
timeout: ITimeout
timeoutFlag: 'CLOSE' | 'OPEN'
taskType?: ITaskType
workerGroup?: string
environmentCode?: string
taskGroupId?: string
failRetryTimes?: number
flag?: 'YES' | 'NO'
taskGroupId?: number
taskGroupPriority?: number
localParams?: ILocalParam[]
rawScript?: string
taskPriority?: string
timeout?: number
timeoutFlag?: boolean
timeoutNotifyStrategy?: string[]
workerGroup?: string
resourceList?: number[]
code?: number
name?: string
}
interface IDataNode {
id?: string
taskType: ITaskType
interface ITaskData
extends Omit<
INodeData,
'timeoutFlag' | 'taskPriority' | 'timeoutNotifyStrategy'
> {
name?: string
processName?: number
taskPriority?: number
timeoutFlag: 'OPEN' | 'CLOSE'
timeoutNotifyStrategy?: string | []
taskParams: {
resourceList: []
rawScript: string
localParams: ILocalParam[]
}
preTasks?: []
}
export {
@ -81,8 +84,8 @@ export {
IEnvironmentNameOption,
ILocalParam,
ITaskType,
ITask,
IDataNode,
ITaskData,
INodeData,
IFormItem,
IJsonItem
}

20
dolphinscheduler-ui-next/src/views/projects/node/use-task.ts → dolphinscheduler-ui-next/src/views/projects/task/components/node/use-task.ts

@ -14,19 +14,25 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import { useShell } from './tasks/use-shell'
import { IJsonItem, ITaskType } from './types'
import { IJsonItem, ITaskType, INodeData } from './types'
export function useTask({
taskType,
projectCode
taskType = 'SHELL',
projectCode,
from,
readonly
}: {
taskType: ITaskType
taskType?: ITaskType
from?: number
projectCode?: number
}): { json: IJsonItem[]; model: object } {
let node = {} as { json: IJsonItem[]; model: object }
readonly?: boolean
}): { json: IJsonItem[]; model: INodeData } {
console.log(taskType, 'taskType')
let node = {} as { json: IJsonItem[]; model: INodeData }
if (taskType === 'SHELL' && projectCode) {
node = useShell({ isCreate: true, projectCode: projectCode })
node = useShell({ projectCode, from, readonly })
}
return node
}

10
dolphinscheduler-ui-next/src/views/projects/task/definition/index.module.scss

@ -42,3 +42,13 @@
justify-content: center;
}
}
.links {
color: dodgerblue;
text-decoration: none;
cursor: pointer;
&:hover {
text-decoration: underline;
}
}

46
dolphinscheduler-ui-next/src/views/projects/task/definition/index.tsx

@ -16,6 +16,7 @@
*/
import { defineComponent, onMounted, toRefs, watch } from 'vue'
import { useRoute } from 'vue-router'
import {
NButton,
NCard,
@ -29,17 +30,25 @@ import {
import { SearchOutlined } from '@vicons/antd'
import { useI18n } from 'vue-i18n'
import { useTable } from './use-table'
import { useTask } from './use-task'
import { TASK_TYPES_MAP } from '@/views/projects/task/constants/task-type'
import Card from '@/components/card'
import VersionModal from './components/version-modal'
import MoveModal from './components/move-modal'
import TaskModal from '@/views/projects/task/components/node/detail-modal'
import styles from './index.module.scss'
import type { INodeData } from './types'
const TaskDefinition = defineComponent({
name: 'task-definition',
setup() {
const route = useRoute()
const projectCode = Number(route.params.projectCode)
const { t } = useI18n()
const { variables, getTableData, createColumns } = useTable()
const { task, onToggleShow, onTaskSave, onEditTask } = useTask(projectCode)
const { variables, getTableData, createColumns } = useTable(onEditTask)
const requestData = () => {
getTableData({
@ -66,7 +75,19 @@ const TaskDefinition = defineComponent({
variables.showMoveModalRef = false
requestData()
}
const onCreate = () => {
onToggleShow(true)
}
const onTaskCancel = () => {
onToggleShow(false)
}
const onTaskSubmit = async (params: { data: INodeData }) => {
const result = await onTaskSave(params.data)
if (result) {
onTaskCancel()
onRefresh()
}
}
onMounted(() => {
createColumns(variables)
requestData()
@ -79,21 +100,27 @@ const TaskDefinition = defineComponent({
return {
t,
...toRefs(variables),
...toRefs(task),
onSearch,
requestData,
onUpdatePageSize,
onRefresh
onRefresh,
onCreate,
onTaskSubmit,
onTaskCancel,
projectCode
}
},
render() {
const { t, onSearch, requestData, onUpdatePageSize, onRefresh } = this
const { t, onSearch, requestData, onUpdatePageSize, onRefresh, onCreate } =
this
return (
<>
<NCard>
<div class={styles['search-card']}>
<div>
<NButton size='small' type='primary'>
<NButton size='small' type='primary' onClick={onCreate}>
{t('project.task.create_task')}
</NButton>
</div>
@ -159,6 +186,15 @@ const TaskDefinition = defineComponent({
onCancel={() => (this.showMoveModalRef = false)}
onRefresh={onRefresh}
/>
<TaskModal
show={this.taskShow}
data={this.taskData}
onSubmit={this.onTaskSubmit}
onCancel={this.onTaskCancel}
projectCode={this.projectCode}
from={1}
readonly={this.taskReadonly}
/>
</>
)
}

11
dolphinscheduler-ui-next/src/views/projects/node/tasks/index.ts → dolphinscheduler-ui-next/src/views/projects/task/definition/types.ts

@ -14,3 +14,14 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
export type { ITaskData, INodeData } from '../components/node/types'
export type { ISingleSaveReq } from '@/service/modules/task-definition/types'
interface IRecord {
processDefinitionCode: number
taskCode: number
taskName: string
}
export { IRecord }

29
dolphinscheduler-ui-next/src/views/projects/task/definition/use-table.ts

@ -30,12 +30,14 @@ import {
deleteTaskDefinition
} from '@/service/modules/task-definition'
import { useRoute } from 'vue-router'
import styles from './index.module.scss'
import type {
TaskDefinitionItem,
TaskDefinitionRes
} from '@/service/modules/task-definition/types'
import type { IRecord } from './types'
export function useTable() {
export function useTable(onEdit: Function) {
const { t } = useI18n()
const route = useRoute()
const projectCode = Number(route.params.projectCode)
@ -48,7 +50,22 @@ export function useTable() {
},
{
title: t('project.task.task_name'),
key: 'taskName'
key: 'taskName',
render: (row: IRecord) =>
h(
'a',
{
class: styles.links,
onClick: () => {
onEdit(row, true)
}
},
{
default: () => {
return row.taskName
}
}
)
},
{
title: t('project.task.workflow_name'),
@ -116,10 +133,10 @@ export function useTable() {
size: 'small',
disabled:
['CONDITIONS', 'SWITCH'].includes(row.taskType) ||
(row.processDefinitionCode &&
(!!row.processDefinitionCode &&
row.processReleaseState === 'ONLINE'),
onClick: () => {
// handleEdit(row)
onEdit(row, false)
}
},
{
@ -141,7 +158,7 @@ export function useTable() {
type: 'info',
size: 'small',
disabled:
row.processDefinitionCode &&
!!row.processDefinitionCode &&
row.processReleaseState === 'ONLINE',
onClick: () => {
variables.showMoveModalRef = true
@ -199,7 +216,7 @@ export function useTable() {
type: 'error',
size: 'small',
disabled:
row.processDefinitionCode &&
!!row.processDefinitionCode &&
row.processReleaseState === 'ONLINE'
},
{

97
dolphinscheduler-ui-next/src/views/projects/task/definition/use-task.ts

@ -0,0 +1,97 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import { reactive } from 'vue'
import {
genTaskCodeList,
saveSingle,
queryTaskDefinitionByCode,
updateWithUpstream
} from '@/service/modules/task-definition'
import { formatParams as formatData } from '../components/node/format-data'
import type { ITaskData, INodeData, ISingleSaveReq, IRecord } from './types'
export function useTask(projectCode: number) {
const task = reactive({
taskShow: false,
taskData: {
taskType: 'SHELL'
},
taskSaving: false,
taskReadonly: false
} as { taskShow: boolean; taskData: ITaskData; taskSaving: boolean; taskReadonly: boolean })
const formatParams = (data: INodeData): ISingleSaveReq => {
const params = formatData(data)
return {
processDefinitionCode: params.processDefinitionCode,
upstreamCodes: params.upstreamCodes,
taskDefinitionJsonObj: JSON.stringify(params.taskDefinitionJsonObj)
}
}
const getTaskCode = async () => {
const result = await genTaskCodeList(1, projectCode)
return result[0]
}
const onToggleShow = (show: boolean) => {
task.taskShow = show
}
const onTaskSave = async (data: INodeData) => {
try {
if (task.taskSaving) return
task.taskSaving = true
if (data.id) {
data.code &&
(await updateWithUpstream(
projectCode,
data.code,
formatParams({ ...data, code: data.code })
))
} else {
const taskCode = await getTaskCode()
await saveSingle(projectCode, formatParams({ ...data, code: taskCode }))
}
task.taskSaving = false
return true
} catch (e) {
window.$message.error((e as Error).message)
task.taskSaving = false
return false
}
}
const onEditTask = async (row: IRecord, readonly: boolean) => {
try {
const result = await queryTaskDefinitionByCode(row.taskCode, projectCode)
task.taskData = { ...result, processName: row.processDefinitionCode }
task.taskShow = true
task.taskReadonly = readonly
} catch (e) {
window.$message.error((e as Error).message)
}
}
return {
task,
onToggleShow,
onTaskSave,
onEditTask
}
}

4
dolphinscheduler-ui-next/src/views/projects/workflow/components/dag/index.tsx

@ -33,7 +33,7 @@ import { useThemeStore } from '@/store/theme/theme'
import VersionModal from '../../definition/components/version-modal'
import { WorkflowDefinition } from './types'
import DagSaveModal from './dag-save-modal'
import TaskModal from '@/views/projects/node/detail-modal'
import TaskModal from '@/views/projects/task/components/node/detail-modal'
import './x6-style.scss'
const props = {
@ -167,7 +167,7 @@ export default defineComponent({
<TaskModal
show={taskModalVisible.value}
projectCode={props.projectCode}
taskDefinition={currTask.value}
data={currTask.value}
onSubmit={taskConfirm}
onCancel={taskCancel}
/>

Loading…
Cancel
Save