diff --git a/ui/src/views/VIEW_README.md b/ui/src/views/VIEW_README.md index 3581dfed473..f8558deeac2 100644 --- a/ui/src/views/VIEW_README.md +++ b/ui/src/views/VIEW_README.md @@ -336,10 +336,11 @@ View 负责路由、资源与模式、图加载、保存/发布/调试编排 - 智能体发布先校验,再打开发布内容表单,提交先保存再发布;失败保留表单。 - 模板中心共享 UI 在 `workflow/components/template-store/`,业务入口查模板,View 确认覆盖、提交并重载详情和保存基准。 创建模板由资源创建流程负责,取消/失败保留模板中心。 -- 调试与导出先保存未提交修改,保存失败不继续;打开历史、模板或默认模型时按原流程关闭调试。 +- 智能体、工具和知识库工作流每次打开调试都先校验画布,通过后按需保存未提交修改;校验或保存失败不打开调试。 + 导出先保存未提交修改,保存失败不继续;打开历史、模板或默认模型时按原流程关闭调试。 - 智能体页面专属按钮组件放在 `workflow/application/components/`。 - `ButtonDebug` 组合调试按钮与对话模块 DebugPanel,通过页面传入的改动检查、保存方法和共享 loading - 完成保存后调试流程;公开 `close()` 供页面其他操作关闭调试。 + `ButtonDebug` 组合调试按钮与对话模块 DebugPanel,通过页面传入的改动检查、画布校验、保存方法和共享 loading + 完成校验、保存后调试流程;每次打开调试都先校验画布,通过后仅保存未提交修改,校验失败提示原因且不保存、不打开调试;公开 `close()` 供页面其他操作关闭调试。 `ButtonAiGenerate` 组合 AI 生成按钮与生成面板;生成面板与调试面板一样自行 Teleport 到 body, 使用固定定位,顶部避开工作流工具栏。 智能体工作流页面通过 `label` 传入 AI 生成、模板中心、默认模型设置、调试和发布历史入口文案。 diff --git a/ui/src/views/workflow/application/components/ButtonDebug.vue b/ui/src/views/workflow/application/components/ButtonDebug.vue index feff0c56f77..1141c4b1189 100644 --- a/ui/src/views/workflow/application/components/ButtonDebug.vue +++ b/ui/src/views/workflow/application/components/ButtonDebug.vue @@ -2,12 +2,14 @@ import { useTemplateRef } from 'vue' import DebugPanel from '@/conversation-panel/view/debug/index.vue' import type { ApplicationDetail } from '@/api/types' +import { showWorkflowValidateError } from '../../components/validate-error' defineOptions({ name: 'ButtonDebug' }) const props = defineProps<{ label: string disabled?: boolean hasUnsavedChanges: () => boolean + validateWorkflow: () => Promise saveWorkflow: () => Promise /** 当前智能体详情,保存后由工作流页更新,调试面板据此展示开场白等配置。 */ application?: ApplicationDetail | null @@ -15,21 +17,23 @@ const props = defineProps<{ const loading = defineModel('loading', { default: false }) const debugPanelRef = useTemplateRef>('debugPanelRef') -/* 调试前保存未提交修改;保存失败时保留画布,不打开调试。 */ +/* 每次打开调试前校验画布,有未提交修改时再保存;失败时不打开调试。 */ async function handleDebug() { if (loading.value || props.disabled) return - if (props.hasUnsavedChanges()) { - loading.value = true - try { - await props.saveWorkflow() - } catch { - // 保存失败已由页面保存流程提示。 - return - } finally { - loading.value = false - } + loading.value = true + try { + await props.validateWorkflow().catch((error: unknown) => { + showWorkflowValidateError(error) + throw error + }) + if (props.hasUnsavedChanges()) await props.saveWorkflow() + debugPanelRef.value?.open() + } catch { + // 校验或保存失败已由对应流程提示。 + return + } finally { + loading.value = false } - debugPanelRef.value?.open() } function close() { diff --git a/ui/src/views/workflow/application/index.vue b/ui/src/views/workflow/application/index.vue index 049b0bed333..252fd1f2781 100644 --- a/ui/src/views/workflow/application/index.vue +++ b/ui/src/views/workflow/application/index.vue @@ -134,6 +134,11 @@ function handleAutoSaveChange() { /* 调试入口 */ const debugButtonRef = useTemplateRef>('debugButtonRef') +function validateWorkflow() { + if (!workflowRef.value) return Promise.reject(new Error('工作流画布尚未加载')) + return workflowRef.value.validate() +} + function closeDebug() { debugButtonRef.value?.close() } @@ -350,6 +355,7 @@ onBeforeUnmount(() => stopAutoSave()) label="调试" :disabled="historyVisible" :has-unsaved-changes="hasUnsavedChanges" + :validate-workflow="validateWorkflow" :save-workflow="saveApplication" :application="applicationDetail" /> diff --git a/ui/src/views/workflow/knowledge/index.vue b/ui/src/views/workflow/knowledge/index.vue index 31ec0514d34..e501b4f8eaf 100644 --- a/ui/src/views/workflow/knowledge/index.vue +++ b/ui/src/views/workflow/knowledge/index.vue @@ -31,6 +31,7 @@ import ButtonTemplateStore from './ButtonTemplateStore.vue' import ButtonPublishHistory from './ButtonPublishHistory.vue' import WorkflowImportDocumentDrawer from '@/views/knowledge-detail/library/document/import-document/WorkflowImportDocumentDrawer.vue' import { goBack } from './navigation' +import { showWorkflowValidateError } from '../components/validate-error' defineOptions({ name: 'KnowledgeWorkflowView' }) @@ -239,13 +240,17 @@ function openDebug() { } async function handleDebug() { - if (loading.value || historyVisible.value) return + if (loading.value || historyVisible.value || !workflowRef.value) return loading.value = true try { + await workflowRef.value.validate().catch((error: unknown) => { + showWorkflowValidateError(error) + throw error + }) if (hasUnsavedChanges()) await saveKnowledgeWorkflow() openDebug() } catch { - // 请求失败时保留当前页面,错误由请求层提示。 + // 校验或保存失败已由对应流程提示,不打开调试。 } finally { loading.value = false } diff --git a/ui/src/views/workflow/tool/index.vue b/ui/src/views/workflow/tool/index.vue index 9549a506e5e..64cbc6b6d6d 100644 --- a/ui/src/views/workflow/tool/index.vue +++ b/ui/src/views/workflow/tool/index.vue @@ -142,25 +142,21 @@ function handleAutoSaveChange() { } } -/* 调试前保存当前画布,运行参数由基础节点声明。 */ +/* 每次打开调试前校验画布,通过后保存未提交修改;运行参数由基础节点声明。 */ const debugDrawerRef = useTemplateRef>('debugDrawerRef') async function handleDebug() { - if (loading.value || historyVisible.value) return + if (loading.value || historyVisible.value || !workflowRef.value) return loading.value = true try { - const valid = await workflowRef.value?.validate().then( - () => true, - (error) => { - showWorkflowValidateError(error) - return false - }, - ) - if (!valid) return + await workflowRef.value.validate().catch((error: unknown) => { + showWorkflowValidateError(error) + throw error + }) if (hasUnsavedChanges()) await saveToolWorkflow() const graph = getGraphData() if (graph) debugDrawerRef.value?.open(graph) } catch { - // 保存失败已由请求层提示,失败时不打开调试。 + // 校验或保存失败已由对应流程提示,不打开调试。 } finally { loading.value = false }