123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428 |
- "use client";
- import React, { useState, useEffect, useCallback } from "react";
- import { useRouter } from "next/navigation";
- import {
- ChevronLeft,
- MessageSquare,
- ArrowUpCircle,
- ArrowDownCircle,
- Gift,
- Users,
- Video,
- Zap,
- } from "lucide-react";
- import Image from "next/image";
- import InfiniteScroll from "react-infinite-scroll-component";
- import Link from "next/link";
- const PAGE_SIZE = 10;
- const PersonalCenter = () => {
- const router = useRouter();
- const [user, setUser] = useState(null);
- const [predictions, setPredictions] = useState([]);
- const [points, setPoints] = useState([]);
- const [isLoading, setIsLoading] = useState(true);
- const [predictionPage, setPredictionPage] = useState(1);
- const [pointPage, setPointPage] = useState(1);
- const [hasMorePredictions, setHasMorePredictions] = useState(true);
- const [hasMorePoints, setHasMorePoints] = useState(true);
- const [activities, setActivities] = useState([]);
- const fetchInitialData = useCallback(async () => {
- try {
- setIsLoading(true);
- const storedUser = JSON.parse(
- localStorage.getItem("currentUser") || "null"
- );
- setUser(storedUser);
- if (storedUser && storedUser.id) {
- const [
- predictionResponse,
- pointResponse,
- userResponse,
- activitiesResponse,
- ] = await Promise.all([
- fetch(
- `/api/prediction?username=${encodeURIComponent(
- storedUser.username
- )}¤t=1&pageSize=${PAGE_SIZE}`
- ),
- fetch(
- `/api/point-history?userId=${encodeURIComponent(
- storedUser.id
- )}¤t=1&pageSize=${PAGE_SIZE}`
- ),
- fetch(`/api/user?id=${encodeURIComponent(storedUser.id)}`),
- fetch("/api/new-activities", {
- headers: {
- "x-from-frontend": "true",
- },
- }),
- ]);
- const [predictionData, pointData, userData, activitiesData] =
- await Promise.all([
- predictionResponse.json(),
- pointResponse.json(),
- userResponse.json(),
- activitiesResponse.json(),
- ]);
- if (predictionData.success) {
- setPredictions(predictionData.data);
- setHasMorePredictions(predictionData.data.length === PAGE_SIZE);
- } else {
- console.error("Failed to fetch predictions:", predictionData.error);
- }
- if (pointData.success) {
- setPoints(pointData.data);
- setHasMorePoints(pointData.data.length === PAGE_SIZE);
- } else {
- console.error("Failed to fetch point history:", pointData.error);
- }
- if (userData.success && userData.data) {
- const updatedUser = { ...storedUser, points: userData.data.points };
- setUser(updatedUser);
- localStorage.setItem("currentUser", JSON.stringify(updatedUser));
- } else {
- console.error("Failed to fetch user data:", userData.error);
- }
- // 处理活动数据
- if (activitiesData.success) {
- setActivities(activitiesData.data);
- } else {
- console.error("Failed to fetch activities:", activitiesData.error);
- }
- } else {
- // 如果用户未登录,仍然获取活动数据
- const activitiesResponse = await activitiesPromise;
- const activitiesData = await activitiesResponse.json();
- if (activitiesData.success) {
- setActivities(activitiesData.data);
- } else {
- console.error("Failed to fetch activities:", activitiesData.error);
- }
- }
- } catch (error) {
- console.error("Error fetching data:", error);
- } finally {
- setIsLoading(false);
- }
- }, []);
- useEffect(() => {
- fetchInitialData();
- }, [fetchInitialData]);
- const loadMorePredictions = async () => {
- if (!hasMorePredictions || !user) return;
- try {
- const response = await fetch(
- `/api/prediction?username=${encodeURIComponent(
- user.username
- )}¤t=${predictionPage + 1}&pageSize=${PAGE_SIZE}`
- );
- const data = await response.json();
- if (data.success) {
- setPredictions((prev) => [...prev, ...data.data]);
- setPredictionPage((prev) => prev + 1);
- setHasMorePredictions(data.data.length === PAGE_SIZE);
- } else {
- console.error("Failed to fetch more predictions:", data.error);
- }
- } catch (error) {
- console.error("Error fetching more predictions:", error);
- }
- };
- const loadMorePoints = async () => {
- if (!hasMorePoints || !user) return;
- try {
- const response = await fetch(
- `/api/point-history?userId=${encodeURIComponent(user.id)}¤t=${
- pointPage + 1
- }&pageSize=${PAGE_SIZE}`
- );
- const data = await response.json();
- if (data.success) {
- setPoints((prev) => [...prev, ...data.data]);
- setPointPage((prev) => prev + 1);
- setHasMorePoints(data.data.length === PAGE_SIZE);
- } else {
- console.error("Failed to fetch more point history:", data.error);
- }
- } catch (error) {
- console.error("Error fetching more point history:", error);
- }
- };
- const handleExchangePoints = () => {
- console.log("兑换积分");
- // router.push('/exchange-points');
- };
- if (isLoading) {
- return (
- <div className="bg-blue-600 text-white min-h-screen p-4">Loading...</div>
- );
- }
- if (!user) {
- return (
- <div className="bg-blue-600 text-white min-h-screen p-4">
- User not found
- </div>
- );
- }
- return (
- <div className="bg-blue-600 text-white min-h-screen p-4">
- <div className="flex items-center mb-4">
- <ChevronLeft
- className="cursor-pointer"
- onClick={() => router.back()}
- size={32}
- />
- <h1 className="text-xl font-bold ml-4">个人中心</h1>
- </div>
- <div className="bg-white text-black rounded-lg p-4 mb-4">
- <div className="flex items-center justify-between">
- <div className="flex items-center">
- <Image
- src="/images/cluo.webp"
- alt="User Avatar"
- width={50}
- height={50}
- className="rounded-full mr-2"
- />
- <div>
- <h2 className="text-xl font-bold">{user.username}</h2>
- <p className="text-gray-600">积分: {user.points}</p>
- </div>
- </div>
- <button
- className="bg-blue-500 hover:bg-blue-600 text-white font-bold py-2 px-2 rounded transition duration-300"
- onClick={() => handleExchangePoints()}
- >
- 兑换积分
- </button>
- </div>
- </div>
- {activities && activities.length > 0 && (
- <div className="bg-white text-black rounded-lg px-4 py-2 mb-4 shadow-md">
- <h3 className="text-lg font-bold mb-1 text-blue-600">最新活动</h3>
- {activities.map((activity) => (
- <Link
- key={activity._id}
- href={activity.link}
- className="flex items-center hover:bg-gray-100 p-2 rounded transition duration-300"
- >
- {activity.icon && (
- <div className="w-5 h-5 mr-2 flex-shrink-0">
- <Image
- src={activity.icon}
- alt={activity.title}
- width={20}
- height={20}
- className="object-contain"
- />
- </div>
- )}
- <p>{activity.title}</p>
- </Link>
- ))}
- </div>
- )}
- <div className="bg-white text-black rounded-lg p-4 mb-4">
- <h3 className="text-lg font-bold mb-2 text-blue-600">预测记录</h3>
- <div id="predictionScroll" className="h-[300px] overflow-auto">
- <InfiniteScroll
- dataLength={predictions.length}
- next={loadMorePredictions}
- hasMore={hasMorePredictions}
- loader={<h4 className="text-center text-gray-500">加载中...</h4>}
- endMessage={
- <p className="text-center text-gray-500">没有更多记录了</p>
- }
- scrollableTarget="predictionScroll"
- >
- {predictions.map((prediction) => (
- <div
- key={prediction._id}
- className="mb-4 border-b border-gray-200 pb-4 hover:bg-gray-50 transition duration-150 ease-in-out"
- >
- <p className="font-bold text-blue-700 mb-2">
- {prediction.matchInfo}
- </p>
- <p className="text-gray-600 mb-2">
- 比赛时间:{" "}
- <span className="font-medium text-black">
- {prediction.matchTime}
- </span>
- </p>
- <div className="grid grid-cols-2 gap-2">
- <p className="mb-1">
- 胜负预测:{" "}
- <span
- className={`font-medium ${
- prediction.whoWillWin === "home"
- ? "text-red-600"
- : prediction.whoWillWin === "away"
- ? "text-green-600"
- : "text-yellow-600"
- }`}
- >
- {prediction.whoWillWin === "home"
- ? "主胜"
- : prediction.whoWillWin === "away"
- ? "客胜"
- : "平局"}
- </span>
- </p>
- <p className="mb-1">
- 结果:{" "}
- <span
- className={`font-medium ${
- prediction.whoWillWinResult === "correct"
- ? "text-green-600"
- : prediction.whoWillWinResult === "incorrect"
- ? "text-red-600"
- : "text-gray-600"
- }`}
- >
- {prediction.whoWillWinResult === "correct"
- ? "正确"
- : prediction.whoWillWinResult === "incorrect"
- ? "错误"
- : "待定"}
- </span>
- </p>
- <p className="mb-1">
- 首先得分:{" "}
- <span
- className={`font-medium ${
- prediction.firstTeamToScore === "home"
- ? "text-red-600"
- : prediction.firstTeamToScore === "away"
- ? "text-green-600"
- : "text-gray-600"
- }`}
- >
- {prediction.firstTeamToScore === "home"
- ? "主队"
- : prediction.firstTeamToScore === "away"
- ? "客队"
- : "无进球"}
- </span>
- </p>
- <p className="mb-1">
- 结果:{" "}
- <span
- className={`font-medium ${
- prediction.firstTeamToScoreResult === "correct"
- ? "text-green-600"
- : prediction.firstTeamToScoreResult === "incorrect"
- ? "text-red-600"
- : "text-gray-600"
- }`}
- >
- {prediction.firstTeamToScoreResult === "correct"
- ? "正确"
- : prediction.firstTeamToScoreResult === "incorrect"
- ? "错误"
- : "待定"}
- </span>
- </p>
- <p className="mb-1">
- 总进球数预测:{" "}
- <span className="font-medium text-purple-600">
- {prediction.totalGoals}
- </span>
- </p>
- <p className="mb-1">
- 结果:{" "}
- <span
- className={`font-medium ${
- prediction.totalGoalsResult === "correct"
- ? "text-green-600"
- : prediction.totalGoalsResult === "incorrect"
- ? "text-red-600"
- : "text-gray-600"
- }`}
- >
- {prediction.totalGoalsResult === "correct"
- ? "正确"
- : prediction.totalGoalsResult === "incorrect"
- ? "错误"
- : "待定"}
- </span>
- </p>
- </div>
- <p className="mt-2">
- 获得积分:{" "}
- <span className="font-medium text-orange-600">
- {prediction.pointsEarned !== null
- ? prediction.pointsEarned
- : "待定"}
- </span>
- </p>
- </div>
- ))}
- </InfiniteScroll>
- </div>
- </div>
- <div className="bg-white text-black rounded-lg p-4 mb-4">
- <h3 className="text-lg font-bold mb-2 text-blue-700">积分记录</h3>
- <div id="pointScroll" style={{ height: "250px", overflow: "auto" }}>
- <InfiniteScroll
- dataLength={points.length}
- next={loadMorePoints}
- hasMore={hasMorePoints}
- loader={<h4>Loading...</h4>}
- endMessage={<p className="text-center">没有更多记录了</p>}
- scrollableTarget="pointScroll"
- >
- {points.map((point) => (
- <div key={point.id} className="mb-3 flex items-start">
- <div className="mr-3 mt-1 flex-shrink-0">
- {point.points > 0 ? (
- <ArrowUpCircle className="text-green-500 w-5 h-5" />
- ) : (
- <ArrowDownCircle className="text-red-500 w-5 h-5" />
- )}
- </div>
- <div className="flex-grow mr-4">
- <p className="text-sm text-gray-600">
- {new Date(point.createdAt).toLocaleDateString()}
- </p>
- <p className="font-medium">{point.reason}</p>
- </div>
- <div
- className={`flex-shrink-0 font-bold ${
- point.points > 0 ? "text-green-600" : "text-red-600"
- }`}
- >
- {point.points > 0 ? "+" : ""}
- {point.points}
- </div>
- </div>
- ))}
- </InfiniteScroll>
- </div>
- </div>
- </div>
- );
- };
- export default PersonalCenter;
|