From 7d654c33026c100d1c7c20c6efa584d82fb0e591 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=99=88=E5=AD=90=E9=BB=98?= <925456043@qq.com> Date: Thu, 20 Aug 2026 11:18:37 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20=E4=BC=98=E5=8C=96=20Agent=20Studio=20?= =?UTF-8?q?=E8=AF=95=E8=BF=90=E8=A1=8C=E4=BA=A4=E4=BA=92?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 将内置工具配置拆分为独立页签 - 支持试运行面板平滑居中展开和快捷收起 - 运行和审批期间禁用会话清理 --- .../ai/agents/agentResponsiveLayout.test.ts | 33 ++- .../ai/agents/components/AgentBaseForm.vue | 253 ----------------- .../components/AgentBuiltinToolsForm.vue | 261 ++++++++++++++++++ .../agents/components/AgentInspectorPanel.vue | 106 ++++++- .../ai/agents/components/AgentTryoutPanel.vue | 28 +- 5 files changed, 405 insertions(+), 276 deletions(-) create mode 100644 easyflow-ui-admin/app/src/views/ai/agents/components/AgentBuiltinToolsForm.vue diff --git a/easyflow-ui-admin/app/src/views/ai/agents/agentResponsiveLayout.test.ts b/easyflow-ui-admin/app/src/views/ai/agents/agentResponsiveLayout.test.ts index 6acdddd9..632e97a2 100644 --- a/easyflow-ui-admin/app/src/views/ai/agents/agentResponsiveLayout.test.ts +++ b/easyflow-ui-admin/app/src/views/ai/agents/agentResponsiveLayout.test.ts @@ -1,24 +1,45 @@ import { describe, expect, it } from 'vitest'; -import baseFormSource from './components/AgentBaseForm.vue?raw'; +import builtinToolsFormSource from './components/AgentBuiltinToolsForm.vue?raw'; import inspectorSource from './components/AgentInspectorPanel.vue?raw'; +import tryoutPanelSource from './components/AgentTryoutPanel.vue?raw'; describe('agent Studio responsive layout contract', () => { it('keeps the inspector usable at the 768px breakpoint', () => { expect(inspectorSource).toContain('@media (max-width: 900px)'); expect(inspectorSource).toMatch( - /@media \(max-width: 900px\)[\s\S]*?left: var\(--space-4\);[\s\S]*?width: auto;/, + /@media \(max-width: 900px\)[\s\S]*?inset:[\s\S]*?var\(--space-4\)[\s\S]*?96px;/, ); }); it('keeps the inspector and builtin tool controls usable at 375px', () => { expect(inspectorSource).toMatch( - /@media \(max-width: 480px\)[\s\S]*?right: var\(--space-2\);[\s\S]*?left: var\(--space-2\);/, + /@media \(max-width: 480px\)[\s\S]*?inset:[\s\S]*?var\(--space-2\)[\s\S]*?96px;/, ); - expect(baseFormSource).toMatch( + expect(builtinToolsFormSource).toMatch( /@media \(max-width: 480px\)[\s\S]*?--agent-tool-approval-column: 72px;/, ); - expect(baseFormSource).toContain('white-space: normal;'); - expect(baseFormSource).toContain('-webkit-line-clamp: 2;'); + expect(builtinToolsFormSource).toContain('white-space: normal;'); + expect(builtinToolsFormSource).toContain('-webkit-line-clamp: 2;'); + }); + + it('supports a centered animated tryout surface without remounting it', () => { + expect(inspectorSource).toContain('label="内置工具"'); + expect(inspectorSource).toContain(':expanded="tryoutExpanded"'); + expect(inspectorSource).toContain("'is-tryout-expanded'"); + expect(inspectorSource).toContain('--motion-duration-medium'); + expect(inspectorSource).toContain( + '@media (prefers-reduced-motion: reduce)', + ); + expect(tryoutPanelSource).toContain(':aria-pressed="Boolean(expanded)"'); + }); + + it('keeps clear disabled throughout active output and async cleanup', () => { + expect(tryoutPanelSource).toContain( + ':disabled="loading || approvalLoading || clearing"', + ); + expect(tryoutPanelSource).toContain( + 'if (loading.value || approvalLoading.value || clearing.value)', + ); }); }); diff --git a/easyflow-ui-admin/app/src/views/ai/agents/components/AgentBaseForm.vue b/easyflow-ui-admin/app/src/views/ai/agents/components/AgentBaseForm.vue index 0439a0f0..6bc2c5cd 100644 --- a/easyflow-ui-admin/app/src/views/ai/agents/components/AgentBaseForm.vue +++ b/easyflow-ui-admin/app/src/views/ai/agents/components/AgentBaseForm.vue @@ -1,6 +1,5 @@ + + + + diff --git a/easyflow-ui-admin/app/src/views/ai/agents/components/AgentInspectorPanel.vue b/easyflow-ui-admin/app/src/views/ai/agents/components/AgentInspectorPanel.vue index 7f119500..01c11708 100644 --- a/easyflow-ui-admin/app/src/views/ai/agents/components/AgentInspectorPanel.vue +++ b/easyflow-ui-admin/app/src/views/ai/agents/components/AgentInspectorPanel.vue @@ -44,11 +44,13 @@ const emit = defineEmits<{ selectIssue: [nodeId: string]; }>(); +const loadAgentBuiltinToolsForm = () => import('./AgentBuiltinToolsForm.vue'); const loadAgentInteractionForm = () => import('./AgentInteractionForm.vue'); const loadAgentKnowledgeForm = () => import('./AgentKnowledgeForm.vue'); const loadAgentSkillInspector = () => import('./AgentSkillInspector.vue'); const loadAgentToolForm = () => import('./AgentToolForm.vue'); const loadAgentTryoutPanel = () => import('./AgentTryoutPanel.vue'); +const AgentBuiltinToolsForm = defineAsyncComponent(loadAgentBuiltinToolsForm); const AgentInteractionForm = defineAsyncComponent(loadAgentInteractionForm); const AgentKnowledgeForm = defineAsyncComponent(loadAgentKnowledgeForm); const AgentSkillInspector = defineAsyncComponent(loadAgentSkillInspector); @@ -65,8 +67,9 @@ const selectedKnowledge = computed(() => { return props.state.knowledgeBindings.find((item) => item.localId === localId); }); -const activeBaseTab = ref<'basic' | 'interaction'>('basic'); +const activeBaseTab = ref<'basic' | 'builtinTools' | 'interaction'>('basic'); const interactionForm = ref(); +const tryoutExpanded = ref(false); const selectedModel = computed(() => props.models.find((item) => item.value === String(props.state.agent.modelId)), ); @@ -96,6 +99,19 @@ function handleMoveSkill(skillId: number | string | undefined, offset: -1 | 1) { emit('moveSkill', skillId, offset); } +function handleCloseTryout() { + tryoutExpanded.value = false; + emit('closeTryout'); +} + +function handleWindowKeydown(event: KeyboardEvent) { + if (event.key !== 'Escape' || !tryoutExpanded.value) { + return; + } + event.preventDefault(); + tryoutExpanded.value = false; +} + watch( () => props.issues, (issues) => { @@ -105,6 +121,16 @@ watch( { deep: true }, ); +watch( + () => props.state.panelMode, + (panelMode) => { + if (panelMode !== 'tryout') { + tryoutExpanded.value = false; + } + }, + { flush: 'sync' }, +); + const selectedTool = computed(() => { if (!props.state.selectedNodeId.startsWith('tool:')) return; const localId = props.state.selectedNodeId.slice('tool:'.length); @@ -141,8 +167,10 @@ let prefetchHandle: number | undefined; let prefetchTimer: number | undefined; onMounted(() => { + window.addEventListener('keydown', handleWindowKeydown); const prefetchHiddenPanels = () => { void Promise.allSettled([ + loadAgentBuiltinToolsForm(), loadAgentInteractionForm(), loadAgentKnowledgeForm(), loadAgentSkillInspector(), @@ -160,6 +188,7 @@ onMounted(() => { }); onBeforeUnmount(() => { + window.removeEventListener('keydown', handleWindowKeydown); if (prefetchHandle !== undefined) { window.cancelIdleCallback(prefetchHandle); } @@ -170,7 +199,13 @@ onBeforeUnmount(() => {