备份:修复前完整项目快照 2026-04-19
This commit is contained in:
@@ -0,0 +1,283 @@
|
||||
import React, { useState, useEffect } from 'react';
|
||||
import { Card, Typography, Form, Input, Button, Avatar, Space, Upload, message, Row, Col, Modal } from 'antd';
|
||||
import { UserOutlined, LockOutlined, PhoneOutlined, MailOutlined, UploadOutlined } from '@ant-design/icons';
|
||||
import { useAuthStore } from '../store/authStore';
|
||||
|
||||
const { Title, Paragraph } = Typography;
|
||||
|
||||
const ProfilePage: React.FC = () => {
|
||||
const { user, setUser } = useAuthStore();
|
||||
const [form] = Form.useForm();
|
||||
const [passwordForm] = Form.useForm();
|
||||
const [loading, setLoading] = useState(false);
|
||||
const [passwordModalVisible, setPasswordModalVisible] = useState(false);
|
||||
const [avatarUrl, setAvatarUrl] = useState<string | undefined>(user?.avatar);
|
||||
const [passportUrl, setPassportUrl] = useState<string | undefined>(user?.passport);
|
||||
const [driverLicenseUrl, setDriverLicenseUrl] = useState<string | undefined>(user?.driverLicense);
|
||||
|
||||
useEffect(() => {
|
||||
if (user) {
|
||||
form.setFieldsValue({
|
||||
name: user.name,
|
||||
phone: user.phone,
|
||||
email: user.email
|
||||
});
|
||||
setAvatarUrl(user.avatar);
|
||||
setPassportUrl(user.passport);
|
||||
setDriverLicenseUrl(user.driverLicense);
|
||||
}
|
||||
}, [user, form]);
|
||||
|
||||
const handleSubmit = async () => {
|
||||
try {
|
||||
const values = await form.validateFields();
|
||||
setLoading(true);
|
||||
|
||||
// 调用API更新用户信息
|
||||
const response = await fetch(`/api/users/${user?.id}`, {
|
||||
method: 'PUT',
|
||||
headers: {
|
||||
'Content-Type': 'application/json'
|
||||
},
|
||||
body: JSON.stringify({
|
||||
...values,
|
||||
avatar: avatarUrl,
|
||||
passport: passportUrl,
|
||||
driverLicense: driverLicenseUrl
|
||||
})
|
||||
});
|
||||
|
||||
const data = await response.json();
|
||||
if (data.success) {
|
||||
message.success('个人信息已更新');
|
||||
// 更新authStore中的用户信息
|
||||
if (user) {
|
||||
const updatedUser = {
|
||||
...user,
|
||||
name: values.name,
|
||||
email: values.email,
|
||||
phone: values.phone,
|
||||
avatar: avatarUrl
|
||||
};
|
||||
setUser(updatedUser);
|
||||
}
|
||||
} else {
|
||||
message.error(data.message || '更新失败');
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('提交失败:', error);
|
||||
message.error('更新失败,请重试');
|
||||
} finally {
|
||||
setLoading(false);
|
||||
}
|
||||
};
|
||||
|
||||
const handlePasswordSubmit = async () => {
|
||||
try {
|
||||
const values = await passwordForm.validateFields();
|
||||
|
||||
// 调用API更新密码
|
||||
const response = await fetch(`/api/users/${user?.id}/password`, {
|
||||
method: 'PUT',
|
||||
headers: {
|
||||
'Content-Type': 'application/json'
|
||||
},
|
||||
body: JSON.stringify({
|
||||
currentPassword: values.currentPassword,
|
||||
newPassword: values.newPassword
|
||||
})
|
||||
});
|
||||
|
||||
const data = await response.json();
|
||||
if (data.success) {
|
||||
message.success('密码已更新');
|
||||
setPasswordModalVisible(false);
|
||||
passwordForm.resetFields();
|
||||
} else {
|
||||
message.error(data.message || '密码更新失败');
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('提交失败:', error);
|
||||
message.error('密码更新失败,请重试');
|
||||
}
|
||||
};
|
||||
|
||||
const handleAvatarChange = (info: any) => {
|
||||
if (info.file.status === 'done') {
|
||||
setAvatarUrl(URL.createObjectURL(info.file.originFileObj));
|
||||
message.success('头像上传成功');
|
||||
} else if (info.file.status === 'error') {
|
||||
message.error('头像上传失败');
|
||||
}
|
||||
};
|
||||
|
||||
const handlePassportChange = (info: any) => {
|
||||
if (info.file.status === 'done') {
|
||||
setPassportUrl(URL.createObjectURL(info.file.originFileObj));
|
||||
message.success('护照上传成功');
|
||||
} else if (info.file.status === 'error') {
|
||||
message.error('护照上传失败');
|
||||
}
|
||||
};
|
||||
|
||||
const handleDriverLicenseChange = (info: any) => {
|
||||
if (info.file.status === 'done') {
|
||||
setDriverLicenseUrl(URL.createObjectURL(info.file.originFileObj));
|
||||
message.success('驾照上传成功');
|
||||
} else if (info.file.status === 'error') {
|
||||
message.error('驾照上传失败');
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<div style={{ padding: 24 }}>
|
||||
<div style={{ marginBottom: 24 }}>
|
||||
<Title level={3}>个人信息</Title>
|
||||
<Paragraph type="secondary">管理个人账号信息</Paragraph>
|
||||
</div>
|
||||
|
||||
<Card>
|
||||
<div style={{ textAlign: 'center', marginBottom: 24 }}>
|
||||
<Space direction="vertical" style={{ alignItems: 'center' }}>
|
||||
<Upload
|
||||
name="avatar"
|
||||
listType="picture-circle"
|
||||
showUploadList={false}
|
||||
onChange={handleAvatarChange}
|
||||
maxCount={1}
|
||||
>
|
||||
{avatarUrl ? (
|
||||
<Avatar size={128} src={avatarUrl} />
|
||||
) : (
|
||||
<Avatar size={128} icon={<UserOutlined />} />
|
||||
)}
|
||||
</Upload>
|
||||
<Typography.Text>点击更换头像</Typography.Text>
|
||||
<Typography.Text strong>{user?.name || user?.username}</Typography.Text>
|
||||
<Typography.Text type="secondary">{user?.role === 'admin' ? '管理员' : user?.role === 'manager' ? '经理' : '普通用户'}</Typography.Text>
|
||||
</Space>
|
||||
</div>
|
||||
|
||||
<Form form={form} layout="vertical" onFinish={handleSubmit}>
|
||||
<Row gutter={16}>
|
||||
<Col span={12}>
|
||||
<Form.Item label="姓名" name="name" rules={[{ required: true, message: '请输入姓名' }]}>
|
||||
<Input placeholder="请输入姓名" />
|
||||
</Form.Item>
|
||||
</Col>
|
||||
<Col span={12}>
|
||||
<Form.Item label="手机号" name="phone" rules={[{ required: true, message: '请输入手机号' }]}>
|
||||
<Input placeholder="请输入手机号" prefix={<PhoneOutlined />} />
|
||||
</Form.Item>
|
||||
</Col>
|
||||
</Row>
|
||||
<Row gutter={16}>
|
||||
<Col span={12}>
|
||||
<Form.Item label="邮箱" name="email" rules={[{ required: true, message: '请输入邮箱' }, { type: 'email', message: '请输入正确的邮箱地址' }]}>
|
||||
<Input placeholder="请输入邮箱" prefix={<MailOutlined />} />
|
||||
</Form.Item>
|
||||
</Col>
|
||||
<Col span={12}>
|
||||
<Form.Item label="用户名" disabled>
|
||||
<Input value={user?.username} placeholder="用户名" />
|
||||
</Form.Item>
|
||||
</Col>
|
||||
</Row>
|
||||
|
||||
<div style={{ marginBottom: 24 }}>
|
||||
<Title level={4}>证件上传</Title>
|
||||
</div>
|
||||
|
||||
<Row gutter={16}>
|
||||
<Col span={12}>
|
||||
<Form.Item label="护照">
|
||||
<Upload
|
||||
name="passport"
|
||||
listType="picture"
|
||||
showUploadList={false}
|
||||
onChange={handlePassportChange}
|
||||
maxCount={1}
|
||||
>
|
||||
<Card
|
||||
style={{ textAlign: 'center', padding: 24, border: '1px dashed #d9d9d9' }}
|
||||
>
|
||||
{passportUrl ? (
|
||||
<img src={passportUrl} alt="护照" style={{ maxWidth: '100%', maxHeight: 200 }} />
|
||||
) : (
|
||||
<Space direction="vertical" style={{ alignItems: 'center' }}>
|
||||
<UploadOutlined style={{ fontSize: 32, color: '#1890ff' }} />
|
||||
<Typography.Text>点击上传护照</Typography.Text>
|
||||
</Space>
|
||||
)}
|
||||
</Card>
|
||||
</Upload>
|
||||
</Form.Item>
|
||||
</Col>
|
||||
<Col span={12}>
|
||||
<Form.Item label="驾照">
|
||||
<Upload
|
||||
name="driverLicense"
|
||||
listType="picture"
|
||||
showUploadList={false}
|
||||
onChange={handleDriverLicenseChange}
|
||||
maxCount={1}
|
||||
>
|
||||
<Card
|
||||
style={{ textAlign: 'center', padding: 24, border: '1px dashed #d9d9d9' }}
|
||||
>
|
||||
{driverLicenseUrl ? (
|
||||
<img src={driverLicenseUrl} alt="驾照" style={{ maxWidth: '100%', maxHeight: 200 }} />
|
||||
) : (
|
||||
<Space direction="vertical" style={{ alignItems: 'center' }}>
|
||||
<UploadOutlined style={{ fontSize: 32, color: '#1890ff' }} />
|
||||
<Typography.Text>点击上传驾照</Typography.Text>
|
||||
</Space>
|
||||
)}
|
||||
</Card>
|
||||
</Upload>
|
||||
</Form.Item>
|
||||
</Col>
|
||||
</Row>
|
||||
|
||||
<div style={{ display: 'flex', justifyContent: 'flex-end', marginTop: 24 }}>
|
||||
<Button type="primary" htmlType="submit" loading={loading}>
|
||||
保存修改
|
||||
</Button>
|
||||
<Button style={{ marginLeft: 16 }} onClick={() => setPasswordModalVisible(true)}>
|
||||
修改密码
|
||||
</Button>
|
||||
</div>
|
||||
</Form>
|
||||
</Card>
|
||||
|
||||
<Modal
|
||||
title="修改密码"
|
||||
open={passwordModalVisible}
|
||||
onCancel={() => setPasswordModalVisible(false)}
|
||||
onOk={handlePasswordSubmit}
|
||||
width={400}
|
||||
>
|
||||
<Form form={passwordForm} layout="vertical">
|
||||
<Form.Item label="当前密码" name="currentPassword" rules={[{ required: true, message: '请输入当前密码' }]}>
|
||||
<Input.Password placeholder="请输入当前密码" prefix={<LockOutlined />} />
|
||||
</Form.Item>
|
||||
<Form.Item label="新密码" name="newPassword" rules={[{ required: true, message: '请输入新密码' }, { min: 6, message: '密码长度至少为6位' }]}>
|
||||
<Input.Password placeholder="请输入新密码" prefix={<LockOutlined />} />
|
||||
</Form.Item>
|
||||
<Form.Item label="确认新密码" name="confirmPassword" rules={[{ required: true, message: '请确认新密码' }, ({ getFieldValue }) => ({
|
||||
validator(_, value) {
|
||||
if (!value || getFieldValue('newPassword') === value) {
|
||||
return Promise.resolve();
|
||||
}
|
||||
return Promise.reject(new Error('两次输入的密码不一致'));
|
||||
}
|
||||
})]}>
|
||||
<Input.Password placeholder="请确认新密码" prefix={<LockOutlined />} />
|
||||
</Form.Item>
|
||||
</Form>
|
||||
</Modal>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default ProfilePage;
|
||||
Reference in New Issue
Block a user