diff --git a/.gitignore b/.gitignore index 118069a..46fc556 100644 --- a/.gitignore +++ b/.gitignore @@ -86,3 +86,4 @@ docs/ # NestJS uploads/ +.omx/ diff --git a/apps/backend/src/api/dto/api.dto.ts b/apps/backend/src/api/dto/api.dto.ts index 98f0311..3cd297d 100644 --- a/apps/backend/src/api/dto/api.dto.ts +++ b/apps/backend/src/api/dto/api.dto.ts @@ -43,6 +43,9 @@ export class ApiDetailDto extends ApiBriefDto { @Expose() updatedAt: Date + @Expose() + currentVersionId?: string + @Expose() @Transform(({ obj }) => { const description = obj.currentVersion?.snapshot?.description diff --git a/apps/frontend/src/components/workbench/WorkbenchSidebar.vue b/apps/frontend/src/components/workbench/WorkbenchSidebar.vue index d398fe2..68479d0 100644 --- a/apps/frontend/src/components/workbench/WorkbenchSidebar.vue +++ b/apps/frontend/src/components/workbench/WorkbenchSidebar.vue @@ -175,7 +175,7 @@ function getEnvColor(envName: string): string { - + 导入 OpenAPI diff --git a/apps/frontend/src/components/workbench/api-editor/ApiEditor.vue b/apps/frontend/src/components/workbench/api-editor/ApiEditor.vue index 60f79fc..16ed207 100644 --- a/apps/frontend/src/components/workbench/api-editor/ApiEditor.vue +++ b/apps/frontend/src/components/workbench/api-editor/ApiEditor.vue @@ -4,9 +4,9 @@ import type { ApiDetail } from '@/types/api' import { useRouteParams, useRouteQuery } from '@vueuse/router' import { AlertCircle, FileText, GitBranch, Loader2, Pencil, Play, Settings2 } from 'lucide-vue-next' import { computed, ref, watch } from 'vue' -import { apiApi } from '@/api/api' import { ScrollArea } from '@/components/ui/scroll-area' import { Tabs, TabsContent, TabsList, TabsTrigger } from '@/components/ui/tabs' +import { useWorkbenchResourceStore } from '@/stores/useWorkbenchResourceStore' import ApiRunnerView from '../api-runner/ApiRunnerView.vue' import VersionHistory from '../version/VersionHistory.vue' import ApiDocView from './ApiDocView.vue' @@ -34,36 +34,49 @@ watch(activeTab, (newV, oldV) => { } }) +const resourceStore = useWorkbenchResourceStore() + const apiDetail = ref(null) const isLoading = ref(false) const loadError = ref(null) const isLoaded = computed(() => apiDetail.value !== null) -async function fetchApiDetail() { +function getCurrentApiRequestKey() { + return `${projectId.value}:${apiId.value}` +} + +async function fetchApiDetail(options: { force?: boolean } = {}) { + const currentProjectId = projectId.value + const currentApiId = apiId.value + if (!currentProjectId || !currentApiId) + return + + const requestKey = `${currentProjectId}:${currentApiId}` isLoading.value = true loadError.value = null + try { - apiDetail.value = await apiApi.getApiDetail(projectId.value, apiId.value) + const detail = await resourceStore.getApiDetail(currentProjectId, currentApiId, options) + if (getCurrentApiRequestKey() !== requestKey) + return + apiDetail.value = detail } catch (error) { + if (getCurrentApiRequestKey() !== requestKey) + return console.error('获取 API 详情失败:', error) loadError.value = `获取 API 详情失败: ${error}` } finally { - isLoading.value = false + if (getCurrentApiRequestKey() === requestKey) { + isLoading.value = false + } } } async function refreshApiDetail() { - loadError.value = null - try { - apiDetail.value = await apiApi.getApiDetail(projectId.value, apiId.value) - } - catch (error) { - console.error('获取 API 详情失败:', error) - loadError.value = `获取 API 详情失败: ${error}` - } + await fetchApiDetail({ force: true }) } // apiId 和 projectId 变化,刷新 API 详情 @@ -117,19 +130,19 @@ watch([apiId, projectId], ([curApiId, curProjectId]) => { - + - + - + - + { /> - + diff --git a/apps/frontend/src/components/workbench/version/VersionCompareSheet.vue b/apps/frontend/src/components/workbench/version/VersionCompareSheet.vue index 2c22b5b..7fc922e 100644 --- a/apps/frontend/src/components/workbench/version/VersionCompareSheet.vue +++ b/apps/frontend/src/components/workbench/version/VersionCompareSheet.vue @@ -10,7 +10,6 @@ import { X, } from 'lucide-vue-next' import { computed, ref, toRaw, watch } from 'vue' -import { versionApi } from '@/api/version' import CodeBlock from '@/components/common/CodeBlock.vue' import { Badge } from '@/components/ui/badge' import { ScrollArea } from '@/components/ui/scroll-area' @@ -25,6 +24,7 @@ import { import { methodBadgeColors } from '@/constants/api' import { versionDiffFieldLabels, versionStatusColors, versionStatusLabels } from '@/constants/version' import { cn } from '@/lib/utils' +import { useWorkbenchResourceStore } from '@/stores/useWorkbenchResourceStore' const props = defineProps<{ projectId: string @@ -48,6 +48,7 @@ interface ComparisonResult { } const isOpen = defineModel('open', { required: true }) +const resourceStore = useWorkbenchResourceStore() const comparison = ref(null) const isLoading = ref(false) @@ -124,47 +125,50 @@ function isComplexValue(value: unknown): boolean { return typeof value === 'object' && value !== null } -async function fetchComparison() { - if (!props.fromVersionId || !props.toVersionId) - return +function getComparisonRequestKey( + projectId = props.projectId, + apiId = props.apiId, + fromVersionId = props.fromVersionId, + toVersionId = props.toVersionId, +) { + return `${projectId}:${apiId}:${fromVersionId ?? ''}:${toVersionId ?? ''}` +} +async function fetchComparison(projectId: string, apiId: string, fromVersionId: string, toVersionId: string) { + const requestKey = getComparisonRequestKey(projectId, apiId, fromVersionId, toVersionId) isLoading.value = true loadError.value = null try { - comparison.value = await versionApi.compareVersions( - props.projectId, - props.apiId, - props.fromVersionId, - props.toVersionId, - ) + const result = await resourceStore.getVersionComparison(projectId, apiId, fromVersionId, toVersionId) + if (!isOpen.value || getComparisonRequestKey() !== requestKey) + return + comparison.value = result } catch (error) { + if (!isOpen.value || getComparisonRequestKey() !== requestKey) + return loadError.value = `获取比较数据失败: ${error}` console.error('Failed to fetch comparison:', error) } finally { - isLoading.value = false + if (!isOpen.value || getComparisonRequestKey() === requestKey) { + isLoading.value = false + } } } watch( - () => [props.fromVersionId, props.toVersionId], - ([from, to]) => { - if (from && to && isOpen.value) { - fetchComparison() - } - else { + () => [isOpen.value, props.projectId, props.apiId, props.fromVersionId, props.toVersionId] as const, + ([open, projectId, apiId, from, to]) => { + if (!open || !from || !to) { comparison.value = null + return } + fetchComparison(projectId, apiId, from, to) }, + { immediate: true }, ) - -watch(isOpen, (open) => { - if (open && props.fromVersionId && props.toVersionId) { - fetchComparison() - } -})