import React, { useState, useEffect, useRef } from 'react' import { Card, Button, Input, List, Badge, Modal, Form, Select, Tabs, Avatar, Empty, message, Popconfirm, Space, Tag, Tooltip } from 'antd' import { PlusOutlined, SendOutlined, TeamOutlined, UserOutlined, ArrowLeftOutlined, DeleteOutlined, SettingOutlined, UserDeleteOutlined } from '@ant-design/icons' import axios from 'axios' const { TextArea } = Input // 移动端检测 function useIsMobile() { const [isMobile, setIsMobile] = useState(window.innerWidth < 768) useEffect(() => { const onResize = () => setIsMobile(window.innerWidth < 768) window.addEventListener('resize', onResize) return () => window.removeEventListener('resize', onResize) }, []) return isMobile } function Messages({ user }) { const isMobile = useIsMobile() const [conversations, setConversations] = useState([]) const [allUsers, setAllUsers] = useState([]) const [activeConv, setActiveConv] = useState(null) const [messages, setMessages] = useState([]) const [inputValue, setInputValue] = useState('') const [sending, setSending] = useState(false) const [createVisible, setCreateVisible] = useState(false) const [membersVisible, setMembersVisible] = useState(false) const [createForm] = Form.useForm() const [creating, setCreating] = useState(false) const bottomRef = useRef(null) const activeConvRef = useRef(null) const lastMsgIdRef = useRef(0) // 打开会话时同步到 ref(供轮询闭包使用) const openConversation = (conv) => { setActiveConv(conv) activeConvRef.current = conv lastMsgIdRef.current = 0 setMessages([]) } const closeConversation = () => { setActiveConv(null) activeConvRef.current = null setMessages([]) } const fetchConversations = async () => { try { const res = await axios.get('/api/chat/conversations') if (res.data.success) setConversations(res.data.data) } catch (err) { /* 静默 */ } } const fetchUsers = async () => { try { const res = await axios.get('/api/chat/users') if (res.data.success) setAllUsers(res.data.data) } catch (err) { /* 静默 */ } } // 拉取当前打开会话的消息(首次全量,之后增量) const fetchMessages = async (conv) => { if (!conv) return try { const after = lastMsgIdRef.current const res = await axios.get(`/api/chat/conversations/${conv.id}/messages`, { params: after ? { after } : {} }) if (res.data.success) { const rows = res.data.data if (rows.length > 0) { lastMsgIdRef.current = rows[rows.length - 1].id setMessages(prev => after > 0 ? [...prev, ...rows] : rows) } } } catch (err) { /* 静默 */ } } // 会话列表 5 秒轮询;打开的会话 3 秒轮询消息 useEffect(() => { fetchConversations() fetchUsers() const convTimer = setInterval(fetchConversations, 5000) const msgTimer = setInterval(() => fetchMessages(activeConvRef.current), 3000) return () => { clearInterval(convTimer); clearInterval(msgTimer) } }, []) // 新消息自动滚动到底部 useEffect(() => { bottomRef.current?.scrollIntoView({ behavior: 'smooth' }) }, [messages.length]) const handleSend = async () => { const content = inputValue.trim() if (!content || !activeConv || sending) return setSending(true) try { const res = await axios.post(`/api/chat/conversations/${activeConv.id}/messages`, { content }) if (res.data.success) { setInputValue('') await fetchMessages(activeConvRef.current) fetchConversations() } } catch (err) { message.error(err.response?.data?.message || '发送失败') } finally { setSending(false) } } const handleCreate = async (values) => { setCreating(true) try { const payload = values.tab === 'group' ? { type: 'group', name: values.name, memberIds: values.memberIds } : { type: 'direct', userId: values.userId } const res = await axios.post('/api/chat/conversations', payload) if (res.data.success) { message.success(res.data.message) setCreateVisible(false) createForm.resetFields() await fetchConversations() // 自动打开新会话 const list = (await axios.get('/api/chat/conversations')).data if (list.success) { const conv = list.data.find(c => c.id === res.data.data.id) if (conv) openConversation(conv) } } } catch (err) { message.error(err.response?.data?.message || '创建失败') } finally { setCreating(false) } } const handleDeleteConv = async (conv) => { try { const res = await axios.delete(`/api/chat/conversations/${conv.id}`) if (res.data.success) { message.success(res.data.message) if (activeConv?.id === conv.id) closeConversation() fetchConversations() } } catch (err) { message.error(err.response?.data?.message || '删除失败') } } const canDelete = (conv) => conv.type === 'direct' || conv.creator_id === user?.id // 群主移除群成员 const handleRemoveMember = async (memberId) => { try { const res = await axios.delete(`/api/chat/conversations/${activeConv.id}/members/${memberId}`) if (res.data.success) { message.success(res.data.message) setMembersVisible(false) // 刷新会话列表与当前会话 const list = await axios.get('/api/chat/conversations') if (list.data.success) { setConversations(list.data.data) const conv = list.data.data.find(c => c.id === activeConv.id) if (conv) { setActiveConv(conv) activeConvRef.current = conv } else { closeConversation() } } } } catch (err) { message.error(err.response?.data?.message || '操作失败') } } const otherOptions = allUsers.filter(u => u.id !== user?.id).map(u => ({ value: u.id, label: ( {u.display_name || u.username} ({u.username}) ) })) // ============ 会话列表 ============ const conversationList = ( <>
我的会话
{conversations.length === 0 ? ( ) : ( ( openConversation(conv)} style={{ cursor: 'pointer', padding: '10px 8px' }} extra={ {conv.unread > 0 && } {canDelete(conv) && ( { e?.stopPropagation(); handleDeleteConv(conv) }} onCancel={(e) => e?.stopPropagation()} okText="确定" cancelText="取消" > )}
{messages.length === 0 && } {messages.map(m => { const mine = m.sender_id === user?.id return (
{!mine &&
{m.sender_name}
}
{m.content}
{m.created_at?.slice(5, 16)}
) })}