hongjli
2025-04-16 ed1c1f25a544eaf08c7a7bcd3af93d7d26c8594e
显示用户昵称
已修改3个文件
已添加2个文件
237 ■■■■ 文件已修改
package-lock.json 32 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
package.json 3 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/components/layout/Navbar.tsx 133 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/services/userService.ts 33 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/store/userStore.ts 36 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
package-lock.json
@@ -17,7 +17,8 @@
        "react-dom": "^19.0.0",
        "react-markdown": "^10.1.0",
        "remark-gfm": "^4.0.1",
        "styled-components": "^6.1.16"
        "styled-components": "^6.1.16",
        "zustand": "^5.0.3"
      },
      "devDependencies": {
        "@eslint/eslintrc": "^3",
@@ -7656,6 +7657,35 @@
        "url": "https://github.com/sponsors/sindresorhus"
      }
    },
    "node_modules/zustand": {
      "version": "5.0.3",
      "resolved": "https://registry.npmjs.org/zustand/-/zustand-5.0.3.tgz",
      "integrity": "sha512-14fwWQtU3pH4dE0dOpdMiWjddcH+QzKIgk1cl8epwSE7yag43k/AD/m4L6+K7DytAOr9gGBe3/EXj9g7cdostg==",
      "license": "MIT",
      "engines": {
        "node": ">=12.20.0"
      },
      "peerDependencies": {
        "@types/react": ">=18.0.0",
        "immer": ">=9.0.6",
        "react": ">=18.0.0",
        "use-sync-external-store": ">=1.2.0"
      },
      "peerDependenciesMeta": {
        "@types/react": {
          "optional": true
        },
        "immer": {
          "optional": true
        },
        "react": {
          "optional": true
        },
        "use-sync-external-store": {
          "optional": true
        }
      }
    },
    "node_modules/zwitch": {
      "version": "2.0.4",
      "resolved": "https://registry.npmjs.org/zwitch/-/zwitch-2.0.4.tgz",
package.json
@@ -18,7 +18,8 @@
    "react-dom": "^19.0.0",
    "react-markdown": "^10.1.0",
    "remark-gfm": "^4.0.1",
    "styled-components": "^6.1.16"
    "styled-components": "^6.1.16",
    "zustand": "^5.0.3"
  },
  "devDependencies": {
    "@eslint/eslintrc": "^3",
src/components/layout/Navbar.tsx
@@ -3,11 +3,16 @@
import Link from 'next/link';
import Image from 'next/image';
import { useState, useEffect } from 'react';
import { useUserStore } from '@/store/userStore';
import { getUserInfo } from '@/services/userService';
const Navbar = () => {
  const [isMenuOpen, setIsMenuOpen] = useState(false);
  const [scrolled, setScrolled] = useState(false);
  const [activeMenu, setActiveMenu] = useState('');
  const [isLoading, setIsLoading] = useState(false);
  const [error, setError] = useState<string | null>(null);
  const { userInfo, token, setUserInfo } = useUserStore();
  // ç›‘听滚动事件,为导航栏添加滚动效果
  useEffect(() => {
@@ -22,6 +27,37 @@
    window.addEventListener('scroll', handleScroll);
    return () => window.removeEventListener('scroll', handleScroll);
  }, []);
  // èŽ·å–ç”¨æˆ·ä¿¡æ¯
  useEffect(() => {
    const fetchUserInfo = async () => {
      if (!token) return;
      setIsLoading(true);
      setError(null);
      try {
        const response = await getUserInfo();
        if (response.code === 200 && response.data) {
          setUserInfo(response.data);
        } else {
          console.error('获取用户信息失败:', response.message);
          setError(response.message || '获取用户信息失败');
          // å¦‚果是认证相关错误,清除用户信息
          if (response.code === 401) {
            useUserStore.getState().clearUserInfo();
          }
        }
      } catch (err) {
        console.error('获取用户信息出错:', err);
        setError('获取用户信息失败');
      } finally {
        setIsLoading(false);
      }
    };
    fetchUserInfo();
  }, [token, setUserInfo]);
  return (
    <nav 
@@ -190,53 +226,66 @@
                transition-all duration-300 ${activeMenu === 'training' ? 'w-full' : 'w-0'}`}></span>
            </a>
            
            {/* ç™»å½•按钮 */}
            <div className="relative group">
              <a href="/login" className="relative overflow-hidden flex items-center justify-center px-4 lg:px-7 py-2 rounded-full border border-[#6ADBFF]/40 bg-gradient-to-r from-[#131C41] to-[#1E2B63] hover:border-[#6ADBFF]/70 transition-all duration-300 group quantum-button">
                <span className="relative z-10 text-white group-hover:text-[#6ADBFF] transition-colors duration-300 quantum-pulse">登录</span>
                {/* é‡å­å…‰çº¿æ•ˆæžœ */}
                <div className="absolute inset-0 overflow-hidden">
                  {/* åº•层辉光效果 */}
                  <div className="absolute inset-0 opacity-0 group-hover:opacity-30 transition-opacity duration-500 bg-gradient-to-r from-[#6ADBFF]/20 to-[#6ADBFF]/40"></div>
            {userInfo ? (
              // ç”¨æˆ·ä¿¡æ¯æ˜¾ç¤º
              <div className="relative">
                <div className="relative overflow-hidden flex items-center justify-center px-4 lg:px-7 py-2 rounded-full bg-[#131C41]/60">
                  <span className="relative z-10 bg-gradient-to-r from-[#6ADBFF] via-[#FF6A88] to-[#F5A800] bg-clip-text text-transparent font-medium">
                    {isLoading ? '加载中...' : userInfo.nickname}
                  </span>
                  
                  {/* é‡å­æ‰«æçº¿ */}
                  <div className="absolute top-[45%] -left-10 h-[1px] w-[120%] bg-gradient-to-r from-transparent via-[#6ADBFF] to-transparent opacity-0 group-hover:opacity-100 transition-opacity duration-300 quantum-scan-line"></div>
                  {/* é‡å­æ•°æ®æµ */}
                  <div className="absolute top-0 h-full w-full">
                    <div className="absolute left-[50%] top-0 bottom-0 w-[1px] bg-gradient-to-b from-transparent via-[#6ADBFF]/30 to-transparent transform scale-y-0 group-hover:scale-y-100 transition-transform duration-700 ease-out"></div>
                  {/* é™æ€è£…饰效果 */}
                  <div className="absolute inset-0 overflow-hidden rounded-full">
                    {/* èƒŒæ™¯æ¸å˜ */}
                    <div className="absolute inset-0 opacity-20 bg-gradient-to-r from-[#6ADBFF]/20 via-[#FF6A88]/20 to-[#F5A800]/20"></div>
                    {/* æŸ”和的光晕效果 */}
                    <div className="absolute inset-0 bg-gradient-to-r from-[#6ADBFF]/5 via-[#FF6A88]/5 to-[#F5A800]/5 blur-sm"></div>
                  </div>
                  {/* é‡å­è¾¹ç¼˜æ•ˆæžœ */}
                  <div className="absolute bottom-0 left-0 right-0 h-[1px] bg-gradient-to-r from-transparent via-[#6ADBFF] to-transparent transform scale-x-0 group-hover:scale-x-100 transition-transform duration-700 ease-out"></div>
                </div>
              </a>
            </div>
                {error && (
                  <div className="absolute top-full mt-2 left-0 right-0 px-4 py-2 bg-red-500/90 text-white text-sm rounded-md">
                    {error}
                  </div>
                )}
              </div>
            ) : (
              <>
                {/* ç™»å½•按钮 */}
                <div className="relative group">
                  <a href="/login" className="relative overflow-hidden flex items-center justify-center px-4 lg:px-7 py-2 rounded-full border border-[#6ADBFF]/40 bg-gradient-to-r from-[#131C41] to-[#1E2B63] hover:border-[#6ADBFF]/70 transition-all duration-300 group quantum-button">
                    <span className="relative z-10 text-white group-hover:text-[#6ADBFF] transition-colors duration-300 quantum-pulse">登录</span>
                    {/* é‡å­å…‰çº¿æ•ˆæžœ */}
                    <div className="absolute inset-0 overflow-hidden">
                      <div className="absolute inset-0 opacity-0 group-hover:opacity-30 transition-opacity duration-500 bg-gradient-to-r from-[#6ADBFF]/20 to-[#6ADBFF]/40"></div>
                      <div className="absolute top-[45%] -left-10 h-[1px] w-[120%] bg-gradient-to-r from-transparent via-[#6ADBFF] to-transparent opacity-0 group-hover:opacity-100 transition-opacity duration-300 quantum-scan-line"></div>
                      <div className="absolute top-0 h-full w-full">
                        <div className="absolute left-[50%] top-0 bottom-0 w-[1px] bg-gradient-to-b from-transparent via-[#6ADBFF]/30 to-transparent transform scale-y-0 group-hover:scale-y-100 transition-transform duration-700 ease-out"></div>
                      </div>
                      <div className="absolute bottom-0 left-0 right-0 h-[1px] bg-gradient-to-r from-transparent via-[#6ADBFF] to-transparent transform scale-x-0 group-hover:scale-x-100 transition-transform duration-700 ease-out"></div>
                    </div>
                  </a>
                </div>
            {/* æ³¨å†ŒæŒ‰é’® */}
            <div className="relative group -ml-2">
              <a href="/register" className="relative overflow-hidden flex items-center justify-center px-4 lg:px-7 py-2 rounded-full border border-[#FF6A88]/40 bg-gradient-to-r from-[#131C41] via-[#1E2B63] to-[#2A1B48] hover:border-[#FF6A88]/70 transition-all duration-300 group quantum-button">
                <span className="relative z-10 text-white group-hover:text-[#FF6A88] transition-colors duration-300 quantum-pulse">注册</span>
                {/* é‡å­å…‰çº¿æ•ˆæžœ */}
                <div className="absolute inset-0 overflow-hidden">
                  {/* åº•层辉光效果 */}
                  <div className="absolute inset-0 opacity-0 group-hover:opacity-30 transition-opacity duration-500 bg-gradient-to-r from-[#FF6A88]/20 to-[#FF6A88]/40"></div>
                  {/* é‡å­æ‰«æçº¿ */}
                  <div className="absolute top-[45%] -left-10 h-[1px] w-[120%] bg-gradient-to-r from-transparent via-[#FF6A88] to-transparent opacity-0 group-hover:opacity-100 transition-opacity duration-300 quantum-scan-line"></div>
                  {/* é‡å­æ•°æ®æµ */}
                  <div className="absolute top-0 h-full w-full">
                    <div className="absolute left-[50%] top-0 bottom-0 w-[1px] bg-gradient-to-b from-transparent via-[#FF6A88]/30 to-transparent transform scale-y-0 group-hover:scale-y-100 transition-transform duration-700 ease-out"></div>
                  </div>
                  {/* é‡å­è¾¹ç¼˜æ•ˆæžœ */}
                  <div className="absolute bottom-0 left-0 right-0 h-[1px] bg-gradient-to-r from-transparent via-[#FF6A88] to-transparent transform scale-x-0 group-hover:scale-x-100 transition-transform duration-700 ease-out"></div>
                {/* æ³¨å†ŒæŒ‰é’® */}
                <div className="relative group -ml-2">
                  <a href="/register" className="relative overflow-hidden flex items-center justify-center px-4 lg:px-7 py-2 rounded-full border border-[#FF6A88]/40 bg-gradient-to-r from-[#131C41] via-[#1E2B63] to-[#2A1B48] hover:border-[#FF6A88]/70 transition-all duration-300 group quantum-button">
                    <span className="relative z-10 text-white group-hover:text-[#FF6A88] transition-colors duration-300 quantum-pulse">注册</span>
                    {/* é‡å­å…‰çº¿æ•ˆæžœ */}
                    <div className="absolute inset-0 overflow-hidden">
                      <div className="absolute inset-0 opacity-0 group-hover:opacity-30 transition-opacity duration-500 bg-gradient-to-r from-[#FF6A88]/20 to-[#FF6A88]/40"></div>
                      <div className="absolute top-[45%] -left-10 h-[1px] w-[120%] bg-gradient-to-r from-transparent via-[#FF6A88] to-transparent opacity-0 group-hover:opacity-100 transition-opacity duration-300 quantum-scan-line"></div>
                      <div className="absolute top-0 h-full w-full">
                        <div className="absolute left-[50%] top-0 bottom-0 w-[1px] bg-gradient-to-b from-transparent via-[#FF6A88]/30 to-transparent transform scale-y-0 group-hover:scale-y-100 transition-transform duration-700 ease-out"></div>
                      </div>
                      <div className="absolute bottom-0 left-0 right-0 h-[1px] bg-gradient-to-r from-transparent via-[#FF6A88] to-transparent transform scale-x-0 group-hover:scale-x-100 transition-transform duration-700 ease-out"></div>
                    </div>
                  </a>
                </div>
              </a>
            </div>
              </>
            )}
          </div>
          {/* ç§»åŠ¨ç«¯èœå•æŒ‰é’® */}
src/services/userService.ts
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,33 @@
import ApiService from '@/utils/api';
interface UserInfo {
  id: number;
  nickname: string;
  accountName: string;
}
interface ApiResponse<T> {
  code: number;
  data?: T;
  message?: string;
}
export async function getUserInfo(): Promise<ApiResponse<UserInfo>> {
  try {
    const token = localStorage.getItem('token');
    if (!token) {
      return {
        code: 401,
        message: '未登录或token无效'
      };
    }
    return await ApiService.get<UserInfo>('/users/info');
  } catch (error) {
    console.error('获取用户信息失败:', error);
    return {
      code: 500,
      message: '网络请求失败'
    };
  }
}
src/store/userStore.ts
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,36 @@
import { create } from 'zustand';
import { StateCreator } from 'zustand';
interface UserInfo {
  id: number;
  nickname: string;
  accountName: string;
}
interface UserState {
  userInfo: UserInfo | null;
  token: string | null;
  setUserInfo: (info: UserInfo | null) => void;
  setToken: (token: string | null) => void;
  clearUserInfo: () => void;
}
const createUserStore: StateCreator<UserState> = (set) => ({
  userInfo: null,
  token: typeof window !== 'undefined' ? localStorage.getItem('token') : null,
  setUserInfo: (info: UserInfo | null) => set({ userInfo: info }),
  setToken: (token: string | null) => {
    if (token) {
      localStorage.setItem('token', token);
    } else {
      localStorage.removeItem('token');
    }
    set({ token });
  },
  clearUserInfo: () => {
    localStorage.removeItem('token');
    set({ userInfo: null, token: null });
  },
});
export const useUserStore = create(createUserStore);