新增设置页面,更新个人信息页面以包含导航按钮,优化用户体验和代码结构。
This commit is contained in:
@@ -192,6 +192,7 @@ const ErrorPage = () => {
|
||||
};
|
||||
|
||||
import ProfilePage from './pages_profile.tsx'
|
||||
import SettingsPage from './pages_settings.tsx'
|
||||
|
||||
// 移动端布局组件 - 包含底部导航
|
||||
const MobileLayout = () => {
|
||||
@@ -224,7 +225,7 @@ const MobileLayout = () => {
|
||||
<BellIcon className="w-6 h-6 mb-1" />
|
||||
<span className="text-xs">通知</span>
|
||||
</Link>
|
||||
<Link
|
||||
<Link
|
||||
to="/mobile/profile"
|
||||
className={`flex flex-col items-center py-2 px-4 ${
|
||||
location.pathname === '/mobile/profile' ? 'text-blue-600' : 'text-gray-500'
|
||||
@@ -273,6 +274,10 @@ const App = () => {
|
||||
{
|
||||
path: 'notifications',
|
||||
element: <NotificationsPage />
|
||||
},
|
||||
{
|
||||
path: 'settings',
|
||||
element: <SettingsPage />
|
||||
}
|
||||
]
|
||||
},
|
||||
|
||||
@@ -104,80 +104,13 @@ export default function ProfilePage() {
|
||||
<p className="text-gray-500">个人信息</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<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
|
||||
{...register('username')}
|
||||
disabled
|
||||
className="w-full px-3 py-2 border border-gray-300 rounded-md shadow-sm focus:outline-none focus:ring-blue-500 focus:border-blue-500 bg-gray-100"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<label className="block text-sm font-medium text-gray-700 mb-1">昵称</label>
|
||||
<input
|
||||
{...register('nickname', { required: '请输入昵称' })}
|
||||
className="w-full px-3 py-2 border border-gray-300 rounded-md shadow-sm focus:outline-none focus:ring-blue-500 focus:border-blue-500"
|
||||
/>
|
||||
{errors.nickname && <p className="mt-1 text-sm text-red-600">{errors.nickname.message}</p>}
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<label className="block text-sm font-medium text-gray-700 mb-1">邮箱</label>
|
||||
<input
|
||||
{...register('email', {
|
||||
required: '请输入邮箱',
|
||||
pattern: {
|
||||
value: /^[A-Z0-9._%+-]+@[A-Z0-9.-]+\.[A-Z]{2,}$/i,
|
||||
message: '请输入有效的邮箱地址'
|
||||
}
|
||||
})}
|
||||
type="email"
|
||||
className="w-full px-3 py-2 border border-gray-300 rounded-md shadow-sm focus:outline-none focus:ring-blue-500 focus:border-blue-500"
|
||||
/>
|
||||
{errors.email && <p className="mt-1 text-sm text-red-600">{errors.email.message}</p>}
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<label className="block text-sm font-medium text-gray-700 mb-1">手机号</label>
|
||||
<input
|
||||
{...register('phone')}
|
||||
className="w-full px-3 py-2 border border-gray-300 rounded-md shadow-sm focus:outline-none focus:ring-blue-500 focus:border-blue-500"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<label className="block text-sm font-medium text-gray-700 mb-1">新密码</label>
|
||||
<input
|
||||
{...register('password')}
|
||||
type="password"
|
||||
placeholder="留空则不修改密码"
|
||||
className="w-full px-3 py-2 border border-gray-300 rounded-md shadow-sm focus:outline-none focus:ring-blue-500 focus:border-blue-500"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div className="flex space-x-3 pt-4">
|
||||
<button
|
||||
type="submit"
|
||||
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"
|
||||
>
|
||||
{isPending ? '保存中...' : '保存'}
|
||||
</button>
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => navigate(-1)}
|
||||
className="px-4 py-2 bg-gray-200 text-gray-800 rounded-md hover:bg-gray-300 focus:outline-none focus:ring-2 focus:ring-gray-500 focus:ring-offset-2"
|
||||
>
|
||||
返回
|
||||
</button>
|
||||
</div>
|
||||
</form>
|
||||
|
||||
<button
|
||||
onClick={() => navigate('/mobile/settings')}
|
||||
className="w-full mt-4 py-2 bg-blue-100 text-blue-600 rounded-md hover:bg-blue-200 transition-colors"
|
||||
>
|
||||
编辑个人信息
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<div className="bg-white rounded-lg shadow mb-4">
|
||||
|
||||
110
client/mobile/pages_settings.tsx
Normal file
110
client/mobile/pages_settings.tsx
Normal file
@@ -0,0 +1,110 @@
|
||||
import React from 'react'
|
||||
import { useForm } from 'react-hook-form'
|
||||
import { useMutation } from '@tanstack/react-query'
|
||||
import { UserAPI } from './api.ts'
|
||||
import { useAuth } from './hooks.tsx'
|
||||
|
||||
export default function SettingsPage() {
|
||||
const { register, handleSubmit, formState: { errors } } = useForm<{
|
||||
nickname: string
|
||||
email: string
|
||||
phone?: string
|
||||
password?: string
|
||||
}>()
|
||||
|
||||
const { mutate: updateUser, isPending } = useMutation({
|
||||
mutationFn: async (data: {
|
||||
nickname: string
|
||||
email: string
|
||||
phone?: string
|
||||
password?: string
|
||||
}) => {
|
||||
const res = await UserAPI.getUsers({ limit: 1 })
|
||||
if (!res.data?.[0]?.id) throw new Error('用户ID不存在')
|
||||
return UserAPI.updateUser(res.data[0].id, {
|
||||
nickname: data.nickname,
|
||||
email: data.email,
|
||||
phone: data.phone,
|
||||
...(data.password ? { password: data.password } : {})
|
||||
})
|
||||
},
|
||||
onSuccess: () => alert('更新成功'),
|
||||
onError: (error) => {
|
||||
console.error('更新失败:', error)
|
||||
alert('更新失败')
|
||||
}
|
||||
})
|
||||
|
||||
const onSubmit = (data: {
|
||||
nickname: string
|
||||
email: string
|
||||
phone?: string
|
||||
password?: string
|
||||
}) => {
|
||||
updateUser(data)
|
||||
}
|
||||
|
||||
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">
|
||||
<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
|
||||
{...register('nickname', { required: '请输入昵称' })}
|
||||
className="w-full px-3 py-2 border border-gray-300 rounded-md shadow-sm focus:outline-none focus:ring-blue-500 focus:border-blue-500"
|
||||
/>
|
||||
{errors.nickname && <p className="mt-1 text-sm text-red-600">{errors.nickname.message}</p>}
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<label className="block text-sm font-medium text-gray-700 mb-1">邮箱</label>
|
||||
<input
|
||||
{...register('email', {
|
||||
required: '请输入邮箱',
|
||||
pattern: {
|
||||
value: /^[A-Z0-9._%+-]+@[A-Z0-9.-]+\.[A-Z]{2,}$/i,
|
||||
message: '请输入有效的邮箱地址'
|
||||
}
|
||||
})}
|
||||
type="email"
|
||||
className="w-full px-3 py-2 border border-gray-300 rounded-md shadow-sm focus:outline-none focus:ring-blue-500 focus:border-blue-500"
|
||||
/>
|
||||
{errors.email && <p className="mt-1 text-sm text-red-600">{errors.email.message}</p>}
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<label className="block text-sm font-medium text-gray-700 mb-1">手机号</label>
|
||||
<input
|
||||
{...register('phone')}
|
||||
className="w-full px-3 py-2 border border-gray-300 rounded-md shadow-sm focus:outline-none focus:ring-blue-500 focus:border-blue-500"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<label className="block text-sm font-medium text-gray-700 mb-1">新密码</label>
|
||||
<input
|
||||
{...register('password')}
|
||||
type="password"
|
||||
placeholder="留空则不修改密码"
|
||||
className="w-full px-3 py-2 border border-gray-300 rounded-md shadow-sm focus:outline-none focus:ring-blue-500 focus:border-blue-500"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div className="flex space-x-3 pt-4">
|
||||
<button
|
||||
type="submit"
|
||||
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"
|
||||
>
|
||||
{isPending ? '保存中...' : '保存'}
|
||||
</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
Reference in New Issue
Block a user