Files
erp/apps/web/src/pages/Settings.tsx
iven 7e8fabb095 feat(auth): add change password API and frontend page
Backend:
- Add ChangePasswordReq DTO with validation (current + new password)
- Add AuthService::change_password() method with credential verification,
  password rehash, and token revocation
- Add POST /api/v1/auth/change-password endpoint with utoipa annotation

Frontend:
- Add changePassword() API function in auth.ts
- Add ChangePassword.tsx page with form validation and confirmation
- Add "修改密码" tab in Settings page

After password change, all refresh tokens are revoked and the user
is redirected to the login page.
2026-04-15 01:32:18 +08:00

122 lines
3.7 KiB
TypeScript

import { Tabs } from 'antd';
import {
BookOutlined,
GlobalOutlined,
MenuOutlined,
NumberOutlined,
SettingOutlined,
BgColorsOutlined,
AuditOutlined,
LockOutlined,
} from '@ant-design/icons';
import DictionaryManager from './settings/DictionaryManager';
import LanguageManager from './settings/LanguageManager';
import MenuConfig from './settings/MenuConfig';
import NumberingRules from './settings/NumberingRules';
import SystemSettings from './settings/SystemSettings';
import ThemeSettings from './settings/ThemeSettings';
import AuditLogViewer from './settings/AuditLogViewer';
import ChangePassword from './settings/ChangePassword';
const Settings: React.FC = () => {
return (
<div>
<div className="erp-page-header" style={{ borderBottom: 'none', marginBottom: 0, paddingBottom: 8 }}>
<div>
<h4></h4>
<div className="erp-page-subtitle"></div>
</div>
</div>
<Tabs
defaultActiveKey="dictionaries"
style={{ marginTop: 8 }}
items={[
{
key: 'dictionaries',
label: (
<span style={{ display: 'flex', alignItems: 'center', gap: 6 }}>
<BookOutlined style={{ fontSize: 14 }} />
</span>
),
children: <DictionaryManager />,
},
{
key: 'languages',
label: (
<span style={{ display: 'flex', alignItems: 'center', gap: 6 }}>
<GlobalOutlined style={{ fontSize: 14 }} />
</span>
),
children: <LanguageManager />,
},
{
key: 'menus',
label: (
<span style={{ display: 'flex', alignItems: 'center', gap: 6 }}>
<MenuOutlined style={{ fontSize: 14 }} />
</span>
),
children: <MenuConfig />,
},
{
key: 'numbering',
label: (
<span style={{ display: 'flex', alignItems: 'center', gap: 6 }}>
<NumberOutlined style={{ fontSize: 14 }} />
</span>
),
children: <NumberingRules />,
},
{
key: 'settings',
label: (
<span style={{ display: 'flex', alignItems: 'center', gap: 6 }}>
<SettingOutlined style={{ fontSize: 14 }} />
</span>
),
children: <SystemSettings />,
},
{
key: 'theme',
label: (
<span style={{ display: 'flex', alignItems: 'center', gap: 6 }}>
<BgColorsOutlined style={{ fontSize: 14 }} />
</span>
),
children: <ThemeSettings />,
},
{
key: 'audit-log',
label: (
<span style={{ display: 'flex', alignItems: 'center', gap: 6 }}>
<AuditOutlined style={{ fontSize: 14 }} />
</span>
),
children: <AuditLogViewer />,
},
{
key: 'change-password',
label: (
<span style={{ display: 'flex', alignItems: 'center', gap: 6 }}>
<LockOutlined style={{ fontSize: 14 }} />
</span>
),
children: <ChangePassword />,
},
]}
/>
</div>
);
};
export default Settings;