好友的相关前端
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;