From 480492db732fc4511f50be9708727fdf040501e0 Mon Sep 17 00:00:00 2001 From: wangyizhi Date: Wed, 9 Feb 2022 23:32:11 +0800 Subject: [PATCH] [Feature][UI Next] DAG backfill (#8325) * [Feature][UI Next] Dag backfill * [Feature][UI Next] Add license header --- .../src/locales/modules/en_US.ts | 3 +- .../src/locales/modules/zh_CN.ts | 3 +- .../modules/process-definition/index.ts | 6 +- .../projects/task/constants/task-type.ts | 4 +- .../workflow/components/dag/dag-canvas.tsx | 42 ++---- .../workflow/components/dag/dag-hooks.ts | 20 ++- .../workflow/components/dag/dag-sidebar.tsx | 36 ++--- .../workflow/components/dag/dag-toolbar.tsx | 111 ++++++++++----- .../workflow/components/dag/dag.module.scss | 4 + .../workflow/components/dag/index.tsx | 83 ++++++++---- .../projects/workflow/components/dag/types.ts | 95 +++++++++++++ .../components/dag/use-canvas-init.ts | 2 +- .../workflow/components/dag/use-cell-query.ts | 54 ++++++++ .../components/dag/use-cell-update.ts | 76 +++++++++++ ...erations.ts => use-custom-cell-builder.ts} | 126 +++++++----------- ...se-canvas-drop.ts => use-dag-drag-drop.ts} | 46 +++++-- .../components/dag/use-graph-backfill.ts | 43 ++++++ .../components/dag/use-node-search.ts | 47 ++++--- .../{use-sidebar-drag.ts => use-text-copy.ts} | 36 ++--- .../definition/components/version-modal.tsx | 6 +- .../workflow/definition/create/index.tsx | 7 +- .../definition/detail/index.module.scss | 41 ++++++ .../workflow/definition/detail/index.tsx | 37 ++++- .../projects/workflow/definition/index.tsx | 15 ++- .../{use-table.ts => use-table.tsx} | 25 ++-- 25 files changed, 687 insertions(+), 281 deletions(-) create mode 100644 dolphinscheduler-ui-next/src/views/projects/workflow/components/dag/types.ts create mode 100644 dolphinscheduler-ui-next/src/views/projects/workflow/components/dag/use-cell-query.ts create mode 100644 dolphinscheduler-ui-next/src/views/projects/workflow/components/dag/use-cell-update.ts rename dolphinscheduler-ui-next/src/views/projects/workflow/components/dag/{use-graph-operations.ts => use-custom-cell-builder.ts} (51%) rename dolphinscheduler-ui-next/src/views/projects/workflow/components/dag/{use-canvas-drop.ts => use-dag-drag-drop.ts} (64%) create mode 100644 dolphinscheduler-ui-next/src/views/projects/workflow/components/dag/use-graph-backfill.ts rename dolphinscheduler-ui-next/src/views/projects/workflow/components/dag/{use-sidebar-drag.ts => use-text-copy.ts} (63%) create mode 100644 dolphinscheduler-ui-next/src/views/projects/workflow/definition/detail/index.module.scss rename dolphinscheduler-ui-next/src/views/projects/workflow/definition/{use-table.ts => use-table.tsx} (95%) diff --git a/dolphinscheduler-ui-next/src/locales/modules/en_US.ts b/dolphinscheduler-ui-next/src/locales/modules/en_US.ts index 206684ba93..1bd8caae06 100644 --- a/dolphinscheduler-ui-next/src/locales/modules/en_US.ts +++ b/dolphinscheduler-ui-next/src/locales/modules/en_US.ts @@ -495,7 +495,8 @@ const project = { grid_layout: 'Grid', dagre_layout: 'Dagre', rows: 'Rows', - cols: 'Cols' + cols: 'Cols', + copy_success: 'Copy Success' } } diff --git a/dolphinscheduler-ui-next/src/locales/modules/zh_CN.ts b/dolphinscheduler-ui-next/src/locales/modules/zh_CN.ts index d9fe1bc7ab..00cefaa46d 100644 --- a/dolphinscheduler-ui-next/src/locales/modules/zh_CN.ts +++ b/dolphinscheduler-ui-next/src/locales/modules/zh_CN.ts @@ -494,7 +494,8 @@ const project = { grid_layout: '网格布局', dagre_layout: '层次布局', rows: '行数', - cols: '列数' + cols: '列数', + copy_success: '复制成功' } } diff --git a/dolphinscheduler-ui-next/src/service/modules/process-definition/index.ts b/dolphinscheduler-ui-next/src/service/modules/process-definition/index.ts index c2a8885848..a6d13135bb 100644 --- a/dolphinscheduler-ui-next/src/service/modules/process-definition/index.ts +++ b/dolphinscheduler-ui-next/src/service/modules/process-definition/index.ts @@ -148,11 +148,11 @@ export function verifyName(params: NameReq, code: CodeReq): any { } export function queryProcessDefinitionByCode( - code: CodeReq, - processCode: CodeReq + code: number, + projectCode: number ): any { return axios({ - url: `/projects/${code}/process-definition/${processCode}`, + url: `/projects/${projectCode}/process-definition/${code}`, method: 'get' }) } diff --git a/dolphinscheduler-ui-next/src/views/projects/task/constants/task-type.ts b/dolphinscheduler-ui-next/src/views/projects/task/constants/task-type.ts index 260cbc9801..52878ab096 100644 --- a/dolphinscheduler-ui-next/src/views/projects/task/constants/task-type.ts +++ b/dolphinscheduler-ui-next/src/views/projects/task/constants/task-type.ts @@ -15,7 +15,7 @@ * limitations under the License. */ -export const ALL_TASK_TYPES: any = { +export const TASK_TYPES_MAP = { SHELL: { alias: 'SHELL' }, @@ -65,3 +65,5 @@ export const ALL_TASK_TYPES: any = { alias: 'WATERDROP' } } + +export type TaskType = keyof typeof TASK_TYPES_MAP diff --git a/dolphinscheduler-ui-next/src/views/projects/workflow/components/dag/dag-canvas.tsx b/dolphinscheduler-ui-next/src/views/projects/workflow/components/dag/dag-canvas.tsx index 221bc8e5be..b6771f19cb 100644 --- a/dolphinscheduler-ui-next/src/views/projects/workflow/components/dag/dag-canvas.tsx +++ b/dolphinscheduler-ui-next/src/views/projects/workflow/components/dag/dag-canvas.tsx @@ -17,53 +17,33 @@ import { defineComponent, ref, inject } from 'vue' import Styles from './dag.module.scss' -import type { PropType, Ref } from 'vue' -import type { Dragged } from './index' -import { useCanvasInit, useCellActive, useCanvasDrop } from './dag-hooks' -import { useRoute } from 'vue-router' - -const props = { - dragged: { - type: Object as PropType>, - default: ref({ - x: 0, - y: 0, - type: '' - }) - } -} +import { useCanvasInit, useCellActive } from './dag-hooks' export default defineComponent({ name: 'workflow-dag-canvas', - props, + emits: ['drop'], setup(props, context) { const readonly = inject('readonly', ref(false)) const graph = inject('graph', ref()) - const route = useRoute() - const projectCode = route.params.projectCode as string const { paper, minimap, container } = useCanvasInit({ readonly, graph }) // Change the style on cell hover and select useCellActive({ graph }) - - // Drop sidebar item in canvas - const { onDrop, onDragenter, onDragover, onDragleave } = useCanvasDrop({ - readonly, - dragged: props.dragged, - graph, - container, - projectCode - }) + const preventDefault = (e: DragEvent) => { + e.preventDefault() + } return () => (
{ + context.emit('drop', e) + }} + onDragenter={preventDefault} + onDragover={preventDefault} + onDragleave={preventDefault} >
diff --git a/dolphinscheduler-ui-next/src/views/projects/workflow/components/dag/dag-hooks.ts b/dolphinscheduler-ui-next/src/views/projects/workflow/components/dag/dag-hooks.ts index a2d57e265f..303ae1f712 100644 --- a/dolphinscheduler-ui-next/src/views/projects/workflow/components/dag/dag-hooks.ts +++ b/dolphinscheduler-ui-next/src/views/projects/workflow/components/dag/dag-hooks.ts @@ -16,19 +16,25 @@ */ import { useCanvasInit } from './use-canvas-init' -import { useGraphOperations } from './use-graph-operations' +import { useCellQuery } from './use-cell-query' import { useCellActive } from './use-cell-active' -import { useSidebarDrag } from './use-sidebar-drag' -import { useCanvasDrop } from './use-canvas-drop' +import { useCellUpdate } from './use-cell-update' import { useNodeSearch } from './use-node-search' import { useGraphAutoLayout } from './use-graph-auto-layout' +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' export { useCanvasInit, - useGraphOperations, + useCellQuery, useCellActive, - useSidebarDrag, - useCanvasDrop, useNodeSearch, - useGraphAutoLayout + useGraphAutoLayout, + useTextCopy, + useCustomCellBuilder, + useGraphBackfill, + useCellUpdate, + useDagDragAndDrop } diff --git a/dolphinscheduler-ui-next/src/views/projects/workflow/components/dag/dag-sidebar.tsx b/dolphinscheduler-ui-next/src/views/projects/workflow/components/dag/dag-sidebar.tsx index 420d4eb523..2ecd49866a 100644 --- a/dolphinscheduler-ui-next/src/views/projects/workflow/components/dag/dag-sidebar.tsx +++ b/dolphinscheduler-ui-next/src/views/projects/workflow/components/dag/dag-sidebar.tsx @@ -15,37 +15,17 @@ * limitations under the License. */ -import type { PropType, Ref } from 'vue' -import type { Dragged } from './index' -import { defineComponent, ref, inject } from 'vue' -import { ALL_TASK_TYPES } from '../../../task/constants/task-type' -import { useSidebarDrag } from './dag-hooks' +import { defineComponent } from 'vue' +import { TASK_TYPES_MAP, TaskType } from '../../../task/constants/task-type' import Styles from './dag.module.scss' -const props = { - dragged: { - type: Object as PropType>, - default: ref({ - x: 0, - y: 0, - type: '' - }) - } -} - export default defineComponent({ name: 'workflow-dag-sidebar', - props, - setup(props) { - const readonly = inject('readonly', ref(false)) - const dragged = props.dragged - const { onDragStart } = useSidebarDrag({ - readonly, - dragged - }) - const allTaskTypes = Object.keys(ALL_TASK_TYPES).map((type) => ({ + emits: ['dragStart'], + setup(props, context) { + const allTaskTypes = Object.keys(TASK_TYPES_MAP).map((type) => ({ type, - ...ALL_TASK_TYPES[type] + ...TASK_TYPES_MAP[type as TaskType] })) return () => ( @@ -54,7 +34,9 @@ export default defineComponent({
onDragStart(e, task.type)} + onDragstart={(e) => { + context.emit('dragStart', e, task.type) + }} > void>, default: () => {} + }, + // If this prop is passed, it means from definition detail + definition: { + // The same as the structure responsed by the queryProcessDefinitionByCode api + type: Object as PropType, + default: null } } export default defineComponent({ name: 'workflow-dag-toolbar', props, + emits: ['versionToggle'], setup(props, context) { const { t } = useI18n() @@ -55,11 +72,11 @@ export default defineComponent({ * Node search and navigate */ const { - searchNode, - getAllNodes, - allNodes, + navigateTo, toggleSearchInput, - searchInputVisible + searchInputVisible, + reQueryNodes, + nodesDropdown } = useNodeSearch({ graph }) /** @@ -94,7 +111,7 @@ export default defineComponent({ * Open workflow version modal */ const openVersionModal = () => { - //TODO, same as the version popup in the workflow list page + context.emit('versionToggle', true) } /** @@ -111,6 +128,11 @@ export default defineComponent({ router.go(-1) } + /** + * Copy workflow name + */ + const { copy } = useTextCopy() + return () => (
- {t('project.dag.create')} +
+ + {props.definition?.processDefinition?.name || + t('project.dag.create')} + + {props.definition?.processDefinition?.name && ( + copy(props.definition?.processDefinition?.name)} + class={Styles['copy-btn']} + > + + + + + )} +
{/* Search node */}
@@ -233,28 +272,30 @@ export default defineComponent({ }} > {/* Version info */} - ( - ( - - - - ) - }} - /> - ), - default: () => t('project.workflow.version_info') - }} - > + {!!props.definition && ( + ( + ( + + + + ) + }} + /> + ), + default: () => t('project.workflow.version_info') + }} + > + )} {/* Save workflow */} , + default: undefined + }, + readonly: { + type: Boolean as PropType, + default: false + } } export default defineComponent({ name: 'workflow-dag', + props, + emits: ['refresh'], setup(props, context) { const theme = useThemeStore() // Whether the graph can be operated - const readonly = ref(false) - provide('readonly', readonly) + provide('readonly', toRef(props, 'readonly')) const graph = ref() provide('graph', graph) - // The sidebar slots - const toolbarSlots = { - left: context.slots.toolbarLeft, - right: context.slots.toolbarRight - } - - // The element currently being dragged up - const dragged = ref({ - x: 0, - y: 0, - type: '' - }) - - // Auto layout + // Auto layout modal const { visible: layoutVisible, toggle: layoutToggle, @@ -67,6 +67,28 @@ export default defineComponent({ cancel } = useGraphAutoLayout({ graph }) + const { onDragStart, onDrop } = useDagDragAndDrop({ + graph, + readonly: toRef(props, 'readonly') + }) + + // backfill + useGraphBackfill({ graph, definition: toRef(props, 'definition') }) + + // version modal + const versionModalShow = ref(false) + const versionToggle = (bool: boolean) => { + if (typeof bool === 'boolean') { + versionModalShow.value = bool + } else { + versionModalShow.value = !versionModalShow.value + } + } + const refreshDetail = () => { + context.emit('refresh') + versionModalShow.value = false + } + return () => (
- +
- - + +
+ {!!props.definition && ( + + )}
) } diff --git a/dolphinscheduler-ui-next/src/views/projects/workflow/components/dag/types.ts b/dolphinscheduler-ui-next/src/views/projects/workflow/components/dag/types.ts new file mode 100644 index 0000000000..77f97db029 --- /dev/null +++ b/dolphinscheduler-ui-next/src/views/projects/workflow/components/dag/types.ts @@ -0,0 +1,95 @@ +/* + * 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 interface ProcessDefinition { + id: number + code: number + name: string + version: number + releaseState: string + projectCode: number + description: string + globalParams: string + globalParamList: any[] + globalParamMap: any + createTime: string + updateTime: string + flag: string + userId: number + userName?: any + projectName?: any + locations: string + scheduleReleaseState?: any + timeout: number + tenantId: number + tenantCode: string + modifyBy?: any + warningGroupId: number +} + +export interface ProcessTaskRelationList { + id: number + name: string + processDefinitionVersion: number + projectCode: any + processDefinitionCode: any + preTaskCode: number + preTaskVersion: number + postTaskCode: any + postTaskVersion: number + conditionType: string + conditionParams: any + createTime: string + updateTime: string +} + +export interface TaskDefinitionList { + id: number + code: any + name: string + version: number + description: string + projectCode: any + userId: number + taskType: string + taskParams: any + taskParamList: any[] + taskParamMap: any + flag: string + taskPriority: string + userName?: any + projectName?: any + workerGroup: string + environmentCode: number + failRetryTimes: number + failRetryInterval: number + timeoutFlag: string + timeoutNotifyStrategy: string + timeout: number + delayTime: number + resourceIds: string + createTime: string + updateTime: string + modifyBy?: any + dependence: string +} + +export interface WorkflowDefinition { + processDefinition: ProcessDefinition + processTaskRelationList: ProcessTaskRelationList[] + taskDefinitionList: TaskDefinitionList[] +} diff --git a/dolphinscheduler-ui-next/src/views/projects/workflow/components/dag/use-canvas-init.ts b/dolphinscheduler-ui-next/src/views/projects/workflow/components/dag/use-canvas-init.ts index 8ec8d12085..ffd5dc2bab 100644 --- a/dolphinscheduler-ui-next/src/views/projects/workflow/components/dag/use-canvas-init.ts +++ b/dolphinscheduler-ui-next/src/views/projects/workflow/components/dag/use-canvas-init.ts @@ -16,7 +16,7 @@ */ import type { Node } from '@antv/x6' -import { ref, onMounted, Ref, onUnmounted } from 'vue' +import { ref, onMounted, Ref } from 'vue' import { Graph } from '@antv/x6' import { NODE, EDGE, X6_NODE_NAME, X6_EDGE_NAME } from './dag-config' import { debounce } from 'lodash' diff --git a/dolphinscheduler-ui-next/src/views/projects/workflow/components/dag/use-cell-query.ts b/dolphinscheduler-ui-next/src/views/projects/workflow/components/dag/use-cell-query.ts new file mode 100644 index 0000000000..243e6c755f --- /dev/null +++ b/dolphinscheduler-ui-next/src/views/projects/workflow/components/dag/use-cell-query.ts @@ -0,0 +1,54 @@ +/* + * 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 +} + +/** + * 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 + } +} diff --git a/dolphinscheduler-ui-next/src/views/projects/workflow/components/dag/use-cell-update.ts b/dolphinscheduler-ui-next/src/views/projects/workflow/components/dag/use-cell-update.ts new file mode 100644 index 0000000000..b69033f0a7 --- /dev/null +++ b/dolphinscheduler-ui-next/src/views/projects/workflow/components/dag/use-cell-update.ts @@ -0,0 +1,76 @@ +/* + * 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 type { TaskType } from '@/views/projects/task/constants/task-type' +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 { + graph: Ref +} + +/** + * Expose some cell query + * @param {Options} options + */ +export function useCellUpdate(options: Options) { + const { graph } = options + + const { buildNode } = useCustomCellBuilder() + + /** + * Set node name by id + * @param {string} id + * @param {string} name + */ + function setNodeName(id: string, newName: string) { + const node = graph.value?.getCellById(id) + if (node) { + const truncation = utils.truncateText(newName, 18) + node.attr('title/text', truncation) + node.setData({ taskName: newName }) + } + } + + /** + * Add a node to the graph + * @param {string} id + * @param {string} taskType + * @param {Coordinate} coordinate Default is { x: 100, y: 100 } + */ + function addNode( + id: string, + type: 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) + graph.value?.addNode(node) + } + + return { + setNodeName, + addNode + } +} diff --git a/dolphinscheduler-ui-next/src/views/projects/workflow/components/dag/use-graph-operations.ts b/dolphinscheduler-ui-next/src/views/projects/workflow/components/dag/use-custom-cell-builder.ts similarity index 51% rename from dolphinscheduler-ui-next/src/views/projects/workflow/components/dag/use-graph-operations.ts rename to dolphinscheduler-ui-next/src/views/projects/workflow/components/dag/use-custom-cell-builder.ts index 2ec36a3a03..390b28cf13 100644 --- a/dolphinscheduler-ui-next/src/views/projects/workflow/components/dag/use-graph-operations.ts +++ b/dolphinscheduler-ui-next/src/views/projects/workflow/components/dag/use-custom-cell-builder.ts @@ -15,24 +15,27 @@ * limitations under the License. */ -import type { Ref } from 'vue' -import type { Node, Graph, Edge } from '@antv/x6' +import type { Node, Edge } from '@antv/x6' import { X6_NODE_NAME, X6_EDGE_NAME } from './dag-config' -import { ALL_TASK_TYPES } from '../../../task/constants/task-type' import utils from '@/utils' +import { WorkflowDefinition } from './types' -interface Options { - graph: Ref -} - -type Coordinate = { x: number; y: number } +export type Coordinate = { x: number; y: number } -/** - * Expose some graph operation methods - * @param {Options} options - */ -export function useGraphOperations(options: Options) { - const { graph } = options +export function useCustomCellBuilder() { + /** + * Convert locationStr to JSON + * @param {string} locationStr + * @returns + */ + function parseLocationStr(locationStr: string) { + let locations = null + if (!locationStr) return locations + try { + locations = JSON.parse(locationStr) + } catch (error) {} + return Array.isArray(locations) ? locations : null + } /** * Build edge metadata @@ -40,7 +43,7 @@ export function useGraphOperations(options: Options) { * @param {string} targetId * @param {string} label */ - function buildEdgeMetadata( + function buildEdge( sourceId: string, targetId: string, label: string = '' @@ -63,7 +66,7 @@ export function useGraphOperations(options: Options) { * @param {string} taskType * @param {Coordinate} coordinate Default is { x: 100, y: 100 } */ - function buildNodeMetadata( + function buildNode( id: string, type: string, taskName: string, @@ -92,74 +95,43 @@ export function useGraphOperations(options: Options) { } /** - * Add a node to the graph - * @param {string} id - * @param {string} taskType - * @param {Coordinate} coordinate Default is { x: 100, y: 100 } + * Build graph JSON + * @param {WorkflowDefinition} definition + * @returns */ - function addNode( - id: string, - type: string, - coordinate: Coordinate = { x: 100, y: 100 } - ) { - if (!ALL_TASK_TYPES[type]) { - console.warn(`taskType:${type} is invalid!`) - return - } - const node = buildNodeMetadata(id, type, '', coordinate) - graph.value?.addNode(node) - } + function buildGraph(definition: WorkflowDefinition) { + const nodes: Node.Metadata[] = [] + const edges: Edge.Metadata[] = [] - /** - * Set node name by id - * @param {string} id - * @param {string} name - */ - function setNodeName(id: string, newName: string) { - const node = graph.value?.getCellById(id) - if (node) { - const truncation = utils.truncateText(newName, 18) - node.attr('title/text', truncation) - node.setData({ taskName: newName }) - } - } + const locations = + parseLocationStr(definition.processDefinition.locations) || [] + const tasks = definition.taskDefinitionList + const connects = definition.processTaskRelationList - /** - * Get 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, - type: data.taskType - } + tasks.forEach((task) => { + const location = locations.find((l) => l.taskCode === task.code) || {} + const node = buildNode(task.code, task.taskType, task.name, { + x: location.x, + y: location.y + }) + nodes.push(node) }) - } - /** - * Navigate to cell - * @param {string} code - */ - function navigateTo(code: string) { - if (!graph.value) return - const cell = graph.value.getCellById(code) - graph.value.scrollToCell(cell, { animation: { duration: 600 } }) - graph.value.cleanSelection() - graph.value.select(cell) + connects + .filter((r) => !!r.preTaskCode) + .forEach((c) => { + const edge = buildEdge(c.preTaskCode + '', c.postTaskCode, c.name) + edges.push(edge) + }) + return { + nodes, + edges + } } return { - buildEdgeMetadata, - buildNodeMetadata, - addNode, - setNodeName, - getNodes, - navigateTo + buildNode, + buildEdge, + buildGraph } } diff --git a/dolphinscheduler-ui-next/src/views/projects/workflow/components/dag/use-canvas-drop.ts b/dolphinscheduler-ui-next/src/views/projects/workflow/components/dag/use-dag-drag-drop.ts similarity index 64% rename from dolphinscheduler-ui-next/src/views/projects/workflow/components/dag/use-canvas-drop.ts rename to dolphinscheduler-ui-next/src/views/projects/workflow/components/dag/use-dag-drag-drop.ts index 7036052c2b..3aa8811957 100644 --- a/dolphinscheduler-ui-next/src/views/projects/workflow/components/dag/use-canvas-drop.ts +++ b/dolphinscheduler-ui-next/src/views/projects/workflow/components/dag/use-dag-drag-drop.ts @@ -15,39 +15,60 @@ * limitations under the License. */ +import { ref } from 'vue' import type { Ref } from 'vue' import type { Graph } from '@antv/x6' -import type { Dragged } from './dag' +import type { Dragged } from '.' import { genTaskCodeList } from '@/service/modules/task-definition' -import { useGraphOperations } from './dag-hooks' +import { useCellUpdate } from './dag-hooks' +import { useRoute } from 'vue-router' interface Options { readonly: Ref graph: Ref - container: Ref - dragged: Ref - projectCode: string } /** - * Drop sidebar item in canvas + * Sidebar item drag && drop in canvas */ -export function useCanvasDrop(options: Options) { - const { readonly, graph, container, dragged, projectCode } = options +export function useDagDragAndDrop(options: Options) { + const { readonly, graph } = options - const { addNode } = useGraphOperations({ graph }) + const route = useRoute() + const projectCode = Number(route.params.projectCode) - const onDrop = (e: DragEvent) => { + const { addNode } = useCellUpdate({ graph }) + + // The element currently being dragged up + const dragged = ref({ + x: 0, + y: 0, + type: '' + }) + + function onDragStart(e: DragEvent, type: string) { + if (readonly.value) { + e.preventDefault() + return + } + dragged.value = { + x: e.offsetX, + y: e.offsetY, + type: type + } + } + + function onDrop(e: DragEvent) { e.stopPropagation() e.preventDefault() if (readonly.value) { return } - if (dragged.value && graph.value && container.value && projectCode) { + if (dragged.value && graph.value && projectCode) { const { type, x: eX, y: eY } = dragged.value const { x, y } = graph.value.clientToLocal(e.clientX, e.clientY) const genNums = 1 - genTaskCodeList(genNums, Number(projectCode)).then((res) => { + genTaskCodeList(genNums, projectCode).then((res) => { const [code] = res addNode(code + '', type, { x: x - eX, y: y - eY }) // openTaskConfigModel(code, type) @@ -60,6 +81,7 @@ export function useCanvasDrop(options: Options) { } return { + onDragStart, onDrop, onDragenter: preventDefault, onDragover: preventDefault, diff --git a/dolphinscheduler-ui-next/src/views/projects/workflow/components/dag/use-graph-backfill.ts b/dolphinscheduler-ui-next/src/views/projects/workflow/components/dag/use-graph-backfill.ts new file mode 100644 index 0000000000..4cd19d37f8 --- /dev/null +++ b/dolphinscheduler-ui-next/src/views/projects/workflow/components/dag/use-graph-backfill.ts @@ -0,0 +1,43 @@ +/* + * 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 } from 'vue' +import { useCustomCellBuilder } from './dag-hooks' +import type { Graph } from '@antv/x6' +import { WorkflowDefinition } from './types' + +interface Options { + graph: Ref + definition: Ref +} + +/** + * Backfill workflow into graph + */ +export function useGraphBackfill(options: Options) { + const { graph, definition } = options + + const { buildGraph } = useCustomCellBuilder() + + watch([graph, definition], () => { + if (graph.value && definition.value) { + graph.value.fromJSON(buildGraph(definition.value)) + } + }) + + return {} +} diff --git a/dolphinscheduler-ui-next/src/views/projects/workflow/components/dag/use-node-search.ts b/dolphinscheduler-ui-next/src/views/projects/workflow/components/dag/use-node-search.ts index 59d96e8852..27eae9f417 100644 --- a/dolphinscheduler-ui-next/src/views/projects/workflow/components/dag/use-node-search.ts +++ b/dolphinscheduler-ui-next/src/views/projects/workflow/components/dag/use-node-search.ts @@ -17,7 +17,7 @@ import type { Graph } from '@antv/x6' import { ref, Ref } from 'vue' -import { useGraphOperations } from './dag-hooks' +import { useCellQuery } from './dag-hooks' interface Options { graph: Ref @@ -29,30 +29,43 @@ interface Options { export function useNodeSearch(options: Options) { const { graph } = options + /** + * Search input visible control + */ const searchInputVisible = ref(false) - const allNodes = ref([]) const toggleSearchInput = () => { searchInputVisible.value = !searchInputVisible.value } - const { getNodes, navigateTo } = useGraphOperations({ graph }) - const searchNode = (val: string) => { - navigateTo(val) + + /** + * 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 getAllNodes = () => { - const nodes = getNodes() - allNodes.value = nodes.map((node) => { - return { - label: node.name, - value: node.code - } - }) + + /** + * Navigate to cell + * @param {string} code + */ + function navigateTo(code: string) { + if (!graph.value) return + const cell = graph.value.getCellById(code) + graph.value.scrollToCell(cell, { animation: { duration: 600 } }) + graph.value.cleanSelection() + graph.value.select(cell) } return { - searchNode, - getAllNodes, - allNodes, + navigateTo, toggleSearchInput, - searchInputVisible + searchInputVisible, + reQueryNodes, + nodesDropdown } } diff --git a/dolphinscheduler-ui-next/src/views/projects/workflow/components/dag/use-sidebar-drag.ts b/dolphinscheduler-ui-next/src/views/projects/workflow/components/dag/use-text-copy.ts similarity index 63% rename from dolphinscheduler-ui-next/src/views/projects/workflow/components/dag/use-sidebar-drag.ts rename to dolphinscheduler-ui-next/src/views/projects/workflow/components/dag/use-text-copy.ts index d7b6c5367b..4bd5450836 100644 --- a/dolphinscheduler-ui-next/src/views/projects/workflow/components/dag/use-sidebar-drag.ts +++ b/dolphinscheduler-ui-next/src/views/projects/workflow/components/dag/use-text-copy.ts @@ -15,33 +15,23 @@ * limitations under the License. */ -import type { Ref } from 'vue' -import type { Dragged } from './dag' - -interface Options { - readonly: Ref - dragged: Ref -} +import { useClipboard } from '@vueuse/core' +import { useMessage } from 'naive-ui' +import { useI18n } from 'vue-i18n' /** - * Sidebar drag + * Text copy with success message */ -export function useSidebarDrag(options: Options) { - const { readonly, dragged } = options - - const onDragStart = (e: DragEvent, type: string) => { - if (readonly.value) { - e.preventDefault() - return - } - dragged.value = { - x: e.offsetX, - y: e.offsetY, - type: type - } +export function useTextCopy() { + const { t } = useI18n() + const { copy } = useClipboard() + const message = useMessage() + const copyText = (text: string) => { + copy(text).then((res) => { + message.success(t('project.dag.copy_success')) + }) } - return { - onDragStart + copy: copyText } } diff --git a/dolphinscheduler-ui-next/src/views/projects/workflow/definition/components/version-modal.tsx b/dolphinscheduler-ui-next/src/views/projects/workflow/definition/components/version-modal.tsx index 31e1ae3b0d..cf104a5d74 100644 --- a/dolphinscheduler-ui-next/src/views/projects/workflow/definition/components/version-modal.tsx +++ b/dolphinscheduler-ui-next/src/views/projects/workflow/definition/components/version-modal.tsx @@ -58,9 +58,11 @@ export default defineComponent({ } watch( - () => props.row.code, + () => props.show, () => { - getTableData(props.row) + if (props.show && props.row?.code) { + getTableData(props.row) + } } ) diff --git a/dolphinscheduler-ui-next/src/views/projects/workflow/definition/create/index.tsx b/dolphinscheduler-ui-next/src/views/projects/workflow/definition/create/index.tsx index 7e2de1b69d..7462b425ba 100644 --- a/dolphinscheduler-ui-next/src/views/projects/workflow/definition/create/index.tsx +++ b/dolphinscheduler-ui-next/src/views/projects/workflow/definition/create/index.tsx @@ -25,11 +25,6 @@ export default defineComponent({ setup() { const theme = useThemeStore() - const slots = { - toolbarLeft: () => left-operations, - toolbarRight: () => right-operations - } - return () => (
- +
) } diff --git a/dolphinscheduler-ui-next/src/views/projects/workflow/definition/detail/index.module.scss b/dolphinscheduler-ui-next/src/views/projects/workflow/definition/detail/index.module.scss new file mode 100644 index 0000000000..baab217347 --- /dev/null +++ b/dolphinscheduler-ui-next/src/views/projects/workflow/definition/detail/index.module.scss @@ -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. + */ + + $borderDark: rgba(255, 255, 255, 0.09); + $borderLight: rgb(239, 239, 245); + $bgDark: rgb(24, 24, 28); + $bgLight: #ffffff; + + .container { + width: 100%; + padding: 20px; + box-sizing: border-box; + height: calc(100vh - 100px); + overflow: hidden; + display: block; + } + + .dark { + border: solid 1px $borderDark; + background-color: $bgDark; + } + + .light { + border: solid 1px $borderLight; + background-color: $bgLight; + } + \ No newline at end of file diff --git a/dolphinscheduler-ui-next/src/views/projects/workflow/definition/detail/index.tsx b/dolphinscheduler-ui-next/src/views/projects/workflow/definition/detail/index.tsx index ebcf9000bd..fd7200d3f8 100644 --- a/dolphinscheduler-ui-next/src/views/projects/workflow/definition/detail/index.tsx +++ b/dolphinscheduler-ui-next/src/views/projects/workflow/definition/detail/index.tsx @@ -15,11 +15,44 @@ * limitations under the License. */ -import { defineComponent } from 'vue' +import { defineComponent, onMounted, ref } from 'vue' +import { useRoute } from 'vue-router' +import { useThemeStore } from '@/store/theme/theme' +import Dag from '../../components/dag' +import { queryProcessDefinitionByCode } from '@/service/modules/process-definition' +import { WorkflowDefinition } from '../../components/dag/types' +import Styles from './index.module.scss' export default defineComponent({ name: 'WorkflowDefinitionDetails', setup() { - return () =>
WorkflowDefinitionDetails
+ const theme = useThemeStore() + const route = useRoute() + const projectCode = Number(route.params.projectCode) + const code = Number(route.params.code) + + const definition = ref() + + const refresh = () => { + queryProcessDefinitionByCode(code, projectCode).then((res: any) => { + definition.value = res + }) + } + + onMounted(() => { + if (!code || !projectCode) return + refresh() + }) + + return () => ( +
+ +
+ ) } }) diff --git a/dolphinscheduler-ui-next/src/views/projects/workflow/definition/index.tsx b/dolphinscheduler-ui-next/src/views/projects/workflow/definition/index.tsx index 3bcc8f3225..792a4f4fa4 100644 --- a/dolphinscheduler-ui-next/src/views/projects/workflow/definition/index.tsx +++ b/dolphinscheduler-ui-next/src/views/projects/workflow/definition/index.tsx @@ -32,11 +32,17 @@ import ImportModal from './components/import-modal' import StartModal from './components/start-modal' import TimingModal from './components/timing-modal' import VersionModal from './components/version-modal' +import { useRouter, useRoute } from 'vue-router' +import type { Router } from 'vue-router' import styles from './index.module.scss' export default defineComponent({ name: 'WorkflowDefinitionList', setup() { + const router: Router = useRouter() + const route = useRoute() + const projectCode = Number(route.params.projectCode) + const { variables, getTableData } = useTable() const requestData = () => { @@ -61,6 +67,12 @@ export default defineComponent({ requestData() } + const createDefinition = () => { + router.push({ + path: `/projects/${projectCode}/workflow/definitions/create` + }) + } + onMounted(() => { requestData() }) @@ -69,6 +81,7 @@ export default defineComponent({ requestData, handleSearch, handleUpdateList, + createDefinition, handleChangePageSize, ...toRefs(variables) } @@ -81,7 +94,7 @@ export default defineComponent({
- + {t('project.workflow.create_workflow')} (this.showRef = true)}> diff --git a/dolphinscheduler-ui-next/src/views/projects/workflow/definition/use-table.ts b/dolphinscheduler-ui-next/src/views/projects/workflow/definition/use-table.tsx similarity index 95% rename from dolphinscheduler-ui-next/src/views/projects/workflow/definition/use-table.ts rename to dolphinscheduler-ui-next/src/views/projects/workflow/definition/use-table.tsx index f2e3044d5b..351cecfa9a 100644 --- a/dolphinscheduler-ui-next/src/views/projects/workflow/definition/use-table.ts +++ b/dolphinscheduler-ui-next/src/views/projects/workflow/definition/use-table.tsx @@ -17,7 +17,7 @@ import { h, ref, reactive } from 'vue' import { useI18n } from 'vue-i18n' -import { useRouter } from 'vue-router' +import { useRouter, RouterLink } from 'vue-router' import type { Router } from 'vue-router' import type { TableColumns } from 'naive-ui/es/data-table/src/interface' import { useAsyncState } from '@vueuse/core' @@ -49,14 +49,21 @@ export function useTable() { title: t('project.workflow.workflow_name'), key: 'name', width: 200, - render: (_row) => - h( - NEllipsis, - { style: 'max-width: 200px' }, - { - default: () => _row.name - } - ) + render: (_row) => ( + + + {_row.name} + + + ) }, { title: t('project.workflow.status'),