iconsulting/packages/admin-client/src/features/settings/presentation/pages/SettingsPage.tsx

239 lines
7.0 KiB
TypeScript

import { useState } from 'react';
import {
Card,
Form,
Input,
Button,
Row,
Col,
Typography,
Descriptions,
Divider,
message,
Modal,
} from 'antd';
import {
UserOutlined,
LockOutlined,
SafetyOutlined,
InfoCircleOutlined,
} from '@ant-design/icons';
import { useAuth } from '../../../../shared/hooks/useAuth';
import api from '../../../../shared/utils/api';
const { Title, Text } = Typography;
export function SettingsPage() {
const { admin, logout } = useAuth();
const [changePasswordForm] = Form.useForm();
const [changingPassword, setChangingPassword] = useState(false);
const [passwordModalOpen, setPasswordModalOpen] = useState(false);
const handleChangePassword = async () => {
try {
const values = await changePasswordForm.validateFields();
if (values.newPassword !== values.confirmPassword) {
message.error('两次输入的密码不一致');
return;
}
setChangingPassword(true);
await api.post('/admin/change-password', {
oldPassword: values.oldPassword,
newPassword: values.newPassword,
});
message.success('密码修改成功,请重新登录');
setPasswordModalOpen(false);
changePasswordForm.resetFields();
// Logout after password change
setTimeout(() => {
logout();
}, 1500);
} catch (error: unknown) {
const err = error as { response?: { data?: { message?: string } } };
message.error(err.response?.data?.message || '密码修改失败');
} finally {
setChangingPassword(false);
}
};
const systemInfo = {
version: '1.0.0',
environment: import.meta.env.MODE,
apiBase: import.meta.env.VITE_API_BASE_URL || '/api/v1',
buildTime: new Date().toISOString().split('T')[0],
};
return (
<div className="p-6">
<Title level={4} className="mb-6"></Title>
<Row gutter={[16, 16]}>
{/* Admin Profile */}
<Col xs={24} lg={12}>
<Card
title={
<span>
<UserOutlined className="mr-2" />
</span>
}
>
<Descriptions column={1} size="small">
<Descriptions.Item label="用户名">
{admin?.username || '-'}
</Descriptions.Item>
<Descriptions.Item label="姓名">
{admin?.name || '-'}
</Descriptions.Item>
<Descriptions.Item label="角色">
{admin?.role || '-'}
</Descriptions.Item>
<Descriptions.Item label="权限">
<div className="flex flex-wrap gap-1">
{admin?.permissions?.map((p: string) => (
<span
key={p}
className="px-2 py-0.5 bg-blue-50 text-blue-600 text-xs rounded"
>
{p}
</span>
)) || '-'}
</div>
</Descriptions.Item>
</Descriptions>
</Card>
</Col>
{/* Security Settings */}
<Col xs={24} lg={12}>
<Card
title={
<span>
<SafetyOutlined className="mr-2" />
</span>
}
>
<div className="mb-4">
<Text type="secondary">
</Text>
</div>
<Button
type="primary"
icon={<LockOutlined />}
onClick={() => setPasswordModalOpen(true)}
>
</Button>
<Divider />
<div className="mb-4">
<Text type="secondary">
退
</Text>
</div>
<Button danger onClick={logout}>
退
</Button>
</Card>
</Col>
{/* System Info */}
<Col xs={24}>
<Card
title={
<span>
<InfoCircleOutlined className="mr-2" />
</span>
}
>
<Row gutter={[32, 16]}>
<Col xs={24} sm={12} md={6}>
<div className="text-gray-500 text-sm mb-1"></div>
<div className="font-medium">{systemInfo.version}</div>
</Col>
<Col xs={24} sm={12} md={6}>
<div className="text-gray-500 text-sm mb-1"></div>
<div className="font-medium">{systemInfo.environment}</div>
</Col>
<Col xs={24} sm={12} md={6}>
<div className="text-gray-500 text-sm mb-1">API </div>
<div className="font-medium text-xs break-all">
{systemInfo.apiBase}
</div>
</Col>
<Col xs={24} sm={12} md={6}>
<div className="text-gray-500 text-sm mb-1"></div>
<div className="font-medium">{systemInfo.buildTime}</div>
</Col>
</Row>
</Card>
</Col>
</Row>
{/* Change Password Modal */}
<Modal
title="修改密码"
open={passwordModalOpen}
onOk={handleChangePassword}
onCancel={() => {
setPasswordModalOpen(false);
changePasswordForm.resetFields();
}}
confirmLoading={changingPassword}
okText="确认修改"
cancelText="取消"
>
<Form
form={changePasswordForm}
layout="vertical"
className="mt-4"
>
<Form.Item
name="oldPassword"
label="当前密码"
rules={[{ required: true, message: '请输入当前密码' }]}
>
<Input.Password placeholder="请输入当前密码" />
</Form.Item>
<Form.Item
name="newPassword"
label="新密码"
rules={[
{ required: true, message: '请输入新密码' },
{ min: 6, message: '密码长度不能少于6位' },
]}
>
<Input.Password placeholder="请输入新密码" />
</Form.Item>
<Form.Item
name="confirmPassword"
label="确认新密码"
rules={[
{ required: true, message: '请确认新密码' },
({ getFieldValue }) => ({
validator(_, value) {
if (!value || getFieldValue('newPassword') === value) {
return Promise.resolve();
}
return Promise.reject(new Error('两次输入的密码不一致'));
},
}),
]}
>
<Input.Password placeholder="请再次输入新密码" />
</Form.Item>
</Form>
</Modal>
</div>
);
}