135 lines
3.9 KiB
TypeScript
135 lines
3.9 KiB
TypeScript
|
|
/**
|
||
|
|
* User Profile Page
|
||
|
|
* 个人信息页面
|
||
|
|
*/
|
||
|
|
import { useState, useEffect } from 'react';
|
||
|
|
import { Form, Input, Button, Card, Avatar, Space, Descriptions, Row, Col } from 'antd';
|
||
|
|
import { UserOutlined, MailOutlined, IdcardOutlined } from '@ant-design/icons';
|
||
|
|
import { request } from '../../utils/request';
|
||
|
|
import { auth } from '../../utils/auth';
|
||
|
|
import { useToast } from '../../contexts/ToastContext';
|
||
|
|
|
||
|
|
export function UserProfile() {
|
||
|
|
const [form] = Form.useForm();
|
||
|
|
const [loading, setLoading] = useState(false);
|
||
|
|
const [userProfile, setUserProfile] = useState<any>(null);
|
||
|
|
const toast = useToast();
|
||
|
|
const user = auth.getUser();
|
||
|
|
|
||
|
|
useEffect(() => {
|
||
|
|
loadUserProfile();
|
||
|
|
}, []);
|
||
|
|
|
||
|
|
const loadUserProfile = async () => {
|
||
|
|
setLoading(true);
|
||
|
|
try {
|
||
|
|
const { data } = await request.get('/users/me');
|
||
|
|
setUserProfile(data);
|
||
|
|
form.setFieldsValue({
|
||
|
|
email: data.email || '',
|
||
|
|
full_name: data.full_name || '',
|
||
|
|
});
|
||
|
|
} catch (error) {
|
||
|
|
toast.error('获取用户信息失败');
|
||
|
|
} finally {
|
||
|
|
setLoading(false);
|
||
|
|
}
|
||
|
|
};
|
||
|
|
|
||
|
|
const handleSubmit = async (values: any) => {
|
||
|
|
setLoading(true);
|
||
|
|
try {
|
||
|
|
await request.put('/users/me/profile', {
|
||
|
|
full_name: values.full_name,
|
||
|
|
email: values.email || null,
|
||
|
|
});
|
||
|
|
toast.success('个人信息更新成功');
|
||
|
|
|
||
|
|
// Update local user info
|
||
|
|
const updatedUser = { ...user, full_name: values.full_name, email: values.email };
|
||
|
|
auth.setUser(updatedUser);
|
||
|
|
|
||
|
|
// Reload profile
|
||
|
|
await loadUserProfile();
|
||
|
|
} catch (error: any) {
|
||
|
|
toast.error(error.response?.data?.detail || '更新失败');
|
||
|
|
} finally {
|
||
|
|
setLoading(false);
|
||
|
|
}
|
||
|
|
};
|
||
|
|
|
||
|
|
return (
|
||
|
|
<Row gutter={24}>
|
||
|
|
<Col span={8}>
|
||
|
|
{/* User Avatar and Basic Info Card */}
|
||
|
|
<Card bordered={false} loading={loading}>
|
||
|
|
<div style={{ textAlign: 'center' }}>
|
||
|
|
<Avatar size={100} icon={<UserOutlined />} />
|
||
|
|
<h2 style={{ marginTop: 24, marginBottom: 8 }}>
|
||
|
|
{userProfile?.full_name || userProfile?.username || '用户'}
|
||
|
|
</h2>
|
||
|
|
<p style={{ color: '#999', marginBottom: 24 }}>
|
||
|
|
@{userProfile?.username}
|
||
|
|
</p>
|
||
|
|
|
||
|
|
{userProfile && (
|
||
|
|
<Descriptions column={1} size="small">
|
||
|
|
<Descriptions.Item label="角色">
|
||
|
|
{userProfile.role === 'admin' ? '管理员' : '普通用户'}
|
||
|
|
</Descriptions.Item>
|
||
|
|
<Descriptions.Item label="创建时间">
|
||
|
|
{new Date(userProfile.created_at).toLocaleString('zh-CN')}
|
||
|
|
</Descriptions.Item>
|
||
|
|
</Descriptions>
|
||
|
|
)}
|
||
|
|
</div>
|
||
|
|
</Card>
|
||
|
|
</Col>
|
||
|
|
|
||
|
|
<Col span={16}>
|
||
|
|
{/* Edit Profile Form */}
|
||
|
|
<Card title="编辑个人信息" bordered={false}>
|
||
|
|
<Form
|
||
|
|
form={form}
|
||
|
|
layout="vertical"
|
||
|
|
onFinish={handleSubmit}
|
||
|
|
autoComplete="off"
|
||
|
|
>
|
||
|
|
<Form.Item
|
||
|
|
label="姓名"
|
||
|
|
name="full_name"
|
||
|
|
rules={[{ required: true, message: '请输入姓名' }]}
|
||
|
|
>
|
||
|
|
<Input
|
||
|
|
prefix={<IdcardOutlined />}
|
||
|
|
placeholder="请输入您的姓名"
|
||
|
|
size="large"
|
||
|
|
/>
|
||
|
|
</Form.Item>
|
||
|
|
|
||
|
|
<Form.Item
|
||
|
|
label="邮箱"
|
||
|
|
name="email"
|
||
|
|
rules={[
|
||
|
|
{ type: 'email', message: '请输入有效的邮箱地址' },
|
||
|
|
]}
|
||
|
|
>
|
||
|
|
<Input
|
||
|
|
prefix={<MailOutlined />}
|
||
|
|
placeholder="请输入邮箱地址(可选)"
|
||
|
|
size="large"
|
||
|
|
/>
|
||
|
|
</Form.Item>
|
||
|
|
|
||
|
|
<Form.Item>
|
||
|
|
<Button type="primary" htmlType="submit" loading={loading} size="large" block>
|
||
|
|
保存修改
|
||
|
|
</Button>
|
||
|
|
</Form.Item>
|
||
|
|
</Form>
|
||
|
|
</Card>
|
||
|
|
</Col>
|
||
|
|
</Row>
|
||
|
|
);
|
||
|
|
}
|