diff --git a/dolphinscheduler-ui-next/src/locales/modules/en_US.ts b/dolphinscheduler-ui-next/src/locales/modules/en_US.ts index ab4a09bd98..8ca116ac72 100644 --- a/dolphinscheduler-ui-next/src/locales/modules/en_US.ts +++ b/dolphinscheduler-ui-next/src/locales/modules/en_US.ts @@ -562,6 +562,7 @@ const project = { task_type: 'Task Type', task_type_tips: 'Please select a task type (required)', process_name: 'Process Name', + process_name_tips: 'Please select a process (required)', child_node: 'Child Node', enter_child_node: 'Enter child node', run_flag: 'Run flag', diff --git a/dolphinscheduler-ui-next/src/locales/modules/zh_CN.ts b/dolphinscheduler-ui-next/src/locales/modules/zh_CN.ts index 1cd1470e31..3103a53857 100644 --- a/dolphinscheduler-ui-next/src/locales/modules/zh_CN.ts +++ b/dolphinscheduler-ui-next/src/locales/modules/zh_CN.ts @@ -560,6 +560,7 @@ const project = { task_type: '任务类型', task_type_tips: '请选择任务类型(必选)', process_name: '工作流名称', + process_name_tips: '请选择工作流(必选)', child_node: '子节点', enter_child_node: '进入该子节点', run_flag: '运行标志', diff --git a/dolphinscheduler-ui-next/src/views/projects/task/components/node/fields/index.ts b/dolphinscheduler-ui-next/src/views/projects/task/components/node/fields/index.ts index 887a75443a..6aaf106533 100644 --- a/dolphinscheduler-ui-next/src/views/projects/task/components/node/fields/index.ts +++ b/dolphinscheduler-ui-next/src/views/projects/task/components/node/fields/index.ts @@ -32,3 +32,4 @@ export { useChildNode } from './use-child-node' export { useShell } from './use-shell' export { useSpark } from './use-spark' +export { useMr } from './use-mr' diff --git a/dolphinscheduler-ui-next/src/views/projects/task/components/node/fields/use-mr.ts b/dolphinscheduler-ui-next/src/views/projects/task/components/node/fields/use-mr.ts new file mode 100644 index 0000000000..c0ea15ede0 --- /dev/null +++ b/dolphinscheduler-ui-next/src/views/projects/task/components/node/fields/use-mr.ts @@ -0,0 +1,196 @@ +/* + * 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, computed } from 'vue' +import { useI18n } from 'vue-i18n' +import { queryResourceByProgramType } from '@/service/modules/resources' +import { removeUselessChildren } from './use-shell' +import { PROGRAM_TYPES, SPARK_VERSIONS, DeployModes } from './use-spark' +import type { IJsonItem } from '../types' + +export function useMr(model: { [field: string]: any }): IJsonItem[] { + const { t } = useI18n() + + const mainClassSpan = computed(() => + model.programType === 'PYTHON' ? 0 : 24 + ) + + const mainJarOptions = ref([]) + const resources: { [field: string]: any } = {} + + const getResourceList = async (programType: string) => { + if (resources[programType] !== void 0) { + mainJarOptions.value = resources[programType] + return + } + try { + const res = await queryResourceByProgramType({ + type: 'FILE', + programType + }) + removeUselessChildren(res) + mainJarOptions.value = res || [] + resources[programType] = res + } catch (err) {} + } + + onMounted(() => { + getResourceList(model.programType) + }) + + return [ + { + type: 'select', + field: 'programType', + span: 12, + name: t('project.node.program_type'), + options: PROGRAM_TYPES, + props: { + 'on-update:value': (value: string) => { + model.mainJar = null + model.mainClass = '' + getResourceList(value) + } + }, + value: model.programType + }, + { + type: 'input', + field: 'mainClass', + span: mainClassSpan, + name: t('project.node.main_class'), + props: { + placeholder: t('project.node.main_class_tips') + }, + validate: { + trigger: ['input', 'blur'], + required: model.programType !== 'PYTHON', + validator(validate: any, value: string) { + if (model.programType !== 'PYTHON' && !value) { + return new Error(t('project.node.main_class_tips')) + } + } + } + }, + { + type: 'tree-select', + field: 'mainJar', + name: t('project.node.main_package'), + props: { + cascade: true, + showPath: true, + checkStrategy: 'child', + placeholder: t('project.node.main_package_tips'), + keyField: 'id', + labelField: 'fullName' + }, + validate: { + trigger: ['input', 'blur'], + required: model.programType !== 'PYTHON', + validator(validate: any, value: string) { + if (!value) { + return new Error(t('project.node.main_package_tips')) + } + } + }, + options: mainJarOptions + }, + { + type: 'input', + field: 'appName', + name: t('project.node.app_name'), + props: { + placeholder: t('project.node.app_name_tips') + } + }, + { + type: 'input', + field: 'mainArgs', + name: t('project.node.main_arguments'), + props: { + type: 'textarea', + placeholder: t('project.node.main_arguments_tips') + } + }, + { + type: 'input', + field: 'others', + name: t('project.node.option_parameters'), + props: { + type: 'textarea', + placeholder: t('project.node.option_parameters_tips') + } + }, + { + type: 'tree-select', + field: 'resourceList', + name: t('project.node.resources'), + options: mainJarOptions, + props: { + multiple: true, + checkable: true, + cascade: true, + showPath: true, + checkStrategy: 'child', + placeholder: t('project.node.resources_tips'), + keyField: 'id', + labelField: 'name' + } + }, + { + type: 'custom-parameters', + field: 'localParams', + name: t('project.node.custom_parameters'), + children: [ + { + type: 'input', + field: 'prop', + span: 10, + props: { + placeholder: t('project.node.prop_tips'), + maxLength: 256 + }, + validate: { + trigger: ['input', 'blur'], + required: true, + validator(validate: any, value: string) { + if (!value) { + return new Error(t('project.node.prop_tips')) + } + + const sameItems = model.localParams.filter( + (item: { prop: string }) => item.prop === value + ) + + if (sameItems.length > 1) { + return new Error(t('project.node.prop_repeat')) + } + } + } + }, + { + type: 'input', + field: 'value', + span: 10, + props: { + placeholder: t('project.node.value_tips'), + maxLength: 256 + } + } + ] + } + ] +} diff --git a/dolphinscheduler-ui-next/src/views/projects/task/components/node/fields/use-process-name.ts b/dolphinscheduler-ui-next/src/views/projects/task/components/node/fields/use-process-name.ts index bce58895f6..b07e055baa 100644 --- a/dolphinscheduler-ui-next/src/views/projects/task/components/node/fields/use-process-name.ts +++ b/dolphinscheduler-ui-next/src/views/projects/task/components/node/fields/use-process-name.ts @@ -23,7 +23,6 @@ import { queryProcessDefinitionByCode } from '@/service/modules/process-definition' import type { IJsonItem } from '../types' -import { number } from 'echarts' export function useProcessName({ model, @@ -137,6 +136,15 @@ export function useProcessName({ disabled: !isCreate, 'on-update:value': onChange }, + validate: { + trigger: ['input', 'blur'], + required: true, + validator(validate: any, value: string) { + if (!value) { + return new Error(t('project.node.process_name_tips')) + } + } + }, options: options } } diff --git a/dolphinscheduler-ui-next/src/views/projects/task/components/node/fields/use-spark.ts b/dolphinscheduler-ui-next/src/views/projects/task/components/node/fields/use-spark.ts index e7c2f466af..ca4bf86d8d 100644 --- a/dolphinscheduler-ui-next/src/views/projects/task/components/node/fields/use-spark.ts +++ b/dolphinscheduler-ui-next/src/views/projects/task/components/node/fields/use-spark.ts @@ -317,7 +317,7 @@ export function useSpark(model: { [field: string]: any }): IJsonItem[] { ] } -const PROGRAM_TYPES = [ +export const PROGRAM_TYPES = [ { label: 'JAVA', value: 'JAVA' @@ -332,7 +332,7 @@ const PROGRAM_TYPES = [ } ] -const SPARK_VERSIONS = [ +export const SPARK_VERSIONS = [ { label: 'SPARK2', value: 'SPARK2' @@ -343,7 +343,7 @@ const SPARK_VERSIONS = [ } ] -const DeployModes = [ +export const DeployModes = [ { label: 'cluster', value: 'cluster' diff --git a/dolphinscheduler-ui-next/src/views/projects/task/components/node/format-data.ts b/dolphinscheduler-ui-next/src/views/projects/task/components/node/format-data.ts index b07b1210ca..a7af76649d 100644 --- a/dolphinscheduler-ui-next/src/views/projects/task/components/node/format-data.ts +++ b/dolphinscheduler-ui-next/src/views/projects/task/components/node/format-data.ts @@ -28,9 +28,9 @@ export function formatParams(data: INodeData): { taskParams.programType = data.programType taskParams.sparkVersion = data.sparkVersion taskParams.mainClass = data.mainClass - taskParams.mainJar = data.mainJar?.length - ? data.mainJar.map((id: number) => ({ id })) - : [] + if (data.mainJar) { + taskParams.mainJar = { id: data.mainJar } + } taskParams.deployMode = data.deployMode taskParams.appName = data.appName taskParams.driverCores = data.driverCores @@ -41,6 +41,16 @@ export function formatParams(data: INodeData): { taskParams.mainArgs = data.mainArgs taskParams.others = data.others } + if (data.taskType === 'MR') { + taskParams.programType = data.programType + taskParams.mainClass = data.mainClass + if (data.mainJar) { + taskParams.mainJar = { id: data.mainJar } + } + taskParams.appName = data.appName + taskParams.mainArgs = data.mainArgs + taskParams.others = data.others + } const params = { processDefinitionCode: data.processName ? String(data.processName) : '', @@ -109,9 +119,7 @@ export function formatModel(data: ITaskData) { ) } if (data.taskParams?.mainJar) { - params.mainJar = data.taskParams.mainJar.map( - (item: { id: number }) => item.id - ) + params.mainJar = data.taskParams?.mainJar.id } return params } diff --git a/dolphinscheduler-ui-next/src/views/projects/task/components/node/tasks/use-mr.ts b/dolphinscheduler-ui-next/src/views/projects/task/components/node/tasks/use-mr.ts new file mode 100644 index 0000000000..3c018b7a27 --- /dev/null +++ b/dolphinscheduler-ui-next/src/views/projects/task/components/node/tasks/use-mr.ts @@ -0,0 +1,82 @@ +/* + * 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 * as Fields from '../fields/index' +import type { IJsonItem, INodeData, ITaskData } from '../types' + +export function useMr({ + projectCode, + from = 0, + readonly, + data +}: { + projectCode: number + from?: number + readonly?: boolean + data?: ITaskData +}) { + const model = reactive({ + taskType: 'MR', + name: '', + flag: 'YES', + description: '', + timeoutFlag: false, + localParams: [], + environmentCode: null, + failRetryInterval: 1, + failRetryTimes: 0, + workerGroup: 'default', + delayTime: 0, + timeout: 30, + programType: 'SCALA' + } as INodeData) + + let extra: IJsonItem[] = [] + if (from === 1) { + extra = [ + Fields.useTaskType(model, readonly), + Fields.useProcessName({ + model, + projectCode, + isCreate: !data?.id, + from, + processName: data?.processName, + code: data?.code + }) + ] + } + + return { + json: [ + Fields.useName(), + ...extra, + Fields.useRunFlag(), + Fields.useDescription(), + Fields.useTaskPriority(), + Fields.useWorkerGroup(), + Fields.useEnvironmentName(model, !data?.id), + ...Fields.useTaskGroup(model, projectCode), + ...Fields.useFailed(), + Fields.useDelayTime(model), + ...Fields.useTimeoutAlarm(model), + ...Fields.useMr(model), + Fields.usePreTasks(model) + ] as IJsonItem[], + model + } +} diff --git a/dolphinscheduler-ui-next/src/views/projects/task/components/node/types.ts b/dolphinscheduler-ui-next/src/views/projects/task/components/node/types.ts index 343887119b..18f2312228 100644 --- a/dolphinscheduler-ui-next/src/views/projects/task/components/node/types.ts +++ b/dolphinscheduler-ui-next/src/views/projects/task/components/node/types.ts @@ -42,7 +42,7 @@ interface ISourceItem { interface ITaskParams { resourceList?: ISourceItem[] - mainJar?: ISourceItem[] + mainJar?: ISourceItem localParams?: ILocalParam[] rawScript?: string programType?: string @@ -84,7 +84,7 @@ interface INodeData extends Omit { preTaskOptions?: [] postTaskOptions?: [] resourceList?: number[] - mainJar?: number[] + mainJar?: number } interface ITaskData diff --git a/dolphinscheduler-ui-next/src/views/projects/task/components/node/use-task.ts b/dolphinscheduler-ui-next/src/views/projects/task/components/node/use-task.ts index a76d63e4d6..354dd438a6 100644 --- a/dolphinscheduler-ui-next/src/views/projects/task/components/node/use-task.ts +++ b/dolphinscheduler-ui-next/src/views/projects/task/components/node/use-task.ts @@ -19,6 +19,7 @@ import { useShell } from './tasks/use-shell' import { useSubProcess } from './tasks/use-sub-process' import { usePython } from './tasks/use-python' import { useSpark } from './tasks/use-spark' +import { useMr } from './tasks/use-mr' import { IJsonItem, INodeData, ITaskData } from './types' export function useTask({ @@ -66,5 +67,13 @@ export function useTask({ data }) } + if (taskType === 'MR') { + node = useMr({ + projectCode, + from, + readonly, + data + }) + } return node }