Skip to content

Commit a086e35

Browse files
committed
fix: API Response UI Error
1 parent 47dc7dd commit a086e35

10 files changed

Lines changed: 60 additions & 106 deletions

File tree

apps/frontend/src/components/workbench/api-editor/ApiEditor.vue

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -78,7 +78,7 @@ watch([apiId, projectId], ([curApiId, curProjectId]) => {
7878
<div class="h-full bg-background">
7979
<div
8080
v-if="isLoading"
81-
class="flex-1 flex items-center justify-center"
81+
class="flex items-center justify-center h-full"
8282
>
8383
<div class="flex flex-col items-center gap-3 text-muted-foreground">
8484
<Loader2 class="h-8 w-8 animate-spin" />
@@ -99,7 +99,7 @@ watch([apiId, projectId], ([curApiId, curProjectId]) => {
9999
<Tabs
100100
v-else-if="isLoaded && apiDetail"
101101
v-model="activeTab"
102-
class="flex-1 flex flex-col h-full"
102+
class="flex-1 flex flex-col size-full"
103103
>
104104
<div class="border-b px-4 py-2 bg-muted/20">
105105
<TabsList class="h-10 bg-transparent p-0 gap-1">

apps/frontend/src/components/workbench/api-editor/doc/ApiRequestBody.vue

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ import { FileJson, FormInput } from 'lucide-vue-next'
44
import { computed } from 'vue'
55
import JsonSchemaPreviewer from '@/components/json-schema/JsonSchemaPreviewer.vue'
66
import { Badge } from '@/components/ui/badge'
7-
import { BODY_NAME_MAP, requestBodyTypeLabels } from '@/constants/api'
7+
import { CONTENT_TYPE_MAP, requestBodyTypeLabels } from '@/constants/api'
88
import ParamTable from './ParamTable.vue'
99
1010
const props = defineProps<{
@@ -42,8 +42,8 @@ const isFormType = computed(() =>
4242
<Badge variant="outline" class="font-mono text-xs">
4343
{{ typeLabel }}
4444
</Badge>
45-
<Badge v-if="BODY_NAME_MAP[body!.type]" variant="secondary" class="font-mono text-xs">
46-
{{ BODY_NAME_MAP[body!.type] }}
45+
<Badge v-if="CONTENT_TYPE_MAP[body!.type]" variant="secondary" class="font-mono text-xs">
46+
{{ CONTENT_TYPE_MAP[body!.type] }}
4747
</Badge>
4848
</div>
4949
</div>

apps/frontend/src/components/workbench/api-editor/editor/RequestBodyEditor.vue

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ import { Label } from '@/components/ui/label'
99
import { RadioGroup, RadioGroupItem } from '@/components/ui/radio-group'
1010
import { Textarea } from '@/components/ui/textarea'
1111
import {
12-
BODY_NAME_MAP,
12+
CONTENT_TYPE_MAP,
1313
FORM_DATA_TYPES,
1414
PARAM_TYPES,
1515
REQUEST_BODY_TYPES,
@@ -63,8 +63,8 @@ const isNoneType = computed(() => currentType.value === 'none')
6363
<div class="space-y-3">
6464
<div class="flex items-center gap-3 h-6">
6565
<Label>请求体类型</Label>
66-
<Badge v-if="BODY_NAME_MAP[currentType]" variant="outline" class="font-mono text-xs">
67-
{{ BODY_NAME_MAP[currentType] }}
66+
<Badge v-if="CONTENT_TYPE_MAP[currentType]" variant="outline" class="font-mono text-xs">
67+
{{ CONTENT_TYPE_MAP[currentType] }}
6868
</Badge>
6969
</div>
7070

apps/frontend/src/components/workbench/api-runner/ResponsePanel.vue

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -69,26 +69,26 @@ function formatSize(bytes: number): string {
6969
</div>
7070
</div>
7171

72-
<div class="min-h-60 flex-1 overflow-hidden">
72+
<div class="min-h-60 grid place-items-center">
7373
<div
7474
v-if="runnerStore.isLoading"
75-
class="h-full flex flex-col items-center justify-center gap-3 text-muted-foreground"
75+
class="flex flex-col items-center gap-3 text-muted-foreground"
7676
>
7777
<Loader2 class="h-8 w-8 animate-spin" />
7878
<span class="text-sm">请求中...</span>
7979
</div>
8080

8181
<div
8282
v-else-if="runnerStore.status === 'error'"
83-
class="h-full flex flex-col items-center justify-center gap-3 text-destructive"
83+
class="flex flex-col items-center gap-3 text-destructive"
8484
>
8585
<AlertCircle class="h-8 w-8" />
8686
<span class="text-sm">{{ runnerStore.errorMessage ?? '请求失败' }}</span>
8787
</div>
8888

8989
<div
9090
v-else-if="runnerStore.status === 'idle'"
91-
class="h-full flex flex-col items-center justify-center gap-3 text-muted-foreground"
91+
class="flex flex-col items-center gap-3 text-muted-foreground"
9292
>
9393
<Code class="h-8 w-8" />
9494
<span class="text-sm">点击「发送请求」获取响应</span>
@@ -97,7 +97,7 @@ function formatSize(bytes: number): string {
9797
<Tabs
9898
v-else
9999
v-model="activeTab"
100-
class="h-full flex flex-col"
100+
class="flex flex-col w-full"
101101
>
102102
<TabsList class="bg-transparent m-2">
103103
<TabsTrigger
@@ -111,7 +111,7 @@ function formatSize(bytes: number): string {
111111
</TabsTrigger>
112112
</TabsList>
113113

114-
<div class="flex-1 overflow-hidden">
114+
<div class="m-2">
115115
<TabsContent value="body" class="h-full mt-0">
116116
<ResponseBody />
117117
</TabsContent>
Lines changed: 6 additions & 49 deletions
Original file line numberDiff line numberDiff line change
@@ -1,59 +1,16 @@
11
<script lang="ts" setup>
2-
import { Check, Copy } from 'lucide-vue-next'
32
import { storeToRefs } from 'pinia'
4-
import { ref } from 'vue'
5-
import { toast } from 'vue-sonner'
6-
import { Button } from '@/components/ui/button'
7-
import { ScrollArea } from '@/components/ui/scroll-area'
8-
import { Textarea } from '@/components/ui/textarea'
3+
import CodeBlock from '@/components/common/CodeBlock.vue'
94
import { useApiRunnerStore } from '@/stores/useApiRunnerStore'
105
116
const runnerStore = useApiRunnerStore()
127
const { curlCommand } = storeToRefs(runnerStore)
13-
14-
const copied = ref(false)
15-
16-
async function copyToClipboard() {
17-
try {
18-
await navigator.clipboard.writeText(curlCommand.value)
19-
copied.value = true
20-
toast.success('已复制到剪贴板')
21-
22-
setTimeout(() => {
23-
copied.value = false
24-
}, 2000)
25-
}
26-
catch (error) {
27-
console.error('复制失败', error)
28-
toast.error('复制失败')
29-
}
30-
}
318
</script>
329

3310
<template>
34-
<ScrollArea class="h-full">
35-
<div class="p-4 space-y-3">
36-
<div class="flex items-center justify-between">
37-
<p class="text-sm text-muted-foreground">
38-
根据当前配置生成的 cURL 命令
39-
</p>
40-
<Button
41-
variant="outline"
42-
size="sm"
43-
class="gap-1.5"
44-
@click="copyToClipboard"
45-
>
46-
<Check v-if="copied" class="h-4 w-4 text-emerald-500" />
47-
<Copy v-else class="h-4 w-4" />
48-
{{ copied ? '已复制' : '复制' }}
49-
</Button>
50-
</div>
51-
52-
<Textarea
53-
v-model="curlCommand"
54-
readonly
55-
class="font-mono text-sm min-h-[200px] resize-none bg-muted/30"
56-
/>
57-
</div>
58-
</ScrollArea>
11+
<CodeBlock
12+
:code="curlCommand"
13+
lang="shellscript"
14+
title="根据当前配置生成的 cURL 命令"
15+
/>
5916
</template>

apps/frontend/src/components/workbench/api-runner/response/ResponseBody.vue

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,5 @@ const isJson = computed(() => {
2727
</script>
2828

2929
<template>
30-
<div class="m-2">
31-
<CodeBlock :code="formattedBody" :lang="isJson ? 'json' : 'plaintext'" />
32-
</div>
30+
<CodeBlock :code="formattedBody" :lang="isJson ? 'json' : 'plaintext'" />
3331
</template>

apps/frontend/src/components/workbench/api-runner/response/ResponseHeaders.vue

Lines changed: 29 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -23,37 +23,35 @@ const headersList = computed(() => {
2323

2424
<template>
2525
<ScrollArea class="h-full">
26-
<div class="p-4">
27-
<Table>
28-
<TableHeader>
29-
<TableRow class="hover:bg-transparent">
30-
<TableHead class="w-[200px]">
31-
Header
32-
</TableHead>
33-
<TableHead>Value</TableHead>
34-
</TableRow>
35-
</TableHeader>
36-
<TableBody>
37-
<TableRow
38-
v-for="header in headersList"
39-
:key="header.key"
40-
class="hover:bg-muted/30"
41-
>
42-
<TableCell class="font-mono text-sm font-medium">
43-
{{ header.key }}
44-
</TableCell>
45-
<TableCell class="font-mono text-sm text-muted-foreground break-all">
46-
{{ header.value }}
47-
</TableCell>
48-
</TableRow>
26+
<Table class="table-fixed w-full">
27+
<TableHeader>
28+
<TableRow class="hover:bg-transparent">
29+
<TableHead class="w-60">
30+
Header
31+
</TableHead>
32+
<TableHead>Value</TableHead>
33+
</TableRow>
34+
</TableHeader>
35+
<TableBody>
36+
<TableRow
37+
v-for="header in headersList"
38+
:key="header.key"
39+
class="hover:bg-muted/30"
40+
>
41+
<TableCell class="font-mono text-sm font-medium">
42+
{{ header.key }}
43+
</TableCell>
44+
<TableCell class="font-mono text-sm text-muted-foreground break-all whitespace-normal">
45+
{{ header.value }}
46+
</TableCell>
47+
</TableRow>
4948

50-
<TableRow v-if="headersList.length === 0">
51-
<TableCell colspan="2" class="text-center text-muted-foreground py-8">
52-
暂无响应头
53-
</TableCell>
54-
</TableRow>
55-
</TableBody>
56-
</Table>
57-
</div>
49+
<TableRow v-if="headersList.length === 0">
50+
<TableCell colspan="2" class="text-center text-muted-foreground py-8">
51+
暂无响应头
52+
</TableCell>
53+
</TableRow>
54+
</TableBody>
55+
</Table>
5856
</ScrollArea>
5957
</template>

apps/frontend/src/constants/api.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -145,7 +145,7 @@ export const HEADER_PARAMS = [
145145
] as const
146146

147147
/** 请求体类型名称映射 */
148-
export const BODY_NAME_MAP: Record<RequestBodyType, string> = {
148+
export const CONTENT_TYPE_MAP: Record<RequestBodyType, string> = {
149149
'json': 'application/json',
150150
'form-data': 'multipart/form-data',
151151
'x-www-form-urlencoded': 'application/x-www-form-urlencoded',

apps/frontend/src/stores/useApiRunnerStore.ts

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ import type {
1111
import { defineStore, storeToRefs } from 'pinia'
1212
import { computed, ref } from 'vue'
1313
import { proxyApi } from '@/api/proxy'
14+
import { CONTENT_TYPE_MAP } from '@/constants/api'
1415
import { convertToRuntimeParam, createEmptyRuntimeParam, generateCurl } from '@/lib/api-runner'
1516
import { useProjectStore } from './useProjectStore'
1617

@@ -88,6 +89,11 @@ export const useApiRunnerStore = defineStore('apiRunner', () => {
8889
}
8990
}
9091

92+
// 添加 Content-Type 头
93+
if (body.value.type) {
94+
result['Content-Type'] = CONTENT_TYPE_MAP[body.value.type]
95+
}
96+
9197
// 添加认证头
9298
if (auth.value.type === 'bearer' && auth.value.bearerToken) {
9399
result.Authorization = `Bearer ${auth.value.bearerToken}`

apps/frontend/src/views/workbench/ApiDetailView.vue

Lines changed: 3 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
<script lang="ts" setup>
22
import { useRouteParams } from '@vueuse/router'
3-
import { computed, onMounted, ref, watch } from 'vue'
3+
import { computed, ref, watch } from 'vue'
44
import { useRouter } from 'vue-router'
55
import { ScrollArea } from '@/components/ui/scroll-area'
66
import ApiEditor from '@/components/workbench/api-editor/ApiEditor.vue'
@@ -10,12 +10,7 @@ import { useApiTreeStore } from '@/stores/useApiTreeStore'
1010
import { useTabStore } from '@/stores/useTabStore'
1111
1212
const tabStore = useTabStore()
13-
const { clearTabsDirty } = tabStore
14-
15-
// 挂载时清除标签页的脏状态
16-
onMounted(() => {
17-
clearTabsDirty()
18-
})
13+
// const { clearTabsDirty } = tabStore
1914
2015
const apiTreeStore = useApiTreeStore()
2116
@@ -118,7 +113,7 @@ function handleDragEnd() {
118113
</script>
119114

120115
<template>
121-
<div class="flex-1 flex flex-col h-full">
116+
<div class="flex-1 flex flex-col min-w-0">
122117
<div class="h-9 border-b border-border flex items-center bg-muted/30">
123118
<ScrollArea orientation="horizontal" class="flex-1">
124119
<div class="flex items-center h-full">

0 commit comments

Comments
 (0)