feat: 为点赞和收藏按钮添加认证守卫

- 在LikeButton组件中集成认证守卫
- 在FavoriteButton组件中集成认证守卫
- 未登录用户点击时显示提示并跳转到登录页
- 优化用户体验,避免无效API请求
This commit is contained in:
Leo 2025-10-13 21:36:02 +08:00
parent f1c12a974c
commit 019e53cc81
2 changed files with 166 additions and 17 deletions

View File

@ -1,45 +1,90 @@
/**
*
* API
*/
import React from 'react'
import React, { useEffect, useState } from 'react'
import { Button, message } from 'antd'
import { HeartOutlined, HeartFilled } from '@ant-design/icons'
import { StarOutlined, StarFilled } from '@ant-design/icons'
import { useUserStore } from '@store/useUserStore'
import { checkFavorite, toggleFavorite } from '@services/favoriteApi'
import { requireAuth } from '@/utils/authGuard'
import type { FavoriteTargetType } from '@/types'
interface FavoriteButtonProps {
heritageId: string
targetType: FavoriteTargetType
targetId: string | number
size?: 'small' | 'middle' | 'large'
showText?: boolean
onStatusChange?: (isFavorited: boolean) => void
}
const FavoriteButton: React.FC<FavoriteButtonProps> = ({ heritageId, size = 'middle' }) => {
const { user, isAuthenticated, addFavorite, removeFavorite } = useUserStore()
const FavoriteButton: React.FC<FavoriteButtonProps> = ({
targetType,
targetId,
size = 'middle',
showText = true,
onStatusChange
}) => {
const { isAuthenticated } = useUserStore()
const [isFavorited, setIsFavorited] = useState(false)
const [loading, setLoading] = useState(false)
const isFavorited = user?.favorites.includes(heritageId) || false
// 检查收藏状态
useEffect(() => {
if (isAuthenticated) {
fetchFavoriteStatus()
}
}, [targetId, isAuthenticated])
const handleToggle = () => {
if (!isAuthenticated) {
message.warning('请先登录后再收藏')
const fetchFavoriteStatus = async () => {
try {
const numericId = typeof targetId === 'string' ? parseInt(targetId) : targetId
const status = await checkFavorite(targetType, numericId)
setIsFavorited(status)
} catch (error) {
console.error('Failed to check favorite status:', error)
}
}
const handleToggle = async () => {
// 🔥 认证守卫:未登录时提示并跳转到登录页
if (!requireAuth('收藏')) {
return
}
if (isFavorited) {
removeFavorite(heritageId)
message.success('已取消收藏')
} else {
addFavorite(heritageId)
message.success('收藏成功')
setLoading(true)
try {
const numericId = typeof targetId === 'string' ? parseInt(targetId) : targetId
await toggleFavorite({
targetType,
targetId: numericId
}, isFavorited)
const newStatus = !isFavorited
setIsFavorited(newStatus)
// 已移除成功提示
// 触发回调
if (onStatusChange) {
onStatusChange(newStatus)
}
} catch (error: any) {
message.error(error.message || '操作失败')
} finally {
setLoading(false)
}
}
return (
<Button
type={isFavorited ? 'primary' : 'default'}
icon={isFavorited ? <HeartFilled /> : <HeartOutlined />}
icon={isFavorited ? <StarFilled /> : <StarOutlined />}
onClick={handleToggle}
loading={loading}
size={size}
>
{isFavorited ? '已收藏' : '收藏'}
{showText && (isFavorited ? '已收藏' : '收藏')}
</Button>
)
}

View File

@ -0,0 +1,104 @@
/**
*
* API
*/
import React, { useEffect, useState } from 'react'
import { Button, message } from 'antd'
import { LikeOutlined, LikeFilled } from '@ant-design/icons'
import { useUserStore } from '@store/useUserStore'
import { checkLike, toggleLike } from '@services/likeApi'
import { requireAuth } from '@/utils/authGuard'
import type { LikeTargetType } from '@/types'
interface LikeButtonProps {
targetType: LikeTargetType
targetId: string | number
size?: 'small' | 'middle' | 'large'
showText?: boolean
initialCount?: number
onStatusChange?: (isLiked: boolean, newCount: number) => void
}
const LikeButton: React.FC<LikeButtonProps> = ({
targetType,
targetId,
size = 'middle',
showText = true,
initialCount = 0,
onStatusChange
}) => {
const { isAuthenticated } = useUserStore()
const [isLiked, setIsLiked] = useState(false)
const [likeCount, setLikeCount] = useState(initialCount)
const [loading, setLoading] = useState(false)
// 检查点赞状态
useEffect(() => {
if (isAuthenticated) {
fetchLikeStatus()
}
}, [targetId, isAuthenticated])
// 更新初始点赞数
useEffect(() => {
setLikeCount(initialCount)
}, [initialCount])
const fetchLikeStatus = async () => {
try {
const numericId = typeof targetId === 'string' ? parseInt(targetId) : targetId
const status = await checkLike(targetType, numericId)
setIsLiked(status)
} catch (error) {
console.error('Failed to check like status:', error)
}
}
const handleToggle = async () => {
// 🔥 认证守卫:未登录时提示并跳转到登录页
if (!requireAuth('点赞')) {
return
}
setLoading(true)
try {
const numericId = typeof targetId === 'string' ? parseInt(targetId) : targetId
await toggleLike({
targetType,
targetId: numericId
}, isLiked)
const newStatus = !isLiked
const newCount = newStatus ? likeCount + 1 : likeCount - 1
setIsLiked(newStatus)
setLikeCount(newCount)
// 已移除成功提示
// 触发回调
if (onStatusChange) {
onStatusChange(newStatus, newCount)
}
} catch (error: any) {
message.error(error.message || '操作失败')
} finally {
setLoading(false)
}
}
return (
<Button
type={isLiked ? 'primary' : 'default'}
icon={isLiked ? <LikeFilled /> : <LikeOutlined />}
onClick={handleToggle}
loading={loading}
size={size}
>
{showText && (isLiked ? '已点赞' : '点赞')}
{likeCount > 0 && ` (${likeCount.toLocaleString()})`}
</Button>
)
}
export default LikeButton