重构用户个人信息页面,将个人信息组件移至独立文件,整合用户信息获取与更新功能,优化表单处理逻辑,提升用户体验和代码可维护性。
This commit is contained in:
@@ -191,72 +191,7 @@ const ErrorPage = () => {
|
||||
);
|
||||
};
|
||||
|
||||
// 添加个人页面组件
|
||||
const ProfilePage = () => {
|
||||
const { logout, user } = useAuth();
|
||||
const navigate = useNavigate();
|
||||
|
||||
const handleLogout = async () => {
|
||||
if (confirm('确定要退出登录吗?')) {
|
||||
await logout();
|
||||
navigate('/mobile/login');
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="p-4">
|
||||
<h1 className="text-2xl font-bold mb-4">我的</h1>
|
||||
<div className="bg-white rounded-lg shadow p-4 mb-4">
|
||||
<div className="flex items-center mb-4">
|
||||
<div className="w-16 h-16 bg-blue-100 rounded-full flex items-center justify-center mr-4">
|
||||
{user?.avatar ? (
|
||||
<img
|
||||
src={user.avatar}
|
||||
alt={user?.nickname || user?.username || '用户'}
|
||||
className="w-16 h-16 rounded-full object-cover"
|
||||
/>
|
||||
) : (
|
||||
<span className="text-2xl text-blue-600">用户</span>
|
||||
)}
|
||||
</div>
|
||||
<div>
|
||||
<h2 className="text-xl font-semibold">{user?.nickname || user?.username || '未登录用户'}</h2>
|
||||
<p className="text-gray-500">个人信息</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div className="bg-white rounded-lg shadow mb-4">
|
||||
<div className="p-4 border-b">
|
||||
<span className="font-medium">设置</span>
|
||||
</div>
|
||||
<div className="divide-y">
|
||||
<div className="p-4 flex justify-between items-center">
|
||||
<span>账号安全</span>
|
||||
<span className="text-gray-400">›</span>
|
||||
</div>
|
||||
<div className="p-4 flex justify-between items-center">
|
||||
<span>通知设置</span>
|
||||
<span className="text-gray-400">›</span>
|
||||
</div>
|
||||
<div className="p-4 flex justify-between items-center">
|
||||
<span>隐私</span>
|
||||
<span className="text-gray-400">›</span>
|
||||
</div>
|
||||
<div className="p-4 flex justify-between items-center">
|
||||
<span>关于</span>
|
||||
<span className="text-gray-400">›</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<button
|
||||
onClick={handleLogout}
|
||||
className="w-full py-3 bg-red-600 text-white rounded-lg hover:bg-red-700 transition-colors"
|
||||
>
|
||||
退出登录
|
||||
</button>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
import ProfilePage from './pages_profile.tsx'
|
||||
|
||||
// 移动端布局组件 - 包含底部导航
|
||||
const MobileLayout = () => {
|
||||
|
||||
@@ -1,63 +1,114 @@
|
||||
import React from 'react'
|
||||
import { useNavigate } from 'react-router'
|
||||
import { useForm } from 'react-hook-form'
|
||||
import { useQuery, useMutation } from '@tanstack/react-query'
|
||||
import { UserAPI } from './api.ts'
|
||||
import type { User } from '../share/types.ts'
|
||||
import { useAuth } from './hooks.tsx'
|
||||
|
||||
export default function ProfilePage() {
|
||||
const navigate = useNavigate()
|
||||
const { register, handleSubmit, formState: { errors }, setValue } = useForm<Omit<User, 'id' | 'role' | 'avatar'> & { password?: string }>()
|
||||
const [loading, setLoading] = React.useState(false)
|
||||
const [user, setUser] = React.useState<User | null>(null)
|
||||
const { register, handleSubmit, formState: { errors }, setValue } = useForm<{
|
||||
username: string
|
||||
nickname: string
|
||||
email: string
|
||||
phone?: string
|
||||
password?: string
|
||||
}>()
|
||||
|
||||
// 获取当前用户信息
|
||||
React.useEffect(() => {
|
||||
const fetchUser = async () => {
|
||||
try {
|
||||
const res = await UserAPI.getUsers({ limit: 1 })
|
||||
if (res.data?.length > 0) {
|
||||
const userData = res.data[0]
|
||||
setUser(userData)
|
||||
setValue('username', userData.username)
|
||||
setValue('nickname', userData.nickname)
|
||||
setValue('email', userData.email)
|
||||
setValue('phone', userData.phone)
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('获取用户信息失败:', error)
|
||||
const { data: user } = useQuery({
|
||||
queryKey: ['currentUser'],
|
||||
queryFn: async () => {
|
||||
const res = await UserAPI.getUsers({ limit: 1 })
|
||||
if (res.data?.length > 0) {
|
||||
const userData = res.data[0]
|
||||
setValue('username', userData.username)
|
||||
setValue('nickname', userData.nickname || '')
|
||||
setValue('email', userData.email || '')
|
||||
setValue('phone', userData.phone || '')
|
||||
return userData
|
||||
}
|
||||
return null
|
||||
}
|
||||
fetchUser()
|
||||
}, [setValue])
|
||||
})
|
||||
|
||||
// 提交表单更新用户信息
|
||||
const onSubmit = async (data: User) => {
|
||||
try {
|
||||
setLoading(true)
|
||||
if (!user?.id) return
|
||||
|
||||
const updatedUser = await UserAPI.updateUser(user.id, {
|
||||
nickname: data.nickname,
|
||||
email: data.email,
|
||||
phone: data.phone,
|
||||
...(data.password ? { password: data.password } : {})
|
||||
})
|
||||
|
||||
setUser(updatedUser.data)
|
||||
alert('更新成功')
|
||||
} catch (error) {
|
||||
console.error('更新失败:', error)
|
||||
alert('更新失败')
|
||||
} finally {
|
||||
setLoading(false)
|
||||
|
||||
// 更新用户信息
|
||||
const { mutate: updateUser, isPending } = useMutation({
|
||||
mutationFn: async (data: {
|
||||
nickname: string
|
||||
email: string
|
||||
phone?: string
|
||||
password?: string
|
||||
}) => {
|
||||
if (!user?.id) throw new Error('用户ID不存在')
|
||||
return UserAPI.updateUser(user.id, {
|
||||
nickname: data.nickname,
|
||||
email: data.email,
|
||||
phone: data.phone,
|
||||
...(data.password ? { password: data.password } : {})
|
||||
})
|
||||
},
|
||||
onSuccess: (updatedUser) => {
|
||||
alert('更新成功')
|
||||
},
|
||||
onError: (error) => {
|
||||
console.error('更新失败:', error)
|
||||
alert('更新失败')
|
||||
}
|
||||
})
|
||||
|
||||
const onSubmit = (data: {
|
||||
username: string
|
||||
nickname: string
|
||||
email: string
|
||||
phone?: string
|
||||
password?: string
|
||||
}) => {
|
||||
updateUser({
|
||||
nickname: data.nickname,
|
||||
email: data.email,
|
||||
phone: data.phone,
|
||||
password: data.password
|
||||
})
|
||||
}
|
||||
|
||||
const { logout } = useAuth()
|
||||
|
||||
const handleLogout = async () => {
|
||||
if (confirm('确定要退出登录吗?')) {
|
||||
await logout()
|
||||
navigate('/mobile/login')
|
||||
}
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="p-4 max-w-md mx-auto">
|
||||
<h1 className="text-2xl font-bold mb-6 text-gray-800">个人信息</h1>
|
||||
<div className="p-4">
|
||||
<h1 className="text-2xl font-bold mb-4">我的</h1>
|
||||
<div className="bg-white rounded-lg shadow p-4 mb-4">
|
||||
<div className="flex items-center mb-4">
|
||||
<div className="w-16 h-16 bg-blue-100 rounded-full flex items-center justify-center mr-4">
|
||||
{user?.avatar ? (
|
||||
<img
|
||||
src={user.avatar}
|
||||
alt={user?.nickname || user?.username || '用户'}
|
||||
className="w-16 h-16 rounded-full object-cover"
|
||||
/>
|
||||
) : (
|
||||
<span className="text-2xl text-blue-600">用户</span>
|
||||
)}
|
||||
</div>
|
||||
<div>
|
||||
<h2 className="text-xl font-semibold">{user?.nickname || user?.username || '未登录用户'}</h2>
|
||||
<p className="text-gray-500">个人信息</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<form onSubmit={handleSubmit(onSubmit)} className="space-y-4">
|
||||
<div className="bg-white rounded-lg shadow p-4 mb-4">
|
||||
<h2 className="text-lg font-semibold mb-4">编辑信息</h2>
|
||||
<form onSubmit={handleSubmit(onSubmit)} className="space-y-4">
|
||||
<div>
|
||||
<label className="block text-sm font-medium text-gray-700 mb-1">用户名</label>
|
||||
<input
|
||||
@@ -113,10 +164,10 @@ export default function ProfilePage() {
|
||||
<div className="flex space-x-3 pt-4">
|
||||
<button
|
||||
type="submit"
|
||||
disabled={loading}
|
||||
disabled={isPending}
|
||||
className="px-4 py-2 bg-blue-600 text-white rounded-md hover:bg-blue-700 focus:outline-none focus:ring-2 focus:ring-blue-500 focus:ring-offset-2 disabled:opacity-50"
|
||||
>
|
||||
{loading ? '保存中...' : '保存'}
|
||||
{isPending ? '保存中...' : '保存'}
|
||||
</button>
|
||||
<button
|
||||
type="button"
|
||||
@@ -127,6 +178,38 @@ export default function ProfilePage() {
|
||||
</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
|
||||
<div className="bg-white rounded-lg shadow mb-4">
|
||||
<div className="p-4 border-b">
|
||||
<span className="font-medium">设置</span>
|
||||
</div>
|
||||
<div className="divide-y">
|
||||
<div className="p-4 flex justify-between items-center">
|
||||
<span>账号安全</span>
|
||||
<span className="text-gray-400">›</span>
|
||||
</div>
|
||||
<div className="p-4 flex justify-between items-center">
|
||||
<span>通知设置</span>
|
||||
<span className="text-gray-400">›</span>
|
||||
</div>
|
||||
<div className="p-4 flex justify-between items-center">
|
||||
<span>隐私</span>
|
||||
<span className="text-gray-400">›</span>
|
||||
</div>
|
||||
<div className="p-4 flex justify-between items-center">
|
||||
<span>关于</span>
|
||||
<span className="text-gray-400">›</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<button
|
||||
onClick={handleLogout}
|
||||
className="w-full py-3 bg-red-600 text-white rounded-lg hover:bg-red-700 transition-colors"
|
||||
>
|
||||
退出登录
|
||||
</button>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
Reference in New Issue
Block a user