Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
21 changes: 19 additions & 2 deletions apps/frontend/src/components/json-schema/JsonSchemaEditor.vue
Original file line number Diff line number Diff line change
@@ -1,8 +1,19 @@
<script lang="ts" setup>
import type { LocalSchemaNode } from '@/types/json-schema'
import { computed, getCurrentInstance } from 'vue'
import { useApiEditorStore } from '@/stores/useApiEditorStore'
import JsonSchemaNode from './JsonSchemaNode.vue'

const emits = defineEmits<{
(e: 'delRoot'): void
}>()

const instance = getCurrentInstance()

const allowDelRoot = computed(() => {
return !!instance?.vnode.props?.onDelRoot
})

const apiEditorStore = useApiEditorStore()
const { setIsDirty } = apiEditorStore

Expand Down Expand Up @@ -93,8 +104,13 @@ function handleDeleteNode({ path }: { path: string }) {
root.value.schemaChanged = true
const pathArr = pathToArr(path)

if (pathArr.length === 0) {
console.warn('[JsonSchemaEditor] 删除节点失败:不能删除根节点')
if (pathArr.length === 1) {
if (allowDelRoot.value) {
emits('delRoot')
}
else {
console.warn('[JsonSchemaEditor] 删除节点失败:不能删除根节点', { path })
}
return
}

Expand All @@ -117,6 +133,7 @@ function handleDeleteNode({ path }: { path: string }) {
<JsonSchemaNode
:node="root"
path=""
:allow-del-root="allowDelRoot"
@add-node="handleAddNode"
@update-node="handleUpdateNode"
@delete-node="handleDeleteNode"
Expand Down
8 changes: 7 additions & 1 deletion apps/frontend/src/components/json-schema/JsonSchemaNode.vue
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,8 @@ const props = defineProps<{
path: string
/** 嵌套层级 */
depth?: number
/** 是否允许删除根节点 */
allowDelRoot?: boolean
}>()

const emits = defineEmits<{
Expand Down Expand Up @@ -62,7 +64,11 @@ const showEmptyChildren = computed(() =>
&& (!props.node.children || props.node.children.length === 0),
)

const canDelete = computed(() => !props.node.isRoot && !props.node.isArrayItem)
const canDelete = computed(() =>
props.allowDelRoot
? !props.node.isArrayItem
: !props.node.isRoot && !props.node.isArrayItem,
)

const canAddSibling = computed(() => !props.node.isRoot && !props.node.isArrayItem)

Expand Down
12 changes: 0 additions & 12 deletions apps/frontend/src/components/workbench/ApiSidebar.vue
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,6 @@ import CloneApiDialog from './dialogs/CloneApiDialog.vue'
import DeleteApiDialog from './dialogs/DeleteApiDialog.vue'
import DeleteGroupDialog from './dialogs/DeleteGroupDialog.vue'
import GroupFormDialog from './dialogs/GroupFormDialog.vue'
import ImportOpenapiDialog from './dialogs/import/ImportOpenapiDialog.vue'

const tabStore = useTabStore()

Expand Down Expand Up @@ -38,12 +37,6 @@ const apiToDelete = ref<ApiBrief | null>(null)
const isCloneApiDialogOpen = ref(false)
const apiToClone = ref<ApiBrief | null>(null)

const isImportDialogOpen = ref(false)

function handleImportOpenapi() {
isImportDialogOpen.value = true
}

function handleCreateGroup(parentId?: string) {
groupDialogMode.value = 'create'
groupDialogParentId.value = parentId
Expand Down Expand Up @@ -103,7 +96,6 @@ function handleDeleteApi(api: ApiBrief) {
@select-api="handleSelectApi"
@clone-api="handleCloneApi"
@delete-api="handleDeleteApi"
@import-openapi="handleImportOpenapi"
/>

<div
Expand Down Expand Up @@ -138,10 +130,6 @@ function handleDeleteApi(api: ApiBrief) {
v-model:open="isCloneApiDialogOpen"
:api="apiToClone"
/>

<ImportOpenapiDialog
v-model:open="isImportDialogOpen"
/>
</aside>
</template>

Expand Down
23 changes: 23 additions & 0 deletions apps/frontend/src/components/workbench/WorkbenchSidebar.vue
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import {
ChevronRight,
Globe,
Home,
Import,
Settings,
} from 'lucide-vue-next'
import { storeToRefs } from 'pinia'
Expand All @@ -25,6 +26,7 @@ import {
} from '@/components/ui/tooltip'
import { cn, getAbbreviation } from '@/lib/utils'
import { useProjectStore } from '@/stores/useProjectStore'
import ImportOpenapiDialog from './dialogs/import/ImportOpenapiDialog.vue'
import ProjectSettingsSheet from './ProjectSettingsSheet.vue'

const router = useRouter()
Expand All @@ -35,6 +37,7 @@ const { setCurEnvId } = projectStore

const isSettingsSheetOpen = ref(false)
const isEnvPopoverOpen = ref(false)
const isImportDialogOpen = ref(false)

function goBack() {
router.push({ name: 'Dashboard' })
Expand Down Expand Up @@ -161,6 +164,22 @@ function getEnvColor(envName: string): string {
</PopoverContent>
</Popover>

<Tooltip>
<TooltipTrigger as-child>
<Button
variant="ghost"
size="icon"
class="h-8 w-8 mt-1 text-muted-foreground hover:text-foreground"
@click="isImportDialogOpen = true"
>
<Import class="h-4 w-4" />
</Button>
</TooltipTrigger>
<TooltipContent side="bottom">
导入 OpenAPI
</TooltipContent>
</Tooltip>

<Tooltip>
<TooltipTrigger as-child>
<Button
Expand All @@ -184,5 +203,9 @@ function getEnvColor(envName: string): string {
v-model:open="isSettingsSheetOpen"
:project="projectDetail"
/>

<ImportOpenapiDialog
v-model:open="isImportDialogOpen"
/>
</aside>
</template>
33 changes: 24 additions & 9 deletions apps/frontend/src/components/workbench/api-editor/ApiEditView.vue
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,10 @@ watch(
{ immediate: true },
)

function handleDiscardChanges() {
apiEditorStore.discardChanges(props.api.id)
}

async function handleSave() {
if (!apiTreeStore.projectId)
return
Expand Down Expand Up @@ -96,15 +100,26 @@ function handleKeyDownSave(e: KeyboardEvent) {
</span>
</div>

<Button
size="sm"
:disabled="!isDirty || isSaving"
@click="handleSave"
>
<Loader2 v-if="isSaving" class="h-4 w-4 mr-1.5 animate-spin" />
<Save v-else class="h-4 w-4 mr-1.5" />
{{ isSaving ? '保存中...' : '保存' }}
</Button>
<div class="flex items-center gap-2">
<Button
v-if="isDirty"
size="sm"
variant="outline"
@click="handleDiscardChanges"
>
放弃修改
</Button>

<Button
size="sm"
:disabled="!isDirty || isSaving"
@click="handleSave"
>
<Loader2 v-if="isSaving" class="h-4 w-4 mr-1.5 animate-spin" />
<Save v-else class="h-4 w-4 mr-1.5" />
{{ isSaving ? '保存中...' : '保存' }}
</Button>
</div>
</div>

<Tabs v-model="activeTab" class="flex-1 flex flex-col overflow-hidden">
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ import {
} from '@/components/ui/select'
import { Textarea } from '@/components/ui/textarea'
import { HTTP_STATUS_CODES, httpStatusLabels, resStatusStyles } from '@/constants/api'
import { genRootSchemaNode } from '@/lib/json-schema'
import { cn } from '@/lib/utils'
import { useApiEditorStore } from '@/stores/useApiEditorStore'

Expand Down Expand Up @@ -90,6 +91,10 @@ function handleAdd() {
expandedKeys.value.add(newId)
}

function handleAddBody(index: number) {
updateResponseBody(index, genRootSchemaNode())
}

function handleRemove(index: number) {
const response = responses.value[index]
if (response) {
Expand All @@ -105,6 +110,10 @@ function updateField<K extends keyof LocalApiResItem>(index: number, key: K, val
function updateLocalSchema(index: number, schema: LocalSchemaNode) {
updateResponseBody(index, schema)
}

function handleDelRoot(index: number) {
updateResponseBody(index, undefined)
}
</script>

<template>
Expand Down Expand Up @@ -212,7 +221,23 @@ function updateLocalSchema(index: number, schema: LocalSchemaNode) {
v-if="response.body"
:model-value="response.body"
@update:model-value="updateLocalSchema(index, $event)"
@del-root="handleDelRoot(index)"
/>
<div v-else class="space-y-2">
<div class="text-center py-8 text-muted-foreground text-sm border rounded-md bg-muted/30">
暂无响应体结构(void)
</div>
<Button
variant="outline"
size="sm"
:disabled="disabled"
class="w-full border-dashed"
@click="handleAddBody(index)"
>
<Plus class="h-4 w-4 mr-1.5" />
添加响应体结构
</Button>
</div>
</div>
</div>
</CollapsibleContent>
Expand Down
2 changes: 0 additions & 2 deletions apps/frontend/src/components/workbench/api-tree/ApiTree.vue
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,6 @@ const emits = defineEmits<{
(e: 'deleteGroup', group: GroupNodeWithApis): void
(e: 'cloneApi', api: ApiBrief): void
(e: 'deleteApi', api: ApiBrief): void
(e: 'importOpenapi'): void
}>()

const apiTreeStore = useApiTreeStore()
Expand Down Expand Up @@ -109,7 +108,6 @@ onMounted(() => {
<ApiTreeToolbar
@create-api="handleToolbarCreateApi"
@create-group="emits('createGroup')"
@import-openapi="emits('importOpenapi')"
/>

<ScrollArea class="flex-1 px-2 min-h-0">
Expand Down
20 changes: 0 additions & 20 deletions apps/frontend/src/components/workbench/api-tree/ApiTreeToolbar.vue
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@ import {
ChevronsUpDown,
FilePlus,
FolderPlus,
Import,
RefreshCw,
Search,
} from 'lucide-vue-next'
Expand All @@ -23,7 +22,6 @@ import { useApiTreeStore } from '@/stores/useApiTreeStore'
const emits = defineEmits<{
(e: 'createGroup'): void
(e: 'createApi'): void
(e: 'importOpenapi'): void
}>()

const apiTreeStore = useApiTreeStore()
Expand Down Expand Up @@ -143,24 +141,6 @@ watch(searchInput, (val) => {
</TooltipContent>
</Tooltip>
</TooltipProvider>

<TooltipProvider :delay-duration="300">
<Tooltip>
<TooltipTrigger as-child>
<Button
variant="ghost"
size="icon"
class="h-7 w-7"
@click="emits('importOpenapi')"
>
<Import class="h-3.5 w-3.5" />
</Button>
</TooltipTrigger>
<TooltipContent side="bottom">
导入 OpenAPI
</TooltipContent>
</Tooltip>
</TooltipProvider>
</div>
</div>
</template>
Loading
Loading