搭建客服端基础框架:路由、侧边栏布局、客服工作台三栏页面
This commit is contained in:
@@ -0,0 +1,186 @@
|
||||
import { useState } from 'react'
|
||||
import { Badge, Input, Tooltip } from 'antd'
|
||||
import { SearchOutlined, UserOutlined, StarFilled } from '@ant-design/icons'
|
||||
|
||||
interface Session {
|
||||
id: string
|
||||
name: string
|
||||
avatar?: string
|
||||
lastMessage: string
|
||||
time: string
|
||||
priority: 'urgent' | 'waiting' | 'active'
|
||||
unread: number
|
||||
}
|
||||
|
||||
const mockSessions: Session[] = [
|
||||
{ id: '1', name: '张三', lastMessage: '我的订单什么时候发货?', time: '10:32', priority: 'urgent', unread: 3 },
|
||||
{ id: '2', name: '李四', lastMessage: '怎么退货啊', time: '10:28', priority: 'active', unread: 0 },
|
||||
{ id: '3', name: '王五', lastMessage: '你们这个产品好用吗', time: '10:15', priority: 'active', unread: 1 },
|
||||
{ id: '4', name: '赵六科技', lastMessage: '企业版价格能否优惠', time: '09:58', priority: 'waiting', unread: 0 },
|
||||
{ id: '5', name: '钱七', lastMessage: '谢谢,问题已解决', time: '09:45', priority: 'active', unread: 0 },
|
||||
]
|
||||
|
||||
const priorityColors = { urgent: '#dc2626', waiting: '#d97706', active: '#2563eb' }
|
||||
const priorityLabels = { urgent: '紧急', waiting: '等待中', active: '进行中' }
|
||||
|
||||
const Dashboard = () => {
|
||||
const [selectedId, setSelectedId] = useState<string>('1')
|
||||
const [message, setMessage] = useState('')
|
||||
|
||||
const selectedSession = mockSessions.find(s => s.id === selectedId)
|
||||
|
||||
return (
|
||||
<div className="h-full flex">
|
||||
{/* 左侧:会话列表 */}
|
||||
<div className="w-[300px] flex-shrink-0 border-r border-neutral-200 bg-white flex flex-col">
|
||||
<div className="p-3 border-b border-neutral-100">
|
||||
<Input prefix={<SearchOutlined />} placeholder="搜索会话..." variant="borderless" />
|
||||
</div>
|
||||
<div className="flex px-3 py-2 gap-2 border-b border-neutral-100">
|
||||
{(['urgent', 'waiting', 'active'] as const).map(p => (
|
||||
<span key={p} className="text-xs px-2 py-0.5 rounded-full cursor-pointer hover:bg-neutral-100" style={{ color: priorityColors[p] }}>
|
||||
{priorityLabels[p]}
|
||||
</span>
|
||||
))}
|
||||
</div>
|
||||
<div className="flex-1 overflow-auto">
|
||||
{mockSessions.map(session => (
|
||||
<div
|
||||
key={session.id}
|
||||
className={`px-3 py-2.5 cursor-pointer border-b border-neutral-50 hover:bg-neutral-50 transition-colors ${selectedId === session.id ? 'bg-blue-50' : ''}`}
|
||||
onClick={() => setSelectedId(session.id)}
|
||||
>
|
||||
<div className="flex items-center justify-between">
|
||||
<div className="flex items-center gap-2">
|
||||
<span className="inline-block w-2 h-2 rounded-full" style={{ backgroundColor: priorityColors[session.priority] }} />
|
||||
<span className="text-sm font-medium text-neutral-800">{session.name}</span>
|
||||
</div>
|
||||
<div className="flex items-center gap-1">
|
||||
{session.unread > 0 && <Badge count={session.unread} size="small" />}
|
||||
<span className="text-xs text-neutral-400">{session.time}</span>
|
||||
</div>
|
||||
</div>
|
||||
<p className="text-xs text-neutral-400 mt-1 truncate pl-4">{session.lastMessage}</p>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* 中间:聊天区 */}
|
||||
<div className="flex-1 flex flex-col min-w-0 bg-white">
|
||||
{selectedSession ? (
|
||||
<>
|
||||
<div className="h-12 px-4 flex items-center justify-between border-b border-neutral-100 flex-shrink-0">
|
||||
<span className="text-sm font-medium text-neutral-800">{selectedSession.name}</span>
|
||||
<div className="flex gap-2">
|
||||
<Tooltip title="转接"><span className="text-neutral-400 cursor-pointer hover:text-neutral-600 text-sm">转接</span></Tooltip>
|
||||
<Tooltip title="结束会话"><span className="text-neutral-400 cursor-pointer hover:text-red-500 text-sm">结束</span></Tooltip>
|
||||
</div>
|
||||
</div>
|
||||
<div className="flex-1 overflow-auto p-4 flex flex-col gap-3">
|
||||
<div className="flex flex-col items-start">
|
||||
<div className="bg-neutral-100 rounded-lg px-3 py-2 text-sm text-neutral-700 max-w-[70%]">
|
||||
你好,我想咨询一下订单物流问题
|
||||
</div>
|
||||
<span className="text-xs text-neutral-400 mt-0.5">10:30</span>
|
||||
</div>
|
||||
<div className="flex flex-col items-end">
|
||||
<div className="bg-blue-500 rounded-lg px-3 py-2 text-sm text-white max-w-[70%]">
|
||||
您好,请提供您的订单号,我帮您查询
|
||||
</div>
|
||||
<span className="text-xs text-neutral-400 mt-0.5">10:31</span>
|
||||
</div>
|
||||
<div className="flex flex-col items-start">
|
||||
<div className="bg-neutral-100 rounded-lg px-3 py-2 text-sm text-neutral-700 max-w-[70%]">
|
||||
订单号是:AB20260714001
|
||||
</div>
|
||||
<span className="text-xs text-neutral-400 mt-0.5">10:31</span>
|
||||
</div>
|
||||
<div className="flex flex-col items-end">
|
||||
<div className="bg-blue-500 rounded-lg px-3 py-2 text-sm text-white max-w-[70%]">
|
||||
好的,正在为您查询,请稍候...
|
||||
</div>
|
||||
<span className="text-xs text-neutral-400 mt-0.5">10:32</span>
|
||||
</div>
|
||||
</div>
|
||||
<div className="p-3 border-t border-neutral-100 flex-shrink-0">
|
||||
<div className="flex items-center gap-2 bg-neutral-50 rounded-lg px-3 py-2">
|
||||
<input
|
||||
className="flex-1 bg-transparent outline-none text-sm text-neutral-700 placeholder:text-neutral-400"
|
||||
placeholder="输入消息... (Enter 发送)"
|
||||
value={message}
|
||||
onChange={e => setMessage(e.target.value)}
|
||||
onKeyDown={e => {
|
||||
if (e.key === 'Enter' && message.trim()) {
|
||||
setMessage('')
|
||||
}
|
||||
}}
|
||||
/>
|
||||
<span className="text-neutral-300 cursor-pointer hover:text-neutral-500">😊</span>
|
||||
<span className="text-neutral-300 cursor-pointer hover:text-neutral-500">📎</span>
|
||||
</div>
|
||||
</div>
|
||||
</>
|
||||
) : (
|
||||
<div className="flex-1 flex items-center justify-center text-neutral-400">
|
||||
选择一个会话开始接待
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
|
||||
{/* 右侧:客户信息面板 */}
|
||||
<div className="w-[300px] flex-shrink-0 border-l border-neutral-200 bg-white overflow-auto">
|
||||
<div className="p-4 border-b border-neutral-100">
|
||||
<div className="flex items-center gap-3">
|
||||
<div className="w-10 h-10 rounded-full bg-blue-100 flex items-center justify-center">
|
||||
<UserOutlined className="text-blue-500" />
|
||||
</div>
|
||||
<div>
|
||||
<div className="text-sm font-medium text-neutral-800">{selectedSession?.name || '访客'}</div>
|
||||
<div className="text-xs text-neutral-400 mt-0.5">网页渠道</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div className="p-4 space-y-4">
|
||||
<div>
|
||||
<div className="text-xs text-neutral-400 mb-1.5">标签</div>
|
||||
<div className="flex flex-wrap gap-1">
|
||||
<span className="text-xs px-2 py-0.5 rounded bg-orange-50 text-orange-600">VIP客户</span>
|
||||
<span className="text-xs px-2 py-0.5 rounded bg-blue-50 text-blue-600">新客户</span>
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
<div className="text-xs text-neutral-400 mb-1.5">联系信息</div>
|
||||
<div className="text-sm text-neutral-700 space-y-1">
|
||||
<div>📱 138****8888</div>
|
||||
<div>📧 zhang***@example.com</div>
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
<div className="text-xs text-neutral-400 mb-1.5">统计数据</div>
|
||||
<div className="grid grid-cols-2 gap-2">
|
||||
<div className="bg-neutral-50 rounded p-2 text-center">
|
||||
<div className="text-lg font-semibold text-neutral-800">12</div>
|
||||
<div className="text-xs text-neutral-400">对话次数</div>
|
||||
</div>
|
||||
<div className="bg-neutral-50 rounded p-2 text-center">
|
||||
<div className="text-lg font-semibold text-green-600 flex items-center justify-center gap-0.5">
|
||||
4.8 <StarFilled className="text-xs" />
|
||||
</div>
|
||||
<div className="text-xs text-neutral-400">满意度</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
<div className="text-xs text-neutral-400 mb-1.5">内部备注</div>
|
||||
<div className="text-sm text-neutral-500 bg-neutral-50 rounded p-2">
|
||||
之前咨询过售后问题...
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
export default Dashboard
|
||||
Reference in New Issue
Block a user