Files
Luminary/web/src/layouts/MainLayout.vue
T
renjue 8e82a23555
CI / docker (push) Successful in 2m49s
Split ingress keys into text/image protocols with Replicate model listing.
Add model input defaults (including prompt), workflow model editing, and ingress API docs.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-06-29 23:40:27 +08:00

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>