Update web page config
This commit is contained in:
148
web/App.tsx
148
web/App.tsx
@@ -1,7 +1,7 @@
|
||||
|
||||
import React, { useState } from 'react';
|
||||
import React, { useState, useEffect } 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 { Bot, Book, User, LayoutDashboard, Mic2, Video, GitBranch, Zap, PanelLeftClose, PanelLeftOpen, History as HistoryIcon, ChevronDown, ChevronRight, Box, Wand2, Wrench, BrainCircuit, AudioLines } from 'lucide-react';
|
||||
|
||||
import { AssistantsPage } from './pages/Assistants';
|
||||
import { KnowledgeBasePage } from './pages/KnowledgeBase';
|
||||
@@ -12,30 +12,132 @@ import { VoiceLibraryPage } from './pages/VoiceLibrary';
|
||||
import { WorkflowsPage } from './pages/Workflows';
|
||||
import { WorkflowEditorPage } from './pages/WorkflowEditor';
|
||||
import { AutoTestPage } from './pages/AutoTest';
|
||||
import { ToolLibraryPage } from './pages/ToolLibrary';
|
||||
import { LLMLibraryPage } from './pages/LLMLibrary';
|
||||
import { ASRLibraryPage } from './pages/ASRLibrary';
|
||||
|
||||
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>
|
||||
);
|
||||
type NavItemType = {
|
||||
path: string;
|
||||
label: string;
|
||||
icon: React.ReactNode;
|
||||
children?: NavItemType[];
|
||||
};
|
||||
|
||||
const SidebarItem: React.FC<{
|
||||
item: NavItemType;
|
||||
isActive: boolean;
|
||||
isCollapsed: boolean;
|
||||
onExpand: () => void;
|
||||
}> = ({ item, isActive, isCollapsed, onExpand }) => {
|
||||
const location = useLocation();
|
||||
const [isOpen, setIsOpen] = useState(false);
|
||||
const hasChildren = item.children && item.children.length > 0;
|
||||
|
||||
// Check if any child is active to auto-expand
|
||||
const isChildActive = hasChildren && item.children!.some(child => location.pathname.startsWith(child.path));
|
||||
|
||||
useEffect(() => {
|
||||
if (isChildActive) {
|
||||
setIsOpen(true);
|
||||
}
|
||||
}, [isChildActive]);
|
||||
|
||||
const handleClick = (e: React.MouseEvent) => {
|
||||
if (hasChildren) {
|
||||
e.preventDefault();
|
||||
if (isCollapsed) {
|
||||
onExpand();
|
||||
setIsOpen(true);
|
||||
} else {
|
||||
setIsOpen(!isOpen);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
const activeClass = "bg-primary/20 text-primary border-r-2 border-primary";
|
||||
const inactiveClass = "text-muted-foreground hover:bg-muted/50 hover:text-foreground";
|
||||
|
||||
if (hasChildren) {
|
||||
return (
|
||||
<div className="space-y-1">
|
||||
<div
|
||||
onClick={handleClick}
|
||||
className={`flex items-center justify-between px-4 py-3 rounded-md transition-all duration-300 cursor-pointer ${isChildActive ? 'text-primary' : 'text-muted-foreground hover:bg-muted/50 hover:text-foreground'} ${isCollapsed ? 'justify-center px-2' : ''}`}
|
||||
title={isCollapsed ? item.label : undefined}
|
||||
>
|
||||
<div className={`flex items-center space-x-3 ${isCollapsed ? 'justify-center w-full' : ''}`}>
|
||||
<div className="shrink-0">{item.icon}</div>
|
||||
{!isCollapsed && <span className="font-medium text-sm whitespace-nowrap overflow-hidden">{item.label}</span>}
|
||||
</div>
|
||||
{!isCollapsed && (
|
||||
<div className="shrink-0 ml-2">
|
||||
{isOpen ? <ChevronDown className="h-4 w-4" /> : <ChevronRight className="h-4 w-4" />}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
|
||||
{!isCollapsed && isOpen && (
|
||||
<div className="ml-4 space-y-1 border-l border-white/10 pl-2 animate-in slide-in-from-left-2 duration-200">
|
||||
{item.children!.map(child => {
|
||||
const childActive = location.pathname.startsWith(child.path);
|
||||
return (
|
||||
<Link
|
||||
key={child.path}
|
||||
to={child.path}
|
||||
className={`flex items-center space-x-3 px-4 py-2 rounded-md transition-all duration-300 text-sm ${childActive ? 'text-primary bg-primary/10' : 'text-muted-foreground hover:text-foreground'}`}
|
||||
>
|
||||
<div className="shrink-0 opacity-70 scale-90">{child.icon}</div>
|
||||
<span className="font-medium whitespace-nowrap overflow-hidden">{child.label}</span>
|
||||
</Link>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<Link
|
||||
to={item.path}
|
||||
title={isCollapsed ? item.label : undefined}
|
||||
className={`flex items-center space-x-3 px-4 py-3 rounded-md transition-all duration-300 ${isActive ? activeClass : inactiveClass} ${isCollapsed ? 'justify-center space-x-0 px-2' : ''}`}
|
||||
>
|
||||
<div className="shrink-0">{item.icon}</div>
|
||||
{!isCollapsed && <span className="font-medium text-sm animate-in fade-in duration-300 whitespace-nowrap overflow-hidden">{item.label}</span>}
|
||||
</Link>
|
||||
);
|
||||
};
|
||||
|
||||
const AppLayout: React.FC<{ children: React.ReactNode }> = ({ children }) => {
|
||||
const location = useLocation();
|
||||
const [isCollapsed, setIsCollapsed] = useState(false);
|
||||
|
||||
const navItems = [
|
||||
const navItems: NavItemType[] = [
|
||||
{ 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: '#creation',
|
||||
label: '创建助手',
|
||||
icon: <Wand2 className="h-5 w-5" />,
|
||||
children: [
|
||||
{ path: '/assistants', label: '小助手', icon: <Bot className="h-5 w-5" /> },
|
||||
{ path: '/workflows', label: '工作流', icon: <GitBranch 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: '#components',
|
||||
label: '组件库',
|
||||
icon: <Box className="h-5 w-5" />,
|
||||
children: [
|
||||
{ path: '/llms', label: '大模型库', icon: <BrainCircuit className="h-5 w-5" /> },
|
||||
{ path: '/asr', label: '语音识别', icon: <AudioLines className="h-5 w-5" /> },
|
||||
{ path: '/voices', label: '声音库', icon: <Mic2 className="h-5 w-5" /> },
|
||||
{ path: '/knowledge', label: '知识库', icon: <Book className="h-5 w-5" /> },
|
||||
{ path: '/tools', label: '工具库', icon: <Wrench className="h-5 w-5" /> },
|
||||
]
|
||||
},
|
||||
{ path: '/profile', label: '个人中心', icon: <User className="h-5 w-5" /> },
|
||||
];
|
||||
|
||||
@@ -60,11 +162,10 @@ const AppLayout: React.FC<{ children: React.ReactNode }> = ({ children }) => {
|
||||
{navItems.map(item => (
|
||||
<SidebarItem
|
||||
key={item.path}
|
||||
to={item.path}
|
||||
icon={item.icon}
|
||||
label={item.label}
|
||||
item={item}
|
||||
isCollapsed={isCollapsed}
|
||||
active={item.path === '/' ? location.pathname === '/' : location.pathname.startsWith(item.path)}
|
||||
isActive={item.path === '/' ? location.pathname === '/' : location.pathname.startsWith(item.path)}
|
||||
onExpand={() => setIsCollapsed(false)}
|
||||
/>
|
||||
))}
|
||||
</nav>
|
||||
@@ -110,7 +211,10 @@ const App: React.FC = () => {
|
||||
<Route path="/" element={<DashboardPage />} />
|
||||
<Route path="/assistants" element={<AssistantsPage />} />
|
||||
<Route path="/voices" element={<VoiceLibraryPage />} />
|
||||
<Route path="/llms" element={<LLMLibraryPage />} />
|
||||
<Route path="/asr" element={<ASRLibraryPage />} />
|
||||
<Route path="/knowledge" element={<KnowledgeBasePage />} />
|
||||
<Route path="/tools" element={<ToolLibraryPage />} />
|
||||
<Route path="/history" element={<HistoryPage />} />
|
||||
<Route path="/workflows" element={<WorkflowsPage />} />
|
||||
<Route path="/workflows/new" element={<WorkflowEditorPage />} />
|
||||
@@ -123,4 +227,4 @@ const App: React.FC = () => {
|
||||
);
|
||||
};
|
||||
|
||||
export default App;
|
||||
export default App;
|
||||
Reference in New Issue
Block a user