Skip to content

Commit

Permalink
Merge pull request #73 from he12345698/develop
Browse files Browse the repository at this point in the history
Fix many thing...
  • Loading branch information
he12345698 authored Sep 3, 2024
2 parents 13f701f + a3120c8 commit 7b1f1c2
Show file tree
Hide file tree
Showing 25 changed files with 407 additions and 220 deletions.
2 changes: 1 addition & 1 deletion frontend/public/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@
manifest.json provides metadata used when your web app is installed on a
user's mobile device or desktop. See https://developers.google.com/web/fundamentals/web-app-manifest/
-->
<link rel="manifest" href="%PUBLIC_URL%/manifest.json" />
<!-- <link rel="manifest" href="%PUBLIC_URL%/manifest.json" /> -->
<!--
Notice the use of %PUBLIC_URL% in the tags above.
It will be replaced with the URL of the `public` folder during the build.
Expand Down
4 changes: 1 addition & 3 deletions frontend/src/App.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ import style from './styles/App.module.css';
import UserData from './pages/UserData';
import ArticleEditor from './pages/ArticleEditor';
import ForgotPasswordPage from './pages/ForgotPasswordPage';
import ResetPasswordPage from './ResetPasswordPage ';
import ResetPasswordPage from './pages//ResetPasswordPage ';
import Header from './components/Header';
import Footer from './components/Footer';
import EmailVerificationPage from './pages/EmailVerificationPage';
Expand All @@ -22,8 +22,6 @@ const MainLayout = ({ children }) => (
</main>
);



