From 3336f30112ce02f11a4ddfd1609b1c3384b0baa3 Mon Sep 17 00:00:00 2001 From: shaohuzhang1 <80892890+shaohuzhang1@users.noreply.github.com> Date: Wed, 12 Feb 2025 11:42:59 +0800 Subject: [PATCH] perf: Workflow Canvas Rendering (#2250) --- ui/src/workflow/common/NodeCascader.vue | 51 ++---------------- ui/src/workflow/common/app-node.ts | 69 +++++++++++++++++++++++-- ui/src/workflow/common/edge.ts | 1 - ui/src/workflow/index.vue | 4 ++ 4 files changed, 72 insertions(+), 53 deletions(-) diff --git a/ui/src/workflow/common/NodeCascader.vue b/ui/src/workflow/common/NodeCascader.vue index db46e46c9..1295f77c1 100644 --- a/ui/src/workflow/common/NodeCascader.vue +++ b/ui/src/workflow/common/NodeCascader.vue @@ -49,51 +49,12 @@ const wheel = (e: any) => { function visibleChange(bool: boolean) { if (bool) { - options.value = getIncomingNode(props.nodeModel.id) + options.value = props.nodeModel.get_up_node_field_list(false, true) } } -function _getIncomingNode(id: String, startId: String, value: Array) { - let list = props.nodeModel.graphModel.getNodeIncomingNode(id) - list = list.filter((item: any) => item.id !== startId) - let firstElement = null - if (list.length > 0) { - list.forEach((item: any) => { - if (!value.some((obj: any) => obj.id === item.id)) { - if (!value.some((value_item) => value_item.value === item.id)) { - value.unshift({ - value: item.id, - label: item.properties.stepName, - type: item.type, - children: item.properties?.config?.fields || [] - }) - if (item.properties?.globalFields && item.type === 'start-node') { - firstElement = { - value: 'global', - label: t('views.applicationWorkflow.variable.global'), - type: 'global', - children: item.properties?.config?.globalFields || [] - } - } - } - } - }) - - list.forEach((item: any) => { - _getIncomingNode(item.id, startId, value) - }) - } - if (firstElement) { - value.unshift(firstElement) - } - return value -} -function getIncomingNode(id: string) { - return _getIncomingNode(id, id, []) -} const validate = () => { - const incomingNodeValue = getIncomingNode(props.nodeModel.id) - options.value = incomingNodeValue + const incomingNodeValue = props.nodeModel.get_up_node_field_list(false, true) if (!data.value || data.value.length === 0) { return Promise.reject(t('views.applicationWorkflow.variable.ReferencingRequired')) } @@ -113,15 +74,9 @@ const validate = () => { } return Promise.resolve('') } -props.nodeModel.graphModel.eventCenter.on('refresh_incoming_node_field', () => { - options.value = getIncomingNode(props.nodeModel.id) -}) -props.nodeModel.graphModel.eventCenter.on('refreshFileUploadConfig', () => { - options.value = getIncomingNode(props.nodeModel.id) -}) defineExpose({ validate }) onMounted(() => { - options.value = getIncomingNode(props.nodeModel.id) + options.value = props.nodeModel.get_up_node_field_list(false, true) }) diff --git a/ui/src/workflow/common/app-node.ts b/ui/src/workflow/common/app-node.ts index 9df0c7350..c1739aca6 100644 --- a/ui/src/workflow/common/app-node.ts +++ b/ui/src/workflow/common/app-node.ts @@ -10,6 +10,8 @@ import i18n from '@/locales' import { WorkflowType } from '@/enums/workflow' import { nodeDict } from '@/workflow/common/data' import { isActive, connect, disconnect } from './teleport' +import { t } from '@/locales' +import { type Dict } from '@/api/type/common' class AppNode extends HtmlResize.view { isMounted r?: any @@ -17,10 +19,16 @@ class AppNode extends HtmlResize.view { app: any root?: any VueNode: any + up_node_field_dict?: Dict> constructor(props: any, VueNode: any) { super(props) this.component = VueNode this.isMounted = false + props.model.clear_next_node_field = this.clear_next_node_field.bind(this) + props.model.get_up_node_field_dict = this.get_up_node_field_dict.bind(this) + props.model.get_node_field_list = this.get_node_field_list.bind(this) + props.model.get_up_node_field_list = this.get_up_node_field_list.bind(this) + if (props.model.properties.noRender) { delete props.model.properties.noRender } else { @@ -30,13 +38,12 @@ class AppNode extends HtmlResize.view { } } function getNodesName(num: number) { - let number = num + const number = num const name = props.model.properties.stepName + number if (!props.graphModel.nodes?.some((node: any) => node.properties.stepName === name.trim())) { props.model.properties.stepName = name } else { - number += 1 - getNodesName(number) + getNodesName(number + 1) } } props.model.properties.config = nodeDict[props.model.type].properties.config @@ -44,7 +51,61 @@ class AppNode extends HtmlResize.view { props.model.height = props.model.properties.height } } + get_node_field_list() { + const result = [] + if (this.props.model.type === 'start-node') { + result.push({ + value: 'global', + label: t('views.applicationWorkflow.variable.global'), + type: 'global', + children: this.props.model.properties?.config?.globalFields || [] + }) + } + result.push({ + value: this.props.model.id, + label: this.props.model.properties.stepName, + type: this.props.model.type, + children: this.props.model.properties?.config?.fields || [] + }) + return result + } + get_up_node_field_dict(contain_self: boolean, use_cache: boolean) { + if (!this.up_node_field_dict || !use_cache) { + const up_node_list = this.props.graphModel.getNodeIncomingNode(this.props.model.id) + this.up_node_field_dict = up_node_list + .filter((node) => node.id != 'start-node') + .map((node) => node.get_up_node_field_dict(true, use_cache)) + .reduce((pre, next) => ({ ...pre, ...next }), {}) + } + if (contain_self) { + return { + ...this.up_node_field_dict, + [this.props.model.id]: this.get_node_field_list() + } + } + return this.up_node_field_dict ? this.up_node_field_dict : {} + } + get_up_node_field_list(contain_self: boolean, use_cache: boolean) { + const result = Object.values(this.get_up_node_field_dict(contain_self, use_cache)).reduce( + (pre, next) => [...pre, ...next], + [] + ) + const start_node_field_list = this.props.graphModel + .getNodeModelById('start-node') + .get_node_field_list() + return [...start_node_field_list, ...result] + } + + clear_next_node_field(contain_self: boolean) { + const next_node_list = this.props.graphModel.getNodeOutgoingNode(this.props.model.id) + next_node_list.forEach((node) => { + node.clear_next_node_field(true) + }) + if (contain_self) { + this.up_node_field_dict = undefined + } + } getAnchorShape(anchorData: any) { const { x, y, type } = anchorData let isConnect = false @@ -276,7 +337,7 @@ class AppNodeModel extends HtmlResize.model { } setAttributes() { - const { t } = i18n.global; + const { t } = i18n.global this.width = this.get_width() const isLoop = (node_id: string, target_node_id: string) => { const up_node_list = this.graphModel.getNodeIncomingNode(node_id) diff --git a/ui/src/workflow/common/edge.ts b/ui/src/workflow/common/edge.ts index 730891096..461b09650 100644 --- a/ui/src/workflow/common/edge.ts +++ b/ui/src/workflow/common/edge.ts @@ -68,7 +68,6 @@ class CustomEdge2 extends BezierEdge { super.componentWillUnmount() } if (isActive()) { - console.log('unmount') disconnect(this.targetId()) } this.unmountVueComponent() diff --git a/ui/src/workflow/index.vue b/ui/src/workflow/index.vue index 3feaf2afc..03361bcec 100644 --- a/ui/src/workflow/index.vue +++ b/ui/src/workflow/index.vue @@ -103,6 +103,10 @@ const renderGraphData = (data?: any) => { lf.value.deleteEdge(id) }) }) + lf.value.graphModel.eventCenter.on('anchor:drop', (data: any) => { + // 清除当前节点下面的子节点的所有缓存 + data.nodeModel.clear_next_node_field(false) + }) setTimeout(() => { lf.value?.fitView()