优化 P0 工作台与访客 Widget,对齐设计稿 UI
- 工作台改为设计稿三栏:320 会话列表 + 聊天区 + 320 客户信息 - 会话列表展示头像色条、最后消息预览、相对时间与未读角标 - 消息方向对齐设计(访客左、客服右)并补系统会话条 - 访客 Widget 还原欢迎语、快捷问题、输入状态动画与实色顶栏 - 会话列表 API 返回 last_message 供列表预览
This commit is contained in:
@@ -1,19 +1,14 @@
|
||||
import { Outlet } from 'react-router-dom'
|
||||
import { Layout } from 'antd'
|
||||
import AgentSidebar from './AgentSidebar'
|
||||
|
||||
const { Content } = Layout
|
||||
|
||||
const AgentLayout = () => {
|
||||
return (
|
||||
<Layout className="h-screen">
|
||||
<div className="flex h-screen overflow-hidden bg-neutral-50">
|
||||
<AgentSidebar />
|
||||
<Layout>
|
||||
<Content className="overflow-auto bg-neutral-50">
|
||||
<Outlet />
|
||||
</Content>
|
||||
</Layout>
|
||||
</Layout>
|
||||
<div className="flex-1 min-w-0 h-full overflow-auto">
|
||||
<Outlet />
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
@@ -1,17 +1,14 @@
|
||||
import { useLocation, useNavigate } from 'react-router-dom'
|
||||
import { Layout, Menu, Dropdown, Avatar } from 'antd'
|
||||
import {
|
||||
AppstoreOutlined, MessageOutlined, HistoryOutlined, TeamOutlined,
|
||||
FileTextOutlined, BarChartOutlined, SettingOutlined, LogoutOutlined, UserOutlined,
|
||||
FileTextOutlined, BarChartOutlined, SettingOutlined, LogoutOutlined,
|
||||
} from '@ant-design/icons'
|
||||
import { useAuth } from '@/stores/auth'
|
||||
|
||||
const { Sider } = Layout
|
||||
|
||||
const menuItems = [
|
||||
{ key: '/agent/dashboard', icon: <AppstoreOutlined />, label: '工作台' },
|
||||
{ key: '/agent/chat-history', icon: <HistoryOutlined />, label: '对话记录' },
|
||||
{ key: '/agent/customers', icon: <TeamOutlined />, label: '客户管理' },
|
||||
{ key: '/agent/chat-history', icon: <HistoryOutlined />, label: '对话记录' },
|
||||
{ key: '/agent/knowledge', icon: <FileTextOutlined />, label: '知识库' },
|
||||
{ key: '/agent/statistics', icon: <BarChartOutlined />, label: '数据统计' },
|
||||
{ key: '/agent/settings', icon: <SettingOutlined />, label: '系统设置' },
|
||||
@@ -34,34 +31,68 @@ const AgentSidebar = () => {
|
||||
navigate('/login', { replace: true })
|
||||
}
|
||||
|
||||
const initial = (user?.nickname || '客').slice(0, 1)
|
||||
|
||||
return (
|
||||
<Sider width={220} className="!bg-white border-r border-neutral-200 flex flex-col">
|
||||
<div className="h-14 flex items-center px-5 border-b border-neutral-100">
|
||||
<MessageOutlined className="text-lg text-[#2563eb] mr-2.5" />
|
||||
<span className="text-base font-semibold text-neutral-900">客服云</span>
|
||||
<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>
|
||||
<Menu
|
||||
mode="inline"
|
||||
selectedKeys={[selectedKey]}
|
||||
items={visibleMenuItems}
|
||||
onClick={({ key }) => navigate(key)}
|
||||
className="border-e-0 mt-2 flex-1"
|
||||
/>
|
||||
<div className="border-t border-neutral-100 p-3">
|
||||
<Dropdown
|
||||
menu={{ items: [{ key: 'logout', icon: <LogoutOutlined />, label: '退出登录', onClick: handleLogout }] }}
|
||||
trigger={['click']}
|
||||
>
|
||||
<div className="flex items-center gap-2 cursor-pointer hover:bg-neutral-50 rounded p-1.5">
|
||||
<Avatar size={28} icon={<UserOutlined />} className="!bg-blue-100 !text-blue-500" />
|
||||
<div className="min-w-0">
|
||||
<div className="text-xs font-medium text-neutral-700 truncate">{user?.nickname || '客服'}</div>
|
||||
<div className="text-xs text-neutral-400">在线</div>
|
||||
</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>
|
||||
</Dropdown>
|
||||
</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>
|
||||
</Sider>
|
||||
</aside>
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user