rwadurian/frontend/admin-web/src/components/layout/Sidebar/Sidebar.tsx

142 lines
5.1 KiB
TypeScript

'use client';
import { FC } from 'react';
import Link from 'next/link';
import Image from 'next/image';
import { usePathname, useRouter } from 'next/navigation';
import { cn } from '@/utils/helpers';
import { useAppSelector, useAppDispatch } from '@/store/redux/hooks';
import { toggleSidebar } from '@/store/redux/slices/settingsSlice';
import { logoutService } from '@/services/authService';
import { toast } from '@/components/common';
import styles from './Sidebar.module.scss';
/**
* 侧边栏菜单项配置
*/
interface MenuItem {
key: string;
icon: string;
label: string;
path: string;
}
// 主导航菜单
const topMenuItems: MenuItem[] = [
{ key: 'dashboard', icon: '/images/Container1.svg', label: '仪表板', path: '/dashboard' },
{ key: 'users', icon: '/images/Container2.svg', label: '用户管理', path: '/users' },
{ key: 'leaderboard', icon: '/images/Container3.svg', label: '龙虎榜', path: '/leaderboard' },
{ key: 'authorization', icon: '/images/Container4.svg', label: '授权管理', path: '/authorization' },
{ key: 'co-managed-wallet', icon: '/images/Container4.svg', label: '共管钱包', path: '/co-managed-wallet' },
{ key: 'notifications', icon: '/images/Container3.svg', label: '通知管理', path: '/notifications' },
{ key: 'pending-actions', icon: '/images/Container3.svg', label: '待办操作', path: '/pending-actions' },
{ key: 'withdrawals', icon: '/images/Container5.svg', label: '提现审核', path: '/withdrawals' },
{ key: 'statistics', icon: '/images/Container5.svg', label: '数据统计', path: '/statistics' },
{ key: 'maintenance', icon: '/images/Container6.svg', label: '系统维护', path: '/maintenance' },
{ key: 'settings', icon: '/images/Container6.svg', label: '系统设置', path: '/settings' },
];
// 底部导航菜单
const bottomMenuItems: MenuItem[] = [
{ key: 'help', icon: '/images/Container7.svg', label: '帮助中心', path: '/help' },
];
/**
* 侧边栏组件
* 基于 UIPro Figma 设计实现
*/
export const Sidebar: FC = () => {
const pathname = usePathname();
const router = useRouter();
const dispatch = useAppDispatch();
const collapsed = useAppSelector((state) => state.settings.sidebarCollapsed);
// 切换侧边栏展开/收起
const handleToggle = () => {
dispatch(toggleSidebar());
};
// 退出登录
const handleLogout = () => {
// 清除所有上下文和存储
logoutService.logout();
toast.success('已安全退出登录');
// 跳转到登录页
router.push('/login');
};
// 渲染菜单项
const renderMenuItem = (item: MenuItem) => {
const isActive = pathname === item.path || pathname.startsWith(`${item.path}/`);
return (
<Link
key={item.key}
href={item.path}
className={cn(styles.sidebar__item, isActive && styles['sidebar__item--active'])}
title={collapsed ? item.label : undefined}
>
<span className={styles.sidebar__icon}>
<Image src={item.icon} width={24} height={28} alt={item.label} />
</span>
{!collapsed && <span className={styles.sidebar__label}>{item.label}</span>}
</Link>
);
};
return (
<aside className={cn(styles.sidebar, collapsed && styles['sidebar--collapsed'])}>
{/* Logo 区域 */}
<div className={styles.sidebar__header}>
<div className={styles.sidebar__logo}>
<span className={styles.sidebar__logoIcon}>
<Image src="/images/Container.svg" width={30} height={36} alt="Logo" priority />
</span>
{!collapsed && <span className={styles.sidebar__logoText}></span>}
</div>
</div>
{/* 主导航区域 */}
<div className={styles.sidebar__navWrapper}>
<nav className={styles.sidebar__nav}>
{topMenuItems.map(renderMenuItem)}
</nav>
{/* 折叠按钮 */}
<button
className={styles.sidebar__toggle}
onClick={handleToggle}
aria-label={collapsed ? '展开侧边栏' : '收起侧边栏'}
>
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2">
{collapsed ? (
<polyline points="9 18 15 12 9 6" />
) : (
<polyline points="15 18 9 12 15 6" />
)}
</svg>
</button>
</div>
{/* 底部导航区域 */}
<div className={styles.sidebar__bottomNav}>
<div className={styles.sidebar__bottomMenu}>
{bottomMenuItems.map(renderMenuItem)}
{/* 退出登录按钮 */}
<button
className={styles.sidebar__logoutItem}
onClick={handleLogout}
title={collapsed ? '退出登录' : undefined}
>
<span className={styles.sidebar__icon}>
<Image src="/images/Container8.svg" width={24} height={28} alt="退出登录" />
</span>
{!collapsed && <span className={styles.sidebar__label}>退</span>}
</button>
</div>
</div>
</aside>
);
};