| import React, { useState, useEffect } from 'react'; |
| import { |
| Input, |
| Button, |
| List, |
| Typography, |
| Space, |
| Spin, |
| Popconfirm, |
| message, |
| Divider, |
| Avatar, |
| } from 'antd'; |
| import { |
| UserAddOutlined, |
| DeleteOutlined, |
| ReloadOutlined, |
| CheckOutlined, |
| CloseOutlined, |
| } from '@ant-design/icons'; |
| import { |
| addFriend, |
| deleteFriend, |
| getFriendsByUserId, |
| getPendingRequests, |
| acceptFriend, |
| rejectFriend, |
| } from '../api/friends'; |
| import axios from 'axios'; |
| |
| const { Title, Text } = Typography; |
| |
| const FriendManager = ({ currentUser, onSelectRelation }) => { |
| const currentUserId = currentUser?.userid; |
| |
| const [friendName, setFriendName] = useState(''); |
| const [friends, setFriends] = useState([]); |
| const [pendingRequests, setPendingRequests] = useState([]); |
| const [userInfoMap, setUserInfoMap] = useState({}); |
| const [loading, setLoading] = useState(false); |
| const [refreshing, setRefreshing] = useState(false); |
| const [pendingLoading, setPendingLoading] = useState(false); |
| |
| useEffect(() => { |
| if (currentUserId) { |
| refreshData(); |
| } |
| }, [currentUserId]); |
| |
| const refreshData = () => { |
| loadFriends(currentUserId); |
| loadPendingRequests(currentUserId); |
| }; |
| |
| const fetchUserInfo = async (userId) => { |
| if (userInfoMap[userId]) return; |
| try { |
| const res = await axios.get(`http://localhost:8080/user/getDecoration?userid=${userId}`); |
| const info = res.data?.data; |
| if (info) { |
| setUserInfoMap((prev) => ({ |
| ...prev, |
| [userId]: { |
| username: info.username, |
| avatar: info.image, |
| }, |
| })); |
| } |
| } catch { |
| setUserInfoMap((prev) => ({ |
| ...prev, |
| [userId]: { |
| username: `用户${userId}`, |
| avatar: null, |
| }, |
| })); |
| } |
| }; |
| |
| const loadFriends = async (userId) => { |
| setRefreshing(true); |
| try { |
| const res = await getFriendsByUserId(userId); |
| const list = res.data || []; |
| setFriends(list); |
| list.forEach(f => fetchUserInfo(getFriendUserId(f))); |
| } catch { |
| message.error('加载好友失败,请稍后重试'); |
| } |
| setRefreshing(false); |
| }; |
| |
| const loadPendingRequests = async (userId) => { |
| setPendingLoading(true); |
| try { |
| const res = await getPendingRequests(userId); |
| const list = res.data || []; |
| setPendingRequests(list); |
| list.forEach(req => { |
| const otherId = req.friend1 === currentUserId ? req.friend2 : req.friend1; |
| fetchUserInfo(otherId); |
| }); |
| } catch { |
| message.error('加载好友申请失败'); |
| } |
| setPendingLoading(false); |
| }; |
| |
| const handleAddFriend = async () => { |
| if (!friendName.trim()) return message.warning('请输入好友用户名'); |
| |
| setLoading(true); |
| try { |
| const res = await axios.get(`http://localhost:8080/user/getUserid?username=${friendName.trim()}`); |
| const newFriendId = res.data?.data; |
| if (!newFriendId) { |
| message.error('未找到该用户名对应的用户'); |
| setLoading(false); |
| return; |
| } |
| |
| if (newFriendId === currentUserId) { |
| message.warning('不能添加自己为好友'); |
| setLoading(false); |
| return; |
| } |
| |
| const isAlreadyFriend = friends.some(f => |
| (f.friend1 === currentUserId && f.friend2 === newFriendId) || |
| (f.friend1 === newFriendId && f.friend2 === currentUserId) |
| ); |
| if (isAlreadyFriend) { |
| message.warning('该用户已是您的好友'); |
| setLoading(false); |
| return; |
| } |
| |
| const result = await addFriend({ friend1: currentUserId, friend2: newFriendId }); |
| if (result.data) { |
| message.success('好友请求已发送'); |
| setFriendName(''); |
| loadPendingRequests(currentUserId); |
| } else { |
| message.error('添加失败'); |
| } |
| } catch { |
| message.error('添加好友失败,请稍后重试'); |
| } finally { |
| setLoading(false); |
| } |
| }; |
| |
| const handleDelete = async (friend1, friend2) => { |
| setLoading(true); |
| try { |
| const res = await deleteFriend(friend1, friend2); |
| if (res.data) { |
| message.success('删除成功'); |
| loadFriends(currentUserId); |
| } else { |
| message.error('删除失败'); |
| } |
| } catch { |
| message.error('删除好友失败'); |
| } finally { |
| setLoading(false); |
| } |
| }; |
| |
| const handleAccept = async (friend1, friend2) => { |
| setPendingLoading(true); |
| try { |
| const res = await acceptFriend(friend1, friend2); |
| if (res.data) { |
| message.success('已同意好友请求'); |
| refreshData(); |
| } else { |
| message.error('操作失败'); |
| } |
| } catch { |
| message.error('同意失败'); |
| } finally { |
| setPendingLoading(false); |
| } |
| }; |
| |
| const handleReject = async (friend1, friend2) => { |
| setPendingLoading(true); |
| try { |
| const res = await rejectFriend(friend1, friend2); |
| if (res.data) { |
| message.info('已拒绝好友请求'); |
| loadPendingRequests(currentUserId); |
| } else { |
| message.error('操作失败'); |
| } |
| } catch { |
| message.error('拒绝失败'); |
| } finally { |
| setPendingLoading(false); |
| } |
| }; |
| |
| const getFriendUserId = (f) => f.friend1 === currentUserId ? f.friend2 : f.friend1; |
| |
| const renderUserMeta = (userId, timeLabel) => { |
| const user = userInfoMap[userId] || {}; |
| return { |
| avatar: <Avatar src={user.avatar} />, |
| title: user.username ? `${user.username}(ID: ${userId})` : `用户ID:${userId}`, |
| description: timeLabel, |
| }; |
| }; |
| |
| return ( |
| <div style={{ maxWidth: 700, margin: 'auto', padding: 24 }}> |
| <Title level={3} style={{ textAlign: 'center', marginBottom: 24 }}> |
| 好友管理 |
| </Title> |
| |
| <Space style={{ marginBottom: 24 }} align="start"> |
| <Input |
| placeholder="输入好友用户名" |
| value={friendName} |
| onChange={(e) => setFriendName(e.target.value)} |
| style={{ width: 220 }} |
| allowClear |
| prefix={<UserAddOutlined />} |
| /> |
| <Button |
| type="primary" |
| loading={loading} |
| onClick={handleAddFriend} |
| disabled={!friendName.trim()} |
| > |
| 添加好友 |
| </Button> |
| </Space> |
| |
| <Divider /> |
| |
| <Title level={4}>好友申请</Title> |
| <Spin spinning={pendingLoading}> |
| {pendingRequests.length === 0 ? ( |
| <Text type="secondary">暂无好友申请</Text> |
| ) : ( |
| <List |
| itemLayout="horizontal" |
| dataSource={pendingRequests} |
| renderItem={(item) => { |
| const otherId = item.friend1 === currentUserId ? item.friend2 : item.friend1; |
| return ( |
| <List.Item |
| actions={[ |
| <Button |
| key="accept" |
| type="primary" |
| icon={<CheckOutlined />} |
| onClick={() => handleAccept(item.friend1, item.friend2)} |
| loading={pendingLoading} |
| size="small" |
| > |
| 同意 |
| </Button>, |
| <Popconfirm |
| key="reject" |
| title="确定拒绝该好友请求?" |
| onConfirm={() => handleReject(item.friend1, item.friend2)} |
| okText="确认" |
| cancelText="取消" |
| > |
| <Button |
| danger |
| icon={<CloseOutlined />} |
| loading={pendingLoading} |
| size="small" |
| > |
| 拒绝 |
| </Button> |
| </Popconfirm>, |
| ]} |
| > |
| <List.Item.Meta {...renderUserMeta(otherId, `申请时间:${new Date(item.requestTime).toLocaleString()}`)} /> |
| </List.Item> |
| ); |
| }} |
| /> |
| )} |
| </Spin> |
| |
| <Divider /> |
| |
| <Space align="center" style={{ marginBottom: 12, justifyContent: 'space-between', width: '100%' }}> |
| <Title level={4} style={{ margin: 0 }}> |
| 我的好友列表 |
| </Title> |
| <Button |
| icon={<ReloadOutlined />} |
| onClick={() => refreshData()} |
| loading={refreshing || pendingLoading} |
| type="link" |
| > |
| 刷新 |
| </Button> |
| </Space> |
| <Spin spinning={refreshing}> |
| {friends.length === 0 ? ( |
| <Text type="secondary">暂无好友</Text> |
| ) : ( |
| <List |
| itemLayout="horizontal" |
| dataSource={friends} |
| renderItem={(f) => { |
| const friendUserId = getFriendUserId(f); |
| return ( |
| <List.Item |
| onClick={() => |
| onSelectRelation({ |
| relationid: f.relationid, |
| friendId: friendUserId, |
| }) |
| } |
| style={{ cursor: 'pointer' }} |
| actions={[ |
| <Popconfirm |
| title="确定删除该好友?" |
| onConfirm={(e) => { |
| e.stopPropagation(); |
| handleDelete(f.friend1, f.friend2); |
| }} |
| okText="确认" |
| cancelText="取消" |
| key="delete" |
| > |
| <Button |
| danger |
| icon={<DeleteOutlined />} |
| loading={loading} |
| size="small" |
| > |
| 删除 |
| </Button> |
| </Popconfirm>, |
| ]} |
| > |
| <List.Item.Meta |
| {...renderUserMeta(friendUserId, `添加时间:${new Date(f.requestTime).toLocaleString()}`)} |
| /> |
| </List.Item> |
| ); |
| }} |
| /> |
| )} |
| </Spin> |
| </div> |
| ); |
| }; |
| |
| export default FriendManager; |