修改好友动态、兴趣小组
Change-Id: I8dc8f304f9ac9c968e316bc997b2aeb58b26fe48
diff --git a/src/pages/FriendMoments/FriendMoments.jsx b/src/pages/FriendMoments/FriendMoments.jsx
index fd017b2..526c06d 100644
--- a/src/pages/FriendMoments/FriendMoments.jsx
+++ b/src/pages/FriendMoments/FriendMoments.jsx
@@ -1,32 +1,76 @@
-import React, { useState, useEffect } from 'react';
+import React, { useContext, useState, useEffect } from 'react';
import axios from 'axios';
import './FriendMoments.css';
import Header from '../../components/Header';
-import { Edit } from '@icon-park/react';
+import { Edit, GoodTwo, Comment } from '@icon-park/react';
+import { UserContext } from '../../context/UserContext'; // 引入用户上下文
const FriendMoments = () => {
const [feeds, setFeeds] = useState([]);
const [filteredFeeds, setFilteredFeeds] = useState([]);
const [query, setQuery] = useState('');
- const [userId, setUserId] = useState(456); // 从状态管理或登录信息获取
+ const [loading, setLoading] = useState(true);
+ const [error, setError] = useState(null);
+ const [commentBoxVisibleId, setCommentBoxVisibleId] = useState(null); // 当前显示评论框的动态ID
+ const [commentInput, setCommentInput] = useState(''); // 当前输入的评论内容
+
+ // 从上下文中获取用户信息
+ const { user } = useContext(UserContext);
+ const userId = user?.userId || null; // 从用户上下文中获取userId
+ const username = user?.username || '未知用户'; // 获取用户名
// Modal state & form fields
const [showModal, setShowModal] = useState(false);
const [title, setTitle] = useState('');
const [content, setContent] = useState('');
const [selectedImages, setSelectedImages] = useState([]);
- const [previewUrls, setPreviewUrls] = useState([]); // 新增:图片预览URLs
+ const [previewUrls, setPreviewUrls] = useState([]);
+
+ // 检查用户是否已登录
+ const isLoggedIn = !!userId;
// 拉取好友动态列表
const fetchFeeds = async () => {
+ if (!isLoggedIn) {
+ setLoading(false);
+ setError('请先登录');
+ return;
+ }
+
+ setLoading(true);
+ setError(null);
try {
- // 修改为新的API路径
+ // 注意这里修改了API路径,使用getAllDynamics接口
const res = await axios.get(`/echo/dynamic/${userId}/getAllDynamics`);
- setFeeds(res.data.posts || []);
- setFilteredFeeds(res.data.posts || []);
+
+ // 检查API返回的数据结构
+ console.log('API响应数据:', res.data);
+
+ // 从响应中提取dynamic数组
+ const dynamicList = res.data.dynamic || [];
+
+ // 将API返回的数据结构转换为前端期望的格式
+ const formattedFeeds = dynamicList.map(item => ({
+ postNo: item.dynamic_id, // 使用API返回的dynamic_id作为帖子ID
+ title: item.title,
+ postContent: item.content,
+ imageUrl: item.images, // 使用API返回的images字段
+ postTime: item.time, // 使用API返回的time字段
+ postLikeNum: item.likes?.length || 0, // 点赞数
+ liked: item.likes?.some(like => like.user_id === userId), // 当前用户是否已点赞
+ user_id: item.user_id, // 发布者ID
+ username: item.username, // 发布者昵称
+ avatar_url: item.avatar_url, // 发布者头像
+ comments: item.comments || [] // 评论列表
+ }));
+
+ setFeeds(formattedFeeds);
+ setFilteredFeeds(formattedFeeds);
} catch (err) {
console.error('获取动态列表失败:', err);
- alert('获取动态列表失败,请稍后重试');
+ setError('获取动态列表失败,请稍后重试');
+ } finally {
+ setLoading(false);
}
};
@@ -59,23 +103,27 @@
const files = Array.from(e.target.files);
if (!files.length) return;
- // 生成预览URLs
const previewUrls = files.map(file => URL.createObjectURL(file));
setSelectedImages(files);
- setPreviewUrls(previewUrls); // 更新预览URLs
+ setPreviewUrls(previewUrls);
};
// 对话框内:提交新动态
const handleSubmit = async () => {
+ if (!isLoggedIn) {
+ alert('请先登录');
+ return;
+ }
+
if (!content.trim()) {
alert('内容不能为空');
return;
}
try {
+ // 使用formData格式提交
const formData = new FormData();
- formData.append('user_id', userId);
formData.append('title', title.trim() || '');
formData.append('content', content.trim());
@@ -84,7 +132,7 @@
formData.append('image_url', file);
});
- // 修改为新的API路径
+ // 调用创建动态API
await axios.post(`/echo/dynamic/${userId}/createDynamic`, formData, {
headers: {
'Content-Type': 'multipart/form-data'
@@ -95,7 +143,7 @@
setTitle('');
setContent('');
setSelectedImages([]);
- setPreviewUrls([]); // 重置预览URLs
+ setPreviewUrls([]);
setShowModal(false);
fetchFeeds();
alert('发布成功');
@@ -105,11 +153,17 @@
}
};
- // 删除动态
+ // 删除动态 - 注意:API文档中未提供删除接口,这里保留原代码
const handleDelete = async (dynamicId) => {
+
+ if (!isLoggedIn) {
+ alert('请先登录');
+ return;
+ }
+
if (!window.confirm('确定要删除这条动态吗?')) return;
try {
- // 修改为新的API路径
+ // 注意:API文档中未提供删除接口,这里使用原代码中的路径
await axios.delete(`/echo/dynamic/me/deleteDynamic/${dynamicId}`);
fetchFeeds();
alert('删除成功');
@@ -120,64 +174,224 @@
};
// 点赞动态
- const handleLike = async (dynamicId) => {
+ const handleLike = async (dynamicId,islike) => {
+ if (islike) {
+ handleUnlike(dynamicId);
+ return
+ }
+ if (!isLoggedIn) {
+ alert('请先登录');
+ return;
+ }
+
+ // 验证dynamicId是否有效
+ if (!dynamicId) {
+ console.error('无效的dynamicId:', dynamicId);
+ alert('点赞失败:动态ID无效');
+ return;
+ }
+
+ // 检查是否已经点赞,防止重复请求
+ // const currentFeed = feeds.find(feed => feed.postNo === dynamicId);
+ // if (currentFeed && currentFeed.liked) {
+ // console.warn('尝试重复点赞,已忽略');
+ // return;
+ // }
+
+ console.log('当前用户ID:', userId);
+ console.log('即将点赞的动态ID:', dynamicId);
+
try {
- // 调用新的点赞API
- const res = await axios.post(`/echo/dynamic/like`, {
- userId,
- dynamicId
+ // 确保参数是整数类型
+ const requestData = {
+ userId: parseInt(userId),
+ dynamicId: parseInt(dynamicId)
+ };
+
+ // 验证参数是否为有效数字
+ if (isNaN(requestData.userId) || isNaN(requestData.dynamicId)) {
+ console.error('无效的参数:', requestData);
+ alert('点赞失败:参数格式错误');
+ return;
+ }
+
+ console.log('点赞请求数据:', requestData);
+
+ const res = await axios.post(`/echo/dynamic/like`, requestData, {
+ headers: {
+ 'Content-Type': 'application/json' // 明确指定JSON格式
+ }
});
+ console.log('点赞API响应:', res.data);
+
if (res.status === 200) {
// 更新本地状态
- setFeeds(feeds.map(feed => {
+ feeds.forEach(feed => {
if (feed.postNo === dynamicId) {
- return {
- ...feed,
- postLikeNum: (feed.postLikeNum || 0) + 1,
- liked: true
- };
+ feed.postLikeNum = (feed.postLikeNum || 0) + 1;
+ feed.liked = true;
}
- return feed;
- }));
+ });
+ setFeeds([...feeds]); // 更新状态以触发重新渲染
} else {
alert(res.data.message || '点赞失败');
}
} catch (err) {
console.error('点赞失败', err);
+
+ // 检查错误响应,获取更详细的错误信息
+ if (err.response) {
+ console.error('错误响应数据:', err.response.data);
+ console.error('错误响应状态:', err.response.status);
+ console.error('错误响应头:', err.response.headers);
+ }
+
alert('点赞失败,请稍后重试');
}
};
// 取消点赞
const handleUnlike = async (dynamicId) => {
+ if (!isLoggedIn) {
+ alert('请先登录');
+ return;
+ }
+
+ // 验证dynamicId是否有效
+ if (!dynamicId) {
+ console.error('无效的dynamicId:', dynamicId);
+ alert('取消点赞失败:动态ID无效');
+ return;
+ }
+
+ // 检查是否已经取消点赞,防止重复请求
+ const currentFeed = feeds.find(feed => feed.postNo === dynamicId);
+ if (currentFeed && !currentFeed.liked) {
+ console.warn('尝试重复取消点赞,已忽略');
+ return;
+ }
+
try {
- // 调用新的取消点赞API
+ // 确保参数是整数类型
+ const requestData = {
+ userId: parseInt(userId),
+ dynamicId: parseInt(dynamicId)
+ };
+
+ // 验证参数是否为有效数字
+ if (isNaN(requestData.userId) || isNaN(requestData.dynamicId)) {
+ console.error('无效的参数:', requestData);
+ alert('取消点赞失败:参数格式错误');
+ return;
+ }
+
+ console.log('取消点赞请求数据:', requestData);
+
const res = await axios.delete(`/echo/dynamic/unlike`, {
- data: { userId, dynamicId }
+ headers: {
+ 'Content-Type': 'application/json' // 明确指定JSON格式
+ },
+ data: requestData // 将参数放在data属性中
});
+ console.log('取消点赞API响应:', res.data);
+
if (res.status === 200) {
// 更新本地状态
- setFeeds(feeds.map(feed => {
+ feeds.forEach(feed => {
if (feed.postNo === dynamicId) {
- return {
- ...feed,
- postLikeNum: Math.max(0, (feed.postLikeNum || 0) - 1),
- liked: false
- };
+ feed.postLikeNum = Math.max(0, (feed.postLikeNum || 0) - 1);
+ feed.liked = false;
}
- return feed;
- }));
+ });
+ setFeeds([...feeds]); // 更新状态以触发重新渲染
} else {
alert(res.data.message || '取消点赞失败');
}
} catch (err) {
console.error('取消点赞失败', err);
+
+ // 检查错误响应,获取更详细的错误信息
+ if (err.response) {
+ console.error('错误响应数据:', err.response.data);
+ console.error('错误响应状态:', err.response.status);
+ console.error('错误响应头:', err.response.headers);
+ }
+
alert('取消点赞失败,请稍后重试');
}
};
+ // 评论好友动态
+ // 评论好友动态
+const handleComment = async (dynamicId) => {
+ if (!isLoggedIn) {
+ alert('请先登录');
+ return;
+ }
+
+ if (!commentInput.trim()) {
+ alert('评论内容不能为空');
+ return;
+ }
+
+ try {
+ const res = await axios.post(`/echo/dynamic/${userId}/feeds/${dynamicId}/comments`, {
+ content: commentInput.trim()
+ });
+
+ if (res.status === 200 || res.status === 201) {
+ // 成功获取评论数据
+ const newComment = {
+ user_id: userId,
+ username: username,
+ content: commentInput.trim(),
+ time: new Date().toISOString() // 使用当前时间作为评论时间
+ };
+
+ // 更新本地状态,添加新评论
+ setFeeds(prevFeeds => {
+ return prevFeeds.map(feed => {
+ if (feed.postNo === dynamicId) {
+ // 确保comments是数组,并且正确合并新评论
+ const currentComments = Array.isArray(feed.comments) ? feed.comments : [];
+ return {
+ ...feed,
+ comments: [...currentComments, newComment]
+ };
+ }
+ return feed;
+ });
+ });
+
+ // 更新过滤后的动态列表
+ setFilteredFeeds(prevFeeds => {
+ return prevFeeds.map(feed => {
+ if (feed.postNo === dynamicId) {
+ // 确保comments是数组,并且正确合并新评论
+ const currentComments = Array.isArray(feed.comments) ? feed.comments : [];
+ return {
+ ...feed,
+ comments: [...currentComments, newComment]
+ };
+ }
+ return feed;
+ });
+ });
+
+ // alert('评论成功');
+ setCommentInput('');
+ setCommentBoxVisibleId(null); // 关闭评论框
+ } else {
+ alert(res.data.error || '评论失败');
+ }
+ } catch (err) {
+ console.error('评论失败', err);
+ alert('评论失败,请稍后重试');
+ }
+};
+
return (
<div className="friend-moments-container">
<Header />
@@ -186,7 +400,7 @@
<Edit theme="outline" size="18" style={{ marginRight: '6px' }} />
创建动态
</button>
- <div className="f-search-bar">
+ {/* <div className="f-search-bar">
<input
className="search-input"
type="text"
@@ -196,46 +410,115 @@
/>
<button className="search-btn" onClick={handleSearch}>搜索</button>
<button className="search-btn" onClick={handleReset}>重置</button>
- </div>
+ </div> */}
</div>
<div className="feed-list">
- {filteredFeeds.map(feed => (
- <div className="feed-item" key={feed.postNo}>
- {feed.title && <h4>{feed.title}</h4>}
- <p>{feed.postContent}</p>
-
- {feed.imageUrl && feed.imageUrl.split(',').length > 0 && (
- <div className="feed-images">
- {feed.imageUrl.split(',').map((url, i) => (
- <img key={i} src={url} alt={`动态图${i}`} />
- ))}
- </div>
- )}
-
- <div className="feed-footer">
- <span className="feed-date">
- {new Date(feed.postTime).toLocaleString()}
- </span>
- <div className="like-container">
- {feed.liked ? (
- <button className="unlike-btn" onClick={() => handleUnlike(feed.postNo)}>
- 已点赞 ({feed.postLikeNum || 0})
- </button>
- ) : (
- <button className="like-btn" onClick={() => handleLike(feed.postNo)}>
- 点赞 ({feed.postLikeNum || 0})
- </button>
- )}
- </div>
- {feed.user_id === userId && (
- <button className="delete-btn" onClick={() => handleDelete(feed.postNo)}>
- 删除
- </button>
- )}
- </div>
+ {loading ? (
+ <div className="loading-message">加载中...</div>
+ ) : error ? (
+ <div className="error-message">{error}</div>
+ ) : !isLoggedIn ? (
+ <div className="login-prompt">
+ <p>请先登录查看好友动态</p>
</div>
- ))}
+ ) : filteredFeeds.length === 0 ? (
+ <div className="empty-message">暂无动态</div>
+ ) : (
+ filteredFeeds.map(feed => (
+ <div className="feed-item" key={feed.postNo || `feed-${Math.random()}`}>
+ {/* 显示发布者信息 */}
+ <div className="feed-author">
+ <img src={feed.avatar_url || 'https://example.com/default-avatar.jpg'} alt={feed.username || '用户头像'} />
+ <div>
+ <h4>{feed.username || '未知用户'}</h4>
+ <span className="feed-date">{new Date(feed.postTime || Date.now()).toLocaleString()}</span>
+ </div>
+ </div>
+
+ {feed.title && <h4 className="feed-title">{feed.title}</h4>}
+ <p className="feed-content">{feed.postContent || '无内容'}</p>
+
+ {feed.imageUrl && (
+ <div className="feed-images">
+ {/* 处理可能是单张图片或多张图片的情况 */}
+ {typeof feed.imageUrl === 'string' ? (
+ <img src={feed.imageUrl} alt="动态图片" />
+ ) : (
+ feed.imageUrl.map((url, i) => (
+ <img key={i} src={url} alt={`动态图${i}`} />
+ ))
+ )}
+ </div>
+ )}
+
+ <div className="feed-footer">
+ <div className="like-container">
+ <button className="icon-btn" onClick={() => handleLike(feed.postNo, feed.liked, feed.user_id)}>
+ <GoodTwo theme="outline" size="24" fill={feed.liked ? '#f00' : '#fff'} />
+ <span>{feed.postLikeNum || 0}</span>
+
+ </button>
+
+ <button
+ className="icon-btn"
+ onClick={() => {
+ setCommentBoxVisibleId(feed.postNo);
+ setCommentInput('');
+ }}
+ >
+ <Comment theme="outline" size="24" fill="#333" />
+ <span>评论</span>
+ </button>
+
+ {commentBoxVisibleId === feed.postNo && (
+ <div className="comment-box">
+ <textarea
+ className="comment-input"
+ placeholder="请输入评论内容..."
+ value={commentInput}
+ onChange={(e) => setCommentInput(e.target.value)}
+ />
+ <button
+ className="submit-comment-btn"
+ onClick={() => handleComment(feed.postNo)}
+ >
+ 发布评论
+ </button>
+ </div>
+ )}
+ </div>
+ {feed.user_id === userId && (
+ <button className="delete-btn" onClick={() => handleDelete(feed.postNo)}>
+ 删除
+ </button>
+ )}
+</div>
+
+ {/* 评论列表 */}
+ {Array.isArray(feed.comments) && feed.comments.length > 0 && (
+ <div className="comments-container">
+ <h5>评论 ({feed.comments.length})</h5>
+ <div className="comments-list">
+ {feed.comments.map((comment, index) => (
+ <div className="comment-item" key={index}>
+ <div className="comment-header">
+ <span className="comment-user">{comment.username || '用户'}</span>
+ {/* <span className="comment-user-id">ID: {comment.user_id}</span> */}
+ <span className="comment-time">
+ {new Date(comment.time || Date.now()).toLocaleString()}
+ </span>
+ </div>
+ <p className="comment-content">{comment.content}</p>
+ </div>
+ ))}
+ </div>
+ </div>
+ )}
+
+ </div>
+ ))
+ )}
</div>
{/* Modal 对话框 */}
@@ -265,7 +548,7 @@
/>
</label>
<div className="cf-preview">
- {previewUrls.map((url, i) => ( // 使用定义的previewUrls
+ {previewUrls.map((url, i) => (
<img key={i} src={url} alt={`预览${i}`} />
))}
</div>
@@ -284,4 +567,4 @@
);
};
-export default FriendMoments;
\ No newline at end of file
+export default FriendMoments;
\ No newline at end of file