message / frontend /src /components /layout /Sidebar.tsx
hunian
feat: 重构前端控制台和工具页面
a43ac26
Raw
History Blame Contribute Delete
4.3 kB
import { Link, useLocation } from 'react-router-dom';
import { Package, ChevronRight, Puzzle, Wrench, Server, Settings } from 'lucide-react';
import { usePlugins } from '@hooks/usePlugins';
import { cn } from '@lib/utils';
/**
* 平台导航项配置
* 一等入口:插件、Tool、MCP、System
*/
const platformNavItems = [
{
path: '/plugins',
label: '插件中心',
icon: Puzzle,
matchPattern: /^\/plugins/,
},
{
path: '/tools',
label: 'Tool 目录',
icon: Wrench,
matchPattern: /^\/tools/,
},
{
path: '/mcp',
label: 'MCP 服务',
icon: Server,
matchPattern: /^\/mcp/,
},
{
path: '/system',
label: '系统状态',
icon: Settings,
matchPattern: /^\/system/,
},
];
/**
* 控制台侧边栏
* 分为平台导航和插件运行区
*/
export function Sidebar() {
const location = useLocation();
const { data: plugins, isLoading } = usePlugins();
// 只显示 enabled 状态的插件
const enabledPlugins = plugins?.filter((p) => p.status === 'enabled') || [];
return (
<aside className="w-56 h-full border-r border-console-border bg-console-surface-raised overflow-y-auto">
<nav className="p-3" aria-label="控制台导航">
{/* 平台导航区 */}
<div className="mb-4">
<h2 className="px-3 mb-2 text-xs font-medium text-console-text-secondary uppercase tracking-wider">
平台
</h2>
<ul className="space-y-0.5" role="list">
{platformNavItems.map((item) => {
const isActive = item.matchPattern.test(location.pathname);
return (
<li key={item.path}>
<Link
to={item.path}
className={cn(
'flex items-center gap-2.5 px-3 py-1.5 rounded-console-sm text-sm transition-colors',
isActive
? 'bg-console-status-info-bg text-console-status-info font-medium'
: 'text-console-text-primary hover:bg-console-surface-base'
)}
aria-current={isActive ? 'page' : undefined}
>
<item.icon className="h-4 w-4 shrink-0" aria-hidden="true" />
<span className="truncate">{item.label}</span>
</Link>
</li>
);
})}
</ul>
</div>
{/* 插件运行区 */}
<div>
<h2 className="px-3 mb-2 text-xs font-medium text-console-text-secondary uppercase tracking-wider">
插件运行
</h2>
{isLoading ? (
<div className="px-3 py-2 text-xs text-console-text-muted">
加载中...
</div>
) : enabledPlugins.length === 0 ? (
<div className="px-3 py-2 text-xs text-console-text-muted">
暂无启用插件
</div>
) : (
<ul className="space-y-0.5" role="list">
{enabledPlugins.map((plugin) => {
const isActive = location.pathname === `/tool/${plugin.metadata.name}`;
return (
<li key={plugin.metadata.name}>
<Link
to={`/tool/${plugin.metadata.name}`}
className={cn(
'flex items-center gap-2.5 px-3 py-1.5 rounded-console-sm text-sm transition-colors',
isActive
? 'bg-console-status-info-bg text-console-status-info font-medium'
: 'text-console-text-primary hover:bg-console-surface-base'
)}
aria-current={isActive ? 'page' : undefined}
>
<Package className="h-4 w-4 shrink-0" aria-hidden="true" />
<span className="flex-1 truncate">
{plugin.metadata.name}
</span>
<ChevronRight className="h-3.5 w-3.5 text-console-text-muted shrink-0" aria-hidden="true" />
</Link>
</li>
);
})}
</ul>
)}
</div>
</nav>
</aside>
);
}