239 lines
7.0 KiB
TypeScript
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>
|
|
);
|
|
}
|