127 lines
6.2 KiB
TypeScript
127 lines
6.2 KiB
TypeScript
|
|
import React, { useState } from 'react';
|
|
import { HashRouter as Router, Routes, Route, Link, useLocation } from 'react-router-dom';
|
|
import { Bot, Phone, Book, User, LayoutDashboard, Mic2, Video, GitBranch, Zap, PanelLeftClose, PanelLeftOpen, History as HistoryIcon } from 'lucide-react';
|
|
|
|
import { AssistantsPage } from './pages/Assistants';
|
|
import { KnowledgeBasePage } from './pages/KnowledgeBase';
|
|
import { HistoryPage } from './pages/History';
|
|
import { ProfilePage } from './pages/Profile';
|
|
import { DashboardPage } from './pages/Dashboard';
|
|
import { VoiceLibraryPage } from './pages/VoiceLibrary';
|
|
import { WorkflowsPage } from './pages/Workflows';
|
|
import { WorkflowEditorPage } from './pages/WorkflowEditor';
|
|
import { AutoTestPage } from './pages/AutoTest';
|
|
|
|
const SidebarItem: React.FC<{ to: string; icon: React.ReactNode; label: string; active: boolean; isCollapsed: boolean }> = ({ to, icon, label, active, isCollapsed }) => (
|
|
<Link
|
|
to={to}
|
|
title={isCollapsed ? label : undefined}
|
|
className={`flex items-center space-x-3 px-4 py-3 rounded-md transition-all duration-300 ${active ? 'bg-primary/20 text-primary border-r-2 border-primary' : 'text-muted-foreground hover:bg-muted/50 hover:text-foreground'} ${isCollapsed ? 'justify-center space-x-0 px-2' : ''}`}
|
|
>
|
|
<div className="shrink-0">{icon}</div>
|
|
{!isCollapsed && <span className="font-medium text-sm animate-in fade-in duration-300 whitespace-nowrap overflow-hidden">{label}</span>}
|
|
</Link>
|
|
);
|
|
|
|
const AppLayout: React.FC<{ children: React.ReactNode }> = ({ children }) => {
|
|
const location = useLocation();
|
|
const [isCollapsed, setIsCollapsed] = useState(false);
|
|
|
|
const navItems = [
|
|
{ path: '/', label: '首页', icon: <LayoutDashboard className="h-5 w-5" /> },
|
|
{ path: '/assistants', label: '小助手', icon: <Bot className="h-5 w-5" /> },
|
|
{ path: '/voices', label: '声音库', icon: <Mic2 className="h-5 w-5" /> },
|
|
{ path: '/history', label: '历史记录', icon: <HistoryIcon className="h-5 w-5" /> },
|
|
{ path: '/knowledge', label: '知识库', icon: <Book className="h-5 w-5" /> },
|
|
{ path: '/workflows', label: '工作流', icon: <GitBranch className="h-5 w-5" /> },
|
|
{ path: '/auto-test', label: '测试助手', icon: <Zap className="h-5 w-5" /> },
|
|
{ path: '/profile', label: '个人中心', icon: <User className="h-5 w-5" /> },
|
|
];
|
|
|
|
return (
|
|
<div className="flex h-screen overflow-hidden">
|
|
{/* Sidebar with Glass effect and collapse logic */}
|
|
<aside
|
|
className={`border-r border-border/40 bg-card/30 backdrop-blur-md hidden md:flex flex-col transition-all duration-300 ease-in-out relative group ${isCollapsed ? 'w-20' : 'w-64'}`}
|
|
>
|
|
<div className={`p-6 flex items-center border-b border-border/40 overflow-hidden transition-all duration-300 ${isCollapsed ? 'justify-center px-4' : 'space-x-3'}`}>
|
|
<div className="h-10 w-10 shrink-0 bg-gradient-to-br from-cyan-400 to-blue-600 rounded-xl flex items-center justify-center shadow-[0_0_20px_rgba(6,182,212,0.5)] border border-white/10">
|
|
<Video className="h-6 w-6 text-white drop-shadow-md" />
|
|
</div>
|
|
{!isCollapsed && (
|
|
<span className="text-lg font-bold tracking-wide whitespace-nowrap bg-clip-text text-transparent bg-gradient-to-r from-white to-white/80 animate-in slide-in-from-left-2">
|
|
AI视频助手
|
|
</span>
|
|
)}
|
|
</div>
|
|
|
|
<nav className="flex-1 p-4 space-y-2 overflow-y-auto overflow-x-hidden custom-scrollbar">
|
|
{navItems.map(item => (
|
|
<SidebarItem
|
|
key={item.path}
|
|
to={item.path}
|
|
icon={item.icon}
|
|
label={item.label}
|
|
isCollapsed={isCollapsed}
|
|
active={item.path === '/' ? location.pathname === '/' : location.pathname.startsWith(item.path)}
|
|
/>
|
|
))}
|
|
</nav>
|
|
|
|
{/* Footer with Version and Collapse Button */}
|
|
<div className={`p-4 border-t border-border/40 flex items-center transition-all duration-300 ${isCollapsed ? 'justify-center' : 'justify-between'}`}>
|
|
{!isCollapsed && (
|
|
<span className="text-[10px] text-muted-foreground font-mono opacity-60 animate-in fade-in">
|
|
SYSTEM v1.0
|
|
</span>
|
|
)}
|
|
<button
|
|
onClick={() => setIsCollapsed(!isCollapsed)}
|
|
title={isCollapsed ? "展开边栏" : "收起边栏"}
|
|
className={`p-1.5 rounded-md hover:bg-white/5 text-muted-foreground hover:text-primary transition-all ${isCollapsed ? '' : 'ml-2'}`}
|
|
>
|
|
{isCollapsed ? <PanelLeftOpen className="h-4 w-4" /> : <PanelLeftClose className="h-4 w-4" />}
|
|
</button>
|
|
</div>
|
|
</aside>
|
|
|
|
{/* Main Content */}
|
|
<main className="flex-1 flex flex-col overflow-hidden relative bg-background">
|
|
<header className="h-16 border-b border-border/40 flex items-center px-6 bg-card/30 backdrop-blur-md md:hidden space-x-3">
|
|
<div className="h-8 w-8 bg-gradient-to-br from-cyan-400 to-blue-600 rounded-lg flex items-center justify-center shadow-lg">
|
|
<Video className="h-5 w-5 text-white" />
|
|
</div>
|
|
<span className="font-bold text-lg whitespace-nowrap text-white">AI视频助手</span>
|
|
</header>
|
|
<div className="flex-1 overflow-auto p-2 md:p-4 transition-all duration-300">
|
|
{children}
|
|
</div>
|
|
</main>
|
|
</div>
|
|
);
|
|
};
|
|
|
|
const App: React.FC = () => {
|
|
return (
|
|
<Router>
|
|
<AppLayout>
|
|
<Routes>
|
|
<Route path="/" element={<DashboardPage />} />
|
|
<Route path="/assistants" element={<AssistantsPage />} />
|
|
<Route path="/voices" element={<VoiceLibraryPage />} />
|
|
<Route path="/knowledge" element={<KnowledgeBasePage />} />
|
|
<Route path="/history" element={<HistoryPage />} />
|
|
<Route path="/workflows" element={<WorkflowsPage />} />
|
|
<Route path="/workflows/new" element={<WorkflowEditorPage />} />
|
|
<Route path="/workflows/edit/:id" element={<WorkflowEditorPage />} />
|
|
<Route path="/auto-test" element={<AutoTestPage />} />
|
|
<Route path="/profile" element={<ProfilePage />} />
|
|
</Routes>
|
|
</AppLayout>
|
|
</Router>
|
|
);
|
|
};
|
|
|
|
export default App;
|