102 lines
4.0 KiB
TypeScript
102 lines
4.0 KiB
TypeScript
import { useLocation, useNavigate } from 'react-router-dom'
|
|
import {
|
|
AppstoreOutlined, MessageOutlined, HistoryOutlined, TeamOutlined,
|
|
FileTextOutlined, BarChartOutlined, SettingOutlined, LogoutOutlined,
|
|
ThunderboltOutlined,
|
|
} from '@ant-design/icons'
|
|
import { useAuth } from '@/stores/auth'
|
|
|
|
const menuItems = [
|
|
{ key: '/agent/dashboard', icon: <AppstoreOutlined />, label: '工作台' },
|
|
{ key: '/agent/customers', icon: <TeamOutlined />, label: '客户管理' },
|
|
{ key: '/agent/chat-history', icon: <HistoryOutlined />, label: '对话记录' },
|
|
{ key: '/agent/knowledge', icon: <FileTextOutlined />, label: '知识库' },
|
|
{ key: '/agent/quick-replies', icon: <ThunderboltOutlined />, label: '快捷回复' },
|
|
{ key: '/agent/statistics', icon: <BarChartOutlined />, label: '数据统计' },
|
|
{ key: '/agent/settings', icon: <SettingOutlined />, label: '系统设置' },
|
|
]
|
|
|
|
const AgentSidebar = () => {
|
|
const location = useLocation()
|
|
const navigate = useNavigate()
|
|
const { user, logout } = useAuth()
|
|
const visibleMenuItems = menuItems.filter(item => {
|
|
if (item.key === '/agent/statistics') return user?.role === 'admin' || user?.role === 'supervisor'
|
|
if (item.key === '/agent/settings') return user?.role === 'admin'
|
|
return true
|
|
})
|
|
|
|
const selectedKey = visibleMenuItems.find(item => location.pathname.startsWith(item.key))?.key || '/agent/dashboard'
|
|
|
|
const handleLogout = () => {
|
|
logout()
|
|
navigate('/login', { replace: true })
|
|
}
|
|
|
|
const initial = (user?.nickname || '客').slice(0, 1)
|
|
|
|
return (
|
|
<aside
|
|
className="shrink-0 flex flex-col h-full bg-white border-r border-neutral-200"
|
|
style={{ width: 'var(--sidebar-width)' }}
|
|
>
|
|
<div
|
|
className="flex items-center gap-2.5 px-4 shrink-0 border-b border-neutral-200"
|
|
style={{ height: 'var(--header-height)' }}
|
|
>
|
|
<div className="w-8 h-8 rounded-lg bg-[#2563eb] flex items-center justify-center shrink-0">
|
|
<MessageOutlined className="text-white text-sm" />
|
|
</div>
|
|
<span className="font-semibold text-neutral-900 text-base truncate">在线客服</span>
|
|
</div>
|
|
|
|
<nav className="flex-1 overflow-y-auto py-3 px-2">
|
|
<ul className="flex flex-col gap-0.5">
|
|
{visibleMenuItems.map(item => {
|
|
const active = selectedKey === item.key
|
|
return (
|
|
<li key={item.key}>
|
|
<button
|
|
type="button"
|
|
onClick={() => navigate(item.key)}
|
|
className={`w-full flex items-center gap-2.5 px-3 py-2 rounded-lg text-sm truncate transition-colors ${
|
|
active
|
|
? 'bg-[#dbeafe] text-[#2563eb] font-medium'
|
|
: 'text-neutral-600 hover:bg-neutral-50'
|
|
}`}
|
|
>
|
|
<span className="text-base leading-none">{item.icon}</span>
|
|
<span className="truncate">{item.label}</span>
|
|
</button>
|
|
</li>
|
|
)
|
|
})}
|
|
</ul>
|
|
</nav>
|
|
|
|
<div className="flex items-center gap-2.5 px-4 shrink-0 border-t border-neutral-200" style={{ height: 52 }}>
|
|
<div className="w-8 h-8 rounded-full bg-[#dbeafe] text-[#2563eb] flex items-center justify-center text-sm font-semibold shrink-0">
|
|
{initial}
|
|
</div>
|
|
<div className="flex-1 min-w-0">
|
|
<div className="text-sm font-medium text-neutral-800 truncate">{user?.nickname || '客服'}</div>
|
|
<div className="text-xs text-neutral-400 flex items-center gap-1">
|
|
<span className="inline-block w-1.5 h-1.5 rounded-full bg-green-500" />
|
|
在线
|
|
</div>
|
|
</div>
|
|
<button
|
|
type="button"
|
|
onClick={handleLogout}
|
|
className="w-7 h-7 rounded-md text-neutral-400 hover:text-neutral-600 hover:bg-neutral-50 flex items-center justify-center"
|
|
title="退出登录"
|
|
>
|
|
<LogoutOutlined className="text-sm" />
|
|
</button>
|
|
</div>
|
|
</aside>
|
|
)
|
|
}
|
|
|
|
export default AgentSidebar
|