From 019e53cc81de542b81fe1c3598d9285e9e9452e3 Mon Sep 17 00:00:00 2001 From: Leo <98382335+gaoziman@users.noreply.github.com> Date: Mon, 13 Oct 2025 21:36:02 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20=E4=B8=BA=E7=82=B9=E8=B5=9E=E5=92=8C?= =?UTF-8?q?=E6=94=B6=E8=97=8F=E6=8C=89=E9=92=AE=E6=B7=BB=E5=8A=A0=E8=AE=A4?= =?UTF-8?q?=E8=AF=81=E5=AE=88=E5=8D=AB?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 在LikeButton组件中集成认证守卫 - 在FavoriteButton组件中集成认证守卫 - 未登录用户点击时显示提示并跳转到登录页 - 优化用户体验,避免无效API请求 --- src/components/FavoriteButton/index.tsx | 79 ++++++++++++++---- src/components/LikeButton/index.tsx | 104 ++++++++++++++++++++++++ 2 files changed, 166 insertions(+), 17 deletions(-) create mode 100644 src/components/LikeButton/index.tsx diff --git a/src/components/FavoriteButton/index.tsx b/src/components/FavoriteButton/index.tsx index 1cbf94a..29629a0 100644 --- a/src/components/FavoriteButton/index.tsx +++ b/src/components/FavoriteButton/index.tsx @@ -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 = ({ heritageId, size = 'middle' }) => { - const { user, isAuthenticated, addFavorite, removeFavorite } = useUserStore() +const FavoriteButton: React.FC = ({ + 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 ( ) } diff --git a/src/components/LikeButton/index.tsx b/src/components/LikeButton/index.tsx new file mode 100644 index 0000000..2b81130 --- /dev/null +++ b/src/components/LikeButton/index.tsx @@ -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 = ({ + 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 ( + + ) +} + +export default LikeButton