const App = () => {

return (
Expand Down
102 changes: 79 additions & 23 deletions frontend/src/components/Header.js
Original file line number Diff line number Diff line change
@@ -1,26 +1,47 @@
import React, { useState, useEffect } from 'react';
import React, { useState, useEffect, useContext } from 'react';
import { Link } from 'react-router-dom';
import { useLocation } from 'react-router-dom';
import styles from '../styles/components/Header.module.css';

import { UserContext } from './UserContext';

const Header = () => {

const [username, setUsername] = useState('');
const [userImage, setUserImage] = useState('');
//const [username, setUsername] = useState('');
// const [userImage, setUserImage] = useState('');
const location = useLocation();
const [isMenuOpen, setIsMenuOpen] = useState(false);
const { user, setUser } = useContext(UserContext);

const toggleMenu = () => {
setIsMenuOpen(!isMenuOpen);
};

useEffect(() => {
const handleClickOutside = (event) => {
const menuContainer = document.querySelector(`.${styles["menu-container"]}`);
const dropdownMenu = document.querySelector(`.${styles["dropdown-menu"]}`);

if (menuContainer && !menuContainer.contains(event.target)) {
setIsMenuOpen(false);
}
};

document.addEventListener('click', handleClickOutside);

return () => {
document.removeEventListener('click', handleClickOutside);
};
}, [isMenuOpen]);

const notifyLogout = async () => {
try {

await fetch('http://localhost:8080/blog/ac/logout-notify', {
await fetch('http://localhost:8080/blog/ac/logout-notify', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'Authorization': `Bearer ${window.localStorage.getItem('token')}`, // 如果需要
},
body: JSON.stringify({
username: username,
username: user?.username,
}),
});
} catch (error) {
Expand All @@ -30,15 +51,12 @@ const Header = () => {

const handleLogout = () => {
notifyLogout();
// 处理登出逻辑,例如清除本地存储的 token,重定向到登录页面等
window.localStorage.removeItem('token');
//setUsername('未登入');
setTimeout(() => {
window.location.href = '/';
}, 100);
};

// 获取用户信息
useEffect(() => {
const fetchUserInfo = async () => {
const token = localStorage.getItem('token');
Expand All @@ -53,21 +71,44 @@ const Header = () => {

if (response.ok) {
const data = await response.json();
console.log('username is:', data.username);
console.log('userImage is:', data.userImage);
setUsername(data.username || '訪客1');
setUserImage(data.userImage || '/Image/GG'); // 默认头像
setUser({
username: data.username || '访客1',
userImage: data.userImage || '/Image/GG', // 设置默认头像
email: data.email,
id: data.id
});
console.log(data)
console.log('id is ',data.id)
} else if (response.status === 401) {
// 如果收到 401 响应,检查是否有新的 token
const data = await response.json();
if (data.token) {
// 更新本地存储中的 token
localStorage.setItem('token', data.token);

// 使用新的 token 重新发起请求
return fetchUserInfo(); // 递归调用以重试请求
} else {
setUser({
username: null,
userImage: '/Image/GG' // 设置默认头像
});
}
} else {
console.log('Response error:', response);
setUser({
username: null,
userImage: '/Image/GG' // 设置默认头像
});
}
} catch (error) {
console.error('Error:', error);
setUsername('Error222');
setUserImage('/Image/GG'); // 默认头像
setUser({
username: null,
userImage: '/Image/GG' // 设置默认头像
});
}
}
};

fetchUserInfo();
}, [location]);

Expand All @@ -84,17 +125,32 @@ const Header = () => {
<Link to="/">首頁</Link>
</nav>
<div className={styles["user-login-container"]}>
{username ? (
{user?.username ? (
<div className={styles["user-info"]}>
<img
src={userImage}
src={user?.userImage}
width="50"
height="50"
alt="User Avatar"
className={styles["user-avatar"]}
/>
<span className={styles.username}>使用者:{username}</span>
<button onClick={handleLogout} className={styles["logout-btn"]}>登出</button>
<span className={styles.username}>
使用者:<span className={styles["username-text"]}>{user?.username}</span>
</span>
<div className={styles["menu-container"]}>
<button className={styles["hamburger-menu"]} onClick={toggleMenu}>
<span></span>
<span></span>
<span></span>
</button>
<div className={`${styles["dropdown-menu"]} ${isMenuOpen ? styles["open"] : ""}`}>
<Link to="/UserData">個人資料</Link>
<Link to="/settings">設定</Link>
<button onClick={handleLogout} className={styles["logout-btn"]}>
登出
</button>
</div>
</div>
</div>
) : (
<div className={styles["login-btn"]}>
Expand Down
1 change: 1 addition & 0 deletions frontend/src/components/UserArticles.js
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ const UserArticles = ({ authorId }) => {
if (!response.ok) {
throw new Error(`HTTP error! status: ${response.status}`);
}
setError('');
return response.json();
})
.then(data => {
Expand Down
5 changes: 3 additions & 2 deletions frontend/src/components/UserAvatar.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,11 @@ import React, { useState, useEffect, useContext } from 'react';
import styles from "../styles/components/UserAvatar.module.css";
import Modal from 'react-modal';
import ImageUpload from "../components/ImageUpload";
import { UserContext } from './UserContext';

const UserAvatar = ({ id }) => {
// const [isModalOpen, setIsModalOpen] = useState(false);// 設定彈跳視窗開關

const { user } = useContext(UserContext);

// 用來管理用戶資料
const [userData, setUserData] = useState({
Expand Down Expand Up @@ -71,7 +72,7 @@ const UserAvatar = ({ id }) => {
<div className="image-container mb-3">
<img
id={styles.profile_avatar}
src={userData.imagelink}
src={user?.userImage}
alt="頭像"
className="img-fluid rounded border border-3 border-dark"
/>
Expand Down
14 changes: 14 additions & 0 deletions frontend/src/components/UserContext.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
import React, { createContext, useState } from 'react';

// 创建 UserContext
export const UserContext = createContext();

export const UserProvider = ({ children }) => {
const [user, setUser] = useState(null); // 初始化用户信息为空

return (
<UserContext.Provider value={{ user, setUser }}>
{children}
</UserContext.Provider>
);
};
62 changes: 31 additions & 31 deletions frontend/src/components/UserProfile.js
Original file line number Diff line number Diff line change
@@ -1,65 +1,64 @@
import React, { useState, useEffect } from 'react';
import styles from "../styles/components/UserProfile.module.css"
import React, { useState, useEffect, useContext } from 'react';
import styles from "../styles/components/UserProfile.module.css";
import { UserContext } from './UserContext';
import { useNavigate } from 'react-router-dom';

// 用來顯示和編輯用戶的基本資料(用戶名、電子郵件、密碼)

const UserProfile = ({ userId }) => {

// 管理不同型態(編輯中 or 顯示中)的編輯狀態
const { user, setUser } = useContext(UserContext); // 取得 setUser 方法
const [editing, setEditing] = useState({
username: false,
email: false,
});

// 用來管理用戶資料
const [userData, setUserData] = useState({
username: '',
email: '',
createdDate: '',
lastLoginDate: '',
lastLoginDate: ''
});

// 用來管理暫時的編輯資料
const [tempUser, setTempUser] = useState({
username: user?.username || '',
email: user?.email || '',
});

// 防止用戶在請求未完成時重複提交
const [loading, setLoading] = useState(false);
const [error, setError] = useState('');
let navigate = useNavigate();
const navigate = useNavigate();

// 獲取後端資料
useEffect(() => {
setLoading(true);

fetch(`http://localhost:8080/blog/api/userProfile/${userId}`)
.then(response => {
console.log('網頁回應:', response);
return response.json();
})
.then(response => response.json())
.then(data => {
console.log("得到的數據", data)
setUserData(data);
setLoading(false);
})
.catch(error => {
console.error("獲取用戶資料失敗", error);
setError("獲取用戶資料失敗");
setLoading(false);
})
});
}, [userId]);

// 輸入變化
const handleInputChange = (e) => {
const { name, value } = e.target;
setUserData(prevData => ({
...prevData, [name]: value
setTempUser(prevTempUser => ({
...prevTempUser, [name]: value
}));
}
};

// 切換編輯狀態
const toggleEdit = (field) => {
setEditing(prev => ({
...prev, [field]: !prev[field]// 取prev狀態的相反
...prev, [field]: !prev[field]
}));
}
};

// 處裡保存
const handleSave = (field) => {
Expand All @@ -71,26 +70,28 @@ const UserProfile = ({ userId }) => {
'Content-Type': 'application/json',
},
body: JSON.stringify(
{ [field]: userData[field] }
{ [field]: tempUser[field] }
),
})
.then(response => {
if (!response.ok) {
throw new Error("保存數據失敗");
}
// 更新 UserContext
setUser(prevUser => ({ ...prevUser, [field]: tempUser[field] }));
setLoading(false);
toggleEdit(field);// 保存成功後切回顯示模式
toggleEdit(field); // 保存成功後切回顯示模式
})
.catch(error => {
setLoading(false);
console.log("保存數據失敗", error);
console.error("保存數據失敗", error);
setError("保存數據失敗");
})
}
});
};

const handleClick = () => {
navigate('/ChangePassword'); // 跳轉到修改密碼頁面
}
};

return (
<div className="row">
Expand All @@ -105,9 +106,9 @@ const UserProfile = ({ userId }) => {
id="username"
className={`form-control ${styles.text_area}`}
name="username"
value={userData.username}
value={tempUser.username}
onChange={handleInputChange}
disabled={!editing.username} //根據編輯狀態關閉或開啟
disabled={!editing.username} // 根據編輯狀態關閉或開啟
/>
{editing.username ? (
<button
Expand Down Expand Up @@ -136,7 +137,7 @@ const UserProfile = ({ userId }) => {
type="email"
id="email"
name="email"
value={userData.email}
value={tempUser.email}
className={`form-control ${styles.text_area}`}
onChange={handleInputChange}
disabled={!editing.email}
Expand Down Expand Up @@ -192,8 +193,7 @@ const UserProfile = ({ userId }) => {
<p className='fw-bold'>{userData.lastLoginDate}</p>
</div>
</div>

);
}

export default UserProfile;
export default UserProfile;
Loading

0 comments on commit 7b1f1c2

Please sign in to comment.