8e82a23555
CI / docker (push) Successful in 2m49s
Add model input defaults (including prompt), workflow model editing, and ingress API docs. Co-authored-by: Cursor <cursoragent@cursor.com>
101 lines
3.0 KiB
Vue
101 lines
3.0 KiB
Vue
<template>
|
|
<div class="layout">
|
|
<aside class="sidebar custom-scrollbar">
|
|
<div class="sidebar-brand">
|
|
<div class="brand-icon">
|
|
<BrandIcon :size="20" />
|
|
</div>
|
|
<span class="brand-name">{{ t('common.brand') }}</span>
|
|
</div>
|
|
|
|
<router-link
|
|
v-for="item in navItems"
|
|
:key="item.path"
|
|
:to="item.path"
|
|
class="nav-item"
|
|
:class="{ active: isActive(item.path) }"
|
|
>
|
|
<el-icon :size="16"><component :is="item.icon" /></el-icon>
|
|
{{ t(item.labelKey) }}
|
|
</router-link>
|
|
|
|
<div class="nav-group-label">{{ t('nav.security') }}</div>
|
|
<router-link
|
|
v-for="item in securityItems"
|
|
:key="item.path"
|
|
:to="item.path"
|
|
class="nav-item"
|
|
:class="{ active: isActive(item.path) }"
|
|
>
|
|
<el-icon :size="16"><component :is="item.icon" /></el-icon>
|
|
{{ t(item.labelKey) }}
|
|
</router-link>
|
|
</aside>
|
|
|
|
<div class="main-area">
|
|
<header class="topbar">
|
|
<span class="topbar-title">{{ currentTitle }}</span>
|
|
<div class="inline-actions">
|
|
<LocaleSwitcher />
|
|
<el-button text @click="logout">{{ t('nav.logout') }}</el-button>
|
|
</div>
|
|
</header>
|
|
<main class="main-content custom-scrollbar">
|
|
<router-view />
|
|
</main>
|
|
</div>
|
|
</div>
|
|
</template>
|
|
|
|
<script setup lang="ts">
|
|
import { computed } from 'vue'
|
|
import { useRoute, useRouter } from 'vue-router'
|
|
import { useI18n } from 'vue-i18n'
|
|
import { Connection, Document, Key, Lock, Odometer, Setting } from '@element-plus/icons-vue'
|
|
import api from '@/api/client'
|
|
import BrandIcon from '@/components/BrandIcon.vue'
|
|
import LocaleSwitcher from '@/components/LocaleSwitcher.vue'
|
|
|
|
const route = useRoute()
|
|
const router = useRouter()
|
|
const { t } = useI18n()
|
|
|
|
const navItems = [
|
|
{ path: '/dashboard', labelKey: 'nav.dashboard', icon: Odometer },
|
|
{ path: '/egress/providers', labelKey: 'nav.providers', icon: Connection },
|
|
{ path: '/ingress/keys', labelKey: 'nav.ingress', icon: Key },
|
|
{ path: '/logs', labelKey: 'nav.logs', icon: Document },
|
|
]
|
|
|
|
const securityItems = [
|
|
{ path: '/security/ip-rules', labelKey: 'nav.ipRules', icon: Lock },
|
|
{ path: '/security/settings', labelKey: 'nav.settings', icon: Setting },
|
|
]
|
|
|
|
const titleMap: Record<string, string> = {
|
|
'/dashboard': 'nav.dashboard',
|
|
'/egress/providers': 'nav.providers',
|
|
'/ingress/keys': 'nav.ingress',
|
|
'/ingress/docs/text': 'apiDocs.text.title',
|
|
'/ingress/docs/image': 'apiDocs.image.title',
|
|
'/logs': 'nav.logs',
|
|
'/security/ip-rules': 'nav.ipRules',
|
|
'/security/settings': 'nav.settings',
|
|
}
|
|
|
|
const currentTitle = computed(() => {
|
|
const key = titleMap[route.path]
|
|
return key ? t(key) : t('common.brand')
|
|
})
|
|
|
|
function isActive(path: string) {
|
|
if (path === '/ingress/keys' && route.path.startsWith('/ingress/')) return true
|
|
return route.path === path || route.path.startsWith(path + '/')
|
|
}
|
|
|
|
async function logout() {
|
|
await api.post('/logout')
|
|
router.push('/login')
|
|
}
|
|
</script>
|