Merge "个人页面修复完成"
diff --git a/Merge/back_ljc/app.py b/Merge/back_ljc/app.py
index f672fc3..b3fb3e1 100644
--- a/Merge/back_ljc/app.py
+++ b/Merge/back_ljc/app.py
@@ -1,6 +1,7 @@
from flask import Flask, jsonify, request, session
from flask_sqlalchemy import SQLAlchemy
from flask_cors import CORS
+from flask_jwt_extended import jwt_required, get_jwt_identity
app = Flask(__name__)
app.config['SQLALCHEMY_DATABASE_URI'] = 'mysql+pymysql://root:123456@10.126.59.25/redbook'
@@ -351,9 +352,10 @@
return jsonify(following_list)
# 关注/取消关注用户
-@app.route('/api/follow/<int:followee_id>', methods=['POST', 'DELETE'])
-def follow_user(followee_id):
- follower_id = session.get('user_id', 1)
+@app.route('/api/follow/<int:follower_id>/<int:followee_id>', methods=['POST', 'DELETE'])
+def follow_user(follower_id,followee_id):
+ # follower_id = session.get('user_id', 1)
+ print(follower_id)
if follower_id == followee_id:
return jsonify({'error': 'Cannot follow yourself'}), 400
@@ -468,6 +470,130 @@
app.logger.error(f"获取用户互动数据失败: {str(e)}")
return jsonify({'error': '获取互动数据失败'}), 500
+# 点赞/取消点赞路由 - 修改为包含用户ID
+@app.route('/api/users/<int:user_id>/posts/<int:post_id>/like', methods=['POST', 'DELETE'])
+def handle_like(user_id, post_id):
+ # 检查用户是否登录
+ if 'user_id' not in session:
+ return jsonify({'error': '未登录'}), 401
+
+ # 验证请求用户ID与登录用户ID是否一致
+ if session['user_id'] != user_id:
+ return jsonify({'error': '无权限操作'}), 403
+
+ post = Post.query.get(post_id)
+
+ if not post:
+ return jsonify({'error': '帖子不存在'}), 404
+
+ # 检查行为类型
+ behavior = Behavior.query.filter_by(
+ user_id=user_id,
+ post_id=post_id,
+ type='like'
+ ).first()
+
+ if request.method == 'POST':
+ # 点赞
+ if not behavior:
+ new_behavior = Behavior(
+ user_id=user_id,
+ post_id=post_id,
+ type='like',
+ value=1
+ )
+ db.session.add(new_behavior)
+ db.session.commit()
+ return jsonify({'message': '点赞成功', 'liked': True})
+ return jsonify({'message': '已点赞', 'liked': True})
+
+ elif request.method == 'DELETE':
+ # 取消点赞
+ if behavior:
+ db.session.delete(behavior)
+ db.session.commit()
+ return jsonify({'message': '已取消点赞', 'liked': False})
+ return jsonify({'message': '未点赞', 'liked': False})
+
+# 收藏/取消收藏路由 - 修改为包含用户ID
+@app.route('/api/users/<int:user_id>/posts/<int:post_id>/favorite', methods=['POST', 'DELETE'])
+def handle_favorite(user_id, post_id):
+ # 检查用户是否登录
+ if 'user_id' not in session:
+ return jsonify({'error': '未登录'}), 401
+
+ # 验证请求用户ID与登录用户ID是否一致
+ if session['user_id'] != user_id:
+ return jsonify({'error': '无权限操作'}), 403
+
+ post = Post.query.get(post_id)
+
+ if not post:
+ return jsonify({'error': '帖子不存在'}), 404
+
+ # 检查行为类型
+ behavior = Behavior.query.filter_by(
+ user_id=user_id,
+ post_id=post_id,
+ type='favorite'
+ ).first()
+
+ if request.method == 'POST':
+ # 收藏
+ if not behavior:
+ new_behavior = Behavior(
+ user_id=user_id,
+ post_id=post_id,
+ type='favorite',
+ value=1
+ )
+ db.session.add(new_behavior)
+ db.session.commit()
+ return jsonify({'message': '收藏成功', 'favorited': True})
+ return jsonify({'message': '已收藏', 'favorited': True})
+
+ elif request.method == 'DELETE':
+ # 取消收藏
+ if behavior:
+ db.session.delete(behavior)
+ db.session.commit()
+ return jsonify({'message': '已取消收藏', 'favorited': False})
+ return jsonify({'message': '未收藏', 'favorited': False})
+
+# 获取帖子互动状态(是否点赞/收藏) - 修改为包含用户ID
+@app.route('/api/users/<int:user_id>/posts/<int:post_id>/interaction-status')
+def get_post_interaction_status(user_id, post_id):
+ # 检查用户是否登录
+ if 'user_id' not in session:
+ return jsonify({
+ 'liked': False,
+ 'favorited': False
+ })
+
+ # 验证请求用户ID与登录用户ID是否一致
+ if session['user_id'] != user_id:
+ return jsonify({
+ 'liked': False,
+ 'favorited': False
+ })
+
+ liked = Behavior.query.filter_by(
+ user_id=user_id,
+ post_id=post_id,
+ type='like'
+ ).first() is not None
+
+ favorited = Behavior.query.filter_by(
+ user_id=user_id,
+ post_id=post_id,
+ type='favorite'
+ ).first() is not None
+
+ return jsonify({
+ 'liked': liked,
+ 'favorited': favorited
+ })
+
if __name__ == '__main__':
app.run(debug=True,port='5715',host='0.0.0.0')
\ No newline at end of file
diff --git a/Merge/front/src/api/api_ljc.js b/Merge/front/src/api/api_ljc.js
index 1adea99..5a30068 100644
--- a/Merge/front/src/api/api_ljc.js
+++ b/Merge/front/src/api/api_ljc.js
@@ -2,7 +2,8 @@
import { getUserInfo } from '../utils/auth';
const api = axios.create({
- baseURL: 'http://10.126.59.25:5715/api/',
+ // baseURL: 'http://10.126.59.25:5715/api/',
+ baseURL: 'http://127.0.0.1:5715/api/',
withCredentials: true
});
@@ -15,11 +16,11 @@
export const getFavorites = (userId) => api.get(`/user/${userId}/favorites`);
// 关注相关API
-export const followUser = (followeeId) => {
- return api.post(`/follow/${followeeId}`);
+export const followUser = (followerId,followeeId) => {
+ return api.post(`/follow/${followerId}/${followeeId}`);
};
-export const unfollowUser = (followeeId) => {
- return api.delete(`/follow/${followeeId}`);
+export const unfollowUser = (followerId,followeeId) => {
+ return api.delete(`/follow/${followerId}/${followeeId}`);
};
// 帖子相关API
@@ -32,5 +33,56 @@
export const getUserInteractions = (userId) => api.get(`/user/${userId}/interactions`);
// 获取粉丝
export const getUserFollowers = (userId) => api.get(`/user/${userId}/followers`);
+// ================= 帖子互动API =================
+
+/**
+ * 点赞帖子
+ * @param {number} userId 用户ID
+ * @param {number} postId 帖子ID
+ * @returns 操作结果
+ */
+export const likePost = (userId, postId) => {
+ return api.post(`/users/${userId}/posts/${postId}/like`);
+};
+
+/**
+ * 取消点赞
+ * @param {number} userId 用户ID
+ * @param {number} postId 帖子ID
+ * @returns 操作结果
+ */
+export const unlikePost = (userId, postId) => {
+ return api.delete(`/users/${userId}/posts/${postId}/like`);
+};
+
+/**
+ * 收藏帖子
+ * @param {number} userId 用户ID
+ * @param {number} postId 帖子ID
+ * @returns 操作结果
+ */
+export const favoritePost = (userId, postId) => {
+ return api.post(`/users/${userId}/posts/${postId}/favorite`);
+};
+
+/**
+ * 取消收藏
+ * @param {number} userId 用户ID
+ * @param {number} postId 帖子ID
+ * @returns 操作结果
+ */
+export const unfavoritePost = (userId, postId) => {
+ return api.delete(`/users/${userId}/posts/${postId}/favorite`);
+};
+
+/**
+ * 获取当前用户对帖子的互动状态
+ * @param {number} userId 用户ID
+ * @param {number} postId 帖子ID
+ * @returns {object} { liked: boolean, favorited: boolean }
+ */
+export const getPostInteractionStatus = (userId, postId) => {
+ return api.get(`/users/${userId}/posts/${postId}/interaction-status`);
+};
export default api;
\ No newline at end of file
diff --git a/Merge/front/src/components/UserProfile.jsx b/Merge/front/src/components/UserProfile.jsx
index c957473..d6a19d9 100644
--- a/Merge/front/src/components/UserProfile.jsx
+++ b/Merge/front/src/components/UserProfile.jsx
@@ -53,7 +53,10 @@
Collections,
ChevronLeft,
ChevronRight,
- Close
+ Close,
+ Bookmark,
+ Group,
+ People
} from '@mui/icons-material';
import { createTheme, ThemeProvider } from '@mui/material/styles';
import { Link, useNavigate } from 'react-router-dom';
@@ -66,7 +69,10 @@
followUser as followUserApi,
unfollowUser as unfollowUserApi,
getUserPosts,
- getUserInteractions
+ getUserInteractions,
+ getUserFollowers,
+ getFavorites,
+ getUserFollowing
} from '../api/api_ljc';
import { fetchPost } from '../api/posts_wzy';
@@ -119,6 +125,10 @@
const navigate = useNavigate();
const [activeTab, setActiveTab] = useState(0);
const [isEditing, setIsEditing] = useState(false);
+ const [favorites, setFavorites] = useState([]);
+ const [following, setFollowing] = useState([]);
+ const [followers, setFollowers] = useState([]);
+ const [follower, setFollower] = useState([]);
const [snackbar, setSnackbar] = useState({ open: false, message: '', severity: 'success' });
const [anchorEl, setAnchorEl] = useState(null);
@@ -199,29 +209,7 @@
fetchInteractions();
- const handleFollowUser = async (followeeId) => {
- try {
- await followUserApi(followeeId);
- showSnackbar('关注成功');
-
- // 更新粉丝列表状态(将刚关注的用户标记为已关注)
- // setFollowers(prev => prev.map(user =>
- // user.id === followeeId ? { ...user, is_following: true } : user
- // ));
-
- // 更新当前用户关注数
- if (currentUser) {
- setCurrentUser(prev => ({
- ...prev,
- following_count: prev.following_count + 1
- }));
- }
-
- } catch (error) {
- console.error('关注操作失败:', error);
- showSnackbar('关注失败,请重试', 'error');
- }
- };
+
const fetchData = async () => {
try {
setLoading(true);
@@ -241,6 +229,22 @@
birthday: profileUserRes.data.birthday || '',
location: profileUserRes.data.location || ''
});
+
+ if (activeTab === 1) {
+ // 加载收藏数据
+ const favoritesRes = await getFavorites(userId);
+ setFavorites(favoritesRes.data);
+ } else if (activeTab === 2) {
+ // 加载关注列表
+ const followingRes = await getUserFollowing(userId);
+ setFollowing(followingRes.data);
+ console.log("following",followingRes.data)
+ } else if (activeTab === 3) {
+ // 加载粉丝列表
+ const followersRes = await getUserFollowers(userId);
+ //
+ setFollowers(followersRes.data.data);
+ }
// 获取用户帖子
const postsRes = await getUserPosts(userId);
@@ -283,7 +287,7 @@
};
fetchData();
- }, [userId]);
+ }, [activeTab,userId]);
// 根据标签页加载数据
useEffect(() => {
@@ -314,18 +318,40 @@
}
};
- const handleFollowUser = async (followeeId) => {
+ const handleFollowUser = async (userId,followeeId) => {
try {
- await followUserApi(followeeId);
+ await followUserApi(userId,followeeId);
showSnackbar('关注成功');
- // 更新当前用户关注数
- if (currentUser) {
- setCurrentUser(prev => ({
- ...prev,
- following_count: prev.following_count + 1
- }));
- }
+ // 更新粉丝列表中的关注状态
+ setFollowers(prevFollowers =>
+ prevFollowers.map(follower =>
+ follower.id === followeeId
+ ? { ...follower, is_following: true }
+ : follower
+ )
+ );
+
+ // 更新当前用户的关注数
+ if (currentUser) {
+ setCurrentUser(prev => ({
+ ...prev,
+ following_count: prev.following_count + 1
+ }));
+ }
+
+ // 如果被关注的用户是当前用户的粉丝,更新粉丝数
+ setFollowers(prevFollowers =>
+ prevFollowers.map(follower =>
+ follower.id === followeeId
+ ? {
+ ...follower,
+ followers_count: follower.followers_count + 1
+ }
+ : follower
+ )
+ );
+
} catch (error) {
console.error('关注操作失败:', error);
@@ -333,13 +359,27 @@
}
};
- const handleUnfollow = async (followeeId, e) => {
- e.stopPropagation(); // 阻止事件冒泡
+ const handleUnfollow = async (userId,followeeId, e) => {
+ // e.stopPropagation(); // 阻止事件冒泡
try {
- await unfollowUserApi(followeeId);
+ await unfollowUserApi(userId,followeeId);
showSnackbar('已取消关注');
+ // 更新关注列表 - 移除取消关注的用户
+ setFollowing(prevFollowing =>
+ prevFollowing.filter(user => user.id !== followeeId)
+ );
+
+ // 更新粉丝列表 - 更新关注状态
+ setFollowers(prevFollowers =>
+ prevFollowers.map(follower =>
+ follower.id === followeeId
+ ? { ...follower, is_following: false }
+ : follower
+ )
+ );
+
// 更新当前用户关注数
if (currentUser) {
setCurrentUser(prev => ({
@@ -616,52 +656,32 @@
}}
>
<Tab icon={isMobile ? <Collections /> : null} label="笔记" />
+ <Tab icon={isMobile ? <Bookmark /> : null} label="收藏" />
+ <Tab icon={isMobile ? <Group /> : null} label="关注" />
+ <Tab icon={isMobile ? <People /> : null} label="粉丝" />
</Tabs>
</Box>
{/* 内容区域 */}
<Box sx={{ mt: 3 }}>
- {/* 只保留笔记标签页 */}
- <Grid container spacing={3}>
- {tabLoading ? (
- <Grid item xs={12} sx={{ display: 'flex', justifyContent: 'center', py: 4 }}>
- <CircularProgress />
- </Grid>
- ) : allPosts.length === 0 ? (
- <Grid item xs={12}>
- <Box sx={{
- display: 'flex',
- flexDirection: 'column',
- alignItems: 'center',
- py: 8,
- textAlign: 'center'
- }}>
- <Collections sx={{ fontSize: 60, color: 'grey.300', mb: 2 }} />
- <Typography variant="h6" sx={{ mb: 1 }}>
- 还没有发布笔记
- </Typography>
- <Typography variant="body1" color="textSecondary" sx={{ mb: 3 }}>
- {isOwnProfile ? '分享你的生活点滴吧~' : '该用户还没有发布任何笔记'}
- </Typography>
- {isOwnProfile && (
- <Button variant="contained" color="primary">
- 发布第一篇笔记
- </Button>
- )}
- </Box>
- </Grid>
- ) : (
- // 显示当前页的帖子
- posts.map((post, index) => (
- <Grid item xs={12} sm={6} lg={3} key={post.id}>
- <Card elevation={0} sx={{
- bgcolor: 'white',
- borderRadius: 3,
- height: '100%',
- display: 'flex',
- flexDirection: 'column'
- }}>
- {/* 只有当帖子有 media_urls 时才显示图片 */}
+
+ {activeTab === 0 && (
+ <Grid container spacing={3}>
+ {tabLoading ? (
+ <Grid item xs={12} sx={{ display: 'flex', justifyContent: 'center', py: 4 }}>
+ <CircularProgress />
+ </Grid>
+ ) : posts.length > 0 ? (
+ posts.map((post, index) => (
+ <Grid item xs={12} sm={6} lg={3} key={post.id}>
+ <Card elevation={0} sx={{
+ bgcolor: 'white',
+ borderRadius: 3,
+ height: '100%',
+ display: 'flex',
+ flexDirection: 'column'
+ }}>
+ {/* 只有当帖子有 media_urls 时才显示图片 */}
{post.media_urls && post.media_urls.length > 0 && (
<CardMedia
component="img"
@@ -670,63 +690,322 @@
alt={post.title}
/>
)}
- <CardContent sx={{ flexGrow: 1 }}>
- <Typography gutterBottom variant="h6" component="div">
- {post.title}
- </Typography>
- <Typography variant="body2" color="text.secondary">
- {post.content ? post.content.substring(0, 60) + '...' : '暂无内容'}
- </Typography>
- </CardContent>
- <CardActions sx={{ justifyContent: 'space-between', px: 2, pb: 2 }}>
- <Box>
- <IconButton aria-label="add to favorites">
- <Favorite />
- <Typography variant="body2" sx={{ ml: 1 }}>
- {post.heat || Math.floor(Math.random() * 1000) + 1000}
- </Typography>
- </IconButton>
- <IconButton aria-label="share">
- <Share />
- </IconButton>
- </Box>
- <Chip
- label={post.type === 'image' ? '图文' : post.type === 'video' ? '视频' : '文档'}
- size="small"
- color="primary"
- variant="outlined"
- />
- </CardActions>
- </Card>
+ <CardContent sx={{ flexGrow: 1 }}>
+ <Typography gutterBottom variant="h6" component="div">
+ {post.title}
+ </Typography>
+ <Typography variant="body2" color="text.secondary">
+ {post.content.substring(0, 60)}...
+ </Typography>
+ </CardContent>
+ <CardActions sx={{ justifyContent: 'space-between', px: 2, pb: 2 }}>
+ <Box>
+ <IconButton aria-label="add to favorites">
+ <Favorite />
+ <Typography variant="body2" sx={{ ml: 1 }}>
+ {post.heat || Math.floor(Math.random() * 1000) + 1000}
+ </Typography>
+ </IconButton>
+ <IconButton aria-label="share">
+ <Share />
+ </IconButton>
+ </Box>
+ <Chip
+ label={post.type === 'image' ? '图文' : post.type === 'video' ? '视频' : '文档'}
+ size="small"
+ color="primary"
+ variant="outlined"
+ />
+ </CardActions>
+ </Card>
+ </Grid>
+ ))
+ ) : (
+ <Grid item xs={12}>
+ <Box sx={{
+ display: 'flex',
+ flexDirection: 'column',
+ alignItems: 'center',
+ py: 8,
+ textAlign: 'center'
+ }}>
+ <Collections sx={{ fontSize: 60, color: 'grey.300', mb: 2 }} />
+ <Typography variant="h6" sx={{ mb: 1 }}>
+ 还没有发布笔记
+ </Typography>
+ <Typography variant="body1" color="textSecondary" sx={{ mb: 3 }}>
+ {isOwnProfile ? '分享你的生活点滴吧~' : '该用户还没有发布任何笔记'}
+ </Typography>
+ {isOwnProfile && (
+ <Button variant="contained" color="primary">
+ 发布第一篇笔记
+ </Button>
+ )}
+ </Box>
</Grid>
- ))
- )}
-
- {/* 分页组件 */}
- {allPosts.length > postsPerPage && (
- <Grid item xs={12}>
- <Stack spacing={2} alignItems="center" sx={{ mt: 4 }}>
- <Typography variant="body2" color="textSecondary">
- 共 {allPosts.length} 篇笔记,第 {currentPage} 页,共 {totalPages} 页
- </Typography>
- <Pagination
- count={totalPages}
- page={currentPage}
- onChange={(event, page) => handlePageChange(page)}
- color="primary"
- size={isMobile ? "small" : "medium"}
- showFirstButton
- showLastButton
- sx={{
- '& .MuiPaginationItem-root': {
- borderRadius: 2,
+ )}
+
+ {posts.length > 0 && (
+ <Grid item xs={12}>
+ <Box sx={{ display: 'flex', justifyContent: 'center', mt: 3 }}>
+ <Button
+ variant="outlined"
+ sx={{
+ borderRadius: 20,
+ px: 4,
+ display: 'flex',
+ alignItems: 'center'
+ }}
+ >
+ <ChevronLeft sx={{ mr: 1 }} />
+ 上一页
+ <ChevronRight sx={{ ml: 2 }} />
+ </Button>
+ </Box>
+ </Grid>
+ )}
+ </Grid>
+ )}
+
+ {activeTab === 1 && (
+ <Grid container spacing={3}>
+ {tabLoading ? (
+ <Grid item xs={12} sx={{ display: 'flex', justifyContent: 'center', py: 4 }}>
+ <CircularProgress />
+ </Grid>
+ ) : favorites.length > 0 ? (
+ favorites.map((favorite) => (
+ <Grid item xs={12} sm={6} md={4} lg={3} key={favorite.id}>
+ <Card elevation={0} sx={{
+ bgcolor: 'white',
+ borderRadius: 3,
+ transition: 'transform 0.3s, box-shadow 0.3s',
+ '&:hover': {
+ transform: 'translateY(-5px)',
+ boxShadow: 3
}
- }}
- />
- </Stack>
- </Grid>
- )}
- </Grid>
+ }}>
+ <Box sx={{
+ height: 160,
+ position: 'relative',
+ borderTopLeftRadius: 16,
+ borderTopRightRadius: 16,
+ overflow: 'hidden'
+ }}>
+ <CardMedia
+ component="img"
+ height="160"
+ image={`https://source.unsplash.com/random/400x300?${favorite.id}`}
+ alt={favorite.title}
+ />
+ <Box sx={{
+ position: 'absolute',
+ top: 8,
+ right: 8,
+ bgcolor: 'rgba(0,0,0,0.6)',
+ color: 'white',
+ px: 1,
+ py: 0.5,
+ borderRadius: 4,
+ fontSize: 12
+ }}>
+ {favorite.type === 'image' ? '图文' : favorite.type === 'video' ? '视频' : '文档'}
+ </Box>
+ </Box>
+ <CardContent>
+ <Typography gutterBottom variant="subtitle1" fontWeight="medium">
+ {favorite.title}
+ </Typography>
+ <Box sx={{ display: 'flex', justifyContent: 'space-between' }}>
+ <Box sx={{ display: 'flex', alignItems: 'center' }}>
+ <Favorite fontSize="small" color="error" />
+ <Typography variant="body2" sx={{ ml: 0.5 }}>
+ {favorite.heat || Math.floor(Math.random() * 1000) + 1000}
+ </Typography>
+ </Box>
+ <Box sx={{ display: 'flex', alignItems: 'center' }}>
+ <Bookmark fontSize="small" color="primary" />
+ <Typography variant="body2" sx={{ ml: 0.5 }}>
+ {Math.floor(Math.random() * 500) + 100}
+ </Typography>
+ </Box>
+ </Box>
+ </CardContent>
+ </Card>
+ </Grid>
+ ))
+ ) : (
+ <Grid item xs={12}>
+ <Box sx={{
+ display: 'flex',
+ flexDirection: 'column',
+ alignItems: 'center',
+ py: 8,
+ textAlign: 'center'
+ }}>
+ <Bookmark sx={{ fontSize: 60, color: 'grey.300', mb: 2 }} />
+ <Typography variant="h6" sx={{ mb: 1 }}>
+ {isOwnProfile ? '你还没有收藏内容' : '该用户没有收藏内容'}
+ </Typography>
+ <Typography variant="body1" color="textSecondary">
+ {isOwnProfile ? '看到喜欢的笔记可以收藏起来哦~' : ''}
+ </Typography>
+ </Box>
+ </Grid>
+ )}
+ </Grid>
+ )}
+
+ {activeTab === 2 && (
+ <Grid container spacing={3}>
+ {tabLoading ? (
+ <Grid item xs={12} sx={{ display: 'flex', justifyContent: 'center', py: 4 }}>
+ <CircularProgress />
+ </Grid>
+ ) : following.length > 0 ? (
+ following.map((follow) => (
+ <Grid item xs={12} sm={6} md={4} key={follow.id}>
+ <Paper
+ elevation={0}
+ sx={{
+ bgcolor: 'white',
+ borderRadius: 3,
+ p: 2,
+ display: 'flex',
+ alignItems: 'center',
+ cursor: 'pointer',
+ '&:hover': {
+ boxShadow: 1
+ }
+ }}
+ onClick={() => navigateToUserProfile(follow.id)}
+ >
+ <Avatar
+ src={follow.avatar || 'https://randomuser.me/api/portraits/men/22.jpg'}
+ sx={{ width: 60, height: 60 }}
+ />
+ <Box sx={{ ml: 2, flexGrow: 1 }}>
+ <Typography fontWeight="medium">{follow.username}</Typography>
+ <Typography variant="body2" color="textSecondary">
+ {follow.followers_count || Math.floor(Math.random() * 100) + 10} 粉丝
+ </Typography>
+ </Box>
+ {isOwnProfile && (
+ <Button
+ variant="outlined"
+ size="small"
+ sx={{ borderRadius: 20 }}
+ onClick={(e) => {
+ e.stopPropagation();
+ handleUnfollow(userId,follow.id);
+ }}
+ >
+ 已关注
+ </Button>
+ )}
+ </Paper>
+ </Grid>
+ ))
+ ) : (
+ <Grid item xs={12}>
+ <Box sx={{
+ display: 'flex',
+ flexDirection: 'column',
+ alignItems: 'center',
+ py: 8,
+ textAlign: 'center'
+ }}>
+ <Group sx={{ fontSize: 60, color: 'grey.300', mb: 2 }} />
+ <Typography variant="h6" sx={{ mb: 1 }}>
+ {isOwnProfile ? '你还没有关注任何人' : '该用户还没有关注任何人'}
+ </Typography>
+ <Typography variant="body1" color="textSecondary">
+ {isOwnProfile ? '发现有趣的人并关注他们吧~' : ''}
+ </Typography>
+ </Box>
+ </Grid>
+ )}
+ </Grid>
+ )}
+ {activeTab === 3 && (
+ <Grid container spacing={3}>
+ {tabLoading ? (
+ <Grid item xs={12} sx={{ display: 'flex', justifyContent: 'center', py: 4 }}>
+ <CircularProgress />
+ </Grid>
+ ) : followers.length > 0 ? (
+ followers.map((follower) => (
+ <Grid item xs={12} sm={6} md={4} key={follower.id}>
+ <Paper
+ elevation={0}
+ sx={{
+ bgcolor: 'white',
+ borderRadius: 3,
+ p: 2,
+ display: 'flex',
+ alignItems: 'center',
+ cursor: 'pointer',
+ '&:hover': {
+ boxShadow: 1
+ }
+ }}
+ onClick={() => navigateToUserProfile(follower.id)}
+ >
+ <Avatar
+ src={follower.avatar || 'https://randomuser.me/api/portraits/men/22.jpg'}
+ sx={{ width: 60, height: 60 }}
+ />
+ <Box sx={{ ml: 2, flexGrow: 1 }}>
+ <Typography fontWeight="medium">{follower.username}</Typography>
+ <Typography variant="body2" color="textSecondary">
+ {follower.bio || '暂无简介'}
+ </Typography>
+ <Typography variant="body2" color="textSecondary">
+ {follower.followers_count} 粉丝
+ </Typography>
+ </Box>
+ {currentUser && currentUser.id !== follower.id && (
+ <Button
+ variant={follower.is_following ? "outlined" : "contained"}
+ color="primary"
+ size="small"
+ sx={{ borderRadius: 20 }}
+ onClick={(e) => {
+ e.stopPropagation();
+ if (follower.is_following) {
+ handleUnfollow(userId,follower.id);
+ } else {
+ handleFollowUser(userId,follower.id);
+ }
+ }}
+ >
+ {follower.is_following ? '已关注' : '关注'}
+ </Button>
+ )}
+ </Paper>
+ </Grid>
+ ))
+ ) : (
+ <Grid item xs={12}>
+ <Box sx={{
+ display: 'flex',
+ flexDirection: 'column',
+ alignItems: 'center',
+ py: 8,
+ textAlign: 'center'
+ }}>
+ <People sx={{ fontSize: 60, color: 'grey.300', mb: 2 }} />
+ <Typography variant="h6" sx={{ mb: 1 }}>
+ {isOwnProfile ? '你还没有粉丝' : '该用户还没有粉丝'}
+ </Typography>
+ <Typography variant="body1" color="textSecondary">
+ {isOwnProfile ? '分享更多内容来吸引粉丝吧~' : ''}
+ </Typography>
+ </Box>
+ </Grid>
+ )}
+ </Grid>
+ )}
</Box>
</Container>