import { useState, useEffect } from "react"; import { View, Text, ScrollView, Image, Input } from "@tarojs/components"; import { withAuth, EmptyState, GeneralNavbar } from "@/components"; import { useGlobalState } from "@/store/global"; import commentService, { CommentActivity } from "@/services/commentService"; import messageService from "@/services/messageService"; import { formatShortRelativeTime } from "@/utils/timeUtils"; import Taro from "@tarojs/taro"; import "./index.scss"; // 评论/回复类型定义 interface CommentReplyItem { id: number; user_id: number; user_avatar: string; user_nickname: string; action_type: "comment" | "reply"; // 评论了你的球局 / 回复了你的评论 time: string; content: string; original_comment?: string; // 被回复的评论内容 activity_image: string; activity_id: number; activity_title: string; parent_id: number | null; // 父评论ID,用于回复 game_id: number; // 球局ID } const CommentReply = () => { const [commentList, setCommentList] = useState([]); const [loading, setLoading] = useState(false); const [refreshing, setRefreshing] = useState(false); const [showReplyInput, setShowReplyInput] = useState(false); const [replyTarget, setReplyTarget] = useState(null); const [replyContent, setReplyContent] = useState(""); const [inputFocus, setInputFocus] = useState(false); const { statusNavbarHeightInfo } = useGlobalState() || {}; const { totalHeight = 98 } = statusNavbarHeightInfo || {}; useEffect(() => { getCommentReplyList(); }, []); // 获取评论和回复列表 const getCommentReplyList = async () => { if (loading) return; setLoading(true); try { const res = await commentService.getMyActivities({ page: 1, pageSize: 20, }); if (res.code === 0 && res.data) { // 映射数据 const mappedList = res.data.rows.map((item: CommentActivity) => ({ id: item.id, user_id: item.user?.id || 0, user_avatar: item.user?.avatar_url || "", user_nickname: item.user?.nickname || "匿名用户", action_type: item.type === "reply" ? "reply" as const : "comment" as const, time: item.create_time, content: item.content || "", original_comment: item.parent_comment?.content || "", activity_image: item.game?.image || "", activity_id: item.game_id, activity_title: item.game?.title || "", parent_id: item.parent_id, game_id: item.game_id, })); setCommentList(mappedList); // 获取所有评论ID列表并标记已读(传入所有ID,包括已读和未读) const allCommentIds = res.data.rows.map((item: any) => item.id); if (allCommentIds.length > 0) { // 使用统一接口标记已读,传入所有评论ID messageService.markAsRead('comment', allCommentIds).catch(e => { console.warn("标记评论已读失败:", e); }); } } } catch (e) { Taro.showToast({ title: "获取列表失败", icon: "none", duration: 2000, }); } finally { setLoading(false); } }; // 处理回复 const handleReply = (e: any, item: CommentReplyItem) => { e.stopPropagation(); // 阻止事件冒泡 setReplyTarget(item); setShowReplyInput(true); setInputFocus(true); setReplyContent(""); }; // 处理点击评论项(跳转到球局详情) const handleCommentClick = (item: CommentReplyItem) => { Taro.navigateTo({ url: `/game_pages/detail/index?id=${item.activity_id}&message_id=${item.id}`, }); }; // 处理点击用户(跳转到个人页) const handleUserClick = (e: any, userId: number) => { e.stopPropagation(); // 阻止事件冒泡 Taro.navigateTo({ url: `/user_pages/other/index?userid=${userId}`, }); }; // 发送回复 const handleSendReply = async () => { if (!replyContent.trim() || !replyTarget) { Taro.showToast({ title: "请输入回复内容", icon: "none", duration: 2000, }); return; } try { // 调用回复接口 // 如果是回复评论,parent_id 使用评论的 parent_id 或 id // 如果是顶级评论,parent_id 为 null,则使用评论的 id 作为 parent_id const parentId = replyTarget.parent_id || replyTarget.id; const res = await commentService.replyComment( parentId, replyTarget.user_id, replyContent.trim() ); if (res.code === 0) { Taro.showToast({ title: "回复成功", icon: "success", duration: 2000, }); // 关闭输入框 setShowReplyInput(false); setReplyTarget(null); setReplyContent(""); setInputFocus(false); // 刷新列表 getCommentReplyList(); } else { throw new Error(res.message || "回复失败"); } } catch (e: any) { Taro.showToast({ title: e.message || "回复失败", icon: "none", duration: 2000, }); } }; // 取消回复 const handleCancelReply = () => { setShowReplyInput(false); setReplyTarget(null); setReplyContent(""); setInputFocus(false); }; // 输入框失去焦点 const handleInputBlur = () => { // 延迟执行,避免点击发送按钮时输入框先失焦导致发送失败 setTimeout(() => { handleCancelReply(); }, 200); }; // 处理返回 const handleBack = () => { Taro.navigateBack(); }; // 处理下拉刷新 const handleRefresh = async () => { setRefreshing(true); try { const res = await commentService.getMyActivities({ page: 1, pageSize: 20, }); if (res.code === 0 && res.data) { const mappedList = res.data.rows.map((item: CommentActivity) => ({ id: item.id, user_id: item.user?.id || 0, user_avatar: item.user?.avatar_url || "", user_nickname: item.user?.nickname || "匿名用户", action_type: item.type === "reply" ? "reply" as const : "comment" as const, time: item.create_time, content: item.content || "", original_comment: item.parent_comment?.content || "", activity_image: item.game?.image || "", activity_id: item.game_id, activity_title: item.game?.title || "", parent_id: item.parent_id, game_id: item.game_id, })); setCommentList(mappedList); // 获取所有评论ID列表并标记已读(传入所有ID) const allCommentIds = res.data.rows.map((item: any) => item.id); if (allCommentIds.length > 0) { messageService.markAsRead('comment', allCommentIds).catch(e => { console.warn("标记评论已读失败:", e); }); } } } catch (e) { Taro.showToast({ title: "刷新失败", icon: "none", duration: 2000, }); } finally { setRefreshing(false); } }; // 渲染评论/回复项 const renderCommentItem = (item: CommentReplyItem) => { const actionText = item.action_type === "comment" ? "评论了你的球局" : "回复了你的评论"; return ( handleCommentClick(item)} > handleUserClick(e, item.user_id)} /> handleUserClick(e, item.user_id)} > {item.user_nickname} {actionText} {formatShortRelativeTime(item.time)} {item.content} {/* 如果是回复,显示被回复的评论 */} {item.action_type === "reply" && item.original_comment && ( {item.original_comment} )} {/* 回复按钮 */} handleReply(e, item)}> 回复 {/* 右侧球局图片 */} { e.stopPropagation(); Taro.navigateTo({ url: `/game_pages/detail/index?id=${item.game_id}`, }); }} /> ); }; return ( {/* 顶部导航栏 */} {/* 评论列表 */} {commentList.length > 0 ? ( {commentList.map(renderCommentItem)} {/* 到底了提示 */} 到底了 ) : ( )} {/* 回复输入框 */} <> {/* 遮罩层 */} setReplyContent(e.detail.value)} onBlur={handleInputBlur} confirmType="send" onConfirm={handleSendReply} /> ); }; export default withAuth(CommentReply);