Browse Source

[Feature][UI Next] Add SHELL into project(#8339)

Amy0104 3 years ago
parent
commit
4c5a90987b
35 changed files with 1662 additions and 46 deletions
  1. 45 0
      dolphinscheduler-ui-next/src/components/form/fields/checkbox.ts
  2. 57 23
      dolphinscheduler-ui-next/src/components/form/fields/custom-parameters.ts
  3. 2 0
      dolphinscheduler-ui-next/src/components/form/fields/index.ts
  4. 12 6
      dolphinscheduler-ui-next/src/components/form/fields/input-number.ts
  5. 33 0
      dolphinscheduler-ui-next/src/components/form/fields/tree-select.ts
  6. 6 3
      dolphinscheduler-ui-next/src/components/form/get-elements-by-json.ts
  7. 6 2
      dolphinscheduler-ui-next/src/components/form/index.tsx
  8. 12 4
      dolphinscheduler-ui-next/src/components/form/types.ts
  9. 3 0
      dolphinscheduler-ui-next/src/components/modal/index.module.scss
  10. 5 1
      dolphinscheduler-ui-next/src/components/modal/index.tsx
  11. 19 6
      dolphinscheduler-ui-next/src/components/monaco-editor/index.tsx
  12. 61 0
      dolphinscheduler-ui-next/src/locales/modules/en_US.ts
  13. 56 0
      dolphinscheduler-ui-next/src/locales/modules/zh_CN.ts
  14. 1 1
      dolphinscheduler-ui-next/src/service/modules/task-group/types.ts
  15. 81 0
      dolphinscheduler-ui-next/src/views/projects/node/detail-modal.tsx
  16. 73 0
      dolphinscheduler-ui-next/src/views/projects/node/detail.tsx
  17. 29 0
      dolphinscheduler-ui-next/src/views/projects/node/fields/index.ts
  18. 31 0
      dolphinscheduler-ui-next/src/views/projects/node/fields/use-delay-time.ts
  19. 32 0
      dolphinscheduler-ui-next/src/views/projects/node/fields/use-description.ts
  20. 94 0
      dolphinscheduler-ui-next/src/views/projects/node/fields/use-environment-name.ts
  21. 42 0
      dolphinscheduler-ui-next/src/views/projects/node/fields/use-failed.ts
  22. 36 0
      dolphinscheduler-ui-next/src/views/projects/node/fields/use-name.ts
  23. 41 0
      dolphinscheduler-ui-next/src/views/projects/node/fields/use-pre-tasks.ts
  24. 38 0
      dolphinscheduler-ui-next/src/views/projects/node/fields/use-run-flag.ts
  25. 191 0
      dolphinscheduler-ui-next/src/views/projects/node/fields/use-shell.ts
  26. 85 0
      dolphinscheduler-ui-next/src/views/projects/node/fields/use-task-group.ts
  27. 92 0
      dolphinscheduler-ui-next/src/views/projects/node/fields/use-task-priority.ts
  28. 86 0
      dolphinscheduler-ui-next/src/views/projects/node/fields/use-timeout-alarm.ts
  29. 59 0
      dolphinscheduler-ui-next/src/views/projects/node/fields/use-worker-group.ts
  30. 16 0
      dolphinscheduler-ui-next/src/views/projects/node/tasks/index.ts
  31. 65 0
      dolphinscheduler-ui-next/src/views/projects/node/tasks/use-shell.ts
  32. 104 0
      dolphinscheduler-ui-next/src/views/projects/node/types.ts
  33. 84 0
      dolphinscheduler-ui-next/src/views/projects/node/use-data.ts
  34. 33 0
      dolphinscheduler-ui-next/src/views/projects/node/use-detail.ts
  35. 32 0
      dolphinscheduler-ui-next/src/views/projects/node/use-task.ts

+ 45 - 0
dolphinscheduler-ui-next/src/components/form/fields/checkbox.ts

@@ -0,0 +1,45 @@
+/*
+ * 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 { h } from 'vue'
+import { NCheckbox, NCheckboxGroup, NSpace } from 'naive-ui'
+import type { IJsonItem } from '../types'
+
+export function renderCheckbox(
+  item: IJsonItem,
+  fields: { [field: string]: any }
+) {
+  const { props, field, options } = item
+  if (!options || options.length === 0) {
+    return h(NCheckbox, {
+      ...props,
+      value: fields[field],
+      onUpdateChecked: (checked: boolean) => void (fields[field] = checked)
+    })
+  }
+  return h(
+    NCheckboxGroup,
+    {
+      value: fields[field],
+      onUpdateValue: (value) => void (fields[field] = value)
+    },
+    () =>
+      h(NSpace, null, () =>
+        options.map((option: object) => h(NCheckbox, { ...option }))
+      )
+  )
+}

+ 57 - 23
dolphinscheduler-ui-next/src/components/form/fields/custom-parameters.ts

@@ -15,13 +15,48 @@
  * limitations under the License.
  */
 
-import { h } from 'vue'
-import { NFormItem, NSpace, NButton, NIcon } from 'naive-ui'
+import { defineComponent, h, renderSlot } from 'vue'
+import { useFormItem } from 'naive-ui/es/_mixins'
+import { NFormItemGi, NSpace, NButton, NIcon, NGrid } from 'naive-ui'
 import { PlusCircleOutlined, DeleteOutlined } from '@vicons/antd'
+import { omit } from 'lodash'
 import getField from './get-field'
 import { formatValidate } from '../utils'
 import type { IJsonItem, FormItemRule } from '../types'
 
+const CustomParameters = defineComponent({
+  name: 'CustomParameters',
+  emits: ['add'],
+  setup(props, ctx) {
+    const formItem = useFormItem({})
+
+    const onAdd = () => void ctx.emit('add')
+
+    return { onAdd, disabled: formItem.mergedDisabledRef }
+  },
+  render() {
+    const { disabled, $slots, onAdd } = this
+    return h(NSpace, null, {
+      default: () => {
+        return [
+          renderSlot($slots, 'default', { disabled }),
+          h(
+            NButton,
+            {
+              tertiary: true,
+              circle: true,
+              type: 'info',
+              disabled,
+              onClick: onAdd
+            },
+            () => h(NIcon, { size: 24 }, () => h(PlusCircleOutlined))
+          )
+        ]
+      }
+    })
+  }
+})
+
 export function renderCustomParameters(
   item: IJsonItem,
   fields: { [field: string]: any },
@@ -30,6 +65,7 @@ export function renderCustomParameters(
   const { field, children = [] } = item
   let defaultValue: { [field: string]: any } = {}
   let ruleItem: { [key: string]: FormItemRule } = {}
+
   children.forEach((child) => {
     defaultValue[child.field] = child.value || null
     if (child.validate) ruleItem[child.field] = formatValidate(child.validate)
@@ -37,18 +73,19 @@ export function renderCustomParameters(
   const getChild = (item: object, i: number) =>
     children.map((child: IJsonItem) => {
       return h(
-        NFormItem,
+        NFormItemGi,
         {
           showLabel: false,
-          path: `${field}[${i}].${child.field}`
+          ...omit(item, ['field', 'type', 'props', 'options']),
+          path: `${field}[${i}].${child.field}`,
+          span: 6
         },
         () => getField(child, item)
       )
     })
-
-  const getChildren = () =>
+  const getChildren = ({ disabled }: { disabled: boolean }) =>
     fields[field].map((item: object, i: number) => {
-      return h(NSpace, { ':key': i }, () => [
+      return h(NGrid, { xGap: 10 }, () => [
         ...getChild(item, i),
         h(
           NButton,
@@ -56,6 +93,7 @@ export function renderCustomParameters(
             tertiary: true,
             circle: true,
             type: 'error',
+            disabled,
             onClick: () => {
               fields[field].splice(i, 1)
               rules.splice(i, 1)
@@ -66,20 +104,16 @@ export function renderCustomParameters(
       ])
     })
 
-  return h(NSpace, null, () => [
-    ...getChildren(),
-    h(
-      NButton,
-      {
-        tertiary: true,
-        circle: true,
-        type: 'info',
-        onClick: () => {
-          rules.push(ruleItem)
-          fields[field].push({ ...defaultValue })
-        }
-      },
-      () => h(NIcon, { size: 24 }, () => h(PlusCircleOutlined))
-    )
-  ])
+  return h(
+    CustomParameters,
+    {
+      onAdd: () => {
+        rules.push(ruleItem)
+        fields[field].push({ ...defaultValue })
+      }
+    },
+    {
+      default: getChildren
+    }
+  )
 }

+ 2 - 0
dolphinscheduler-ui-next/src/components/form/fields/index.ts

@@ -22,3 +22,5 @@ export { renderCustomParameters } from './custom-parameters'
 export { renderSwitch } from './switch'
 export { renderInputNumber } from './input-number'
 export { renderSelect } from './select'
+export { renderCheckbox } from './checkbox'
+export { renderTreeSelect } from './tree-select'

+ 12 - 6
dolphinscheduler-ui-next/src/components/form/fields/input-number.ts

@@ -23,11 +23,17 @@ export function renderInputNumber(
   item: IJsonItem,
   fields: { [field: string]: any }
 ) {
-  const { props, field } = item
+  const { props, field, slots = {} } = item
 
-  return h(NInputNumber, {
-    ...props,
-    value: fields[field],
-    onUpdateValue: (value) => void (fields[field] = value)
-  })
+  return h(
+    NInputNumber,
+    {
+      ...props,
+      value: fields[field],
+      onUpdateValue: (value) => void (fields[field] = value)
+    },
+    {
+      ...slots
+    }
+  )
 }

+ 33 - 0
dolphinscheduler-ui-next/src/components/form/fields/tree-select.ts

@@ -0,0 +1,33 @@
+/*
+ * 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 { h } from 'vue'
+import { NTreeSelect } from 'naive-ui'
+import type { IJsonItem } from '../types'
+
+export function renderTreeSelect(
+  item: IJsonItem,
+  fields: { [field: string]: any }
+) {
+  const { props = {}, field, options = [] } = item
+  return h(NTreeSelect, {
+    ...props,
+    value: fields[field],
+    onUpdateValue: (value) => void (fields[field] = value),
+    options
+  })
+}

+ 6 - 3
dolphinscheduler-ui-next/src/components/form/get-elements-by-json.ts

@@ -15,6 +15,7 @@
  * limitations under the License.
  */
 
+import { toRef } from 'vue'
 import { formatValidate } from './utils'
 import getField from './fields/get-field'
 import { omit } from 'lodash'
@@ -29,18 +30,20 @@ export default function getElementByJson(
   const initialValues: { [field: string]: any } = {}
   const elements = []
   for (let item of json) {
-    const { name, value, field, children, validate, ...rest } = item
-    if (value) {
+    const { name, value, field, span, children, validate, ...rest } = item
+    if (value || value === 0) {
       fields[field] = value
       initialValues[field] = value
     }
     if (validate) rules[field] = formatValidate(validate)
+    const spanRef = span === void 0 ? 24 : toRef(item, 'span')
     elements.push({
       showLabel: !!name,
       ...omit(rest, ['type', 'props', 'options']),
       label: name,
       path: !children ? field : '',
-      widget: () => getField(item, fields, rules)
+      widget: () => getField(item, fields, rules),
+      span: spanRef
     })
   }
   return { rules, elements, initialValues }

+ 6 - 2
dolphinscheduler-ui-next/src/components/form/index.tsx

@@ -15,7 +15,7 @@
  * limitations under the License.
  */
 
-import { defineComponent, PropType, toRefs, h } from 'vue'
+import { defineComponent, PropType, toRefs, h, toRef, isRef } from 'vue'
 import { NSpin, NGrid, NForm, NFormItemGi } from 'naive-ui'
 import { useForm } from './use-form'
 import type { GridProps, IMeta } from './types'
@@ -55,7 +55,11 @@ const Form = defineComponent({
             {elements.map((element) => {
               const { span = 24, path, widget, ...formItemProps } = element
               return (
-                <NFormItemGi {...formItemProps} span={span} path={path}>
+                <NFormItemGi
+                  {...formItemProps}
+                  span={isRef(span) ? span.value : span}
+                  path={path}
+                >
                   {h(widget)}
                 </NFormItemGi>
               )

+ 12 - 4
dolphinscheduler-ui-next/src/components/form/types.ts

@@ -14,14 +14,15 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-
+import { Ref } from 'vue'
 import type {
   GridProps,
   FormProps,
   FormItemGiProps,
   FormItemRule,
   FormRules,
-  SelectOption
+  SelectOption,
+  TreeSelectOption
 } from 'naive-ui'
 
 type IType =
@@ -32,11 +33,16 @@ type IType =
   | 'switch'
   | 'input-number'
   | 'select'
+  | 'checkbox'
+  | 'tree-select'
 
-type IOption = SelectOption
+interface IOption extends SelectOption, TreeSelectOption {
+  label: string
+}
 
-interface IFormItem extends FormItemGiProps {
+interface IFormItem extends Omit<FormItemGiProps, 'span'> {
   widget: any
+  span?: any
 }
 
 interface IMeta extends Omit<FormProps, 'model'> {
@@ -54,6 +60,8 @@ interface IJsonItem {
   value?: any
   options?: IOption[]
   children?: IJsonItem[]
+  slots?: object
+  span?: number | Ref<number>
 }
 
 export {

+ 3 - 0
dolphinscheduler-ui-next/src/components/modal/index.module.scss

@@ -18,3 +18,6 @@
 .container {
   width: 600px;
 }
+.modal-card {
+  max-height: 100vh;
+}

+ 5 - 1
dolphinscheduler-ui-next/src/components/modal/index.tsx

@@ -81,7 +81,11 @@ const Modal = defineComponent({
         mask-closable={false}
         auto-focus={this.autoFocus}
       >
-        <NCard title={this.title}>
+        <NCard
+          title={this.title}
+          class={styles['modal-card']}
+          contentStyle={{ overflowY: 'auto' }}
+        >
           {{
             default: () => renderSlot($slots, 'default'),
             footer: () => (

+ 19 - 6
dolphinscheduler-ui-next/src/components/monaco-editor/index.tsx

@@ -98,17 +98,30 @@ export default defineComponent({
         }
       }
     )
+    watch(
+      () => formItem.mergedDisabledRef.value,
+      (value) => {
+        editor?.updateOptions({ readOnly: value })
+      }
+    )
 
     onMounted(async () => {
       await nextTick()
       const dom = editorRef.value
       if (dom) {
-        editor = monaco.editor.create(dom, props.options, {
-          value: props.defaultValue ?? props.value,
-          language: props.language,
-          readOnly: props.readOnly,
-          automaticLayout: true
-        })
+        editor = monaco.editor.create(
+          dom,
+          {
+            ...props.options,
+            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() || ''

+ 61 - 0
dolphinscheduler-ui-next/src/locales/modules/en_US.ts

@@ -497,6 +497,67 @@ const project = {
     rows: 'Rows',
     cols: 'Cols',
     copy_success: 'Copy Success'
+  },
+  node: {
+    current_node_settings: 'Current node settings',
+    instructions: 'Instructions',
+    view_history: 'View history',
+    view_log: 'View log',
+    enter_this_child_node: 'Enter this child node',
+    name: 'Node name',
+    name_tips: 'Please enter name (required)',
+    task_type: 'Task Type',
+    task_type_tips: 'Please select a task type (required)',
+    process_name: 'Process Name',
+    run_flag: 'Run flag',
+    normal: 'Normal',
+    prohibition_execution: 'Prohibition execution',
+    description: 'Description',
+    description_tips: 'Please enter description',
+    task_priority: 'Task priority',
+    worker_group: 'Worker group',
+    worker_group_tips:
+      'The Worker group no longer exists, please select the correct Worker group!',
+    environment_name: 'Environment Name',
+    task_group_name: 'Task group name',
+    task_group_queue_priority: 'Priority',
+    number_of_failed_retries: 'Number of failed retries',
+    times: 'Times',
+    failed_retry_interval: 'Failed retry interval',
+    minute: 'Minute',
+    delay_execution_time: 'Delay execution time',
+    state: 'State',
+    branch_flow: 'Branch flow',
+    cancel: 'Cancel',
+    loading: 'Loading...',
+    confirm: 'Confirm',
+    success: 'Success',
+    failed: 'Failed',
+    backfill_tips:
+      'The newly created sub-Process has not yet been executed and cannot enter the sub-Process',
+    task_instance_tips:
+      'The task has not been executed and cannot enter the sub-Process',
+    branch_tips:
+      'Cannot select the same node for successful branch flow and failed branch flow',
+    timeout_alarm: 'Timeout alarm',
+    timeout_strategy: 'Timeout strategy',
+    timeout_strategy_tips: 'Timeout strategy must be selected',
+    timeout_failure: 'Timeout failure',
+    timeout_period: 'Timeout period',
+    timeout_period_tips: 'Timeout must be a positive integer',
+    script: 'Script',
+    script_tips: 'Please enter script(required)',
+    resources: 'Resources',
+    resources_tips: 'Please select resources',
+    non_resources_tips: 'Please delete all non-existent resources',
+    useless_resources_tips: 'Unauthorized or deleted resources',
+    custom_parameters: 'Custom Parameters',
+    copy_success: 'Copy success',
+    copy_failed: 'The browser does not support automatic copying',
+    prop_tips: 'prop(required)',
+    prop_repeat: 'prop is repeat',
+    value_tips: 'value(optional)',
+    pre_tasks: 'Pre tasks'
   }
 }
 

+ 56 - 0
dolphinscheduler-ui-next/src/locales/modules/zh_CN.ts

@@ -496,6 +496,62 @@ const project = {
     rows: '行数',
     cols: '列数',
     copy_success: '复制成功'
+  },
+  node: {
+    current_node_settings: '当前节点设置',
+    instructions: '使用说明',
+    view_history: '查看历史',
+    view_log: '查看日志',
+    enter_this_child_node: '进入该子节点',
+    name: '节点名称',
+    name_tips: '请输入名称(必填)',
+    task_type: '任务类型',
+    task_type_tips: '请选择任务类型(必选)',
+    process_name: '工作流名称',
+    run_flag: '运行标志',
+    normal: '正常',
+    prohibition_execution: '禁止执行',
+    description: '描述',
+    description_tips: '请输入描述',
+    task_priority: '任务优先级',
+    worker_group: 'Worker分组',
+    worker_group_tips: '该Worker分组已经不存在,请选择正确的Worker分组!',
+    environment_name: '环境名称',
+    task_group_name: '任务组名称',
+    task_group_queue_priority: '组内优先级',
+    number_of_failed_retries: '失败重试次数',
+    times: '次',
+    failed_retry_interval: '失败重试间隔',
+    minute: '分',
+    delay_execution_time: '延时执行时间',
+    state: '状态',
+    branch_flow: '分支流转',
+    cancel: '取消',
+    loading: '正在努力加载中...',
+    confirm: '确定',
+    success: '成功',
+    failed: '失败',
+    backfill_tips: '新创建子工作流还未执行,不能进入子工作流',
+    task_instance_tips: '该任务还未执行,不能进入子工作流',
+    branch_tips: '成功分支流转和失败分支流转不能选择同一个节点',
+    timeout_alarm: '超时告警',
+    timeout_strategy: '超时策略',
+    timeout_strategy_tips: '超时策略必须选一个',
+    timeout_failure: '超时失败',
+    timeout_period: '超时时长',
+    timeout_period_tips: '超时时长必须为正整数',
+    script: '脚本',
+    script_tips: '请输入脚本(必填)',
+    resources: '资源',
+    resources_tips: '请选择资源',
+    no_resources_tips: '请删除所有未授权或已删除资源',
+    useless_resources_tips: '未授权或已删除资源',
+    custom_parameters: '自定义参数',
+    copy_failed: '该浏览器不支持自动复制',
+    prop_tips: 'prop(必填)',
+    prop_repeat: 'prop中有重复',
+    value_tips: 'value(选填)',
+    pre_tasks: '前置任务'
   }
 }
 

+ 1 - 1
dolphinscheduler-ui-next/src/service/modules/task-group/types.ts

@@ -18,7 +18,7 @@
 interface ListReq {
   pageNo: number
   pageSize: number
-  searchVal?: string
+  projectCode?: number
 }
 
 interface TaskGroupIdReq {

+ 81 - 0
dolphinscheduler-ui-next/src/views/projects/node/detail-modal.tsx

@@ -0,0 +1,81 @@
+/*
+ * 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 { defineComponent, PropType, ref } from 'vue'
+import { useI18n } from 'vue-i18n'
+import Modal from '@/components/modal'
+import Detail from './detail'
+import type { IDataNode, ITask } from './types'
+
+const props = {
+  show: {
+    type: Boolean as PropType<boolean>,
+    default: false
+  },
+  nodeData: {
+    type: Object as PropType<IDataNode>,
+    default: {
+      taskType: 'SHELL'
+    }
+  },
+  type: {
+    type: String as PropType<string>,
+    default: ''
+  },
+  taskDefinition: {
+    type: Object as PropType<ITask>
+  }
+}
+
+const NodeDetailModal = defineComponent({
+  name: 'NodeDetailModal',
+  props,
+  emits: ['cancel', 'update'],
+  setup(props, { emit }) {
+    const { t } = useI18n()
+    const detailRef = ref()
+    const onConfirm = () => {
+      detailRef.value.onSubmit()
+    }
+    const onCancel = () => {
+      emit('cancel')
+    }
+
+    return {
+      t,
+      detailRef,
+      onConfirm,
+      onCancel
+    }
+  },
+  render() {
+    const { t, show, onConfirm, onCancel } = this
+    return (
+      <Modal
+        show={show}
+        title={`${t('project.node.current_node_settings')}`}
+        onConfirm={onConfirm}
+        confirmLoading={false}
+        onCancel={() => void onCancel()}
+      >
+        <Detail ref='detailRef' taskType='SHELL' projectCode={111} />
+      </Modal>
+    )
+  }
+})
+
+export default NodeDetailModal

+ 73 - 0
dolphinscheduler-ui-next/src/views/projects/node/detail.tsx

@@ -0,0 +1,73 @@
+/*
+ * 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 { defineComponent, PropType, ref, toRefs } 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'
+
+const props = {
+  projectCode: {
+    type: Number as PropType<number>
+  },
+  taskType: {
+    type: String as PropType<ITaskType>,
+    default: 'SHELL',
+    required: true
+  }
+}
+
+const NodeDetail = defineComponent({
+  name: 'NodeDetail',
+  props,
+  setup(props, { expose }) {
+    const { taskType, projectCode } = props
+
+    const { json, model } = useTask({ taskType, projectCode })
+    const { state, onSubmit } = useDetail()
+
+    const jsonRef = ref(json)
+
+    const { rules, elements } = getElementByJson(jsonRef.value, model)
+
+    expose({
+      onSubmit: () => void onSubmit(model)
+    })
+
+    return { rules, elements, model, ...toRefs(state) }
+  },
+  render() {
+    const { rules, elements, model } = this
+    return (
+      <Form
+        ref='formRef'
+        meta={{
+          model,
+          rules,
+          elements
+        }}
+        layout={{
+          xGap: 10
+        }}
+      ></Form>
+    )
+  }
+})
+
+export default NodeDetail

+ 29 - 0
dolphinscheduler-ui-next/src/views/projects/node/fields/index.ts

@@ -0,0 +1,29 @@
+/*
+ * 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.
+ */
+
+export { useName } from './use-name'
+export { useRunFlag } from './use-run-flag'
+export { useDescription } from './use-description'
+export { useTaskPriority } from './use-task-priority'
+export { useWorkerGroup } from './use-worker-group'
+export { useEnvironmentName } from './use-environment-name'
+export { useTaskGroup } from './use-task-group'
+export { useFailed } from './use-failed'
+export { useDelayTime } from './use-delay-time'
+export { useTimeoutAlarm } from './use-timeout-alarm'
+export { usePreTasks } from './use-pre-tasks'
+export { useShell } from './use-shell'

+ 31 - 0
dolphinscheduler-ui-next/src/views/projects/node/fields/use-delay-time.ts

@@ -0,0 +1,31 @@
+/*
+ * 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'
+
+export function useDelayTime() {
+  const { t } = useI18n()
+  return {
+    type: 'input-number',
+    field: 'delayTime',
+    name: t('project.node.delay_execution_time'),
+    span: 12,
+    slots: {
+      suffix: () => t('project.node.minute')
+    }
+  }
+}

+ 32 - 0
dolphinscheduler-ui-next/src/views/projects/node/fields/use-description.ts

@@ -0,0 +1,32 @@
+/*
+ * 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'
+
+export function useDescription() {
+  const { t } = useI18n()
+  return {
+    type: 'input',
+    field: 'desc',
+    name: t('project.node.description'),
+    props: {
+      placeholder: t('project.node.description_tips'),
+      rows: 2,
+      type: 'textarea'
+    }
+  }
+}

+ 94 - 0
dolphinscheduler-ui-next/src/views/projects/node/fields/use-environment-name.ts

@@ -0,0 +1,94 @@
+/*
+ * 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, reactive, onMounted, watch } from 'vue'
+import { useI18n } from 'vue-i18n'
+import { queryAllEnvironmentList } from '@/service/modules/environment'
+import type { IEnvironmentNameOption } from '../types'
+
+export function useEnvironmentName(
+  model: { [field: string]: any },
+  isCreate: boolean
+) {
+  const { t } = useI18n()
+
+  let environmentList = [] as IEnvironmentNameOption[]
+  const options = ref([] as IEnvironmentNameOption[])
+  const loading = ref(false)
+  const value = ref()
+
+  const getEnvironmentList = async () => {
+    if (loading.value) return
+    loading.value = true
+    try {
+      const res = await queryAllEnvironmentList()
+      environmentList = res.map((item: { code: string; name: string }) => ({
+        label: item.name,
+        value: item.code
+      }))
+      options.value = environmentList.filter((option: IEnvironmentNameOption) =>
+        filterByWorkerGroup(option)
+      )
+      loading.value = false
+    } catch (err) {
+      loading.value = false
+    }
+  }
+
+  const filterByWorkerGroup = (option: IEnvironmentNameOption) => {
+    if (!model.workerGroup) return false
+    if (!option?.workerGroups?.length) return false
+    if (option.workerGroups.indexOf(model.workerGroup) === -1) return false
+    return true
+  }
+
+  watch(
+    () => options.value.length,
+    () => {
+      if (isCreate && options.value.length === 1 && !value.value) {
+        model.environmentCode = options.value[0].value
+      }
+      if (options.value.length === 0) model.environmentCode = null
+    }
+  )
+
+  watch(
+    () => model.workerGroup,
+    () => {
+      if (!model.workerGroup) return
+      options.value = environmentList.filter((option: IEnvironmentNameOption) =>
+        filterByWorkerGroup(option)
+      )
+    }
+  )
+
+  onMounted(() => {
+    getEnvironmentList()
+  })
+
+  return {
+    type: 'select',
+    field: 'environmentCode',
+    span: 12,
+    name: t('project.node.environment_name'),
+    props: {
+      loading: loading,
+      clearable: true
+    },
+    options: options
+  }
+}

+ 42 - 0
dolphinscheduler-ui-next/src/views/projects/node/fields/use-failed.ts

@@ -0,0 +1,42 @@
+/*
+ * 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'
+
+export function useFailed() {
+  const { t } = useI18n()
+  return [
+    {
+      type: 'input-number',
+      field: 'maxRetryTimes',
+      name: t('project.node.number_of_failed_retries'),
+      span: 12,
+      slots: {
+        suffix: () => t('project.node.times')
+      }
+    },
+    {
+      type: 'input-number',
+      field: 'retryInterval',
+      name: t('project.node.failed_retry_interval'),
+      span: 12,
+      slots: {
+        suffix: () => t('project.node.minute')
+      }
+    }
+  ]
+}

+ 36 - 0
dolphinscheduler-ui-next/src/views/projects/node/fields/use-name.ts

@@ -0,0 +1,36 @@
+/*
+ * 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'
+
+export function useName() {
+  const { t } = useI18n()
+  return {
+    type: 'input',
+    field: 'name',
+    name: t('project.node.name'),
+    props: {
+      placeholder: t('project.node.name_tips'),
+      maxLength: 100
+    },
+    validate: {
+      trigger: ['input', 'blur'],
+      required: true,
+      message: t('project.node.name_tips')
+    }
+  }
+}

+ 41 - 0
dolphinscheduler-ui-next/src/views/projects/node/fields/use-pre-tasks.ts

@@ -0,0 +1,41 @@
+/*
+ * 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'
+
+export function usePreTasks() {
+  const { t } = useI18n()
+
+  const options = ref([])
+  const loading = ref(false)
+
+  onMounted(() => {})
+
+  return {
+    type: 'select',
+    field: 'preTasks',
+    span: 24,
+    name: t('project.node.pre_tasks'),
+    props: {
+      loading,
+      multiple: true,
+      filterable: true
+    },
+    options
+  }
+}

+ 38 - 0
dolphinscheduler-ui-next/src/views/projects/node/fields/use-run-flag.ts

@@ -0,0 +1,38 @@
+/*
+ * 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'
+
+export function useRunFlag() {
+  const { t } = useI18n()
+  const options = [
+    {
+      label: t('project.node.normal'),
+      value: 'YES'
+    },
+    {
+      label: t('project.node.prohibition_execution'),
+      value: 'NO'
+    }
+  ]
+  return {
+    type: 'radio',
+    field: 'runFlag',
+    name: t('project.node.run_flag'),
+    options: options
+  }
+}

+ 191 - 0
dolphinscheduler-ui-next/src/views/projects/node/fields/use-shell.ts

@@ -0,0 +1,191 @@
+/*
+ * 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 { queryResourceList } from '@/service/modules/resources'
+
+export function useShell(model: { [field: string]: any }) {
+  const { t } = useI18n()
+  const options = ref([])
+
+  const loading = ref(false)
+
+  const getResourceList = async () => {
+    if (loading.value) return
+    loading.value = true
+    try {
+      const res = await queryResourceList({ type: 'FILE' })
+      removeUselessChildren(res)
+      options.value = res || []
+      loading.value = false
+    } catch (err) {
+      loading.value = false
+    }
+  }
+
+  onMounted(() => {
+    getResourceList()
+  })
+
+  return [
+    {
+      type: 'editor',
+      field: 'shell',
+      name: t('project.node.script'),
+      validate: {
+        trigger: ['input', 'trigger'],
+        required: true,
+        message: t('project.node.script_tips')
+      }
+    },
+    {
+      type: 'tree-select',
+      field: 'resourceList',
+      name: t('project.node.resources'),
+      options,
+      props: {
+        multiple: true,
+        checkable: true,
+        cascade: true,
+        showPath: true,
+        checkStrategy: 'child',
+        placeholder: t('project.node.resources_tips'),
+        keyField: 'id',
+        labelField: 'name',
+        loading
+      }
+    },
+    {
+      type: 'custom-parameters',
+      field: 'localParams',
+      name: t('project.node.custom_parameters'),
+      children: [
+        {
+          type: 'input',
+          field: 'prop',
+          span: 6,
+          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: 'select',
+          field: 'direct',
+          span: 4,
+          options: DIRECT_LIST,
+          value: 'IN'
+        },
+        {
+          type: 'select',
+          field: 'type',
+          span: 6,
+          options: TYPE_LIST,
+          value: 'VARCHAR'
+        },
+        {
+          type: 'input',
+          field: 'value',
+          span: 6,
+          props: {
+            placeholder: t('project.node.value_tips'),
+            maxLength: 256
+          }
+        }
+      ]
+    }
+  ]
+}
+
+function removeUselessChildren(list: { children?: [] }[]) {
+  if (!list.length) return
+  list.forEach((item) => {
+    if (!item.children) return
+    if (item.children.length === 0) {
+      delete item.children
+      return
+    }
+    removeUselessChildren(item.children)
+  })
+}
+
+export const TYPE_LIST = [
+  {
+    value: 'VARCHAR',
+    label: 'VARCHAR'
+  },
+  {
+    value: 'INTEGER',
+    label: 'INTEGER'
+  },
+  {
+    value: 'LONG',
+    label: 'LONG'
+  },
+  {
+    value: 'FLOAT',
+    label: 'FLOAT'
+  },
+  {
+    value: 'DOUBLE',
+    label: 'DOUBLE'
+  },
+  {
+    value: 'DATE',
+    label: 'DATE'
+  },
+  {
+    value: 'TIME',
+    label: 'TIME'
+  },
+  {
+    value: 'TIMESTAMP',
+    label: 'TIMESTAMP'
+  },
+  {
+    value: 'BOOLEAN',
+    label: 'BOOLEAN'
+  }
+]
+
+export const DIRECT_LIST = [
+  {
+    value: 'IN',
+    label: 'IN'
+  },
+  {
+    value: 'OUT',
+    label: 'OUT'
+  }
+]

+ 85 - 0
dolphinscheduler-ui-next/src/views/projects/node/fields/use-task-group.ts

@@ -0,0 +1,85 @@
+/*
+ * 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, watch, computed, onMounted } from 'vue'
+import { useI18n } from 'vue-i18n'
+import { queryTaskGroupListPagingByProjectCode } from '@/service/modules/task-group'
+
+export function useTaskGroup(
+  model: { [field: string]: any },
+  projectCode: number
+) {
+  const { t } = useI18n()
+
+  const options = ref([])
+  const loading = ref(false)
+  const priorityDisabled = computed(() => !model.taskGroupId)
+
+  const getTaskGroupList = async () => {
+    if (loading.value) return
+    loading.value = true
+    try {
+      const { totalList = [] } = await queryTaskGroupListPagingByProjectCode({
+        pageNo: 1,
+        pageSize: 2147483647,
+        projectCode
+      })
+      options.value = totalList.map((item: { id: string; name: string }) => ({
+        label: item.name,
+        value: item.id
+      }))
+      loading.value = false
+    } catch (err) {
+      loading.value = false
+    }
+  }
+
+  onMounted(() => {
+    getTaskGroupList()
+  })
+
+  watch(
+    () => model.taskGroupId,
+    (taskGroupId) => {
+      if (!taskGroupId) model.taskGroupPriority = 0
+    }
+  )
+
+  return [
+    {
+      type: 'select',
+      field: 'taskGroupId',
+      span: 12,
+      name: t('project.node.task_group_name'),
+      props: {
+        loading
+      },
+      options
+    },
+    {
+      type: 'input-number',
+      field: 'taskGroupPriority',
+      name: t('project.node.task_group_queue_priority'),
+      props: {
+        max: Math.pow(10, 60) - 1,
+        disabled: priorityDisabled
+      },
+      span: 12,
+      value: 0
+    }
+  ]
+}

+ 92 - 0
dolphinscheduler-ui-next/src/views/projects/node/fields/use-task-priority.ts

@@ -0,0 +1,92 @@
+/*
+ * 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 { 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'
+
+export function useTaskPriority() {
+  const { t } = useI18n()
+  const options = markRaw([
+    {
+      label: 'HIGHEST',
+      value: 'HIGHEST',
+      icon: ArrowUpOutlined,
+      color: '#ff0000'
+    },
+    {
+      label: 'HIGH',
+      value: 'HIGH',
+      icon: ArrowUpOutlined,
+      color: '#ff0000'
+    },
+    {
+      label: 'MEDIUM',
+      value: 'MEDIUM',
+      icon: ArrowUpOutlined,
+      color: '#EA7D24'
+    },
+    {
+      label: 'LOW',
+      value: 'LOW',
+      icon: ArrowDownOutlined,
+      color: '#2A8734'
+    },
+    {
+      label: 'LOWEST',
+      value: 'LOWEST',
+      icon: ArrowDownOutlined,
+      color: '#2A8734'
+    }
+  ])
+  const renderOption = ({
+    node,
+    option
+  }: {
+    node: VNode
+    option: ITaskPriorityOption
+  }): VNodeChild =>
+    h(node, null, {
+      default: () => [
+        h(
+          NIcon,
+          {
+            color: option.color
+          },
+          {
+            default: () => h(option.icon)
+          }
+        ),
+        option.label as string
+      ]
+    })
+  return {
+    type: 'select',
+    field: 'taskInstancePriority',
+    name: t('project.node.task_priority'),
+    options,
+    validate: {
+      required: true
+    },
+    props: {
+      renderOption
+    },
+    value: 'MEDIUM'
+  }
+}

+ 86 - 0
dolphinscheduler-ui-next/src/views/projects/node/fields/use-timeout-alarm.ts

@@ -0,0 +1,86 @@
+/*
+ * 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 { computed, watch } from 'vue'
+import { useI18n } from 'vue-i18n'
+
+export function useTimeoutAlarm(model: { [field: string]: any }) {
+  const { t } = useI18n()
+  const span = computed(() => (model.enable ? 12 : 0))
+
+  const strategyOptions = [
+    {
+      label: t('project.node.timeout_alarm'),
+      value: 'WARN'
+    },
+    {
+      label: t('project.node.timeout_failure'),
+      value: 'FAILED'
+    }
+  ]
+  watch(
+    () => model.enable,
+    (enable) => {
+      model.strategy = enable ? ['WARN'] : []
+      model.interval = enable ? 30 : null
+    }
+  )
+
+  return [
+    {
+      type: 'switch',
+      field: 'enable',
+      name: t('project.node.timeout_alarm')
+    },
+    {
+      type: 'checkbox',
+      field: 'strategy',
+      name: t('project.node.timeout_strategy'),
+      options: strategyOptions,
+      span: span,
+      validate: {
+        trigger: ['input'],
+        validator(validate: any, value: []) {
+          if (model.enable && !value.length) {
+            return new Error(t('project.node.timeout_strategy_tips'))
+          }
+        }
+      },
+      value: ['WARN']
+    },
+    {
+      type: 'input-number',
+      field: 'interval',
+      name: t('project.node.timeout_period'),
+      span,
+      props: {
+        max: Math.pow(10, 10) - 1
+      },
+      slots: {
+        suffix: () => t('project.node.minute')
+      },
+      validate: {
+        trigger: ['input'],
+        validator(validate: any, value: number) {
+          if (model.enable && !/^[1-9]\d*$/.test(String(value))) {
+            return new Error(t('project.node.timeout_period_tips'))
+          }
+        }
+      }
+    }
+  ]
+}

+ 59 - 0
dolphinscheduler-ui-next/src/views/projects/node/fields/use-worker-group.ts

@@ -0,0 +1,59 @@
+/*
+ * 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 { queryAllWorkerGroups } from '@/service/modules/worker-groups'
+
+export function useWorkerGroup() {
+  const { t } = useI18n()
+
+  const options = ref([] as { label: string; value: string }[])
+  const loading = ref(false)
+
+  const getWorkerGroups = async () => {
+    if (loading.value) return
+    loading.value = true
+    try {
+      const res = await queryAllWorkerGroups()
+      options.value = res.map((item: string) => ({ label: item, value: item }))
+      loading.value = false
+    } catch (err) {
+      loading.value = false
+    }
+  }
+
+  onMounted(() => {
+    getWorkerGroups()
+  })
+  return {
+    type: 'select',
+    field: 'workerGroup',
+    span: 12,
+    name: t('project.node.worker_group'),
+    props: {
+      loading: loading
+    },
+    options: options,
+    validate: {
+      trigger: ['input', 'blur'],
+      required: true,
+      message: t('project.node.worker_group_tips')
+    },
+    value: 'default'
+  }
+}

+ 16 - 0
dolphinscheduler-ui-next/src/views/projects/node/tasks/index.ts

@@ -0,0 +1,16 @@
+/*
+ * 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.
+ */

+ 65 - 0
dolphinscheduler-ui-next/src/views/projects/node/tasks/use-shell.ts

@@ -0,0 +1,65 @@
+/*
+ * 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 { IJsonItem } from '../types'
+
+export function useShell({
+  isCreate,
+  projectCode
+}: {
+  isCreate: boolean
+  projectCode: number
+}) {
+  const model = reactive({
+    name: '',
+    runFlag: 'YES',
+    desc: '',
+    enable: false,
+    localParams: [],
+    environmentCode: null,
+    workerGroup: 'default'
+  } as {
+    name: string
+    runFlag: string
+    desc: string
+    workerGroup: string
+    taskGroupId: string
+    enable: boolean
+    localParams: []
+    environmentCode: string | null
+  })
+
+  return {
+    json: [
+      Fields.useName(),
+      Fields.useRunFlag(),
+      Fields.useDescription(),
+      Fields.useTaskPriority(),
+      Fields.useWorkerGroup(),
+      Fields.useEnvironmentName(model, isCreate),
+      ...Fields.useTaskGroup(model, projectCode),
+      ...Fields.useFailed(),
+      Fields.useDelayTime(),
+      ...Fields.useTimeoutAlarm(model),
+      ...Fields.useShell(model),
+      Fields.usePreTasks()
+    ] as IJsonItem[],
+    model
+  }
+}

+ 104 - 0
dolphinscheduler-ui-next/src/views/projects/node/types.ts

@@ -0,0 +1,104 @@
+/*
+ * 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 { VNode } from 'vue'
+import type { SelectOption } from 'naive-ui'
+import type { IFormItem, IJsonItem } from '@/components/form/types'
+
+interface ITaskPriorityOption extends SelectOption {
+  icon: VNode
+  color: string
+}
+interface IEnvironmentNameOption {
+  label: string
+  value: string
+  workerGroups?: string[]
+}
+interface ILocalParam {
+  prop: string
+  direct: string
+  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 =
+  | 'SHELL'
+  | 'SUB_PROCESS'
+  | 'PROCEDURE'
+  | 'SQL'
+  | 'SPARK'
+  | 'FLINK'
+  | 'MapReduce'
+  | 'PYTHON'
+  | 'DEPENDENT'
+  | 'HTTP'
+  | 'DataX'
+  | 'PIGEON'
+  | 'SQOOP'
+  | 'CONDITIONS'
+  | 'DATA_QUALITY'
+  | 'SWITCH'
+  | 'SEATUNNEL'
+
+interface ITask {
+  code?: string
+  timeoutNotifyStrategy?: string
+  taskParams: ITaskParams
+  description?: string
+  id: number
+  delayTime?: number
+  failRetryTimes?: number
+  name: string
+  params?: object
+  failRetryInterval?: number
+  flag: string
+  taskPriority?: number
+  timeout: ITimeout
+  timeoutFlag: 'CLOSE' | 'OPEN'
+  taskType?: ITaskType
+  workerGroup?: string
+  environmentCode?: string
+  taskGroupId?: string
+  taskGroupPriority?: number
+}
+
+interface IDataNode {
+  id?: string
+  taskType: ITaskType
+}
+
+export {
+  ITaskPriorityOption,
+  IEnvironmentNameOption,
+  ILocalParam,
+  ITaskType,
+  ITask,
+  IDataNode,
+  IFormItem,
+  IJsonItem
+}

+ 84 - 0
dolphinscheduler-ui-next/src/views/projects/node/use-data.ts

@@ -0,0 +1,84 @@
+/*
+ * 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
+    }
+  } else {
+  }
+
+  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 - 0
dolphinscheduler-ui-next/src/views/projects/node/use-detail.ts

@@ -0,0 +1,33 @@
+/*
+ * 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
+  }
+}

+ 32 - 0
dolphinscheduler-ui-next/src/views/projects/node/use-task.ts

@@ -0,0 +1,32 @@
+/*
+ * 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 { useShell } from './tasks/use-shell'
+import { IJsonItem, ITaskType } from './types'
+
+export function useTask({
+  taskType,
+  projectCode
+}: {
+  taskType: ITaskType
+  projectCode?: number
+}): { json: IJsonItem[]; model: object } {
+  let node = {} as { json: IJsonItem[]; model: object }
+  if (taskType === 'SHELL' && projectCode) {
+    node = useShell({ isCreate: true, projectCode: projectCode })
+  }
+  return node
+}