|
@@ -0,0 +1,239 @@
|
|
|
|
|
+import { useState, useEffect } from 'react';
|
|
|
|
|
+import { Button } from '@/client/components/ui/button';
|
|
|
|
|
+import { Card, CardContent, CardDescription, CardHeader, CardTitle } from '@/client/components/ui/card';
|
|
|
|
|
+import { Input } from '@/client/components/ui/input';
|
|
|
|
|
+import { Label } from '@/client/components/ui/label';
|
|
|
|
|
+import { Badge } from '@/client/components/ui/badge';
|
|
|
|
|
+import { useUser } from '../hooks/AuthProvider';
|
|
|
|
|
+import { User, Mail, Phone, Calendar, Shield } from 'lucide-react';
|
|
|
|
|
+import { format } from 'date-fns';
|
|
|
|
|
+
|
|
|
|
|
+export default function ProfilePage() {
|
|
|
|
|
+ const { user, updateUser } = useUser();
|
|
|
|
|
+ const [isEditing, setIsEditing] = useState(false);
|
|
|
|
|
+ const [formData, setFormData] = useState({
|
|
|
|
|
+ username: '',
|
|
|
|
|
+ email: '',
|
|
|
|
|
+ phone: '',
|
|
|
|
|
+ avatar: ''
|
|
|
|
|
+ });
|
|
|
|
|
+
|
|
|
|
|
+ useEffect(() => {
|
|
|
|
|
+ if (user) {
|
|
|
|
|
+ setFormData({
|
|
|
|
|
+ username: user.username || '',
|
|
|
|
|
+ email: user.email || '',
|
|
|
|
|
+ phone: user.phone || '',
|
|
|
|
|
+ avatar: user.avatar || ''
|
|
|
|
|
+ });
|
|
|
|
|
+ }
|
|
|
|
|
+ }, [user]);
|
|
|
|
|
+
|
|
|
|
|
+ const handleSave = async () => {
|
|
|
|
|
+ try {
|
|
|
|
|
+ await updateUser(formData);
|
|
|
|
|
+ setIsEditing(false);
|
|
|
|
|
+ } catch (error) {
|
|
|
|
|
+ console.error('更新用户信息失败:', error);
|
|
|
|
|
+ }
|
|
|
|
|
+ };
|
|
|
|
|
+
|
|
|
|
|
+ const handleCancel = () => {
|
|
|
|
|
+ setFormData({
|
|
|
|
|
+ username: user?.username || '',
|
|
|
|
|
+ email: user?.email || '',
|
|
|
|
|
+ phone: user?.phone || '',
|
|
|
|
|
+ avatar: user?.avatar || ''
|
|
|
|
|
+ });
|
|
|
|
|
+ setIsEditing(false);
|
|
|
|
|
+ };
|
|
|
|
|
+
|
|
|
|
|
+ if (!user) {
|
|
|
|
|
+ return (
|
|
|
|
|
+ <div className="min-h-screen flex items-center justify-center">
|
|
|
|
|
+ <Card>
|
|
|
|
|
+ <CardContent className="pt-6">
|
|
|
|
|
+ <p>请先登录</p>
|
|
|
|
|
+ </CardContent>
|
|
|
|
|
+ </Card>
|
|
|
|
|
+ </div>
|
|
|
|
|
+ );
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ return (
|
|
|
|
|
+ <div className="max-w-4xl mx-auto p-6">
|
|
|
|
|
+ <div className="mb-6">
|
|
|
|
|
+ <h1 className="text-3xl font-bold text-gray-900 mb-2">个人中心</h1>
|
|
|
|
|
+ <p className="text-gray-600">管理您的个人信息和账户设置</p>
|
|
|
|
|
+ </div>
|
|
|
|
|
+
|
|
|
|
|
+ <div className="grid gap-6 md:grid-cols-2">
|
|
|
|
|
+ {/* 基本信息 */}
|
|
|
|
|
+ <Card>
|
|
|
|
|
+ <CardHeader>
|
|
|
|
|
+ <CardTitle className="flex items-center gap-2">
|
|
|
|
|
+ <User className="h-5 w-5" />
|
|
|
|
|
+ 基本信息
|
|
|
|
|
+ </CardTitle>
|
|
|
|
|
+ <CardDescription>您的账户基本信息</CardDescription>
|
|
|
|
|
+ </CardHeader>
|
|
|
|
|
+ <CardContent className="space-y-4">
|
|
|
|
|
+ <div className="flex items-center space-x-4">
|
|
|
|
|
+ <div className="w-20 h-20 bg-gray-200 rounded-full flex items-center justify-center">
|
|
|
|
|
+ <User className="h-10 w-10 text-gray-400" />
|
|
|
|
|
+ </div>
|
|
|
|
|
+ <div>
|
|
|
|
|
+ <h3 className="font-semibold">{user.username}</h3>
|
|
|
|
|
+ <Badge variant="outline">{user.userType === 'premium' ? '高级会员' : '普通会员'}</Badge>
|
|
|
|
|
+ </div>
|
|
|
|
|
+ </div>
|
|
|
|
|
+
|
|
|
|
|
+ <div className="space-y-3">
|
|
|
|
|
+ <div>
|
|
|
|
|
+ <Label>用户ID</Label>
|
|
|
|
|
+ <p className="text-sm text-gray-600">{user.id}</p>
|
|
|
|
|
+ </div>
|
|
|
|
|
+ <div>
|
|
|
|
|
+ <Label>注册时间</Label>
|
|
|
|
|
+ <p className="text-sm text-gray-600 flex items-center gap-1">
|
|
|
|
|
+ <Calendar className="h-4 w-4" />
|
|
|
|
|
+ {user.createdAt && format(new Date(user.createdAt), 'yyyy-MM-dd HH:mm')}
|
|
|
|
|
+ </p>
|
|
|
|
|
+ </div>
|
|
|
|
|
+ </div>
|
|
|
|
|
+ </CardContent>
|
|
|
|
|
+ </Card>
|
|
|
|
|
+
|
|
|
|
|
+ {/* 联系信息 */}
|
|
|
|
|
+ <Card>
|
|
|
|
|
+ <CardHeader>
|
|
|
|
|
+ <CardTitle>联系信息</CardTitle>
|
|
|
|
|
+ <CardDescription>更新您的联系方式</CardDescription>
|
|
|
|
|
+ </CardHeader>
|
|
|
|
|
+ <CardContent className="space-y-4">
|
|
|
|
|
+ {isEditing ? (
|
|
|
|
|
+ <>
|
|
|
|
|
+ <div>
|
|
|
|
|
+ <Label htmlFor="username">用户名</Label>
|
|
|
|
|
+ <Input
|
|
|
|
|
+ id="username"
|
|
|
|
|
+ value={formData.username}
|
|
|
|
|
+ onChange={(e) => setFormData({ ...formData, username: e.target.value })}
|
|
|
|
|
+ />
|
|
|
|
|
+ </div>
|
|
|
|
|
+ <div>
|
|
|
|
|
+ <Label htmlFor="email">邮箱</Label>
|
|
|
|
|
+ <Input
|
|
|
|
|
+ id="email"
|
|
|
|
|
+ type="email"
|
|
|
|
|
+ value={formData.email}
|
|
|
|
|
+ onChange={(e) => setFormData({ ...formData, email: e.target.value })}
|
|
|
|
|
+ />
|
|
|
|
|
+ </div>
|
|
|
|
|
+ <div>
|
|
|
|
|
+ <Label htmlFor="phone">手机号</Label>
|
|
|
|
|
+ <Input
|
|
|
|
|
+ id="phone"
|
|
|
|
|
+ type="tel"
|
|
|
|
|
+ value={formData.phone}
|
|
|
|
|
+ onChange={(e) => setFormData({ ...formData, phone: e.target.value })}
|
|
|
|
|
+ />
|
|
|
|
|
+ </div>
|
|
|
|
|
+ <div className="flex gap-2">
|
|
|
|
|
+ <Button onClick={handleSave}>保存</Button>
|
|
|
|
|
+ <Button variant="outline" onClick={handleCancel}>取消</Button>
|
|
|
|
|
+ </div>
|
|
|
|
|
+ </>
|
|
|
|
|
+ ) : (
|
|
|
|
|
+ <>
|
|
|
|
|
+ <div>
|
|
|
|
|
+ <Label>用户名</Label>
|
|
|
|
|
+ <p className="text-sm text-gray-600">{user.username}</p>
|
|
|
|
|
+ </div>
|
|
|
|
|
+ <div>
|
|
|
|
|
+ <Label>邮箱</Label>
|
|
|
|
|
+ <p className="text-sm text-gray-600 flex items-center gap-1">
|
|
|
|
|
+ <Mail className="h-4 w-4" />
|
|
|
|
|
+ {user.email || '未设置'}
|
|
|
|
|
+ </p>
|
|
|
|
|
+ </div>
|
|
|
|
|
+ <div>
|
|
|
|
|
+ <Label>手机号</Label>
|
|
|
|
|
+ <p className="text-sm text-gray-600 flex items-center gap-1">
|
|
|
|
|
+ <Phone className="h-4 w-4" />
|
|
|
|
|
+ {user.phone || '未设置'}
|
|
|
|
|
+ </p>
|
|
|
|
|
+ </div>
|
|
|
|
|
+ <Button onClick={() => setIsEditing(true)}>编辑信息</Button>
|
|
|
|
|
+ </>
|
|
|
|
|
+ )}
|
|
|
|
|
+ </CardContent>
|
|
|
|
|
+ </Card>
|
|
|
|
|
+
|
|
|
|
|
+ {/* 账户状态 */}
|
|
|
|
|
+ <Card>
|
|
|
|
|
+ <CardHeader>
|
|
|
|
|
+ <CardTitle className="flex items-center gap-2">
|
|
|
|
|
+ <Shield className="h-5 w-5" />
|
|
|
|
|
+ 账户状态
|
|
|
|
|
+ </CardTitle>
|
|
|
|
|
+ <CardDescription>您的会员状态和余额信息</CardDescription>
|
|
|
|
|
+ </CardHeader>
|
|
|
|
|
+ <CardContent>
|
|
|
|
|
+ <div className="space-y-4">
|
|
|
|
|
+ <div>
|
|
|
|
|
+ <Label>会员等级</Label>
|
|
|
|
|
+ <div className="flex items-center gap-2">
|
|
|
|
|
+ <Badge className={user.userType === 'premium' ? 'bg-purple-500' : 'bg-gray-500'}>
|
|
|
|
|
+ {user.userType === 'premium' ? '高级会员' : '普通会员'}
|
|
|
|
|
+ </Badge>
|
|
|
|
|
+ {user.userType !== 'premium' && (
|
|
|
|
|
+ <Button size="sm" onClick={() => navigate('/member/recharge')}>
|
|
|
|
|
+ 升级会员
|
|
|
|
|
+ </Button>
|
|
|
|
|
+ )}
|
|
|
|
|
+ </div>
|
|
|
|
|
+ </div>
|
|
|
|
|
+ <div>
|
|
|
|
|
+ <Label>剩余文档处理次数</Label>
|
|
|
|
|
+ <p className="text-2xl font-bold text-blue-600">{user.remainingCount || 0}</p>
|
|
|
|
|
+ </div>
|
|
|
|
|
+ <div>
|
|
|
|
|
+ <Label>有效期至</Label>
|
|
|
|
|
+ <p className="text-sm text-gray-600">
|
|
|
|
|
+ {user.expireAt ? format(new Date(user.expireAt), 'yyyy-MM-dd') : '长期有效'}
|
|
|
|
|
+ </p>
|
|
|
|
|
+ </div>
|
|
|
|
|
+ </div>
|
|
|
|
|
+ </CardContent>
|
|
|
|
|
+ </Card>
|
|
|
|
|
+
|
|
|
|
|
+ {/* 使用统计 */}
|
|
|
|
|
+ <Card>
|
|
|
|
|
+ <CardHeader>
|
|
|
|
|
+ <CardTitle>使用统计</CardTitle>
|
|
|
|
|
+ <CardDescription>您的使用记录</CardDescription>
|
|
|
|
|
+ </CardHeader>
|
|
|
|
|
+ <CardContent>
|
|
|
|
|
+ <div className="space-y-3">
|
|
|
|
|
+ <div className="flex justify-between">
|
|
|
|
|
+ <span>本月处理文档</span>
|
|
|
|
|
+ <span className="font-semibold">{user.monthlyUsage || 0} 份</span>
|
|
|
|
|
+ </div>
|
|
|
|
|
+ <div className="flex justify-between">
|
|
|
|
|
+ <span>总处理文档</span>
|
|
|
|
|
+ <span className="font-semibold">{user.totalUsage || 0} 份</span>
|
|
|
|
|
+ </div>
|
|
|
|
|
+ <div className="flex justify-between">
|
|
|
|
|
+ <span>最后使用时间</span>
|
|
|
|
|
+ <span className="text-sm text-gray-600">
|
|
|
|
|
+ {user.lastUsedAt ? format(new Date(user.lastUsedAt), 'MM-dd HH:mm') : '从未使用'}
|
|
|
|
|
+ </span>
|
|
|
|
|
+ </div>
|
|
|
|
|
+ </div>
|
|
|
|
|
+ </CardContent>
|
|
|
|
|
+ </Card>
|
|
|
|
|
+ </div>
|
|
|
|
|
+ </div>
|
|
|
|
|
+ );
|
|
|
|
|
+}
|