jiachenlong/frontend/src/pages/Settings.jsx

300 lines
11 KiB
JavaScript

import React, { useState, useEffect } from 'react'
import { useNavigate } from 'react-router-dom'
export default function Settings() {
const [loading, setLoading] = useState(true)
const [saving, setSaving] = useState(false)
const [error, setError] = useState('')
const [success, setSuccess] = useState('')
const [form, setForm] = useState({
username: '',
email: '',
phone: '',
bio: ''
})
const [passwordForm, setPasswordForm] = useState({
oldPassword: '',
newPassword: '',
confirmPassword: ''
})
const navigate = useNavigate()
const token = localStorage.getItem('token')
useEffect(() => {
fetchUserInfo()
}, [])
const fetchUserInfo = async () => {
try {
const res = await fetch('/api/users/me', {
headers: { 'Authorization': 'Bearer ' + token }
})
if (res.ok) {
const data = await res.json()
console.log('用户信息:', data)
setForm({
username: data.username || '',
email: data.email || '',
phone: data.phone || '',
bio: data.bio || ''
})
} else {
console.error('获取用户信息失败:', res.status)
}
} catch (e) {
console.error('获取用户信息异常:', e)
// 尝试从本地存储获取
const userStr = localStorage.getItem('user')
if (userStr) {
try {
const user = JSON.parse(userStr)
setForm({
username: user.username || '',
email: user.email || '',
phone: user.phone || '',
bio: user.bio || ''
})
} catch (e2) {
setError('获取用户信息失败')
}
}
} finally {
setLoading(false)
}
}
const handleSubmit = async (e) => {
e.preventDefault()
setError('')
setSuccess('')
setSaving(true)
try {
const res = await fetch('/api/users/me', {
method: 'PUT',
headers: {
'Authorization': 'Bearer ' + token,
'Content-Type': 'application/json'
},
body: JSON.stringify(form)
})
if (res.ok) {
const data = await res.json()
setSuccess('保存成功!')
// 更新本地存储的用户信息
const userStr = localStorage.getItem('user')
if (userStr) {
const user = JSON.parse(userStr)
user.username = data.username || user.username
user.email = data.email || user.email
user.phone = data.phone || user.phone
localStorage.setItem('user', JSON.stringify(user))
}
} else {
const data = await res.json()
setError(data.error?.message || '保存失败')
}
} catch (e) {
setError('保存失败,请重试')
} finally {
setSaving(false)
}
}
const handlePasswordChange = async (e) => {
e.preventDefault()
setError('')
setSuccess('')
if (!passwordForm.oldPassword) {
setError('请输入当前密码')
return
}
if (passwordForm.newPassword !== passwordForm.confirmPassword) {
setError('两次输入的密码不一致')
return
}
if (passwordForm.newPassword.length < 6) {
setError('密码长度至少6位')
return
}
setSaving(true)
console.log('开始修改密码...')
try {
console.log('发送请求到 /api/auth/change-password')
const res = await fetch('/api/auth/change-password', {
method: 'POST',
headers: {
'Authorization': 'Bearer ' + token,
'Content-Type': 'application/json'
},
body: JSON.stringify({
old_password: passwordForm.oldPassword,
new_password: passwordForm.newPassword
})
})
console.log('响应状态:', res.status)
const data = await res.json()
console.log('响应数据:', data)
if (res.ok) {
setSuccess('密码修改成功!')
setPasswordForm({ oldPassword: '', newPassword: '', confirmPassword: '' })
} else {
setError(data.error?.message || data.detail || '密码修改失败')
}
} catch (e) {
setError('密码修改失败,请重试')
} finally {
setSaving(false)
}
}
if (loading) {
return (
<div style={{ minHeight: '100vh', background: '#0f172a', display: 'flex', alignItems: 'center', justifyContent: 'center' }}>
<div style={{ color: '#fff' }}>加载中...</div>
</div>
)
}
return (
<div style={{ minHeight: '100vh', background: '#0f172a', padding: '20px', paddingBottom: '80px' }}>
{/* 顶部导航 */}
<div style={{ display: 'flex', alignItems: 'center', marginBottom: '24px' }}>
<div onClick={() => navigate('/')} style={{ cursor: 'pointer', fontSize: '20px' }}></div>
<div style={{ color: '#fff', fontSize: '18px', fontWeight: 'bold', marginLeft: '16px' }}>个人设置</div>
</div>
{error && <div style={{ background: '#fee2e2', color: '#dc2626', padding: '12px', borderRadius: '8px', marginBottom: '16px' }}>{error}</div>}
{success && <div style={{ background: '#dcfce7', color: '#16a34a', padding: '12px', borderRadius: '8px', marginBottom: '16px' }}>{success}</div>}
{/* 基本信息 */}
<div style={{ background: '#1e293b', borderRadius: '12px', padding: '20px', marginBottom: '16px' }}>
<div style={{ color: '#fff', fontSize: '16px', fontWeight: 'bold', marginBottom: '16px' }}>基本信息</div>
<form onSubmit={handleSubmit}>
<div style={{ marginBottom: '16px' }}>
<label style={{ display: 'block', color: '#94a3b8', fontSize: '14px', marginBottom: '8px' }}>用户名</label>
<input
type="text"
value={form.username}
onChange={(e) => setForm({ ...form, username: e.target.value })}
style={{ width: '100%', padding: '12px', borderRadius: '8px', border: '1px solid #334155', background: '#0f172a', color: '#fff' }}
/>
</div>
<div style={{ marginBottom: '16px' }}>
<label style={{ display: 'block', color: '#94a3b8', fontSize: '14px', marginBottom: '8px' }}>邮箱</label>
<input
type="email"
value={form.email}
onChange={(e) => setForm({ ...form, email: e.target.value })}
style={{ width: '100%', padding: '12px', borderRadius: '8px', border: '1px solid #334155', background: '#0f172a', color: '#fff' }}
/>
</div>
<div style={{ marginBottom: '16px' }}>
<label style={{ display: 'block', color: '#94a3b8', fontSize: '14px', marginBottom: '8px' }}>手机号</label>
<input
type="tel"
value={form.phone}
onChange={(e) => setForm({ ...form, phone: e.target.value })}
style={{ width: '100%', padding: '12px', borderRadius: '8px', border: '1px solid #334155', background: '#0f172a', color: '#fff' }}
/>
</div>
<div style={{ marginBottom: '16px' }}>
<label style={{ display: 'block', color: '#94a3b8', fontSize: '14px', marginBottom: '8px' }}>简介</label>
<textarea
value={form.bio}
onChange={(e) => setForm({ ...form, bio: e.target.value })}
rows={3}
style={{ width: '100%', padding: '12px', borderRadius: '8px', border: '1px solid #334155', background: '#0f172a', color: '#fff', resize: 'none' }}
/>
</div>
<button
type="submit"
disabled={saving}
style={{ width: '100%', padding: '14px', borderRadius: '8px', border: 'none', background: '#3b82f6', color: '#fff', fontSize: '16px', cursor: saving ? 'not-allowed' : 'pointer', opacity: saving ? 0.6 : 1 }}
>
{saving ? '保存中...' : '保存修改'}
</button>
</form>
</div>
{/* 修改密码 */}
<div style={{ background: '#1e293b', borderRadius: '12px', padding: '20px', marginBottom: '16px' }}>
<div style={{ color: '#fff', fontSize: '16px', fontWeight: 'bold', marginBottom: '16px' }}>修改密码</div>
<form onSubmit={handlePasswordChange}>
<div style={{ marginBottom: '16px' }}>
<label style={{ display: 'block', color: '#94a3b8', fontSize: '14px', marginBottom: '8px' }}>当前密码</label>
<input
type="password"
value={passwordForm.oldPassword}
onChange={(e) => setPasswordForm({ ...passwordForm, oldPassword: e.target.value })}
style={{ width: '100%', padding: '12px', borderRadius: '8px', border: '1px solid #334155', background: '#0f172a', color: '#fff' }}
placeholder="请输入当前密码"
/>
</div>
<div style={{ marginBottom: '16px' }}>
<label style={{ display: 'block', color: '#94a3b8', fontSize: '14px', marginBottom: '8px' }}>新密码</label>
<input
type="password"
value={passwordForm.newPassword}
onChange={(e) => setPasswordForm({ ...passwordForm, newPassword: e.target.value })}
style={{ width: '100%', padding: '12px', borderRadius: '8px', border: '1px solid #334155', background: '#0f172a', color: '#fff' }}
placeholder="至少6位"
/>
</div>
<div style={{ marginBottom: '16px' }}>
<label style={{ display: 'block', color: '#94a3b8', fontSize: '14px', marginBottom: '8px' }}>确认新密码</label>
<input
type="password"
value={passwordForm.confirmPassword}
onChange={(e) => setPasswordForm({ ...passwordForm, confirmPassword: e.target.value })}
style={{ width: '100%', padding: '12px', borderRadius: '8px', border: '1px solid #334155', background: '#0f172a', color: '#fff' }}
placeholder="再次输入新密码"
/>
</div>
<button
type="submit"
disabled={saving}
style={{ width: '100%', padding: '14px', borderRadius: '8px', border: 'none', background: '#f59e0b', color: '#fff', fontSize: '16px', cursor: saving ? 'not-allowed' : 'pointer', opacity: saving ? 0.6 : 1 }}
>
{saving ? '修改中...' : '修改密码'}
</button>
</form>
</div>
{/* 退出登录 */}
<div style={{ background: '#1e293b', borderRadius: '12px', padding: '20px' }}>
<button
onClick={() => {
localStorage.removeItem('token')
localStorage.removeItem('user')
window.location.hash = '#/login'
}}
style={{ width: '100%', padding: '14px', borderRadius: '8px', border: '1px solid #ef4444', background: 'transparent', color: '#ef4444', fontSize: '16px', cursor: 'pointer' }}
>
退出登录
</button>
</div>
</div>
)
}