Skip to content

Commit e4b2116

Browse files
committed
refactor: import open api btn position change
1 parent fa3a36a commit e4b2116

6 files changed

Lines changed: 97 additions & 102 deletions

File tree

apps/frontend/src/components/workbench/ApiSidebar.vue

Lines changed: 0 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,6 @@ import CloneApiDialog from './dialogs/CloneApiDialog.vue'
99
import DeleteApiDialog from './dialogs/DeleteApiDialog.vue'
1010
import DeleteGroupDialog from './dialogs/DeleteGroupDialog.vue'
1111
import GroupFormDialog from './dialogs/GroupFormDialog.vue'
12-
import ImportOpenapiDialog from './dialogs/import/ImportOpenapiDialog.vue'
1312
1413
const tabStore = useTabStore()
1514
@@ -38,12 +37,6 @@ const apiToDelete = ref<ApiBrief | null>(null)
3837
const isCloneApiDialogOpen = ref(false)
3938
const apiToClone = ref<ApiBrief | null>(null)
4039
41-
const isImportDialogOpen = ref(false)
42-
43-
function handleImportOpenapi() {
44-
isImportDialogOpen.value = true
45-
}
46-
4740
function handleCreateGroup(parentId?: string) {
4841
groupDialogMode.value = 'create'
4942
groupDialogParentId.value = parentId
@@ -103,7 +96,6 @@ function handleDeleteApi(api: ApiBrief) {
10396
@select-api="handleSelectApi"
10497
@clone-api="handleCloneApi"
10598
@delete-api="handleDeleteApi"
106-
@import-openapi="handleImportOpenapi"
10799
/>
108100

109101
<div
@@ -138,10 +130,6 @@ function handleDeleteApi(api: ApiBrief) {
138130
v-model:open="isCloneApiDialogOpen"
139131
:api="apiToClone"
140132
/>
141-
142-
<ImportOpenapiDialog
143-
v-model:open="isImportDialogOpen"
144-
/>
145133
</aside>
146134
</template>
147135

apps/frontend/src/components/workbench/WorkbenchSidebar.vue

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ import {
33
ChevronRight,
44
Globe,
55
Home,
6+
Import,
67
Settings,
78
} from 'lucide-vue-next'
89
import { storeToRefs } from 'pinia'
@@ -25,6 +26,7 @@ import {
2526
} from '@/components/ui/tooltip'
2627
import { cn, getAbbreviation } from '@/lib/utils'
2728
import { useProjectStore } from '@/stores/useProjectStore'
29+
import ImportOpenapiDialog from './dialogs/import/ImportOpenapiDialog.vue'
2830
import ProjectSettingsSheet from './ProjectSettingsSheet.vue'
2931
3032
const router = useRouter()
@@ -35,6 +37,7 @@ const { setCurEnvId } = projectStore
3537
3638
const isSettingsSheetOpen = ref(false)
3739
const isEnvPopoverOpen = ref(false)
40+
const isImportDialogOpen = ref(false)
3841
3942
function goBack() {
4043
router.push({ name: 'Dashboard' })
@@ -161,6 +164,22 @@ function getEnvColor(envName: string): string {
161164
</PopoverContent>
162165
</Popover>
163166

167+
<Tooltip>
168+
<TooltipTrigger as-child>
169+
<Button
170+
variant="ghost"
171+
size="icon"
172+
class="h-8 w-8 mt-1 text-muted-foreground hover:text-foreground"
173+
@click="isImportDialogOpen = true"
174+
>
175+
<Import class="h-4 w-4" />
176+
</Button>
177+
</TooltipTrigger>
178+
<TooltipContent side="bottom">
179+
导入 OpenAPI
180+
</TooltipContent>
181+
</Tooltip>
182+
164183
<Tooltip>
165184
<TooltipTrigger as-child>
166185
<Button
@@ -184,5 +203,9 @@ function getEnvColor(envName: string): string {
184203
v-model:open="isSettingsSheetOpen"
185204
:project="projectDetail"
186205
/>
206+
207+
<ImportOpenapiDialog
208+
v-model:open="isImportDialogOpen"
209+
/>
187210
</aside>
188211
</template>

apps/frontend/src/components/workbench/api-tree/ApiTree.vue

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,6 @@ const emits = defineEmits<{
2727
(e: 'deleteGroup', group: GroupNodeWithApis): void
2828
(e: 'cloneApi', api: ApiBrief): void
2929
(e: 'deleteApi', api: ApiBrief): void
30-
(e: 'importOpenapi'): void
3130
}>()
3231
3332
const apiTreeStore = useApiTreeStore()
@@ -109,7 +108,6 @@ onMounted(() => {
109108
<ApiTreeToolbar
110109
@create-api="handleToolbarCreateApi"
111110
@create-group="emits('createGroup')"
112-
@import-openapi="emits('importOpenapi')"
113111
/>
114112

115113
<ScrollArea class="flex-1 px-2 min-h-0">

apps/frontend/src/components/workbench/api-tree/ApiTreeToolbar.vue

Lines changed: 0 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,6 @@ import {
44
ChevronsUpDown,
55
FilePlus,
66
FolderPlus,
7-
Import,
87
RefreshCw,
98
Search,
109
} from 'lucide-vue-next'
@@ -23,7 +22,6 @@ import { useApiTreeStore } from '@/stores/useApiTreeStore'
2322
const emits = defineEmits<{
2423
(e: 'createGroup'): void
2524
(e: 'createApi'): void
26-
(e: 'importOpenapi'): void
2725
}>()
2826
2927
const apiTreeStore = useApiTreeStore()
@@ -143,24 +141,6 @@ watch(searchInput, (val) => {
143141
</TooltipContent>
144142
</Tooltip>
145143
</TooltipProvider>
146-
147-
<TooltipProvider :delay-duration="300">
148-
<Tooltip>
149-
<TooltipTrigger as-child>
150-
<Button
151-
variant="ghost"
152-
size="icon"
153-
class="h-7 w-7"
154-
@click="emits('importOpenapi')"
155-
>
156-
<Import class="h-3.5 w-3.5" />
157-
</Button>
158-
</TooltipTrigger>
159-
<TooltipContent side="bottom">
160-
导入 OpenAPI
161-
</TooltipContent>
162-
</Tooltip>
163-
</TooltipProvider>
164144
</div>
165145
</div>
166146
</template>

apps/frontend/src/components/workbench/dialogs/import/ImportOpenapiPreview.vue

Lines changed: 58 additions & 68 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,8 @@
11
<script lang="ts" setup>
2-
import type { GroupNodeWithApis, HttpMethod } from '@/types/api'
2+
import type { HttpMethod } from '@/types/api'
33
import type { ConflictStrategy, ImportPreview } from '@/types/import'
44
import { AlertTriangle, ChevronDown, ChevronRight, FileText, FolderOpen } from 'lucide-vue-next'
5+
import { storeToRefs } from 'pinia'
56
import { computed, ref } from 'vue'
67
import { Badge } from '@/components/ui/badge'
78
import { Button } from '@/components/ui/button'
@@ -13,7 +14,6 @@ import {
1314
} from '@/components/ui/collapsible'
1415
import { Label } from '@/components/ui/label'
1516
import { RadioGroup, RadioGroupItem } from '@/components/ui/radio-group'
16-
import { ScrollArea } from '@/components/ui/scroll-area'
1717
import {
1818
Select,
1919
SelectContent,
@@ -40,30 +40,19 @@ const emits = defineEmits<{
4040
}>()
4141
4242
const apiTreeStore = useApiTreeStore()
43+
const { flatGroups } = storeToRefs(apiTreeStore)
44+
45+
/** 按 tags 自动创建分组(Reka-ui 不支持清空选项,,,) */
46+
const AUTO_GROUP_VALUE = '__auto__'
4347
4448
// 配置选项
4549
const conflictStrategy = ref<ConflictStrategy>('skip')
46-
const targetGroupId = ref<string>('')
50+
const targetGroupId = ref<string>(AUTO_GROUP_VALUE)
4751
const createMissingGroups = ref(true)
4852
4953
// 分组展开状态
5054
const expandedGroups = ref<Set<string>>(new Set())
5155
52-
// 扁平化分组列表
53-
const flatGroups = computed(() => {
54-
const result: { id: string, name: string, level: number }[] = []
55-
56-
function traverse(nodes: GroupNodeWithApis[], level: number) {
57-
for (const node of nodes) {
58-
result.push({ id: node.id, name: node.name, level })
59-
traverse(node.children, level + 1)
60-
}
61-
}
62-
63-
traverse(apiTreeStore.tree, 0)
64-
return result
65-
})
66-
6756
// 按分组组织 API
6857
const apisGroupedByGroup = computed(() => {
6958
const groups = new Map<string, typeof props.preview.apis>()
@@ -87,7 +76,7 @@ function toggleGroup(groupName: string) {
8776
function handleSubmit() {
8877
emits('submit', {
8978
conflictStrategy: conflictStrategy.value,
90-
targetGroupId: targetGroupId.value || undefined,
79+
targetGroupId: targetGroupId.value === AUTO_GROUP_VALUE ? undefined : targetGroupId.value,
9180
createMissingGroups: createMissingGroups.value,
9281
})
9382
}
@@ -194,11 +183,14 @@ function getMethodClass(method: HttpMethod) {
194183
<!-- 目标分组 -->
195184
<div class="space-y-2">
196185
<Label for="target-group">目标分组(可选)</Label>
197-
<Select v-model="targetGroupId" :disabled="!flatGroups.length">
186+
<Select v-model="targetGroupId">
198187
<SelectTrigger id="target-group">
199-
<SelectValue :placeholder="`${flatGroups.length > 0 ? '' : '无可选分组,'}按 tags 自动创建分组`" />
188+
<SelectValue placeholder="按 tags 自动创建分组" />
200189
</SelectTrigger>
201190
<SelectContent>
191+
<SelectItem :value="AUTO_GROUP_VALUE">
192+
<span class="text-muted-foreground">按 tags 自动创建分组</span>
193+
</SelectItem>
202194
<SelectItem
203195
v-for="group in flatGroups"
204196
:key="group.id"
@@ -218,7 +210,7 @@ function getMethodClass(method: HttpMethod) {
218210
<Checkbox
219211
id="create-groups"
220212
:checked="createMissingGroups"
221-
:disabled="!!targetGroupId"
213+
:disabled="targetGroupId !== AUTO_GROUP_VALUE"
222214
@update:checked="createMissingGroups = $event as boolean"
223215
/>
224216
<Label for="create-groups" class="font-normal cursor-pointer">
@@ -232,54 +224,52 @@ function getMethodClass(method: HttpMethod) {
232224
<!-- API 预览列表 -->
233225
<div class="space-y-2">
234226
<Label>接口预览</Label>
235-
<ScrollArea class="h-[200px] rounded-md border">
236-
<div class="p-2 space-y-1">
237-
<Collapsible
238-
v-for="[groupName, apis] in apisGroupedByGroup"
239-
:key="groupName"
240-
:open="expandedGroups.has(groupName)"
241-
@update:open="toggleGroup(groupName)"
242-
>
243-
<CollapsibleTrigger class="flex items-center gap-2 w-full p-2 hover:bg-muted rounded-md text-left">
244-
<component
245-
:is="expandedGroups.has(groupName) ? ChevronDown : ChevronRight"
246-
class="h-4 w-4 text-muted-foreground shrink-0"
247-
/>
248-
<FolderOpen class="h-4 w-4 text-muted-foreground shrink-0" />
249-
<span class="text-sm font-medium flex-1 truncate">{{ groupName }}</span>
250-
<Badge variant="secondary" class="ml-auto">
251-
{{ apis.length }}
252-
</Badge>
253-
</CollapsibleTrigger>
254-
<CollapsibleContent>
255-
<div class="ml-6 pl-4 border-l space-y-1">
256-
<div
257-
v-for="api in apis"
258-
:key="`${api.method}-${api.path}`"
259-
class="flex items-center gap-2 p-2 text-sm rounded-md"
260-
:class="api.hasConflict ? 'bg-amber-50 dark:bg-amber-950/20' : 'hover:bg-muted'"
227+
<div class="p-2 space-y-1">
228+
<Collapsible
229+
v-for="[groupName, apis] in apisGroupedByGroup"
230+
:key="groupName"
231+
:open="expandedGroups.has(groupName)"
232+
@update:open="toggleGroup(groupName)"
233+
>
234+
<CollapsibleTrigger class="flex items-center gap-2 w-full p-2 hover:bg-muted rounded-md text-left">
235+
<component
236+
:is="expandedGroups.has(groupName) ? ChevronDown : ChevronRight"
237+
class="h-4 w-4 text-muted-foreground shrink-0"
238+
/>
239+
<FolderOpen class="h-4 w-4 text-muted-foreground shrink-0" />
240+
<span class="text-sm font-medium flex-1 truncate">{{ groupName }}</span>
241+
<Badge variant="secondary" class="ml-auto">
242+
{{ apis.length }}
243+
</Badge>
244+
</CollapsibleTrigger>
245+
<CollapsibleContent>
246+
<div class="ml-6 pl-4 border-l space-y-1">
247+
<div
248+
v-for="api in apis"
249+
:key="`${api.method}-${api.path}`"
250+
class="flex items-center gap-2 p-2 text-sm rounded-md"
251+
:class="api.hasConflict ? 'bg-amber-50 dark:bg-amber-950/20' : 'hover:bg-muted'"
252+
>
253+
<span
254+
class="font-mono text-xs font-bold w-16 shrink-0"
255+
:class="getMethodClass(api.method)"
261256
>
262-
<span
263-
class="font-mono text-xs font-bold w-16 shrink-0"
264-
:class="getMethodClass(api.method)"
265-
>
266-
{{ api.method }}
267-
</span>
268-
<span class="font-mono text-xs text-muted-foreground truncate flex-1">
269-
{{ api.path }}
270-
</span>
271-
<span class="text-xs truncate max-w-[120px]">
272-
{{ api.name }}
273-
</span>
274-
<Badge v-if="api.hasConflict" variant="outline" class="text-amber-600 border-amber-300 shrink-0">
275-
冲突
276-
</Badge>
277-
</div>
257+
{{ api.method }}
258+
</span>
259+
<span class="font-mono text-xs text-muted-foreground truncate flex-1">
260+
{{ api.path }}
261+
</span>
262+
<span class="text-xs truncate max-w-[120px]">
263+
{{ api.name }}
264+
</span>
265+
<Badge v-if="api.hasConflict" variant="outline" class="text-amber-600 border-amber-300 shrink-0">
266+
冲突
267+
</Badge>
278268
</div>
279-
</CollapsibleContent>
280-
</Collapsible>
281-
</div>
282-
</ScrollArea>
269+
</div>
270+
</CollapsibleContent>
271+
</Collapsible>
272+
</div>
283273
</div>
284274

285275
<!-- 操作按钮 -->

apps/frontend/src/stores/useApiTreeStore.ts

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,21 @@ export const useApiTreeStore = defineStore('apiTree', () => {
4444
return filterTreeByQuery(tree.value, query)
4545
})
4646

47+
/** 扁平化分组列表 */
48+
const flatGroups = computed(() => {
49+
const result: { id: string, name: string, level: number }[] = []
50+
51+
function traverse(nodes: GroupNodeWithApis[], level: number) {
52+
for (const node of nodes) {
53+
result.push({ id: node.id, name: node.name, level })
54+
traverse(node.children, level + 1)
55+
}
56+
}
57+
58+
traverse(tree.value, 0)
59+
return result
60+
})
61+
4762
// ========== 辅助方法 ==========
4863

4964
/** 根据关键词过滤树 */
@@ -417,6 +432,7 @@ export const useApiTreeStore = defineStore('apiTree', () => {
417432
// 计算属性
418433
hasTree,
419434
filteredTree,
435+
flatGroups,
420436

421437
// 方法
422438
setProjectId,

0 commit comments

Comments
 (0)