feat: 更新路由和侧边栏导航
- 侧边栏添加 Knowledge、Settings、Team 页面链接 - 路由新增对应页面配置 - 注册 Model handler 路由 Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -70,21 +70,24 @@ func main() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// 3. 自动迁移表
|
// 3. 自动迁移表
|
||||||
db.AutoMigrate(&model.DatabaseInfo{}, &model.SubTableInfo{})
|
db.AutoMigrate(&model.DatabaseInfo{}, &model.SubTableInfo{}, &model.ModelInfo{})
|
||||||
|
|
||||||
// 4. 初始化 Repository
|
// 4. 初始化 Repository
|
||||||
dbRepo := repository.NewDatabaseRepository(db)
|
dbRepo := repository.NewDatabaseRepository(db)
|
||||||
subTableRepo := repository.NewSubTableRepository(db)
|
subTableRepo := repository.NewSubTableRepository(db)
|
||||||
|
modelRepo := repository.NewModelRepository(db)
|
||||||
|
|
||||||
// 5. 初始化 Service
|
// 5. 初始化 Service
|
||||||
dbService := service.NewDatabaseService(dbRepo, subTableRepo)
|
dbService := service.NewDatabaseService(dbRepo, subTableRepo)
|
||||||
subTableService := service.NewSubTableService(subTableRepo, dbRepo)
|
subTableService := service.NewSubTableService(subTableRepo, dbRepo)
|
||||||
neo4jService := service.NewNeo4jService(dbRepo)
|
neo4jService := service.NewNeo4jService(dbRepo)
|
||||||
|
modelService := service.NewModelService(modelRepo)
|
||||||
|
|
||||||
// 6. 初始化 Handler
|
// 6. 初始化 Handler
|
||||||
dbHandler := handler.NewDatabaseHandler(dbService)
|
dbHandler := handler.NewDatabaseHandler(dbService)
|
||||||
subTableHandler := handler.NewSubTableHandler(subTableService)
|
subTableHandler := handler.NewSubTableHandler(subTableService)
|
||||||
neo4jHandler := handler.NewNeo4jHandler(neo4jService)
|
neo4jHandler := handler.NewNeo4jHandler(neo4jService)
|
||||||
|
modelHandler := handler.NewModelHandler(modelService)
|
||||||
systemHandler := handler.NewSystemHandler()
|
systemHandler := handler.NewSystemHandler()
|
||||||
|
|
||||||
// 7. 设置路由
|
// 7. 设置路由
|
||||||
@@ -163,6 +166,17 @@ func main() {
|
|||||||
neo4jGroup.POST("/relationships", neo4jHandler.GetRelationships)
|
neo4jGroup.POST("/relationships", neo4jHandler.GetRelationships)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Model 管理模块
|
||||||
|
modelGroup := r.Group("/model")
|
||||||
|
{
|
||||||
|
modelGroup.GET("/list", modelHandler.List)
|
||||||
|
modelGroup.GET("/:id", modelHandler.GetByID)
|
||||||
|
modelGroup.POST("/add", modelHandler.Create)
|
||||||
|
modelGroup.PUT("/:id", modelHandler.Update)
|
||||||
|
modelGroup.DELETE("/:id", modelHandler.Delete)
|
||||||
|
modelGroup.POST("/test", modelHandler.Test)
|
||||||
|
}
|
||||||
|
|
||||||
// 系统信息模块
|
// 系统信息模块
|
||||||
r.GET("/system/info", systemHandler.GetSystemInfo)
|
r.GET("/system/info", systemHandler.GetSystemInfo)
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,10 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { computed } from 'vue'
|
import { computed, ref } from 'vue'
|
||||||
import { useRouter, useRoute } from 'vue-router'
|
import { useRouter, useRoute } from 'vue-router'
|
||||||
|
import { ElMessageBox } from 'element-plus'
|
||||||
|
|
||||||
|
// 下拉菜单展开状态
|
||||||
|
const userDropdownVisible = ref(false)
|
||||||
|
|
||||||
const router = useRouter()
|
const router = useRouter()
|
||||||
const route = useRoute()
|
const route = useRoute()
|
||||||
@@ -19,13 +23,13 @@ const mainMenu: MenuItem[] = [
|
|||||||
{ name: 'Skills', icon: 'fa-wand-magic-sparkles', badge: 21, path: '/mcp' },
|
{ name: 'Skills', icon: 'fa-wand-magic-sparkles', badge: 21, path: '/mcp' },
|
||||||
{ name: 'Tools', icon: 'fa-tools', badge: 13, path: '/model-apis' },
|
{ name: 'Tools', icon: 'fa-tools', badge: 13, path: '/model-apis' },
|
||||||
{ name: 'Database', icon: 'fa-database', path: '/database' },
|
{ name: 'Database', icon: 'fa-database', path: '/database' },
|
||||||
{ name: 'Knowledge', icon: 'fa-book' },
|
{ name: 'Knowledge', icon: 'fa-brain', path: '/knowledge' },
|
||||||
]
|
]
|
||||||
|
|
||||||
const bottomMenu: MenuItem[] = [
|
const bottomMenu: MenuItem[] = [
|
||||||
{ name: 'API Keys', icon: 'fa-key' },
|
{ name: 'API Keys', icon: 'fa-key', path: '/api-keys' },
|
||||||
{ name: 'Settings', icon: 'fa-gear' },
|
{ name: 'Settings', icon: 'fa-gear', path: '/settings' },
|
||||||
{ name: 'Team', icon: 'fa-users' },
|
{ name: 'Team', icon: 'fa-users', path: '/team' },
|
||||||
{ name: 'Service Accounts', icon: 'fa-user-shield' },
|
{ name: 'Service Accounts', icon: 'fa-user-shield' },
|
||||||
{ name: 'Integrations', icon: 'fa-plug' },
|
{ name: 'Integrations', icon: 'fa-plug' },
|
||||||
]
|
]
|
||||||
@@ -37,8 +41,13 @@ const bottomMenu2: MenuItem[] = [
|
|||||||
|
|
||||||
const activeMenu = computed(() => {
|
const activeMenu = computed(() => {
|
||||||
const currentPath = route.path
|
const currentPath = route.path
|
||||||
|
// Check main menu
|
||||||
const menuItem = mainMenu.find(item => item.path === currentPath)
|
const menuItem = mainMenu.find(item => item.path === currentPath)
|
||||||
return menuItem ? menuItem.name : 'Dashboard'
|
if (menuItem) return menuItem.name
|
||||||
|
// Check bottom menu
|
||||||
|
const bottomItem = bottomMenu.find(item => item.path === currentPath)
|
||||||
|
if (bottomItem) return bottomItem.name
|
||||||
|
return 'Dashboard'
|
||||||
})
|
})
|
||||||
|
|
||||||
const navigateTo = (item: MenuItem) => {
|
const navigateTo = (item: MenuItem) => {
|
||||||
@@ -46,6 +55,32 @@ const navigateTo = (item: MenuItem) => {
|
|||||||
router.push(item.path)
|
router.push(item.path)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 用户菜单操作
|
||||||
|
const handleUserCommand = (command: string) => {
|
||||||
|
switch (command) {
|
||||||
|
case 'settings':
|
||||||
|
// 全局设置
|
||||||
|
router.push('/settings')
|
||||||
|
break
|
||||||
|
case 'userManagement':
|
||||||
|
// 用户管理
|
||||||
|
router.push('/user-management')
|
||||||
|
break
|
||||||
|
case 'logout':
|
||||||
|
// 退出登录
|
||||||
|
ElMessageBox.confirm('确定要退出登录吗?', '提示', {
|
||||||
|
confirmButtonText: '确定',
|
||||||
|
cancelButtonText: '取消',
|
||||||
|
type: 'warning',
|
||||||
|
}).then(() => {
|
||||||
|
// 清除登录状态
|
||||||
|
localStorage.removeItem('token')
|
||||||
|
router.push('/login')
|
||||||
|
}).catch(() => {})
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
@@ -90,7 +125,9 @@ const navigateTo = (item: MenuItem) => {
|
|||||||
<li v-for="item in bottomMenu" :key="item.name">
|
<li v-for="item in bottomMenu" :key="item.name">
|
||||||
<a
|
<a
|
||||||
href="#"
|
href="#"
|
||||||
class="flex items-center justify-between px-3 py-2.5 rounded-lg hover:bg-dark-600 text-gray-400 hover:text-white transition-colors text-sm"
|
class="flex items-center justify-between px-3 py-2.5 rounded-lg transition-colors text-sm"
|
||||||
|
:class="activeMenu === item.name ? 'bg-dark-600 text-white' : 'text-gray-400 hover:bg-dark-600 hover:text-white'"
|
||||||
|
@click="navigateTo(item)"
|
||||||
>
|
>
|
||||||
<div class="flex items-center gap-3">
|
<div class="flex items-center gap-3">
|
||||||
<i :class="['fa-solid', item.icon, 'w-5', 'text-center']"></i>
|
<i :class="['fa-solid', item.icon, 'w-5', 'text-center']"></i>
|
||||||
@@ -117,6 +154,7 @@ const navigateTo = (item: MenuItem) => {
|
|||||||
<a
|
<a
|
||||||
href="#"
|
href="#"
|
||||||
class="flex items-center gap-3 px-3 py-2.5 rounded-lg hover:bg-dark-600 text-gray-400 hover:text-white transition-colors text-sm"
|
class="flex items-center gap-3 px-3 py-2.5 rounded-lg hover:bg-dark-600 text-gray-400 hover:text-white transition-colors text-sm"
|
||||||
|
@click="navigateTo(item)"
|
||||||
>
|
>
|
||||||
<i :class="['fa-solid', item.icon, 'w-5', 'text-center']"></i>
|
<i :class="['fa-solid', item.icon, 'w-5', 'text-center']"></i>
|
||||||
<span>{{ item.name }}</span>
|
<span>{{ item.name }}</span>
|
||||||
@@ -126,14 +164,66 @@ const navigateTo = (item: MenuItem) => {
|
|||||||
</nav>
|
</nav>
|
||||||
|
|
||||||
<!-- 底部用户信息 -->
|
<!-- 底部用户信息 -->
|
||||||
<div class="p-4 border-t border-dark-500">
|
<div class="border-t border-dark-500">
|
||||||
|
<el-dropdown trigger="click" @command="handleUserCommand" class="user-dropdown" @visible-change="(v: boolean) => userDropdownVisible = v">
|
||||||
|
<div class="w-full flex items-center justify-between cursor-pointer hover:bg-dark-600 px-4 py-3 transition-colors">
|
||||||
<div class="flex items-center gap-3">
|
<div class="flex items-center gap-3">
|
||||||
<img src="https://picsum.photos/id/64/40/40" alt="User Avatar" class="w-8 h-8 rounded-full object-cover">
|
<img src="https://picsum.photos/id/64/40/40" alt="User Avatar" class="w-8 h-8 rounded-full object-cover">
|
||||||
<div>
|
<div class="min-w-0">
|
||||||
<div class="font-medium text-sm text-gray-300">Alex Smith</div>
|
<div class="font-medium text-sm text-gray-300 truncate">Alex Smith</div>
|
||||||
<div class="text-xs text-gray-500">alex@gmail.com</div>
|
<div class="text-xs text-gray-500 truncate">alex@gmail.com</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
<i :class="['fa-solid', userDropdownVisible ? 'fa-chevron-up' : 'fa-chevron-down', 'text-xs', 'text-gray-500']"></i>
|
||||||
|
</div>
|
||||||
|
<template #dropdown>
|
||||||
|
<el-dropdown-menu>
|
||||||
|
<el-dropdown-item command="settings">
|
||||||
|
<i class="fa-solid fa-gear w-4 text-center"></i>
|
||||||
|
Settings
|
||||||
|
</el-dropdown-item>
|
||||||
|
<el-dropdown-item command="userManagement">
|
||||||
|
<i class="fa-solid fa-user w-4 text-center"></i>
|
||||||
|
Users
|
||||||
|
</el-dropdown-item>
|
||||||
|
<el-dropdown-item divided command="logout">
|
||||||
|
<i class="fa-solid fa-arrow-right-from-bracket w-4 text-center"></i>
|
||||||
|
Sign Out
|
||||||
|
</el-dropdown-item>
|
||||||
|
</el-dropdown-menu>
|
||||||
|
</template>
|
||||||
|
</el-dropdown>
|
||||||
</div>
|
</div>
|
||||||
</aside>
|
</aside>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
|
<style>
|
||||||
|
.user-dropdown {
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
.user-dropdown .el-dropdown-menu {
|
||||||
|
background-color: #262626;
|
||||||
|
border: none;
|
||||||
|
padding: 6px;
|
||||||
|
border-radius: 10px;
|
||||||
|
min-width: 200px;
|
||||||
|
}
|
||||||
|
.user-dropdown .el-dropdown-menu__item {
|
||||||
|
color: white;
|
||||||
|
padding: 8px 14px;
|
||||||
|
border-radius: 6px;
|
||||||
|
font-size: 13px;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 10px;
|
||||||
|
}
|
||||||
|
.user-dropdown .el-dropdown-menu__item:hover {
|
||||||
|
background-color: #F97316;
|
||||||
|
color: white;
|
||||||
|
}
|
||||||
|
.user-dropdown .el-dropdown-menu__item--divided {
|
||||||
|
border-top: 1px solid #404040;
|
||||||
|
margin-top: 4px;
|
||||||
|
padding-top: 8px;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
|||||||
@@ -5,6 +5,8 @@ import Agents from '@/views/Agents.vue'
|
|||||||
import MCP from '@/views/MCP.vue'
|
import MCP from '@/views/MCP.vue'
|
||||||
import ModelAPIs from '@/views/ModelAPIs.vue'
|
import ModelAPIs from '@/views/ModelAPIs.vue'
|
||||||
import Database from '@/views/Database.vue'
|
import Database from '@/views/Database.vue'
|
||||||
|
import Knowledge from '@/views/Knowledge.vue'
|
||||||
|
import Settings from '@/views/Settings.vue'
|
||||||
|
|
||||||
const router = createRouter({
|
const router = createRouter({
|
||||||
history: createWebHistory(import.meta.env.BASE_URL),
|
history: createWebHistory(import.meta.env.BASE_URL),
|
||||||
@@ -38,6 +40,16 @@ const router = createRouter({
|
|||||||
path: '/database',
|
path: '/database',
|
||||||
name: 'database',
|
name: 'database',
|
||||||
component: Database
|
component: Database
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: '/knowledge',
|
||||||
|
name: 'knowledge',
|
||||||
|
component: Knowledge
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: '/settings',
|
||||||
|
name: 'settings',
|
||||||
|
component: Settings
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
})
|
})
|
||||||
|
|||||||
Reference in New Issue
Block a user