feat: 添加认证服务模块,包括注册、登录、OAuth、令牌管理及密码更新功能,并新增密码修改组件。

This commit is contained in:
2026-01-31 13:45:22 +08:00
parent 53336bf260
commit b1601b6873
2 changed files with 47 additions and 22 deletions

View File

@@ -1,6 +1,6 @@
import { useState } from 'react'; import { useState, useEffect } from 'react';
import { Icon } from '@iconify/react'; import { Icon } from '@iconify/react';
import { updatePassword } from '../../../services/authService'; import { updatePassword, getCurrentUser, type User } from '../../../services/authService';
import './ChangePassword.css'; import './ChangePassword.css';
/** /**
@@ -8,12 +8,26 @@ import './ChangePassword.css';
* Allows users to change their login password * Allows users to change their login password
*/ */
function ChangePassword() { function ChangePassword() {
const [user, setUser] = useState<User | null>(null);
const [oldPassword, setOldPassword] = useState(''); const [oldPassword, setOldPassword] = useState('');
const [newPassword, setNewPassword] = useState(''); const [newPassword, setNewPassword] = useState('');
const [confirmPassword, setConfirmPassword] = useState(''); const [confirmPassword, setConfirmPassword] = useState('');
const [loading, setLoading] = useState(false); const [loading, setLoading] = useState(false);
const [message, setMessage] = useState<{ type: 'success' | 'error'; text: string } | null>(null); const [message, setMessage] = useState<{ type: 'success' | 'error'; text: string } | null>(null);
useEffect(() => {
loadUser();
}, []);
const loadUser = async () => {
try {
const userData = await getCurrentUser();
setUser(userData);
} catch (error) {
console.error('Failed to load user info', error);
}
};
const handleSubmit = async (e: React.FormEvent) => { const handleSubmit = async (e: React.FormEvent) => {
e.preventDefault(); e.preventDefault();
@@ -28,7 +42,7 @@ function ChangePassword() {
return; return;
} }
if (oldPassword === newPassword) { if (user?.has_password && oldPassword === newPassword) {
setMessage({ type: 'error', text: '新密码不能与旧密码相同' }); setMessage({ type: 'error', text: '新密码不能与旧密码相同' });
return; return;
} }
@@ -37,28 +51,35 @@ function ChangePassword() {
setMessage(null); setMessage(null);
try { try {
await updatePassword(oldPassword, newPassword); // If user has no password, send empty string as old password
setMessage({ type: 'success', text: '登录密码修改成功' }); await updatePassword(user?.has_password ? oldPassword : '', newPassword);
setMessage({ type: 'success', text: user?.has_password ? '登录密码修改成功' : '登录密码设置成功' });
// Clear forms // Clear forms
setOldPassword(''); setOldPassword('');
setNewPassword(''); setNewPassword('');
setConfirmPassword(''); setConfirmPassword('');
// Refresh user info to update has_password status
loadUser();
} catch (error) { } catch (error) {
setMessage({ setMessage({
type: 'error', type: 'error',
text: error instanceof Error ? error.message : '修改密码失败,请检查旧密码是否正确' text: error instanceof Error ? error.message : '操作失败'
}); });
} finally { } finally {
setLoading(false); setLoading(false);
} }
}; };
const hasPassword = user?.has_password ?? false;
return ( return (
<div className="change-password-settings"> <div className="change-password-settings">
<section className="password-section"> <section className="password-section">
<h3> <h3>
<Icon icon="solar:lock-password-bold-duotone" className="text-primary" /> <Icon icon="solar:lock-password-bold-duotone" className="text-primary" />
{hasPassword ? '修改登录密码' : '设置登录密码'}
</h3> </h3>
{message && ( {message && (
@@ -69,21 +90,23 @@ function ChangePassword() {
)} )}
<form onSubmit={handleSubmit} className="password-form"> <form onSubmit={handleSubmit} className="password-form">
<div className="form-group"> {hasPassword && (
<label htmlFor="login-old-password"></label> <div className="form-group">
<input <label htmlFor="login-old-password"></label>
type="password" <input
id="login-old-password" type="password"
value={oldPassword} id="login-old-password"
onChange={(e) => setOldPassword(e.target.value)} value={oldPassword}
placeholder="输入当前使用的登录密码" onChange={(e) => setOldPassword(e.target.value)}
disabled={loading} placeholder="输入当前使用的登录密码"
required disabled={loading}
/> required
</div> />
</div>
)}
<div className="form-group"> <div className="form-group">
<label htmlFor="login-new-password"></label> <label htmlFor="login-new-password">{hasPassword ? '新密码' : '设置密码'}</label>
<input <input
type="password" type="password"
id="login-new-password" id="login-new-password"
@@ -113,11 +136,11 @@ function ChangePassword() {
<button type="submit" disabled={loading} className="password-btn"> <button type="submit" disabled={loading} className="password-btn">
{loading ? ( {loading ? (
<> <>
<Icon icon="line-md:loading-loop" /> ... <Icon icon="line-md:loading-loop" /> {hasPassword ? '修改中...' : '设置中...'}
</> </>
) : ( ) : (
<> <>
<Icon icon="solar:check-read-bold" /> <Icon icon="solar:check-read-bold" /> {hasPassword ? '修改密码' : '设置密码'}
</> </>
)} )}
</button> </button>
@@ -130,6 +153,7 @@ function ChangePassword() {
<li>使</li> <li>使</li>
<li>使</li> <li>使</li>
<li></li> <li></li>
{!hasPassword && <li>使使</li>}
</ul> </ul>
</div> </div>
</div> </div>

View File

@@ -17,6 +17,7 @@ export interface User {
username: string; username: string;
avatar?: string; avatar?: string;
is_active: boolean; is_active: boolean;
has_password?: boolean;
created_at: string; created_at: string;
updated_at: string; updated_at: string;
} }