mirror of
https://gitee.com/dolphinscheduler/DolphinScheduler.git
synced 2024-12-02 12:17:43 +08:00
[Feature][UI Next] Create workflow (#8362)
This commit is contained in:
parent
9e9edbfe27
commit
4b06b76068
@ -545,7 +545,8 @@ const project = {
|
||||
basic_info: 'Basic Information',
|
||||
minute: 'Minute',
|
||||
key: 'Key',
|
||||
value: 'Value'
|
||||
value: 'Value',
|
||||
success: 'Success'
|
||||
},
|
||||
node: {
|
||||
current_node_settings: 'Current node settings',
|
||||
|
@ -543,7 +543,8 @@ const project = {
|
||||
basic_info: '基本信息',
|
||||
minute: '分',
|
||||
key: '键',
|
||||
value: '值'
|
||||
value: '值',
|
||||
success: '成功'
|
||||
},
|
||||
node: {
|
||||
current_node_settings: '当前节点设置',
|
||||
|
@ -39,11 +39,11 @@ export function queryListPaging(params: PageReq & ListReq, code: number): any {
|
||||
}
|
||||
|
||||
export function createProcessDefinition(
|
||||
data: ProcessDefinitionReq & NameReq,
|
||||
code: CodeReq
|
||||
data: ProcessDefinitionReq,
|
||||
projectCode: number
|
||||
): any {
|
||||
return axios({
|
||||
url: `/projects/${code}/process-definition`,
|
||||
url: `/projects/${projectCode}/process-definition`,
|
||||
method: 'post',
|
||||
data
|
||||
})
|
||||
|
@ -53,7 +53,8 @@ interface ListReq extends PageReq {
|
||||
userId?: number
|
||||
}
|
||||
|
||||
interface ProcessDefinitionReq extends NameReq {
|
||||
interface ProcessDefinitionReq {
|
||||
name: string
|
||||
locations: string
|
||||
taskDefinitionJson: string
|
||||
taskRelationJson: string
|
||||
|
@ -19,37 +19,71 @@ 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'
|
||||
import type { NodeData } from '@/views/projects/workflow/components/dag/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>
|
||||
type: Object as PropType<NodeData>,
|
||||
default: { code: 0, taskType: 'SHELL', name: '' }
|
||||
},
|
||||
projectCode: {
|
||||
type: Number as PropType<number>,
|
||||
required: true
|
||||
},
|
||||
readonly: {
|
||||
type: Boolean as PropType<boolean>,
|
||||
default: false
|
||||
}
|
||||
}
|
||||
|
||||
const NodeDetailModal = defineComponent({
|
||||
name: 'NodeDetailModal',
|
||||
props,
|
||||
emits: ['cancel', 'update'],
|
||||
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 = () => {
|
||||
detailRef.value.onSubmit()
|
||||
emit('submit', {
|
||||
formRef: detailRef.value.formRef,
|
||||
form: mapFormToTaskDefinition(detailRef.value.form)
|
||||
})
|
||||
}
|
||||
const onCancel = () => {
|
||||
emit('cancel')
|
||||
@ -63,7 +97,15 @@ const NodeDetailModal = defineComponent({
|
||||
}
|
||||
},
|
||||
render() {
|
||||
const { t, show, onConfirm, onCancel } = this
|
||||
const {
|
||||
t,
|
||||
show,
|
||||
onConfirm,
|
||||
onCancel,
|
||||
projectCode,
|
||||
taskDefinition,
|
||||
readonly
|
||||
} = this
|
||||
return (
|
||||
<Modal
|
||||
show={show}
|
||||
@ -72,7 +114,11 @@ const NodeDetailModal = defineComponent({
|
||||
confirmLoading={false}
|
||||
onCancel={onCancel}
|
||||
>
|
||||
<Detail ref='detailRef' taskType='SHELL' projectCode={111} />
|
||||
<Detail
|
||||
ref='detailRef'
|
||||
taskType={taskDefinition.taskType}
|
||||
projectCode={projectCode}
|
||||
/>
|
||||
</Modal>
|
||||
)
|
||||
}
|
||||
|
@ -15,7 +15,7 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import { defineComponent, PropType, ref, toRefs } from 'vue'
|
||||
import { defineComponent, PropType, ref, toRef, toRefs } from 'vue'
|
||||
import Form from '@/components/form'
|
||||
import { useTask } from './use-task'
|
||||
import { useDetail } from './use-detail'
|
||||
@ -40,14 +40,15 @@ const NodeDetail = defineComponent({
|
||||
const { taskType, projectCode } = props
|
||||
|
||||
const { json, model } = useTask({ taskType, projectCode })
|
||||
const { state, onSubmit } = useDetail()
|
||||
const { state } = useDetail()
|
||||
|
||||
const jsonRef = ref(json)
|
||||
|
||||
const { rules, elements } = getElementByJson(jsonRef.value, model)
|
||||
|
||||
expose({
|
||||
onSubmit: () => void onSubmit(model)
|
||||
formRef: toRef(state, 'formRef'),
|
||||
form: model
|
||||
})
|
||||
|
||||
return { rules, elements, model, ...toRefs(state) }
|
||||
|
@ -18,6 +18,7 @@
|
||||
import { VNode } from 'vue'
|
||||
import type { SelectOption } from 'naive-ui'
|
||||
import type { IFormItem, IJsonItem } from '@/components/form/types'
|
||||
import type { TaskType } from '@/views/projects/task/constants/task-type'
|
||||
|
||||
interface ITaskPriorityOption extends SelectOption {
|
||||
icon: VNode
|
||||
@ -46,27 +47,10 @@ interface ITimeout {
|
||||
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'
|
||||
type ITaskType = TaskType
|
||||
|
||||
interface ITask {
|
||||
code?: string
|
||||
code: number
|
||||
timeoutNotifyStrategy?: string
|
||||
taskParams: ITaskParams
|
||||
description?: string
|
||||
|
@ -16,7 +16,7 @@
|
||||
*/
|
||||
|
||||
import { useCanvasInit } from './use-canvas-init'
|
||||
import { useCellQuery } from './use-cell-query'
|
||||
import { useBusinessMapper } from './use-business-mapper'
|
||||
import { useCellActive } from './use-cell-active'
|
||||
import { useCellUpdate } from './use-cell-update'
|
||||
import { useNodeSearch } from './use-node-search'
|
||||
@ -25,10 +25,11 @@ import { useTextCopy } from './use-text-copy'
|
||||
import { useCustomCellBuilder } from './use-custom-cell-builder'
|
||||
import { useGraphBackfill } from './use-graph-backfill'
|
||||
import { useDagDragAndDrop } from './use-dag-drag-drop'
|
||||
import { useTaskEdit } from './use-task-edit'
|
||||
|
||||
export {
|
||||
useCanvasInit,
|
||||
useCellQuery,
|
||||
useBusinessMapper,
|
||||
useCellActive,
|
||||
useNodeSearch,
|
||||
useGraphAutoLayout,
|
||||
@ -36,5 +37,6 @@ export {
|
||||
useCustomCellBuilder,
|
||||
useGraphBackfill,
|
||||
useCellUpdate,
|
||||
useDagDragAndDrop
|
||||
useDagDragAndDrop,
|
||||
useTaskEdit
|
||||
}
|
||||
|
@ -28,6 +28,7 @@ import {
|
||||
NDynamicInput
|
||||
} from 'naive-ui'
|
||||
import { queryTenantList } from '@/service/modules/tenants'
|
||||
import { SaveForm } from './types'
|
||||
import './x6-style.scss'
|
||||
|
||||
const props = {
|
||||
@ -67,7 +68,7 @@ export default defineComponent({
|
||||
})
|
||||
})
|
||||
|
||||
const formValue = ref({
|
||||
const formValue = ref<SaveForm>({
|
||||
name: '',
|
||||
description: '',
|
||||
tenantCode: 'default',
|
||||
|
@ -16,7 +16,10 @@
|
||||
*/
|
||||
|
||||
import { defineComponent } from 'vue'
|
||||
import { TASK_TYPES_MAP, TaskType } from '../../../task/constants/task-type'
|
||||
import {
|
||||
TaskType,
|
||||
TASK_TYPES_MAP
|
||||
} from '@/views/projects/task/constants/task-type'
|
||||
import Styles from './dag.module.scss'
|
||||
|
||||
export default defineComponent({
|
||||
@ -35,7 +38,7 @@ export default defineComponent({
|
||||
class={Styles.draggable}
|
||||
draggable='true'
|
||||
onDragstart={(e) => {
|
||||
context.emit('dragStart', e, task.type)
|
||||
context.emit('dragStart', e, task.type as TaskType)
|
||||
}}
|
||||
>
|
||||
<em
|
||||
|
@ -25,12 +25,15 @@ import DagAutoLayoutModal from './dag-auto-layout-modal'
|
||||
import {
|
||||
useGraphAutoLayout,
|
||||
useGraphBackfill,
|
||||
useDagDragAndDrop
|
||||
useDagDragAndDrop,
|
||||
useTaskEdit,
|
||||
useBusinessMapper
|
||||
} from './dag-hooks'
|
||||
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 './x6-style.scss'
|
||||
|
||||
const props = {
|
||||
@ -42,13 +45,17 @@ const props = {
|
||||
readonly: {
|
||||
type: Boolean as PropType<boolean>,
|
||||
default: false
|
||||
},
|
||||
projectCode: {
|
||||
type: Number as PropType<number>,
|
||||
default: 0
|
||||
}
|
||||
}
|
||||
|
||||
export default defineComponent({
|
||||
name: 'workflow-dag',
|
||||
props,
|
||||
emits: ['refresh'],
|
||||
emits: ['refresh', 'save'],
|
||||
setup(props, context) {
|
||||
const theme = useThemeStore()
|
||||
|
||||
@ -68,9 +75,20 @@ export default defineComponent({
|
||||
cancel
|
||||
} = useGraphAutoLayout({ graph })
|
||||
|
||||
// Edit task
|
||||
const {
|
||||
taskConfirm,
|
||||
taskModalVisible,
|
||||
currTask,
|
||||
taskCancel,
|
||||
appendTask,
|
||||
taskDefinitions
|
||||
} = useTaskEdit({ graph })
|
||||
|
||||
const { onDragStart, onDrop } = useDagDragAndDrop({
|
||||
graph,
|
||||
readonly: toRef(props, 'readonly')
|
||||
readonly: toRef(props, 'readonly'),
|
||||
appendTask
|
||||
})
|
||||
|
||||
// backfill
|
||||
@ -99,9 +117,19 @@ export default defineComponent({
|
||||
saveModalShow.value = !versionModalShow.value
|
||||
}
|
||||
}
|
||||
const onSave = (form: any) => {
|
||||
// TODO
|
||||
console.log(form)
|
||||
const { getConnects, getLocations } = useBusinessMapper()
|
||||
const onSave = (saveForm: any) => {
|
||||
const edges = graph.value?.getEdges() || []
|
||||
const nodes = graph.value?.getNodes() || []
|
||||
const connects = getConnects(nodes, edges, taskDefinitions.value as any)
|
||||
const locations = getLocations(nodes)
|
||||
context.emit('save', {
|
||||
taskDefinitions: taskDefinitions.value,
|
||||
saveForm,
|
||||
connects,
|
||||
locations
|
||||
})
|
||||
saveModelToggle(false)
|
||||
}
|
||||
|
||||
return () => (
|
||||
@ -136,6 +164,13 @@ export default defineComponent({
|
||||
/>
|
||||
)}
|
||||
<DagSaveModal v-model:show={saveModalShow.value} onSave={onSave} />
|
||||
<TaskModal
|
||||
show={taskModalVisible.value}
|
||||
projectCode={props.projectCode}
|
||||
taskDefinition={currTask.value}
|
||||
onSubmit={taskConfirm}
|
||||
onCancel={taskCancel}
|
||||
/>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
@ -15,6 +15,8 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import { TaskType } from '@/views/projects/task/constants/task-type'
|
||||
|
||||
export interface ProcessDefinition {
|
||||
id: number
|
||||
code: number
|
||||
@ -41,37 +43,37 @@ export interface ProcessDefinition {
|
||||
warningGroupId: number
|
||||
}
|
||||
|
||||
export interface ProcessTaskRelationList {
|
||||
id: number
|
||||
export interface Connect {
|
||||
id?: number
|
||||
name: string
|
||||
processDefinitionVersion: number
|
||||
projectCode: any
|
||||
processDefinitionCode: any
|
||||
processDefinitionVersion?: number
|
||||
projectCode?: number
|
||||
processDefinitionCode?: number
|
||||
preTaskCode: number
|
||||
preTaskVersion: number
|
||||
postTaskCode: any
|
||||
postTaskCode: number
|
||||
postTaskVersion: number
|
||||
conditionType: string
|
||||
conditionParams: any
|
||||
createTime: string
|
||||
updateTime: string
|
||||
createTime?: string
|
||||
updateTime?: string
|
||||
}
|
||||
|
||||
export interface TaskDefinitionList {
|
||||
export interface TaskDefinition {
|
||||
id: number
|
||||
code: any
|
||||
code: number
|
||||
name: string
|
||||
version: number
|
||||
description: string
|
||||
projectCode: any
|
||||
userId: number
|
||||
taskType: string
|
||||
taskType: TaskType
|
||||
taskParams: any
|
||||
taskParamList: any[]
|
||||
taskParamMap: any
|
||||
flag: string
|
||||
taskPriority: string
|
||||
userName?: any
|
||||
userName: any
|
||||
projectName?: any
|
||||
workerGroup: string
|
||||
environmentCode: number
|
||||
@ -84,12 +86,49 @@ export interface TaskDefinitionList {
|
||||
resourceIds: string
|
||||
createTime: string
|
||||
updateTime: string
|
||||
modifyBy?: any
|
||||
modifyBy: any
|
||||
dependence: string
|
||||
}
|
||||
|
||||
export type NodeData = {
|
||||
code: number
|
||||
taskType: TaskType
|
||||
name: string
|
||||
} & Partial<TaskDefinition>
|
||||
|
||||
export interface WorkflowDefinition {
|
||||
processDefinition: ProcessDefinition
|
||||
processTaskRelationList: ProcessTaskRelationList[]
|
||||
taskDefinitionList: TaskDefinitionList[]
|
||||
processTaskRelationList: Connect[]
|
||||
taskDefinitionList: TaskDefinition[]
|
||||
}
|
||||
|
||||
export interface Dragged {
|
||||
x: number
|
||||
y: number
|
||||
type: TaskType
|
||||
}
|
||||
|
||||
export interface Coordinate {
|
||||
x: number
|
||||
y: number
|
||||
}
|
||||
|
||||
export interface GlobalParam {
|
||||
key: string
|
||||
value: string
|
||||
}
|
||||
|
||||
export interface SaveForm {
|
||||
name: string
|
||||
description: string
|
||||
tenantCode: string
|
||||
timeoutFlag: boolean
|
||||
timeout: number
|
||||
globalParams: GlobalParam[]
|
||||
}
|
||||
|
||||
export interface Location {
|
||||
taskCode: number
|
||||
x: number
|
||||
y: number
|
||||
}
|
||||
|
@ -0,0 +1,118 @@
|
||||
/*
|
||||
* 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 type { Node, Edge } from '@antv/x6'
|
||||
import { Connect, Location, TaskDefinition } from './types'
|
||||
import { get } from 'lodash'
|
||||
|
||||
/**
|
||||
* Handling business entity and x6 entity conversion
|
||||
* @param {Options} options
|
||||
*/
|
||||
export function useBusinessMapper() {
|
||||
/**
|
||||
* Get connects, connects and processTaskRelationList are the same
|
||||
* @param {Node[]} nodes
|
||||
* @param {Edge[]} edges
|
||||
* @param {TaskDefinition[]} taskDefinitions
|
||||
* @returns {Connect[]}
|
||||
*/
|
||||
function getConnects(
|
||||
nodes: Node[],
|
||||
edges: Edge[],
|
||||
taskDefinitions: TaskDefinition[]
|
||||
): Connect[] {
|
||||
interface TailNodes {
|
||||
[code: string]: boolean
|
||||
}
|
||||
// Nodes in DAG whose in-degree is not 0
|
||||
const tailNodes: TailNodes = {}
|
||||
// If there is an edge target to a node, the node is tailNode
|
||||
edges.forEach((edge) => {
|
||||
const targetId = edge.getTargetCellId()
|
||||
tailNodes[targetId] = true
|
||||
})
|
||||
const isHeadNode = (code: string) => !tailNodes[code]
|
||||
|
||||
interface TasksMap {
|
||||
[code: string]: TaskDefinition
|
||||
}
|
||||
const tasksMap: TasksMap = {}
|
||||
nodes.forEach((node) => {
|
||||
const code = node.id
|
||||
const task = taskDefinitions.find((t) => t.code === Number(code))
|
||||
if (task) {
|
||||
tasksMap[code] = task
|
||||
}
|
||||
})
|
||||
|
||||
const headConnects: Connect[] = nodes
|
||||
.filter((node) => isHeadNode(node.id))
|
||||
.map((node) => {
|
||||
const task = tasksMap[node.id]
|
||||
return {
|
||||
name: '',
|
||||
preTaskCode: 0,
|
||||
preTaskVersion: 0,
|
||||
postTaskCode: task.code,
|
||||
postTaskVersion: task.version || 0,
|
||||
// conditionType and conditionParams are reserved
|
||||
conditionType: 'NONE',
|
||||
conditionParams: {}
|
||||
}
|
||||
})
|
||||
|
||||
const tailConnects: Connect[] = edges.map((edge) => {
|
||||
const labels = edge.getLabels()
|
||||
const labelName = get(labels, ['0', 'attrs', 'label', 'text'], '')
|
||||
const sourceId = edge.getSourceCellId()
|
||||
const prevTask = tasksMap[sourceId]
|
||||
const targetId = edge.getTargetCellId()
|
||||
const task = tasksMap[targetId]
|
||||
|
||||
return {
|
||||
name: labelName,
|
||||
preTaskCode: prevTask.code,
|
||||
preTaskVersion: prevTask.version || 0,
|
||||
postTaskCode: task.code,
|
||||
postTaskVersion: task.version || 0,
|
||||
// conditionType and conditionParams are reserved
|
||||
conditionType: 'NONE',
|
||||
conditionParams: {}
|
||||
}
|
||||
})
|
||||
|
||||
return headConnects.concat(tailConnects)
|
||||
}
|
||||
|
||||
function getLocations(nodes: Node[]): Location[] {
|
||||
return nodes.map((node) => {
|
||||
const code = +node.id
|
||||
const { x, y } = node.getPosition()
|
||||
return {
|
||||
taskCode: code,
|
||||
x,
|
||||
y
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
return {
|
||||
getLocations,
|
||||
getConnects
|
||||
}
|
||||
}
|
@ -1,54 +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 type { Ref } from 'vue'
|
||||
import type { Graph } from '@antv/x6'
|
||||
import { TaskType } from '../../../task/constants/task-type'
|
||||
|
||||
interface Options {
|
||||
graph: Ref<Graph | undefined>
|
||||
}
|
||||
|
||||
/**
|
||||
* Expose some cell-related query methods and refs
|
||||
* @param {Options} options
|
||||
*/
|
||||
export function useCellQuery(options: Options) {
|
||||
const { graph } = options
|
||||
|
||||
/**
|
||||
* Get all nodes
|
||||
*/
|
||||
function getNodes() {
|
||||
const nodes = graph.value?.getNodes()
|
||||
if (!nodes) return []
|
||||
return nodes.map((node) => {
|
||||
const position = node.getPosition()
|
||||
const data = node.getData()
|
||||
return {
|
||||
code: node.id,
|
||||
position: position,
|
||||
name: data.taskName as string,
|
||||
type: data.taskType as TaskType
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
return {
|
||||
getNodes
|
||||
}
|
||||
}
|
@ -18,9 +18,9 @@
|
||||
import type { Ref } from 'vue'
|
||||
import type { Graph } from '@antv/x6'
|
||||
import type { TaskType } from '@/views/projects/task/constants/task-type'
|
||||
import type { Coordinate } from './types'
|
||||
import { TASK_TYPES_MAP } from '@/views/projects/task/constants/task-type'
|
||||
import { useCustomCellBuilder } from './dag-hooks'
|
||||
import type { Coordinate } from './use-custom-cell-builder'
|
||||
import utils from '@/utils'
|
||||
|
||||
interface Options {
|
||||
@ -59,13 +59,14 @@ export function useCellUpdate(options: Options) {
|
||||
function addNode(
|
||||
id: string,
|
||||
type: string,
|
||||
name: string,
|
||||
coordinate: Coordinate = { x: 100, y: 100 }
|
||||
) {
|
||||
if (!TASK_TYPES_MAP[type as TaskType]) {
|
||||
console.warn(`taskType:${type} is invalid!`)
|
||||
return
|
||||
}
|
||||
const node = buildNode(id, type, '', coordinate)
|
||||
const node = buildNode(id, type, name, coordinate)
|
||||
graph.value?.addNode(node)
|
||||
}
|
||||
|
||||
|
@ -18,9 +18,7 @@
|
||||
import type { Node, Edge } from '@antv/x6'
|
||||
import { X6_NODE_NAME, X6_EDGE_NAME } from './dag-config'
|
||||
import utils from '@/utils'
|
||||
import { WorkflowDefinition } from './types'
|
||||
|
||||
export type Coordinate = { x: number; y: number }
|
||||
import { WorkflowDefinition, Coordinate } from './types'
|
||||
|
||||
export function useCustomCellBuilder() {
|
||||
/**
|
||||
@ -110,7 +108,7 @@ export function useCustomCellBuilder() {
|
||||
|
||||
tasks.forEach((task) => {
|
||||
const location = locations.find((l) => l.taskCode === task.code) || {}
|
||||
const node = buildNode(task.code, task.taskType, task.name, {
|
||||
const node = buildNode(task.code + '', task.taskType, task.name, {
|
||||
x: location.x,
|
||||
y: location.y
|
||||
})
|
||||
|
@ -19,39 +19,33 @@ import { ref } from 'vue'
|
||||
import type { Ref } from 'vue'
|
||||
import type { Graph } from '@antv/x6'
|
||||
import { genTaskCodeList } from '@/service/modules/task-definition'
|
||||
import { useCellUpdate } from './dag-hooks'
|
||||
import { Dragged } from './types'
|
||||
import { TaskType } from '@/views/projects/task/constants/task-type'
|
||||
import { useRoute } from 'vue-router'
|
||||
|
||||
interface Options {
|
||||
readonly: Ref<boolean>
|
||||
graph: Ref<Graph | undefined>
|
||||
}
|
||||
|
||||
interface Dragged {
|
||||
x: number
|
||||
y: number
|
||||
type: string
|
||||
appendTask: (code: number, type: TaskType, coor: Coordinate) => void
|
||||
}
|
||||
|
||||
/**
|
||||
* Sidebar item drag && drop in canvas
|
||||
*/
|
||||
export function useDagDragAndDrop(options: Options) {
|
||||
const { readonly, graph } = options
|
||||
const { readonly, graph, appendTask } = options
|
||||
|
||||
const route = useRoute()
|
||||
const projectCode = Number(route.params.projectCode)
|
||||
|
||||
const { addNode } = useCellUpdate({ graph })
|
||||
|
||||
// The element currently being dragged up
|
||||
const dragged = ref<Dragged>({
|
||||
x: 0,
|
||||
y: 0,
|
||||
type: ''
|
||||
type: 'SHELL'
|
||||
})
|
||||
|
||||
function onDragStart(e: DragEvent, type: string) {
|
||||
function onDragStart(e: DragEvent, type: TaskType) {
|
||||
if (readonly.value) {
|
||||
e.preventDefault()
|
||||
return
|
||||
@ -75,8 +69,7 @@ export function useDagDragAndDrop(options: Options) {
|
||||
const genNums = 1
|
||||
genTaskCodeList(genNums, projectCode).then((res) => {
|
||||
const [code] = res
|
||||
addNode(code + '', type, { x: x - eX, y: y - eY })
|
||||
// openTaskConfigModel(code, type)
|
||||
appendTask(code, type, { x: x - eX, y: y - eY })
|
||||
})
|
||||
}
|
||||
}
|
||||
|
@ -17,7 +17,6 @@
|
||||
|
||||
import type { Graph } from '@antv/x6'
|
||||
import { ref, Ref } from 'vue'
|
||||
import { useCellQuery } from './dag-hooks'
|
||||
|
||||
interface Options {
|
||||
graph: Ref<Graph | undefined>
|
||||
@ -40,12 +39,12 @@ export function useNodeSearch(options: Options) {
|
||||
/**
|
||||
* Search dropdown control
|
||||
*/
|
||||
const { getNodes } = useCellQuery({ graph })
|
||||
const nodesDropdown = ref<{ label: string; value: string }[]>([])
|
||||
const reQueryNodes = () => {
|
||||
nodesDropdown.value = getNodes().map((node) => ({
|
||||
label: node.name,
|
||||
value: node.code
|
||||
const nodes = graph.value?.getNodes() || []
|
||||
nodesDropdown.value = nodes.map((node) => ({
|
||||
label: node.getData().taskName,
|
||||
value: node.id
|
||||
}))
|
||||
}
|
||||
|
||||
|
@ -0,0 +1,119 @@
|
||||
/*
|
||||
* 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 type { Ref } from 'vue'
|
||||
import type { Graph } from '@antv/x6'
|
||||
import type { Coordinate, NodeData } from './types'
|
||||
import { TaskType } from '@/views/projects/task/constants/task-type'
|
||||
import { useCellUpdate } from './dag-hooks'
|
||||
|
||||
interface Options {
|
||||
graph: Ref<Graph | undefined>
|
||||
}
|
||||
|
||||
/**
|
||||
* Edit task configuration when dbclick
|
||||
* @param {Options} options
|
||||
* @returns
|
||||
*/
|
||||
export function useTaskEdit(options: Options) {
|
||||
const { graph } = options
|
||||
|
||||
const { addNode, setNodeName } = useCellUpdate({ graph })
|
||||
|
||||
const taskDefinitions = ref<NodeData[]>([])
|
||||
const currTask = ref<NodeData>({
|
||||
taskType: 'SHELL',
|
||||
code: 0,
|
||||
name: ''
|
||||
})
|
||||
const taskModalVisible = ref(false)
|
||||
|
||||
/**
|
||||
* Append a new task
|
||||
*/
|
||||
function appendTask(code: number, type: TaskType, coordinate: Coordinate) {
|
||||
addNode(code + '', type, '', coordinate)
|
||||
taskDefinitions.value.push({
|
||||
code,
|
||||
taskType: type,
|
||||
name: ''
|
||||
})
|
||||
openTaskModal({ code, taskType: type, name: '' })
|
||||
}
|
||||
|
||||
function openTaskModal(task: NodeData) {
|
||||
currTask.value = task
|
||||
taskModalVisible.value = true
|
||||
}
|
||||
|
||||
/**
|
||||
* The confirm event in task config modal
|
||||
* @param formRef
|
||||
* @param from
|
||||
*/
|
||||
function taskConfirm({ formRef, form }: any) {
|
||||
formRef.validate((errors: any) => {
|
||||
if (!errors) {
|
||||
// override target config
|
||||
taskDefinitions.value = taskDefinitions.value.map((task) => {
|
||||
if (task.code === currTask.value?.code) {
|
||||
setNodeName(task.code + '', form.name)
|
||||
console.log(form)
|
||||
console.log(JSON.stringify(form))
|
||||
return {
|
||||
code: task.code,
|
||||
...form
|
||||
}
|
||||
}
|
||||
return task
|
||||
})
|
||||
taskModalVisible.value = false
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* The cancel event in task config modal
|
||||
*/
|
||||
function taskCancel() {
|
||||
taskModalVisible.value = false
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
if (graph.value) {
|
||||
graph.value.on('cell:dblclick', ({ cell }) => {
|
||||
const code = Number(cell.id)
|
||||
const definition = taskDefinitions.value.find((t) => t.code === code)
|
||||
if (definition) {
|
||||
currTask.value = definition
|
||||
}
|
||||
taskModalVisible.value = true
|
||||
})
|
||||
}
|
||||
})
|
||||
|
||||
return {
|
||||
currTask,
|
||||
taskModalVisible,
|
||||
taskConfirm,
|
||||
taskCancel,
|
||||
appendTask,
|
||||
taskDefinitions
|
||||
}
|
||||
}
|
@ -16,15 +16,71 @@
|
||||
*/
|
||||
|
||||
import { defineComponent } from 'vue'
|
||||
import { useMessage } from 'naive-ui'
|
||||
import Dag from '../../components/dag'
|
||||
import { useThemeStore } from '@/store/theme/theme'
|
||||
import { useRoute, useRouter } from 'vue-router'
|
||||
import {
|
||||
SaveForm,
|
||||
TaskDefinition,
|
||||
Connect,
|
||||
Location
|
||||
} from '../../components/dag/types'
|
||||
import { createProcessDefinition } from '@/service/modules/process-definition'
|
||||
import { useI18n } from 'vue-i18n'
|
||||
import Styles from './index.module.scss'
|
||||
|
||||
interface SaveData {
|
||||
saveForm: SaveForm
|
||||
taskDefinitions: TaskDefinition[]
|
||||
connects: Connect[]
|
||||
locations: Location[]
|
||||
}
|
||||
|
||||
export default defineComponent({
|
||||
name: 'WorkflowDefinitionCreate',
|
||||
setup() {
|
||||
const theme = useThemeStore()
|
||||
|
||||
const message = useMessage()
|
||||
const { t } = useI18n()
|
||||
const route = useRoute()
|
||||
const router = useRouter()
|
||||
const projectCode = Number(route.params.projectCode)
|
||||
|
||||
const onSave = ({
|
||||
taskDefinitions,
|
||||
saveForm,
|
||||
connects,
|
||||
locations
|
||||
}: SaveData) => {
|
||||
const globalParams = saveForm.globalParams.map((p) => {
|
||||
return {
|
||||
prop: p.key,
|
||||
value: p.value,
|
||||
direct: 'IN',
|
||||
type: 'VARCHAR'
|
||||
}
|
||||
})
|
||||
|
||||
createProcessDefinition(
|
||||
{
|
||||
taskDefinitionJson: JSON.stringify(taskDefinitions),
|
||||
taskRelationJson: JSON.stringify(connects),
|
||||
locations: JSON.stringify(locations),
|
||||
name: saveForm.name,
|
||||
tenantCode: saveForm.tenantCode,
|
||||
description: saveForm.description,
|
||||
globalParams: JSON.stringify(globalParams),
|
||||
timeout: saveForm.timeoutFlag ? saveForm.timeout : 0
|
||||
},
|
||||
projectCode
|
||||
).then((res: any) => {
|
||||
message.success(t('project.dag.success'))
|
||||
router.push({ path: `/projects/${projectCode}/workflow-definition` })
|
||||
})
|
||||
}
|
||||
|
||||
return () => (
|
||||
<div
|
||||
class={[
|
||||
@ -32,7 +88,7 @@ export default defineComponent({
|
||||
theme.darkTheme ? Styles['dark'] : Styles['light']
|
||||
]}
|
||||
>
|
||||
<Dag />
|
||||
<Dag projectCode={projectCode} onSave={onSave} />
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
@ -39,6 +39,8 @@ export default defineComponent({
|
||||
})
|
||||
}
|
||||
|
||||
const save = () => {}
|
||||
|
||||
onMounted(() => {
|
||||
if (!code || !projectCode) return
|
||||
refresh()
|
||||
@ -51,7 +53,12 @@ export default defineComponent({
|
||||
theme.darkTheme ? Styles['dark'] : Styles['light']
|
||||
]}
|
||||
>
|
||||
<Dag definition={definition.value} onRefresh={refresh} />
|
||||
<Dag
|
||||
definition={definition.value}
|
||||
onRefresh={refresh}
|
||||
projectCode={projectCode}
|
||||
onSave={save}
|
||||
/>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user