调整前后台导航样式

This commit is contained in:
2026-06-16 12:41:21 +08:00
parent e78a1cd65f
commit 496759ccdf
4 changed files with 242 additions and 155 deletions
File diff suppressed because one or more lines are too long
+21 -1
View File
@@ -8,7 +8,27 @@
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin /> <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin />
<link href="https://fonts.googleapis.com/css2?family=Outfit:wght@300;400;500;600;700&display=swap" rel="stylesheet" /> <link href="https://fonts.googleapis.com/css2?family=Outfit:wght@300;400;500;600;700&display=swap" rel="stylesheet" />
<title>后台管理</title> <title>后台管理</title>
<script type="module" crossorigin src="/assets/index-ECGzn5Wr.js"></script> <script>
(function() {
var cached = localStorage.getItem('siteInfo');
if (cached) {
try {
var info = JSON.parse(cached);
if (info.siteName) {
document.title = info.siteName + ' - 管理后台';
}
if (info.siteLogo) {
var link = document.querySelector('link[rel="icon"]');
if (link) {
link.href = info.siteLogo;
link.type = 'image/png';
}
}
} catch (e) {}
}
})();
</script>
<script type="module" crossorigin src="/assets/index-C5wDengw.js"></script>
<link rel="stylesheet" crossorigin href="/assets/index-D7ShJUt4.css"> <link rel="stylesheet" crossorigin href="/assets/index-D7ShJUt4.css">
</head> </head>
<body> <body>
+88 -30
View File
@@ -49,7 +49,6 @@ const AdminLayout: React.FC = () => {
const navigate = useNavigate(); const navigate = useNavigate();
const location = useLocation(); const location = useLocation();
const { user, loading, logout } = useAdminStore(); const { user, loading, logout } = useAdminStore();
const [collapsed, setCollapsed] = useState(false);
const [menuItems, setMenuItems] = useState<any[]>([]); const [menuItems, setMenuItems] = useState<any[]>([]);
const [pwdModal, setPwdModal] = useState(false); const [pwdModal, setPwdModal] = useState(false);
const [pwdForm] = Form.useForm(); const [pwdForm] = Form.useForm();
@@ -204,42 +203,61 @@ const AdminLayout: React.FC = () => {
}); });
return ( return (
<Layout style={{ minHeight: '100vh' }}> <Layout style={{ minHeight: '100vh', background: '#f1f5f9' }}>
<Sider <Sider
collapsible width={240}
collapsed={collapsed}
onCollapse={setCollapsed}
width={220}
theme="light" theme="light"
style={{ style={{
background: '#ffffff', position: 'fixed',
borderRight: '1px solid #e5e7eb', left: 16,
top: 16,
bottom: 16,
width: 240,
background: 'linear-gradient(180deg, #ffffff 0%, #f8fafc 100%)',
borderRadius: '20px',
boxShadow: '0 4px 32px rgba(0, 0, 0, 0.06), 0 1px 8px rgba(0, 0, 0, 0.04)',
border: '1px solid rgba(0, 0, 0, 0.06)',
zIndex: 100,
}} }}
> >
{/* Logo */} {/* Logo */}
<div style={{ <div style={{
height: 64, display: 'flex', alignItems: 'center', height: 80,
justifyContent: 'center', gap: 10, display: 'flex',
borderBottom: '1px solid #e5e7eb', alignItems: 'center',
justifyContent: 'center',
gap: 12,
background: 'linear-gradient(135deg, rgba(99, 102, 241, 0.06) 0%, rgba(139, 92, 246, 0.04) 100%)',
}}> }}>
<div style={{ <div style={{
width: 32, height: 32, borderRadius: 8, width: 42,
background: 'linear-gradient(135deg, #6366f1, #8b5cf6)', height: 42,
display: 'flex', alignItems: 'center', justifyContent: 'center', borderRadius: 14,
boxShadow: '0 2px 8px rgba(99,102,241,0.3)', background: 'linear-gradient(135deg, #6366f1 0%, #8b5cf6 50%, #a78bfa 100%)',
display: 'flex',
alignItems: 'center',
justifyContent: 'center',
boxShadow: '0 4px 16px rgba(99, 102, 241, 0.35)',
overflow: 'hidden', overflow: 'hidden',
}}> }}>
{siteLogo ? ( {siteLogo ? (
<img src={siteLogo} alt="logo" style={{ width: 24, height: 24, objectFit: 'contain' }} /> <img src={siteLogo} alt="logo" style={{ width: 28, height: 28, objectFit: 'contain' }} />
) : ( ) : (
<ThunderboltOutlined style={{ fontSize: 16, color: '#fff' }} /> <ThunderboltOutlined style={{ fontSize: 20, color: '#ffffff' }} />
)} )}
</div> </div>
{!collapsed && ( <span style={{
<span style={{ color: '#1f2937', fontSize: 15, fontWeight: 600, letterSpacing: -0.02 }}> color: '#1e293b',
fontSize: 17,
fontWeight: 700,
letterSpacing: -0.02,
background: 'linear-gradient(135deg, #6366f1, #8b5cf6)',
WebkitBackgroundClip: 'text',
WebkitTextFillColor: 'transparent',
backgroundClip: 'text',
}}>
{siteName} {siteName}
</span> </span>
)}
</div> </div>
{/* Menu */} {/* Menu */}
@@ -263,15 +281,26 @@ const AdminLayout: React.FC = () => {
</Sider> </Sider>
<Layout> <Layout style={{ marginLeft: 272, marginTop: 16, marginRight: 16, marginBottom: 16, background: 'transparent' }}>
{/* Header */} {/* Header */}
<div style={{ <div style={{
height: 56, background: '#fff', borderBottom: '1px solid #f0f0f5', height: 72,
display: 'flex', alignItems: 'center', justifyContent: 'space-between', background: '#ffffff',
padding: '0 24px', borderRadius: '16px 16px 0 0',
boxShadow: '0 2px 12px rgba(0, 0, 0, 0.04)',
display: 'flex',
alignItems: 'center',
justifyContent: 'space-between',
padding: '0 32px',
fontFamily: 'var(--font-sans)', fontFamily: 'var(--font-sans)',
border: '1px solid rgba(0, 0, 0, 0.06)',
borderBottom: 'none',
}}> }}>
<Typography.Text strong style={{ fontSize: 16 }}> <Typography.Text strong style={{
fontSize: 18,
color: '#1e293b',
fontWeight: 600,
}}>
{antMenuItems.find(m => m.key === activeKey)?.label {antMenuItems.find(m => m.key === activeKey)?.label
|| antMenuItems.flatMap(m => m.children || []).find((c: any) => c.key === activeKey)?.label || antMenuItems.flatMap(m => m.children || []).find((c: any) => c.key === activeKey)?.label
|| '管理后台'} || '管理后台'}
@@ -288,10 +317,31 @@ const AdminLayout: React.FC = () => {
if (key === 'changePwd') { setPwdModal(true); pwdForm.resetFields(); } if (key === 'changePwd') { setPwdModal(true); pwdForm.resetFields(); }
}, },
}} placement="bottomRight" arrow> }} placement="bottomRight" arrow>
<div style={{ display: 'flex', alignItems: 'center', gap: 8, cursor: 'pointer', padding: '4px 8px', borderRadius: 8, transition: 'background 0.2s' }}> <div style={{
<Avatar size={28} icon={<UserOutlined />} display: 'flex',
style={{ background: 'linear-gradient(135deg, #6366f1, #8b5cf6)' }} /> alignItems: 'center',
<Typography.Text style={{ fontSize: 13, fontWeight: 500 }}> gap: 10,
cursor: 'pointer',
padding: '8px 12px',
borderRadius: 12,
transition: 'all 0.25s ease',
background: 'linear-gradient(135deg, rgba(248, 250, 252, 0.9) 0%, rgba(241, 245, 249, 0.9) 100%)',
}}
onMouseEnter={(e) => {
e.currentTarget.style.background = '#ffffff';
e.currentTarget.style.boxShadow = '0 4px 12px rgba(0, 0, 0, 0.08)';
}}
onMouseLeave={(e) => {
e.currentTarget.style.background = 'linear-gradient(135deg, rgba(248, 250, 252, 0.9) 0%, rgba(241, 245, 249, 0.9) 100%)';
e.currentTarget.style.boxShadow = 'none';
}}
>
<Avatar size={32} icon={<UserOutlined />}
style={{
background: 'linear-gradient(135deg, #6366f1 0%, #8b5cf6 100%)',
boxShadow: '0 4px 12px rgba(99, 102, 241, 0.3)',
}} />
<Typography.Text style={{ fontSize: 14, fontWeight: 600, color: '#1e293b' }}>
{user?.username} {user?.username}
</Typography.Text> </Typography.Text>
</div> </div>
@@ -299,7 +349,15 @@ const AdminLayout: React.FC = () => {
</div> </div>
{/* Content */} {/* Content */}
<Content style={{ padding: 24, background: '#f5f6fa', overflow: 'auto' }}> <Content style={{
padding: 32,
background: '#ffffff',
borderRadius: '0 0 16px 16px',
boxShadow: '0 4px 24px rgba(0, 0, 0, 0.06)',
overflow: 'auto',
border: '1px solid rgba(0, 0, 0, 0.06)',
borderTop: 'none',
}}>
{antMenuItems.length === 0 && !isAdminUser ? ( {antMenuItems.length === 0 && !isAdminUser ? (
<div style={{ display: 'flex', flexDirection: 'column', alignItems: 'center', justifyContent: 'center', height: '60vh', color: '#94a3b8' }}> <div style={{ display: 'flex', flexDirection: 'column', alignItems: 'center', justifyContent: 'center', height: '60vh', color: '#94a3b8' }}>
<LockOutlined style={{ fontSize: 48, marginBottom: 16, color: '#cbd5e1' }} /> <LockOutlined style={{ fontSize: 48, marginBottom: 16, color: '#cbd5e1' }} />
+132 -123
View File
@@ -115,8 +115,7 @@ const iconMap: Record<string, React.ReactNode> = {
CloudServerOutlined: <CloudServerOutlined />, CloudServerOutlined: <CloudServerOutlined />,
}; };
const EXPANDED_W = 240; const SIDEBAR_W = 240;
const COLLAPSED_W = 68;
const GRADIENTS = [ const GRADIENTS = [
{ gradient: 'linear-gradient(135deg, #c9a96e, #a67c52)', shadow: 'rgba(201,169,110,0.25)', icon: <StarFilled /> }, { gradient: 'linear-gradient(135deg, #c9a96e, #a67c52)', shadow: 'rgba(201,169,110,0.25)', icon: <StarFilled /> },
@@ -132,8 +131,6 @@ const AppLayout: React.FC = () => {
const [pwdModalOpen, setPwdModalOpen] = useState(false); const [pwdModalOpen, setPwdModalOpen] = useState(false);
const [rechargeModalOpen, setRechargeModalOpen] = useState(false); const [rechargeModalOpen, setRechargeModalOpen] = useState(false);
const [pwdForm] = Form.useForm(); const [pwdForm] = Form.useForm();
const [collapsed, setCollapsed] = useState(false);
const [toggleHover, setToggleHover] = useState(false);
const [selectedPlan, setSelectedPlan] = useState<number | null>(null); const [selectedPlan, setSelectedPlan] = useState<number | null>(null);
const [menuItems, setMenuItems] = useState<MenuConfig[]>([]); const [menuItems, setMenuItems] = useState<MenuConfig[]>([]);
const [rechargeOptions, setRechargeOptions] = useState<any[]>([]); const [rechargeOptions, setRechargeOptions] = useState<any[]>([]);
@@ -174,14 +171,7 @@ const AppLayout: React.FC = () => {
// LocalStorage keys // LocalStorage keys
const PENDING_ORDER_KEY = 'pending_payment_order'; const PENDING_ORDER_KEY = 'pending_payment_order';
// 监听预览弹窗状态,关闭浮动按钮
useEffect(() => {
const handleModalOpen = () => {
setToggleHover(false);
};
window.addEventListener('previewOpen', handleModalOpen);
return () => window.removeEventListener('previewOpen', handleModalOpen);
}, []);
useEffect(() => { useEffect(() => {
getSiteInfo().then(info => { getSiteInfo().then(info => {
@@ -298,7 +288,7 @@ const AppLayout: React.FC = () => {
}, [user]); }, [user]);
const selectedKey = location.pathname.startsWith('/records') ? '/records' : location.pathname; const selectedKey = location.pathname.startsWith('/records') ? '/records' : location.pathname;
const sidebarW = collapsed ? COLLAPSED_W : EXPANDED_W; const sidebarW = SIDEBAR_W;
const userMenuItems = [ const userMenuItems = [
{ key: 'profile', icon: <UserOutlined />, label: `账号: ${user?.username}`, disabled: true }, { key: 'profile', icon: <UserOutlined />, label: `账号: ${user?.username}`, disabled: true },
@@ -406,43 +396,47 @@ const AppLayout: React.FC = () => {
<Layout style={{ minHeight: '100vh' }}> <Layout style={{ minHeight: '100vh' }}>
{/* Desktop Sidebar */} {/* Desktop Sidebar */}
<div className="desktop-sidebar" style={{ <div className="desktop-sidebar" style={{
width: sidebarW, position: 'fixed', left: 0, top: 0, bottom: 0, zIndex: 100, width: sidebarW, position: 'fixed', left: 16, top: 16, bottom: 16, zIndex: 100,
background: 'var(--nav-bg)', background: 'linear-gradient(180deg, #ffffff 0%, #f8fafc 100%)',
borderRight: '1px solid var(--nav-border)', borderRadius: '20px',
boxShadow: '0 4px 32px rgba(0, 0, 0, 0.06), 0 1px 8px rgba(0, 0, 0, 0.04)',
display: 'flex', flexDirection: 'column', display: 'flex', flexDirection: 'column',
transition: 'width 0.25s ease', transition: 'width 0.25s ease, left 0.25s ease',
overflow: 'hidden', overflow: 'hidden',
border: '1px solid rgba(0, 0, 0, 0.06)',
}}> }}>
{/* Logo + Toggle */} {/* Logo */}
<div style={{ <div style={{
height: 72, display: 'flex', alignItems: 'center', height: 80, display: 'flex', alignItems: 'center',
justifyContent: collapsed ? 'center' : 'space-between', justifyContent: 'flex-start',
borderBottom: '1px solid #e5e7eb', padding: '0 20px',
padding: collapsed ? '0' : '0 16px 0 20px',
flexShrink: 0, flexShrink: 0,
background: 'linear-gradient(135deg, rgba(99, 102, 241, 0.06) 0%, rgba(139, 92, 246, 0.04) 100%)',
}}> }}>
<div style={{ display: 'flex', alignItems: 'center', gap: 12, overflow: 'hidden' }}> <div style={{ display: 'flex', alignItems: 'center', gap: 14 }}>
<div style={{ <div style={{
width: 38, height: 38, borderRadius: 12, flexShrink: 0, width: 42, height: 42, borderRadius: 14, flexShrink: 0,
background: 'linear-gradient(135deg, #6366f1, #8b5cf6)', background: 'linear-gradient(135deg, #6366f1 0%, #8b5cf6 50%, #a78bfa 100%)',
display: 'flex', alignItems: 'center', justifyContent: 'center', display: 'flex', alignItems: 'center', justifyContent: 'center',
boxShadow: '0 2px 12px rgba(99,102,241,0.3)', overflow: 'hidden', boxShadow: '0 4px 16px rgba(99, 102, 241, 0.35)',
overflow: 'hidden',
}}> }}>
{siteLogo ? ( {siteLogo ? (
<img src={siteLogo} alt="logo" style={{ width: 28, height: 28, objectFit: 'contain' }} /> <img src={siteLogo} alt="logo" style={{ width: 28, height: 28, objectFit: 'contain' }} />
) : ( ) : (
<ThunderboltOutlined style={{ fontSize: 18, color: '#1f2937' }} /> <ThunderboltOutlined style={{ fontSize: 20, color: '#ffffff' }} />
)} )}
</div> </div>
{!collapsed && ( <span style={{
<span style={{ color: '#1e293b', fontSize: 17, fontWeight: 700, letterSpacing: -0.02,
color: 'var(--nav-text)', fontSize: 16, fontWeight: 600, letterSpacing: -0.02, whiteSpace: 'nowrap',
whiteSpace: 'nowrap', opacity: collapsed ? 0 : 1, background: 'linear-gradient(135deg, #6366f1, #8b5cf6)',
transition: 'opacity 0.2s ease', WebkitBackgroundClip: 'text',
}}> WebkitTextFillColor: 'transparent',
{siteName} backgroundClip: 'text',
</span> }}>
)} {siteName}
</span>
</div> </div>
</div> </div>
@@ -468,43 +462,42 @@ const AppLayout: React.FC = () => {
const renderMenuItem = (item: MenuConfig, depth: number = 0) => { const renderMenuItem = (item: MenuConfig, depth: number = 0) => {
const isActive = item.path === selectedKey; const isActive = item.path === selectedKey;
const menuIcon = iconMap[item.icon] || <HomeOutlined />; const menuIcon = iconMap[item.icon] || <HomeOutlined />;
const el = ( return (
<div key={item.id} onClick={() => item.path && navigate(item.path)} style={{ <div key={item.id} onClick={() => item.path && navigate(item.path)} style={{
display: 'flex', alignItems: 'center', display: 'flex', alignItems: 'center',
justifyContent: collapsed ? 'center' : 'flex-start', justifyContent: 'flex-start',
gap: collapsed ? 0 : 12, gap: 12,
padding: collapsed ? '10px 0' : depth > 0 ? '7px 14px 7px 32px' : '9px 16px', padding: depth > 0 ? '8px 14px 8px 36px' : '10px 16px',
borderRadius: 8, margin: '1px 0', cursor: 'pointer', borderRadius: 12, margin: '2px 6px', cursor: 'pointer',
fontSize: depth > 0 ? 12 : 13, fontWeight: isActive ? 500 : 400, fontSize: depth > 0 ? 13 : 14, fontWeight: isActive ? 600 : 400,
color: isActive ? 'var(--nav-text)' : 'var(--nav-text-muted)', color: isActive ? '#4f46e5' : '#475569',
background: isActive ? 'var(--nav-active)' : 'transparent', background: isActive ? 'linear-gradient(135deg, rgba(99, 102, 241, 0.1) 0%, rgba(139, 92, 246, 0.08) 100%)' : 'transparent',
transition: 'all 0.2s ease', transition: 'all 0.2s ease',
}}> }}>
<span style={{ fontSize: depth > 0 ? 14 : 16, flexShrink: 0 }}>{menuIcon}</span> <span style={{
{!collapsed && <span style={{ whiteSpace: 'nowrap' }}>{item.label}</span>} fontSize: depth > 0 ? 14 : 16,
flexShrink: 0,
color: isActive ? '#6366f1' : '#64748b',
}}>{menuIcon}</span>
<span style={{ whiteSpace: 'nowrap' }}>{item.label}</span>
</div> </div>
); );
return collapsed ? <Tooltip key={item.id} title={item.label} placement="right">{el}</Tooltip> : el;
}; };
// Render groups with children // Render groups with children
groups.sort((a, b) => (a.sortOrder ?? 0) - (b.sortOrder ?? 0)).forEach(g => { groups.sort((a, b) => (a.sortOrder ?? 0) - (b.sortOrder ?? 0)).forEach(g => {
const children = (childMap[g.id] || []).sort((a, b) => (a.sortOrder ?? 0) - (b.sortOrder ?? 0)); const children = (childMap[g.id] || []).sort((a, b) => (a.sortOrder ?? 0) - (b.sortOrder ?? 0));
if (!collapsed) { items.push(
items.push( <div key={g.id}>
<div key={g.id}> <div style={{
<div style={{ color: '#94a3b8', fontSize: 12, fontWeight: 600,
color: 'var(--nav-text-muted)', fontSize: 10, fontWeight: 500, padding: '12px 16px 6px', letterSpacing: 0.5, textTransform: 'uppercase',
padding: '8px 16px 6px', letterSpacing: 0.05, textTransform: 'uppercase', }}>
}}> {g.label}
{g.label}
</div>
{children.map(c => renderMenuItem(c, 1))}
</div> </div>
); {children.map(c => renderMenuItem(c, 1))}
} else { </div>
children.forEach(c => items.push(renderMenuItem(c))); );
}
}); });
// Render top-level pages // Render top-level pages
@@ -518,91 +511,107 @@ const AppLayout: React.FC = () => {
{/* Recharge button - opens modal */} {/* Recharge button - opens modal */}
<div onClick={() => setRechargeModalOpen(true)} style={{ <div onClick={() => setRechargeModalOpen(true)} style={{
margin: collapsed ? '4px auto' : '4px 14px', margin: '8px 12px',
padding: collapsed ? '10px' : '10px 14px', padding: '12px 20px',
borderRadius: 10, cursor: 'pointer', borderRadius: 14, cursor: 'pointer',
display: 'flex', alignItems: 'center', justifyContent: collapsed ? 'center' : 'flex-start', display: 'flex', alignItems: 'center', justifyContent: 'center',
gap: collapsed ? 0 : 10, gap: 8,
color: '#c9a96e', fontSize: 13, fontWeight: 500, color: '#ffffff', fontSize: 14, fontWeight: 600,
background: 'rgba(201,169,110,0.08)', background: 'linear-gradient(135deg, #6366f1 0%, #8b5cf6 100%)',
border: '1px solid rgba(201,169,110,0.15)', boxShadow: '0 4px 16px rgba(99, 102, 241, 0.4)',
transition: 'all 0.2s', transition: 'all 0.3s ease',
}} }}
onMouseEnter={(e) => { e.currentTarget.style.background = 'rgba(201,169,110,0.15)'; }} onMouseEnter={(e) => {
onMouseLeave={(e) => { e.currentTarget.style.background = 'rgba(201,169,110,0.08)'; }} e.currentTarget.style.background = 'linear-gradient(135deg, #4f46e5 0%, #7c3aed 100%)';
e.currentTarget.style.transform = 'translateY(-1px)';
e.currentTarget.style.boxShadow = '0 6px 20px rgba(99, 102, 241, 0.5)';
}}
onMouseLeave={(e) => {
e.currentTarget.style.background = 'linear-gradient(135deg, #6366f1 0%, #8b5cf6 100%)';
e.currentTarget.style.transform = 'translateY(0)';
e.currentTarget.style.boxShadow = '0 4px 16px rgba(99, 102, 241, 0.4)';
}}
> >
<PlusOutlined style={{ fontSize: 14 }} /> <PlusOutlined style={{ fontSize: 16 }} />
{!collapsed && <span></span>} <span></span>
</div> </div>
{/* User block at bottom-left */} {/* User block at bottom-left */}
<div style={{ padding: collapsed ? '12px 8px' : '14px 14px', borderTop: '1px solid #e5e7eb', flexShrink: 0 }}> <div style={{ padding: '16px 16px', flexShrink: 0 }}>
<Dropdown menu={{ items: userMenuItems, onClick: handleUserMenuClick }} placement="topRight" arrow> <Dropdown menu={{ items: userMenuItems, onClick: handleUserMenuClick }} placement="topRight" arrow>
<div style={{ <div style={{
display: 'flex', alignItems: 'center', display: 'flex', alignItems: 'center',
justifyContent: collapsed ? 'center' : 'flex-start', justifyContent: 'flex-start',
gap: collapsed ? 0 : 10, gap: 12,
padding: collapsed ? '8px' : '8px 10px', padding: '10px 14px',
borderRadius: 12, cursor: 'pointer', transition: 'background 0.2s', borderRadius: 14, cursor: 'pointer',
background: 'rgba(0,0,0,0.02)', transition: 'all 0.25s ease',
background: 'linear-gradient(135deg, rgba(248, 250, 252, 0.9) 0%, rgba(241, 245, 249, 0.9) 100%)',
boxShadow: '0 2px 12px rgba(0, 0, 0, 0.04)',
}} }}
onMouseEnter={(e) => e.currentTarget.style.background = 'rgba(0,0,0,0.04)'} onMouseEnter={(e) => {
onMouseLeave={(e) => e.currentTarget.style.background = 'rgba(0,0,0,0.02)'} e.currentTarget.style.background = 'linear-gradient(135deg, #ffffff 0%, #f8fafc 100%)';
e.currentTarget.style.boxShadow = '0 4px 16px rgba(0, 0, 0, 0.08)';
}}
onMouseLeave={(e) => {
e.currentTarget.style.background = 'linear-gradient(135deg, rgba(248, 250, 252, 0.9) 0%, rgba(241, 245, 249, 0.9) 100%)';
e.currentTarget.style.boxShadow = '0 2px 12px rgba(0, 0, 0, 0.04)';
}}
> >
<Avatar size={32} icon={<UserOutlined />} <Avatar size={36} icon={<UserOutlined />}
style={{ background: 'linear-gradient(135deg, #4a5568, #2d3748)', flexShrink: 0 }} /> style={{
{!collapsed && ( background: 'linear-gradient(135deg, #6366f1 0%, #8b5cf6 100%)',
<div style={{ flex: 1, minWidth: 0 }}> flexShrink: 0,
<div style={{ color: 'var(--nav-text)', fontSize: 13, fontWeight: 500, overflow: 'hidden', textOverflow: 'ellipsis', whiteSpace: 'nowrap', letterSpacing: -0.01 }}> boxShadow: '0 4px 12px rgba(99, 102, 241, 0.3)',
{user?.username} }} />
</div> <div style={{ flex: 1, minWidth: 0 }}>
<div style={{ color: 'var(--nav-text-muted)', fontSize: 11, letterSpacing: 0 }}>: {user?.credits || 0}</div> <div style={{ color: '#1e293b', fontSize: 14, fontWeight: 600, overflow: 'hidden', textOverflow: 'ellipsis', whiteSpace: 'nowrap', letterSpacing: -0.01 }}>
{user?.username}
</div> </div>
)} <div style={{
color: '#6366f1',
fontSize: 12,
fontWeight: 500,
letterSpacing: 0,
background: 'rgba(99, 102, 241, 0.08)',
padding: '2px 8px',
borderRadius: 6,
display: 'inline-block',
}}>: {user?.credits || 0}</div>
</div>
</div> </div>
</Dropdown> </Dropdown>
</div> </div>
</div> </div>
{/* Floating sidebar toggle hover zone */}
<div
className="desktop-sidebar-toggle-zone"
onMouseEnter={() => setToggleHover(true)}
onMouseLeave={() => setToggleHover(false)}
style={{
position: 'fixed', left: sidebarW - 16, top: 0, bottom: 0,
zIndex: 110, width: 32, cursor: 'default',
transition: 'left 0.25s ease',
}}
>
{toggleHover && (
<div onClick={() => setCollapsed(prev => !prev)} style={{
position: 'absolute', left: '50%', top: '50%',
transform: 'translate(-50%, -50%)',
width: 24, height: 56, borderRadius: '0 8px 8px 0',
background: '#f8f9fb', border: '1px solid #e5e7eb', borderLeft: 'none',
display: 'flex', alignItems: 'center', justifyContent: 'center', cursor: 'pointer',
boxShadow: '2px 0 12px rgba(0,0,0,0.2)',
animation: 'fadeInRight 0.2s ease both',
}}>
{collapsed
? <RightOutlined style={{ color: '#818cf8', fontSize: 12 }} />
: <LeftOutlined style={{ color: '#818cf8', fontSize: 12 }} />}
</div>
)}
</div>
{/* Main Content */} {/* Main Content */}
<div className="desktop-content" style={{ <div className="desktop-content" style={{
marginLeft: sidebarW, flex: 1, minHeight: '100vh', background: '#f8f9fc', marginLeft: sidebarW + 48,
padding: '24px 32px 32px', transition: 'margin-left 0.25s ease', marginRight: 32,
marginTop: 16,
marginBottom: 16,
flex: 1,
minHeight: 'calc(100vh - 32px)',
background: 'transparent',
padding: 0,
transition: 'margin-left 0.25s ease',
}}> }}>
<Outlet /> <div style={{
background: '#ffffff',
borderRadius: '20px',
boxShadow: '0 4px 32px rgba(0, 0, 0, 0.06), 0 1px 8px rgba(0, 0, 0, 0.04)',
minHeight: '100%',
padding: '24px 32px 32px',
border: '1px solid rgba(0, 0, 0, 0.06)',
}}>
<Outlet />
</div>
</div> </div>
{/* Mobile Bottom Nav */} {/* Mobile Bottom Nav */}
<div className="mobile-bottom-nav"> <div className="mobile-bottom-nav">
{menuItems.map((item) => { {menuItems.filter((item) => (item.menu_type ?? item.menuType) !== 'group').map((item) => {
if (!item.path) return null;
const isActive = item.path === selectedKey; const isActive = item.path === selectedKey;
return ( return (
<div key={item.id} <div key={item.id}