feat: 为评论区添加认证守卫
- 在发表评论功能中添加认证检查 - 在评论点赞功能中添加认证检查 - 未登录用户操作时显示提示并跳转到登录页 - 移除未使用的imports优化代码
This commit is contained in:
parent
019e53cc81
commit
153d8d40ad
@ -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>
|
||||
)
|
||||
|
||||
Loading…
Reference in New Issue
Block a user