diff --git a/easyflow-ui-admin/app/src/router/__tests__/detail-return-navigation.test.ts b/easyflow-ui-admin/app/src/router/__tests__/detail-return-navigation.test.ts new file mode 100644 index 00000000..7cb68256 --- /dev/null +++ b/easyflow-ui-admin/app/src/router/__tests__/detail-return-navigation.test.ts @@ -0,0 +1,57 @@ +import { readFileSync } from 'node:fs'; +import { resolve } from 'node:path'; + +import { describe, expect, it } from 'vitest'; + +function readViewSource(relativePath: string) { + return readFileSync(resolve('app/src/views', relativePath), 'utf8'); +} + +describe('detail return navigation', () => { + it.each([ + ['system/sysFeedback/sysFeedbackDetail.vue', '/sys/sysFeedback'], + ['system/sysJob/SysJobLogList.vue', '/sys/sysJob'], + ['ai/workflow/WorkflowDesign.vue', '/ai/workflow'], + ['ai/workflow/RunPage.vue', '/ai/workflow'], + ['ai/workflow/execute/WorkflowExecResultList.vue', '/ai/workflow'], + ])('returns %s to its fixed parent list', (relativePath, parentPath) => { + const source = readViewSource(relativePath); + + expect(source).not.toMatch(/router\.(?:back|go)\s*\(/); + expect(source).toContain(`path: '${parentPath}'`); + }); + + it('returns approval details to the originating approval tab', () => { + const detailSource = readViewSource('system/approval/ApprovalDetail.vue'); + const listSource = readViewSource('system/approval/ApprovalManage.vue'); + + expect(detailSource).not.toMatch(/router\.(?:back|go)\s*\(/); + expect(detailSource).toContain("path: '/sys/approval'"); + expect(detailSource).toContain('query: { tab }'); + expect(listSource).toContain('tab: activeTab.value'); + }); + + it('returns plugin tool editing to its plugin tool list with a safe fallback', () => { + const editSource = readViewSource('ai/plugin/PluginToolEdit.vue'); + const listSource = readViewSource('ai/plugin/PluginToolTable.vue'); + + expect(editSource).not.toMatch(/router\.(?:back|go)\s*\(/); + expect(editSource).toContain("path: '/ai/plugin/tools'"); + expect(editSource).toContain("path: '/ai/plugin'"); + expect(listSource).toContain('pluginId: props.pluginId'); + }); + + it('returns execution steps to the filtered execution record list', () => { + const stepSource = readViewSource( + 'ai/workflow/execute/WorkflowExecStepList.vue', + ); + const recordSource = readViewSource( + 'ai/workflow/execute/WorkflowExecResultList.vue', + ); + + expect(stepSource).not.toMatch(/router\.(?:back|go)\s*\(/); + expect(stepSource).toContain("name: 'ExecRecord'"); + expect(stepSource).toContain('query: workflowId ? { workflowId } : {}'); + expect(recordSource).toContain('workflowId: $route.query.workflowId'); + }); +}); diff --git a/easyflow-ui-admin/app/src/views/ai/plugin/PluginToolEdit.vue b/easyflow-ui-admin/app/src/views/ai/plugin/PluginToolEdit.vue index 33aa9dfa..3026b321 100644 --- a/easyflow-ui-admin/app/src/views/ai/plugin/PluginToolEdit.vue +++ b/easyflow-ui-admin/app/src/views/ai/plugin/PluginToolEdit.vue @@ -164,7 +164,15 @@ function handleClickHeader(index: number) { } function back() { - router.back(); + const pluginId = String(route.query.pluginId || ''); + if (pluginId) { + void router.replace({ + path: '/ai/plugin/tools', + query: { id: pluginId }, + }); + return; + } + void router.replace({ path: '/ai/plugin' }); } function updatePluginTool(index: number) { diff --git a/easyflow-ui-admin/app/src/views/ai/plugin/PluginToolTable.vue b/easyflow-ui-admin/app/src/views/ai/plugin/PluginToolTable.vue index 5eab963d..2be6200c 100644 --- a/easyflow-ui-admin/app/src/views/ai/plugin/PluginToolTable.vue +++ b/easyflow-ui-admin/app/src/views/ai/plugin/PluginToolTable.vue @@ -53,6 +53,7 @@ const handleEdit = (row: any) => { query: { id: row.id, pageKey: '/ai/plugin', + pluginId: props.pluginId, }, }); }; diff --git a/easyflow-ui-admin/app/src/views/ai/workflow/RunPage.vue b/easyflow-ui-admin/app/src/views/ai/workflow/RunPage.vue index e30d4835..badf4b07 100644 --- a/easyflow-ui-admin/app/src/views/ai/workflow/RunPage.vue +++ b/easyflow-ui-admin/app/src/views/ai/workflow/RunPage.vue @@ -61,7 +61,10 @@ function onAsyncExecute(info: any) { class="bg-background-deep flex h-full max-h-[calc(100vh-90px)] w-full flex-col gap-6 overflow-hidden p-6" >
- + {{ $t('button.back') }}
diff --git a/easyflow-ui-admin/app/src/views/ai/workflow/WorkflowDesign.vue b/easyflow-ui-admin/app/src/views/ai/workflow/WorkflowDesign.vue index f0466337..980e1377 100644 --- a/easyflow-ui-admin/app/src/views/ai/workflow/WorkflowDesign.vue +++ b/easyflow-ui-admin/app/src/views/ai/workflow/WorkflowDesign.vue @@ -895,7 +895,7 @@ function onAsyncExecute(info: any) { v-if="!props.shareMode" :icon="ArrowLeft" link - @click="router.back()" + @click="backToWorkflowList" >
- + {{ $t('button.back') }}
diff --git a/easyflow-ui-admin/app/src/views/ai/workflow/execute/WorkflowExecStepList.vue b/easyflow-ui-admin/app/src/views/ai/workflow/execute/WorkflowExecStepList.vue index 4d24c6e7..bb21e871 100644 --- a/easyflow-ui-admin/app/src/views/ai/workflow/execute/WorkflowExecStepList.vue +++ b/easyflow-ui-admin/app/src/views/ai/workflow/execute/WorkflowExecStepList.vue @@ -44,6 +44,13 @@ function reset(formEl: FormInstance | undefined) { formEl?.resetFields(); pageDataRef.value.setQuery({}); } +function backToExecRecords() { + const workflowId = $route.query.workflowId; + void router.replace({ + name: 'ExecRecord', + query: workflowId ? { workflowId } : {}, + }); +} function getTagType(row: any) { switch (row.status) { case 1: { @@ -71,7 +78,7 @@ function getTagType(row: any) {