171 lines
5.4 KiB
TypeScript
171 lines
5.4 KiB
TypeScript
import { NavLink, useNavigate } from 'react-router-dom';
|
|
import { Dropdown, Avatar, Tooltip } from 'antd';
|
|
import {
|
|
SearchOutlined,
|
|
MessageOutlined,
|
|
RobotOutlined,
|
|
CompassOutlined,
|
|
BookOutlined,
|
|
ApartmentOutlined,
|
|
BarChartOutlined,
|
|
TeamOutlined,
|
|
LogoutOutlined,
|
|
LeftOutlined,
|
|
RightOutlined,
|
|
UserOutlined,
|
|
CreditCardOutlined,
|
|
ShoppingCartOutlined,
|
|
DatabaseOutlined
|
|
} from '@ant-design/icons';
|
|
import { useAuth } from '../store/auth';
|
|
import kaiwuIcon from '../assets/brand/kaiwu-icon-gradient-transparent.png';
|
|
import './Sidebar.css';
|
|
|
|
interface Props {
|
|
onOpenPalette?: () => void;
|
|
onNavigate?: () => void;
|
|
collapsed?: boolean;
|
|
onToggleCollapse?: () => void;
|
|
}
|
|
|
|
const NAV_GROUPS: Array<{
|
|
label: string;
|
|
items: Array<{ to: string; icon: React.ReactNode; label: string; end?: boolean }>;
|
|
}> = [
|
|
{
|
|
label: '工作台',
|
|
items: [
|
|
{ to: '/chat', icon: <MessageOutlined />, label: '聊天' },
|
|
{ to: '/agents', icon: <RobotOutlined />, label: '我的智能体' },
|
|
// { to: '/marketplace', icon: <CompassOutlined />, label: '智能体广场' }
|
|
]
|
|
},
|
|
{
|
|
label: '资源',
|
|
items: [
|
|
{ to: '/knowledge', icon: <DatabaseOutlined />, label: '知识库' },
|
|
{ to: '/prompts', icon: <BookOutlined />, label: 'Prompt 模板库' },
|
|
{ to: '/workflows', icon: <ApartmentOutlined />, label: '工作流' }
|
|
]
|
|
},
|
|
{
|
|
label: '管理',
|
|
items: [
|
|
{ to: '/stats', icon: <BarChartOutlined />, label: '调用统计' },
|
|
{ to: '/teams', icon: <TeamOutlined />, label: '团队' }
|
|
]
|
|
},
|
|
{
|
|
label: '商城',
|
|
items: [
|
|
{ to: '/points-mall', icon: <ShoppingCartOutlined />, label: 'Token 商城' },
|
|
{ to: '/pricing', icon: <CreditCardOutlined />, label: '会员计划' }
|
|
]
|
|
}
|
|
];
|
|
|
|
export default function Sidebar({ onOpenPalette, onNavigate, collapsed, onToggleCollapse }: Props) {
|
|
const { user, logout } = useAuth();
|
|
const navigate = useNavigate();
|
|
|
|
const isMac =
|
|
typeof navigator !== 'undefined' && /mac|iphone|ipad|ipod/i.test(navigator.platform || '');
|
|
const cmdKey = isMac ? '⌘' : 'Ctrl';
|
|
|
|
return (
|
|
<aside className={`sidebar ${collapsed ? 'is-collapsed' : ''}`}>
|
|
<div className="sidebar-brand">
|
|
<img src={kaiwuIcon} alt="鲸域AI" className="sidebar-brand-logo" />
|
|
{!collapsed && <span className="sidebar-brand-name">鲸域AI</span>}
|
|
<div className="sidebar-brand-toggle" onClick={onToggleCollapse}>
|
|
{collapsed ? <RightOutlined style={{ fontSize: 12 }} /> : <LeftOutlined style={{ fontSize: 12 }} />}
|
|
</div>
|
|
</div>
|
|
|
|
<div className="sidebar-search" onClick={onOpenPalette}>
|
|
<div className="sidebar-search-input">
|
|
<SearchOutlined className="sidebar-search-icon" />
|
|
{!collapsed && (
|
|
<>
|
|
<span className="sidebar-search-placeholder">快速搜索</span>
|
|
<span className="sidebar-search-suffix">{cmdKey} K</span>
|
|
</>
|
|
)}
|
|
</div>
|
|
</div>
|
|
|
|
<div className="sidebar-scroll">
|
|
{NAV_GROUPS.map((group) => (
|
|
<div key={group.label} className="sidebar-nav-group">
|
|
{!collapsed && <div className="sidebar-nav-label">{group.label}</div>}
|
|
{group.items.map((it) => (
|
|
<Tooltip key={it.to} title={collapsed ? it.label : ''} placement="right">
|
|
<NavLink
|
|
to={it.to}
|
|
end={it.end}
|
|
className={({ isActive }) => `sidebar-nav-item ${isActive ? 'active' : ''}`}
|
|
onClick={onNavigate}
|
|
>
|
|
<span className="sidebar-nav-icon">{it.icon}</span>
|
|
{!collapsed && <span>{it.label}</span>}
|
|
</NavLink>
|
|
</Tooltip>
|
|
))}
|
|
</div>
|
|
))}
|
|
</div>
|
|
|
|
{user && (
|
|
<div className="sidebar-user">
|
|
<Dropdown
|
|
menu={{
|
|
items: [
|
|
{
|
|
key: 'name',
|
|
label: <span className="sidebar-user-role">{user.phone}</span>,
|
|
disabled: true
|
|
},
|
|
{
|
|
key: 'profile',
|
|
icon: <UserOutlined />,
|
|
label: '个人中心',
|
|
onClick: () => {
|
|
navigate('/profile');
|
|
onNavigate?.();
|
|
}
|
|
},
|
|
{ type: 'divider' },
|
|
{
|
|
key: 'logout',
|
|
icon: <LogoutOutlined />,
|
|
label: '退出登录',
|
|
onClick: async () => {
|
|
await logout();
|
|
navigate('/login');
|
|
onNavigate?.();
|
|
}
|
|
}
|
|
]
|
|
}}
|
|
placement="topLeft"
|
|
>
|
|
<div className="sidebar-user-card">
|
|
<div className="sidebar-user-avatar">
|
|
{(user.name?.charAt(0) || '?').toUpperCase()}
|
|
</div>
|
|
{!collapsed && (
|
|
<div className="sidebar-user-info">
|
|
<div className="sidebar-user-name">{user.name}</div>
|
|
<div className="sidebar-user-role">
|
|
{user.role === 'admin' ? '管理员' : '成员'}
|
|
</div>
|
|
</div>
|
|
)}
|
|
</div>
|
|
</Dropdown>
|
|
</div>
|
|
)}
|
|
</aside>
|
|
);
|
|
}
|