feat: 为评论区添加认证守卫

- 在发表评论功能中添加认证检查
- 在评论点赞功能中添加认证检查
- 未登录用户操作时显示提示并跳转到登录页
- 移除未使用的imports优化代码
This commit is contained in:
Leo 2025-10-13 21:37:07 +08:00
parent 019e53cc81
commit 153d8d40ad

View File

@ -1,16 +1,19 @@
/**
*
* API和点赞功能
*/
import React, { useState, useEffect } from 'react'
import { List, Avatar, Rate, Button, Input, message, Empty } from 'antd'
import { List, Avatar, Button, Input, message, Empty } from 'antd'
import { LikeOutlined, LikeFilled } from '@ant-design/icons'
import dayjs from 'dayjs'
import relativeTime from 'dayjs/plugin/relativeTime'
import 'dayjs/locale/zh-cn'
import type { Comment } from '@types/index'
import { getCommentsByTarget, addComment } from '@services/api'
import type { CommentItem, CommentTargetType } from '@/types'
import { getCommentList, addComment } from '@services/commentApi'
import { toggleLike } from '@services/likeApi'
import { useUserStore } from '@store/useUserStore'
import { requireAuth } from '@/utils/authGuard'
import './index.css'
dayjs.extend(relativeTime)
@ -19,43 +22,53 @@ dayjs.locale('zh-cn')
const { TextArea } = Input
interface CommentSectionProps {
targetType: 'heritage' | 'inheritor' | 'course' | 'news'
targetId: string
showRating?: boolean
targetType: CommentTargetType
targetId: string | number
}
const CommentSection: React.FC<CommentSectionProps> = ({
targetType,
targetId,
showRating = false,
}) => {
const [comments, setComments] = useState<Comment[]>([])
const [comments, setComments] = useState<CommentItem[]>([])
const [loading, setLoading] = useState(false)
const [submitting, setSubmitting] = useState(false)
const [content, setContent] = useState('')
const [rating, setRating] = useState(5)
const [total, setTotal] = useState(0)
const [currentPage, setCurrentPage] = useState(1)
const [pageSize] = useState(10)
const { user, isAuthenticated } = useUserStore()
const { isAuthenticated } = useUserStore()
useEffect(() => {
fetchComments()
}, [targetType, targetId])
}, [targetType, targetId, currentPage])
const fetchComments = async () => {
setLoading(true)
try {
const data = await getCommentsByTarget(targetType, targetId)
setComments(data)
} catch (error) {
const numericId = typeof targetId === 'string' ? parseInt(targetId) : targetId
const result = await getCommentList({
targetType,
targetId: numericId,
pageNum: currentPage,
pageSize: pageSize,
sortType: 'latest'
})
setComments(result.records)
setTotal(result.total)
} catch (error: any) {
console.error('Failed to fetch comments:', error)
message.error(error.message || '获取评论失败')
} finally {
setLoading(false)
}
}
const handleSubmit = async () => {
if (!isAuthenticated) {
message.warning('请先登录后再发表评论')
// 🔥 认证守卫:未登录时提示并跳转到登录页
if (!requireAuth('发表评论')) {
return
}
@ -66,44 +79,67 @@ const CommentSection: React.FC<CommentSectionProps> = ({
setSubmitting(true)
try {
const newComment = await addComment({
userId: user!.id,
userName: user!.nickname,
userAvatar: user!.avatar,
const numericId = typeof targetId === 'string' ? parseInt(targetId) : targetId
await addComment({
targetType,
targetId,
content,
rating: showRating ? rating : undefined,
likeCount: 0,
replyCount: 0,
targetId: numericId,
content: content.trim(),
})
setComments([newComment, ...comments])
setContent('')
setRating(5)
message.success('评论发表成功')
} catch (error) {
message.error('评论发表失败')
// 重新加载评论列表
setCurrentPage(1)
fetchComments()
} catch (error: any) {
console.error('Failed to add comment:', error)
message.error(error.message || '评论发表失败')
} finally {
setSubmitting(false)
}
}
// 处理评论点赞
const handleCommentLike = async (commentId: number, isLiked: boolean) => {
// 🔥 认证守卫:未登录时提示并跳转到登录页
if (!requireAuth('点赞评论')) {
return
}
try {
await toggleLike({
targetType: 'comment',
targetId: commentId
}, isLiked)
// 更新评论的点赞状态和数量
setComments(prevComments =>
prevComments.map(comment => {
if (comment.id === commentId) {
return {
...comment,
isLiked: !isLiked,
likeCount: isLiked ? comment.likeCount - 1 : comment.likeCount + 1
}
}
return comment
})
)
} catch (error: any) {
message.error(error.message || '操作失败')
}
}
return (
<div className="comment-section">
<div className="comment-header">
<h3> ({comments.length})</h3>
<h3> ({total})</h3>
</div>
{/* 评论输入框 */}
{isAuthenticated && (
<div className="comment-editor">
{showRating && (
<div className="comment-rating">
<span></span>
<Rate value={rating} onChange={setRating} />
</div>
)}
<TextArea
rows={4}
placeholder="写下你的评论..."
@ -138,8 +174,14 @@ const CommentSection: React.FC<CommentSectionProps> = ({
renderItem={(item) => (
<List.Item
actions={[
<span key="like" className="comment-action">
<LikeOutlined /> {item.likeCount}
<span
key="like"
className="comment-action"
onClick={() => handleCommentLike(item.id, item.isLiked)}
style={{ cursor: 'pointer' }}
>
{item.isLiked ? <LikeFilled style={{ color: '#c8363d' }} /> : <LikeOutlined />}
{' '}{item.likeCount}
</span>,
]}
>
@ -148,22 +190,28 @@ const CommentSection: React.FC<CommentSectionProps> = ({
title={
<div className="comment-title">
<span className="comment-author">{item.userName}</span>
{item.rating && (
<Rate disabled defaultValue={item.rating} style={{ fontSize: 14 }} />
)}
</div>
}
description={
<div className="comment-content">
<p>{item.content}</p>
<span className="comment-time">
{dayjs(item.createdAt).fromNow()}
{dayjs(item.createTime).fromNow()}
</span>
</div>
}
/>
</List.Item>
)}
pagination={
total > pageSize ? {
current: currentPage,
pageSize: pageSize,
total: total,
onChange: (page) => setCurrentPage(page),
showTotal: (total) => `${total} 条评论`,
} : false
}
/>
</div>
)