perf: 优化智能体与工作流幕布渲染性能

- 分阶段加载智能体配置并按需缓存 MCP 工具

- 合并画布状态更新与节点尺寸监听,启用大图可视区域渲染和静态连线

- 隔离 Tinyflow Store 实例并补充数据同步与回归测试
This commit is contained in:
2026-07-27 18:27:01 +08:00
parent dc7e46260b
commit aedefe6b5e
39 changed files with 1349 additions and 408 deletions

View File

@@ -5,22 +5,27 @@ import type {
AgentValidationIssue,
} from '../types';
import { computed, nextTick, ref, watch } from 'vue';
import {
computed,
defineAsyncComponent,
nextTick,
onBeforeUnmount,
onMounted,
ref,
watch,
} from 'vue';
import { Close } from '@element-plus/icons-vue';
import { ElButton, ElTabPane, ElTabs } from 'element-plus';
import AgentBaseForm from './AgentBaseForm.vue';
import AgentInteractionForm from './AgentInteractionForm.vue';
import AgentKnowledgeForm from './AgentKnowledgeForm.vue';
import AgentToolForm from './AgentToolForm.vue';
import AgentTryoutPanel from './AgentTryoutPanel.vue';
const props = defineProps<{
categories: AgentOption[];
issues: AgentValidationIssue[];
knowledges: AgentOption[];
mcps: AgentOption[];
mcpToolsLoading: Record<string, boolean>;
models: AgentOption[];
pluginTools: AgentOption[];
state: AgentDraftState;
@@ -30,10 +35,24 @@ const props = defineProps<{
const emit = defineEmits<{
change: [];
closeTryout: [];
loadMcpTools: [id: string];
removeCapability: [];
selectIssue: [nodeId: string];
}>();
const loadAgentInteractionForm = () => import('./AgentInteractionForm.vue');
const loadAgentKnowledgeForm = () => import('./AgentKnowledgeForm.vue');
const loadAgentToolForm = () => import('./AgentToolForm.vue');
const loadAgentTryoutPanel = () => import('./AgentTryoutPanel.vue');
const AgentInteractionForm = defineAsyncComponent(loadAgentInteractionForm);
const AgentKnowledgeForm = defineAsyncComponent(loadAgentKnowledgeForm);
const AgentToolForm = defineAsyncComponent(loadAgentToolForm);
const AgentTryoutPanel = defineAsyncComponent(loadAgentTryoutPanel);
interface AgentInteractionFormExpose {
focusField: (field: string) => Promise<void> | void;
}
const selectedKnowledge = computed(() => {
if (!props.state.selectedNodeId.startsWith('knowledge:')) return;
const localId = props.state.selectedNodeId.slice('knowledge:'.length);
@@ -41,7 +60,7 @@ const selectedKnowledge = computed(() => {
});
const activeBaseTab = ref<'basic' | 'interaction'>('basic');
const interactionForm = ref<InstanceType<typeof AgentInteractionForm>>();
const interactionForm = ref<AgentInteractionFormExpose>();
const selectedModel = computed(() =>
props.models.find((item) => item.value === String(props.state.agent.modelId)),
);
@@ -56,7 +75,7 @@ function isInteractionIssue(issue?: AgentValidationIssue) {
async function focusIssue(issue: AgentValidationIssue) {
if (issue.nodeId !== 'agent-base') return;
activeBaseTab.value = isInteractionIssue(issue) ? 'interaction' : 'basic';
if (isInteractionIssue(issue)) {
if (isInteractionIssue(issue) && issue.field) {
await nextTick();
await interactionForm.value?.focusField(issue.field);
}
@@ -94,6 +113,49 @@ const selectedToolOptions = computed(() => {
if (selectedToolKind.value === 'mcp') return props.mcps;
return props.pluginTools;
});
watch(
() =>
selectedToolKind.value === 'mcp'
? String(selectedTool.value?.targetId || '')
: '',
(mcpId) => {
if (mcpId) {
emit('loadMcpTools', mcpId);
}
},
{ immediate: true },
);
let prefetchHandle: number | undefined;
let prefetchTimer: number | undefined;
onMounted(() => {
const prefetchHiddenPanels = () => {
void Promise.allSettled([
loadAgentInteractionForm(),
loadAgentKnowledgeForm(),
loadAgentToolForm(),
loadAgentTryoutPanel(),
]);
};
if (typeof window.requestIdleCallback === 'function') {
prefetchHandle = window.requestIdleCallback(prefetchHiddenPanels, {
timeout: 3000,
});
return;
}
prefetchTimer = window.setTimeout(prefetchHiddenPanels, 1000);
});
onBeforeUnmount(() => {
if (prefetchHandle !== undefined) {
window.cancelIdleCallback(prefetchHandle);
}
if (prefetchTimer !== undefined) {
window.clearTimeout(prefetchTimer);
}
});
</script>
<template>
@@ -162,6 +224,9 @@ const selectedToolOptions = computed(() => {
v-else-if="selectedTool"
:binding="selectedTool"
:kind="selectedToolKind"
:mcp-tools-loading="
Boolean(mcpToolsLoading[String(selectedTool.targetId || '')])
"
:options="selectedToolOptions"
@change="emit('change')"
@remove="emit('removeCapability')"