好友的相关前端

Change-Id: Iebe50bff7e96fcf6c13b57c159182f54d0a38b93
diff --git a/src/components/FriendManager.jsx b/src/components/FriendManager.jsx
new file mode 100644
index 0000000..374ac83
--- /dev/null
+++ b/src/components/FriendManager.jsx
@@ -0,0 +1,359 @@
+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;