iloom-flatten/src/components/layout/TextileLayout.tsx

179 lines
6.4 KiB
TypeScript

import React, { useState } from 'react';
import { Outlet, useNavigate, useLocation } from 'react-router-dom';
import { motion } from 'framer-motion';
import {
FileText,
Warehouse,
CreditCard,
LogOut,
Home,
ChevronRight,
Factory,
Menu,
X
} from 'lucide-react';
import { useAuth } from '../../contexts/AuthContext';
import { useResponsive } from '../../hooks/useResponsive';
import { STORAGE_KEYS } from '../../config/app';
interface NavItem {
id: string;
icon: React.ElementType;
label: string;
path: string;
}
const navItems: NavItem[] = [
{ id: 'home', icon: Home, label: '首页', path: '/textile' },
{ id: 'plans', icon: FileText, label: '计划总览', path: '/textile/plans' },
{ id: 'fabric', icon: Warehouse, label: '已生产坯布', path: '/textile/fabric-warehouse' },
{ id: 'yarn', icon: Warehouse, label: '原料纱仓库', path: '/textile/yarn-warehouse' },
{ id: 'payment', icon: CreditCard, label: '待结款', path: '/textile/payments' }
];
export function TextileLayout() {
const navigate = useNavigate();
const location = useLocation();
const { auth, logout } = useAuth();
const { isMobile } = useResponsive();
// 从 localStorage 读取侧边栏状态,默认展开
const [sidebarCollapsed, setSidebarCollapsed] = useState(() => {
const saved = localStorage.getItem(STORAGE_KEYS.textileSidebarCollapsed);
return saved ? JSON.parse(saved) : false;
});
const [mobileMenuOpen, setMobileMenuOpen] = useState(false);
// 保存侧边栏状态到 localStorage
const toggleSidebar = () => {
const newState = !sidebarCollapsed;
setSidebarCollapsed(newState);
localStorage.setItem(STORAGE_KEYS.textileSidebarCollapsed, JSON.stringify(newState));
};
const isActive = (path: string) => {
const currentPath = location.pathname;
// 首页精确匹配
if (path === '/textile') {
return currentPath === '/textile';
}
// 精确匹配当前路径
if (currentPath === path) return true;
// 对于 /textile/plans 路径,不应该匹配子路径
if (path === '/textile/plans') {
return currentPath === '/textile/plans';
}
// 对于其他路径,匹配子路径
if (currentPath.startsWith(path + '/')) return true;
return false;
};
const handleLogout = async () => {
await logout();
navigate('/login');
};
return (
<div className="min-h-screen bg-slate-50">
{/* 桌面端侧边栏 - 固定不动,无入场动画 */}
{!isMobile && (
<aside
className={`fixed left-0 top-0 h-screen bg-gradient-to-b from-slate-900 via-slate-800 to-slate-900 text-white z-50 flex flex-col ${
sidebarCollapsed ? 'w-20' : 'w-64'
}`}
>
{/* Logo区域 */}
<div className="h-16 flex items-center px-4 border-b border-slate-700/50">
<div className="flex items-center gap-3">
<div className="w-10 h-10 rounded-xl bg-gradient-to-br from-emerald-500 to-green-600 flex items-center justify-center shadow-lg">
<Factory className="w-6 h-6 text-white" />
</div>
{!sidebarCollapsed && (
<div>
<h1 className="font-bold text-lg leading-tight"></h1>
<p className="text-xs text-slate-400">{auth.company?.name || '未登录'}</p>
</div>
)}
</div>
</div>
{/* 导航菜单 */}
<nav className="flex-1 py-4 px-3 overflow-y-auto">
<div className="space-y-1">
{navItems.map((item) => {
const active = isActive(item.path);
const Icon = item.icon;
return (
<button
key={item.id}
onClick={() => navigate(item.path)}
className={`w-full flex items-center gap-3 px-3 py-3 rounded-xl transition-colors duration-200 group ${
active
? 'bg-gradient-to-r from-emerald-500/20 to-green-500/20 text-emerald-400 border border-emerald-500/30'
: 'text-slate-300 hover:bg-slate-700/50 hover:text-white'
}`}
>
<div className={`w-10 h-10 rounded-lg flex items-center justify-center ${
active ? 'bg-emerald-500/20' : 'bg-slate-700/50 group-hover:bg-slate-600'
}`}>
<Icon className="w-5 h-5" />
</div>
{!sidebarCollapsed && (
<div className="flex-1 text-left">
<div className="font-medium text-sm">{item.label}</div>
</div>
)}
{!sidebarCollapsed && active && (
<ChevronRight className="w-4 h-4 text-emerald-400" />
)}
</button>
);
})}
</div>
</nav>
{/* 底部操作区 */}
<div className="p-3 border-t border-slate-700/50 space-y-2">
<button
onClick={toggleSidebar}
className="w-full flex items-center justify-center gap-2 px-3 py-2 rounded-lg text-slate-400 hover:bg-slate-700/50 hover:text-white transition-colors"
>
<div className={`w-8 h-8 rounded-lg bg-slate-700/50 flex items-center justify-center ${
sidebarCollapsed ? 'rotate-180' : ''
}`}>
<ChevronRight className="w-4 h-4" />
</div>
{!sidebarCollapsed && <span className="text-sm"></span>}
</button>
<motion.button
onClick={handleLogout}
whileHover={{ scale: 1.02 }}
whileTap={{ scale: 0.98 }}
className="w-full flex items-center gap-3 px-3 py-3 rounded-xl text-red-400 hover:bg-red-500/10 transition-colors"
>
<div className="w-10 h-10 rounded-lg bg-red-500/10 flex items-center justify-center">
<LogOut className="w-5 h-5" />
</div>
{!sidebarCollapsed && <span className="font-medium text-sm">退</span>}
</motion.button>
</div>
</aside>
)}
{/* 主内容区 - 移动端无侧边栏,桌面端有侧边栏 */}
<main className={`${
!isMobile && (sidebarCollapsed ? 'ml-20' : 'ml-64')
}`}>
<Outlet />
</main>
</div>
);
}