Files
to_docx/client/src/pages/Profile.jsx
T

185 lines
6.9 KiB
React
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import { useState, useEffect } from 'react';
import { useNavigate } from 'react-router-dom';
import { useAuth } from '../context/AuthContext';
export default function Profile() {
const { user } = useAuth();
const navigate = useNavigate();
const [newEmail, setNewEmail] = useState('');
const [newPassword, setNewPassword] = useState('');
const [emailCode, setEmailCode] = useState('');
const [loadingCode, setLoadingCode] = useState(false);
const [loadingSubmit, setLoadingSubmit] = useState(false);
const [message, setMessage] = useState('');
const [error, setError] = useState('');
const [smtpEnabled, setSmtpEnabled] = useState(false);
useEffect(() => {
fetch('/api/sys/config')
.then(res => res.json())
.then(data => {
if (data && data.smtp_enabled !== undefined) {
setSmtpEnabled(data.smtp_enabled);
}
})
.catch(err => console.error("获取系统配置失败:", err));
}, []);
if (!user) {
navigate('/login');
return null;
}
const handleSendCode = async () => {
setError('');
setMessage('');
setLoadingCode(true);
try {
const token = localStorage.getItem('token');
const res = await fetch('/api/auth/send-code', {
method: 'POST',
headers: { Authorization: `Bearer ${token}` }
});
const data = await res.json();
if (!res.ok) throw new Error(data.detail || data.error);
setMessage(data.message || '验证码已发送到原邮箱 (请查看后端控制台输出模拟邮件)');
} catch (err) {
setError(err.message);
} finally {
setLoadingCode(false);
}
};
const handleSubmit = async (e) => {
e.preventDefault();
setError('');
setMessage('');
if (!newEmail && !newPassword) {
setError('请输入你要修改的新邮箱或新密码');
return;
}
if (smtpEnabled && !emailCode) {
setError('请输入原邮箱收到的验证码');
return;
}
setLoadingSubmit(true);
try {
const token = localStorage.getItem('token');
const res = await fetch('/api/auth/update-profile', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
Authorization: `Bearer ${token}`
},
body: JSON.stringify({
email_code: emailCode || null,
new_email: newEmail || null,
new_password: newPassword || null
})
});
const data = await res.json();
if (!res.ok) throw new Error(data.detail || data.error);
setMessage('信息更新成功!如果您修改了密码,建议重新登录。');
setNewEmail('');
setNewPassword('');
setEmailCode('');
} catch (err) {
setError(err.message);
} finally {
setLoadingSubmit(false);
}
};
return (
<div style={styles.container}>
<div style={styles.card}>
<h2 style={styles.title}>修改个人信息</h2>
{error && <div style={styles.error}>{error}</div>}
{message && <div style={styles.success}>{message}</div>}
<div style={styles.infoBox}>
<p><strong>当前账号</strong> {user.username}</p>
<p><strong>当前邮箱</strong> {user.email || '未绑定'}</p>
</div>
<form onSubmit={handleSubmit}>
<div style={styles.formGroup}>
<label style={styles.label}>新邮箱 (不修改请留空)</label>
<input
type="email"
value={newEmail}
onChange={(e) => setNewEmail(e.target.value)}
style={styles.input}
/>
</div>
<div style={styles.formGroup}>
<label style={styles.label}>新密码 (不修改请留空)</label>
<input
type="password"
value={newPassword}
onChange={(e) => setNewPassword(e.target.value)}
style={styles.input}
minLength={6}
/>
</div>
<hr style={styles.divider} />
{smtpEnabled && (
<div style={styles.formGroup}>
<label style={styles.label}>验证码校验 (原邮箱)</label>
<div style={styles.flexRow}>
<input
type="text"
value={emailCode}
onChange={(e) => setEmailCode(e.target.value)}
style={{...styles.input, flex: 1}}
placeholder="请输入6位验证码"
maxLength={6}
/>
<button
type="button"
onClick={handleSendCode}
disabled={loadingCode}
style={styles.btnSecondary}
>
{loadingCode ? '发送中...' : '获取验证码'}
</button>
</div>
<small style={styles.hintText}>我们向您的原邮箱发送了一封包含验证码的邮件请查收以验证您的身份</small>
</div>
)}
<button type="submit" style={styles.button} disabled={loadingSubmit}>
{loadingSubmit ? '提交中...' : '确认修改'}
</button>
</form>
</div>
</div>
);
}
const styles = {
container: { display: 'flex', justifyContent: 'center', padding: '40px 20px', minHeight: 'calc(100vh - 60px)' },
card: { width: '100%', maxWidth: '450px', background: 'white', borderRadius: '8px', padding: '30px', boxShadow: '0 2px 10px rgba(0,0,0,0.05)' },
title: { margin: '0 0 20px 0', fontSize: '20px', color: '#111827' },
infoBox: { backgroundColor: '#f9fafb', padding: '15px', borderRadius: '6px', marginBottom: '20px', fontSize: '14px', color: '#4b5563', border: '1px solid #e5e7eb' },
formGroup: { marginBottom: '16px' },
label: { display: 'block', marginBottom: '6px', fontSize: '13px', fontWeight: '500', color: '#374151' },
input: { width: '100%', padding: '10px', border: '1px solid #d1d5db', borderRadius: '6px', fontSize: '14px', boxSizing: 'border-box' },
flexRow: { display: 'flex', gap: '10px' },
btnSecondary: { padding: '0 15px', backgroundColor: '#f3f4f6', border: '1px solid #d1d5db', borderRadius: '6px', cursor: 'pointer', fontSize: '13px', color: '#374151', whiteSpace: 'nowrap' },
button: { width: '100%', padding: '12px', backgroundColor: '#3b82f6', color: 'white', border: 'none', borderRadius: '6px', fontSize: '15px', cursor: 'pointer', marginTop: '10px', fontWeight: '500' },
error: { color: '#ef4444', backgroundColor: '#fef2f2', padding: '10px', borderRadius: '6px', marginBottom: '16px', fontSize: '13px' },
success: { color: '#10b981', backgroundColor: '#ecfdf5', padding: '10px', borderRadius: '6px', marginBottom: '16px', fontSize: '13px' },
divider: { border: 'none', borderTop: '1px solid #e5e7eb', margin: '20px 0' },
hintText: { fontSize: '12px', color: '#9ca3af', display: 'block', marginTop: '6px' }
};