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

View File

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