| import React, { useEffect, useState } from 'react'; |
| import axios from 'axios'; |
| import './Recommend.css'; |
| import { useUser } from '../../../context/UserContext'; |
| import { useLocation } from 'wouter'; |
| import toast from 'react-hot-toast'; |
| import CreatePlaylistModal from './CreatePlaylistModal'; |
| import { confirmAlert } from 'react-confirm-alert'; |
| import 'react-confirm-alert/src/react-confirm-alert.css'; |
| import AuthButton from '../../../components/AuthButton'; |
| |
| const Recommend = () => { |
| const { user } = useUser(); |
| const [paidLists, setPaidLists] = useState([]); |
| const [popularSeeds, setPopularSeeds] = useState([]); |
| const [recommendedSeeds, setRecommendedSeeds] = useState({ |
| movie: [], |
| tv: [], |
| anime: [] |
| }); |
| |
| const [showModal, setShowModal] = useState(false); |
| const [, navigate] = useLocation(); |
| |
| useEffect(() => { |
| axios |
| .get('/playlist/page', { params: { page: 1, size: 8 } }) |
| .then((res) => { |
| if (res.data.code === 0) { |
| setPaidLists(res.data.data); |
| } else { |
| toast.error(`获取片单失败:${res.data.msg}`); |
| } |
| }) |
| .catch((err) => { |
| console.error('请求片单失败', err); |
| toast.error('请求片单失败,请稍后重试'); |
| }); |
| |
| axios |
| .get('/echo/recommendation/popular', { params: { limit: 16 } }) |
| .then((res) => setPopularSeeds(res.data)) |
| .catch((err) => { |
| console.error('获取热门资源失败', err); |
| toast.error('获取热门资源失败'); |
| }); |
| }, []); |
| |
| useEffect(() => { |
| if (user?.userId) { |
| axios |
| .get(`/echo/recommendation/seeds/${user.userId}`) |
| .then((res) => { |
| const categorized = { movie: [], tv: [], anime: [] }; |
| res.data.forEach((seed) => { |
| if (seed.category === 'movie') categorized.movie.push(seed); |
| else if (seed.category === 'tv') categorized.tv.push(seed); |
| else if (seed.category === 'anime') categorized.anime.push(seed); |
| }); |
| setRecommendedSeeds(categorized); |
| }) |
| .catch((err) => { |
| console.error('获取个性化推荐失败', err); |
| toast.error('获取个性化推荐失败'); |
| }); |
| } |
| }, [user]); |
| |
| const handleDelete = (id) => { |
| confirmAlert({ |
| title: '确认删除', |
| message: '确定删除此片单吗?', |
| buttons: [ |
| { |
| label: '确定', |
| onClick: async () => { |
| const toastId = toast.loading('正在删除...'); |
| try { |
| await axios.delete('/playlist', { |
| params: { ids: id }, |
| paramsSerializer: (params) => |
| `ids=${Array.isArray(params.ids) ? params.ids.join(',') : params.ids}` |
| }); |
| setPaidLists(paidLists.filter((list) => list.id !== id)); |
| toast.success('删除成功', { id: toastId }); |
| } catch (error) { |
| console.error('删除失败', error); |
| toast.error('删除失败,请稍后重试', { id: toastId }); |
| } |
| } |
| }, |
| { label: '取消' } |
| ] |
| }); |
| }; |
| |
| const handlePurchase = (id) => { |
| confirmAlert({ |
| title: '确认购买', |
| message: '确定支付该片单?', |
| buttons: [ |
| { |
| label: '确定', |
| onClick: async () => { |
| const toastId = toast.loading('购买中...'); |
| try { |
| const res = await axios.post(`/playlist/${id}/pay`); |
| if (res.data.code === 0) { |
| toast.success('购买成功', { id: toastId }); |
| navigate(`/playlist/${id}`); |
| } else { |
| toast.error(`购买失败:${res.data.msg}`, { id: toastId }); |
| } |
| } catch (err) { |
| console.error('支付失败', err); |
| toast.error('购买失败,请稍后重试', { id: toastId }); |
| } |
| } |
| }, |
| { label: '取消' } |
| ] |
| }); |
| }; |
| |
| const renderSeedCard = (seed) => ( |
| <div |
| className="seed-card" |
| key={seed.id} |
| onClick={() => navigate(`/seed/${seed.id}`)} |
| style={{ cursor: 'pointer' }} |
| > |
| <img src={seed.imageUrl || '/default-cover.jpg'} alt={seed.title} /> |
| <div className="title">{seed.title}</div> |
| </div> |
| ); |
| |
| |
| const renderSection = (title, seeds) => ( |
| <> |
| <h2>{title}</h2> |
| <div className="seed-list">{seeds.map(renderSeedCard)}</div> |
| </> |
| ); |
| |
| return ( |
| <div className="recommendation-page"> |
| <h2>💰 付费片单</h2> |
| {user && user.role === 'admin' && ( |
| <button className="create-button" onClick={() => setShowModal(true)}> |
| ➕ 创建片单 |
| </button> |
| )} |
| |
| <div className="recommend-paid-row"> |
| {paidLists.map((list) => ( |
| <div className="paid-card" key={list.id}> |
| <img |
| className="paid-cover" |
| src={list.coverUrl || '/default-cover.jpg'} |
| alt={list.title} |
| /> |
| <div className="paid-title">{list.title}</div> |
| |
| {user && user.role === 'admin' ? ( |
| <div className="admin-actions"> |
| <button onClick={() => handleDelete(list.id)}>删除</button> |
| </div> |
| ) : list.isPaid ? ( |
| <button onClick={() => navigate(`/playlist/${list.id}`)}>详情</button> |
| ) : ( |
| <AuthButton roles={['chocolate', 'ice-cream']} onClick={() => handlePurchase(list.id)}> |
| 购买 |
| </AuthButton> |
| )} |
| |
| </div> |
| ))} |
| </div> |
| |
| <h2>🎬 正在热映</h2> |
| <div className="seed-list popular-row"> |
| {popularSeeds.slice(0, 8).map(renderSeedCard)} |
| </div> |
| |
| <h2>🎯 猜你喜欢</h2> |
| {user ? ( |
| <> |
| {recommendedSeeds.movie.length > 0 && |
| renderSection('🎞️ 电影推荐', recommendedSeeds.movie)} |
| {recommendedSeeds.tv.length > 0 && |
| renderSection('📺 电视剧推荐', recommendedSeeds.tv)} |
| {recommendedSeeds.anime.length > 0 && |
| renderSection('🎌 动漫推荐', recommendedSeeds.anime)} |
| </> |
| ) : ( |
| <div className="login-reminder">请登录以获取个性化推荐</div> |
| )} |
| |
| {showModal && ( |
| <CreatePlaylistModal |
| onClose={() => setShowModal(false)} |
| onSuccess={(newPlaylist) => { |
| setPaidLists([newPlaylist, ...paidLists]); |
| setShowModal(false); |
| }} |
| /> |
| )} |
| </div> |
| ); |
| }; |
| |
| export default Recommend; |