Commit b60729a2 authored by fisherdaddy's avatar fisherdaddy

fix: google login

parent e321ffa6
// src/components/Header.jsx
import React from 'react';
import React, { useState, useEffect, useRef } from 'react';
import { Link, useNavigate } from 'react-router-dom';
import LanguageSelector from './LanguageSelector';
import { useTranslation } from '../js/i18n';
......@@ -10,12 +10,31 @@ function Header() {
const { t } = useTranslation();
const navigate = useNavigate();
const user = JSON.parse(localStorage.getItem('user'));
const [menuOpen, setMenuOpen] = useState(false);
const menuRef = useRef(null);
const handleLogout = () => {
localStorage.removeItem('user');
navigate('/login');
};
const toggleMenu = () => {
setMenuOpen((prev) => !prev);
};
// 点击菜单外部时关闭菜单
useEffect(() => {
const handleClickOutside = (event) => {
if (menuRef.current && !menuRef.current.contains(event.target)) {
setMenuOpen(false);
}
};
document.addEventListener('mousedown', handleClickOutside);
return () => {
document.removeEventListener('mousedown', handleClickOutside);
};
}, []);
return (
<header>
<nav>
......@@ -30,10 +49,21 @@ function Header() {
<div className="auth-container">
{user ? (
<div className="user-info">
<span>
{t('welcome')}, {user.name || user.given_name}
</span>
<button onClick={handleLogout}>{t('logout')}</button>
{/* 头像容器 */}
<div className="avatar-container" ref={menuRef}>
<img
src={user.picture}
alt="User Avatar"
className="avatar"
onClick={toggleMenu}
/>
{/* 下拉菜单 */}
{menuOpen && (
<div className="dropdown-menu">
<button onClick={handleLogout}>{t('logout')}</button>
</div>
)}
</div>
</div>
) : (
<Link to="/login">{t('login')}</Link>
......
......@@ -2,16 +2,20 @@
import React, { useEffect } from 'react';
import { useNavigate } from 'react-router-dom';
import { GoogleLogin } from '@react-oauth/google';
import * as jwt_decode from 'jwt-decode';
const Login = () => {
const navigate = useNavigate();
const handleLoginSuccess = (credentialResponse) => {
const { credential } = credentialResponse;
const decoded = jwt_decode.default(credential);
// 手动解码 JWT
const base64Url = credential.split('.')[1];
const base64 = base64Url.replace(/-/g, '+').replace(/_/g, '/');
const decodedPayload = JSON.parse(window.atob(base64));
// 将用户信息保存到 localStorage 或上下文
localStorage.setItem('user', JSON.stringify(decoded));
localStorage.setItem('user', JSON.stringify(decodedPayload));
navigate('/'); // 登录成功后重定向到首页
};
......
......@@ -97,3 +97,45 @@ header nav {
margin-top: 10px;
}
}
/* 头像容器 */
.avatar-container {
position: relative;
display: inline-block;
}
/* 用户头像 */
.avatar {
width: 32px;
height: 32px;
border-radius: 50%;
cursor: pointer;
}
/* 下拉菜单 */
.dropdown-menu {
position: absolute;
top: 100%; /* 紧贴在头像下方 */
left: 50%;
transform: translateX(-50%);
margin-top: 5px; /* 可选:头像和菜单之间的间距 */
background-color: white;
border: 1px solid #ccc;
padding: 10px;
min-width: 100px; /* 可选:设置最小宽度 */
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.15);
z-index: 1000;
}
/* 下拉菜单中的按钮 */
.dropdown-menu button {
background: none;
border: none;
cursor: pointer;
font-size: 16px;
color: #333;
}
.dropdown-menu button:hover {
color: #007bff;
}
\ No newline at end of file
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment