Spaces:
Sleeping
Sleeping
| import React from 'react'; | |
| import { Link, useLocation } from 'react-router-dom'; | |
| import { Home, Cpu, MessageSquare, Activity, Settings, Menu, X } from 'lucide-react'; | |
| import { useState } from 'react'; | |
| import { clsx } from 'clsx'; | |
| const navItems = [ | |
| { path: '/', icon: Home, label: '首页' }, | |
| { path: '/devices', icon: Cpu, label: '设备控制' }, | |
| { path: '/ai', icon: MessageSquare, label: 'AI 交互' }, | |
| { path: '/system', icon: Activity, label: '系统状态' }, | |
| ]; | |
| export const Layout: React.FC<{ children: React.ReactNode }> = ({ children }) => { | |
| const [isMobileMenuOpen, setIsMobileMenuOpen] = useState(false); | |
| const location = useLocation(); | |
| return ( | |
| <div className="min-h-screen bg-zinc-950 text-zinc-100 flex"> | |
| {/* Sidebar - Desktop */} | |
| <aside className="hidden md:flex flex-col w-64 border-r border-zinc-800 bg-zinc-900/50 backdrop-blur-xl"> | |
| <div className="p-6"> | |
| <h1 className="text-xl font-bold bg-gradient-to-r from-blue-400 to-emerald-400 bg-clip-text text-transparent"> | |
| IoT Playground | |
| </h1> | |
| </div> | |
| <nav className="flex-1 px-4 space-y-2"> | |
| {navItems.map((item) => ( | |
| <Link | |
| key={item.path} | |
| to={item.path} | |
| className={clsx( | |
| "flex items-center space-x-3 px-4 py-3 rounded-xl transition-all duration-200", | |
| location.pathname === item.path | |
| ? "bg-blue-600/20 text-blue-400 shadow-[inset_0_0_20px_rgba(37,99,235,0.1)]" | |
| : "hover:bg-zinc-800 text-zinc-400 hover:text-zinc-200" | |
| )} | |
| > | |
| <item.icon size={20} /> | |
| <span className="font-medium">{item.label}</span> | |
| </Link> | |
| ))} | |
| </nav> | |
| <div className="p-4 border-t border-zinc-800"> | |
| <button className="flex items-center space-x-3 px-4 py-3 w-full rounded-xl hover:bg-zinc-800 text-zinc-400 transition-all"> | |
| <Settings size={20} /> | |
| <span className="font-medium">设置</span> | |
| </button> | |
| </div> | |
| </aside> | |
| {/* Mobile Header */} | |
| <div className="md:hidden fixed top-0 left-0 right-0 h-16 bg-zinc-900/80 backdrop-blur-lg border-b border-zinc-800 z-50 px-4 flex items-center justify-between"> | |
| <h1 className="text-lg font-bold bg-gradient-to-r from-blue-400 to-emerald-400 bg-clip-text text-transparent"> | |
| IoT Playground | |
| </h1> | |
| <button onClick={() => setIsMobileMenuOpen(!isMobileMenuOpen)} className="p-2 text-zinc-400 hover:text-zinc-200"> | |
| {isMobileMenuOpen ? <X size={24} /> : <Menu size={24} />} | |
| </button> | |
| </div> | |
| {/* Mobile Menu Overlay */} | |
| {isMobileMenuOpen && ( | |
| <div className="md:hidden fixed inset-0 bg-zinc-950/90 backdrop-blur-md z-40 pt-20 px-6"> | |
| <nav className="space-y-4"> | |
| {navItems.map((item) => ( | |
| <Link | |
| key={item.path} | |
| to={item.path} | |
| onClick={() => setIsMobileMenuOpen(false)} | |
| className={clsx( | |
| "flex items-center space-x-4 px-6 py-4 rounded-2xl text-lg font-medium transition-all", | |
| location.pathname === item.path ? "bg-blue-600 text-white" : "text-zinc-400" | |
| )} | |
| > | |
| <item.icon size={24} /> | |
| <span>{item.label}</span> | |
| </Link> | |
| ))} | |
| </nav> | |
| </div> | |
| )} | |
| {/* Main Content */} | |
| <main className="flex-1 pt-16 md:pt-0 overflow-y-auto"> | |
| <div className="max-w-7xl mx-auto p-6 md:p-10"> | |
| {children} | |
| </div> | |
| </main> | |
| </div> | |
| ); | |
| }; | |