feat: 忘记密码/修改密码 + 设置页结构重组 + barrel cleanup + LoginPage 版本统一

新增:
  - ForgotPasswordModal — 手机号+短信验证码重置密码 (AuthAPI.resetPassword)
  - ChangePasswordModal — 当前密码验证后修改密码 (AuthAPI.changePassword)
  - useSmsCooldown Hook — 通用短信发送冷却逻辑,供注册/忘记密码复用
  - PasswordSetting — 设置页账号安全区块,双选项:修改密码 / 短信验证码重置

  修改:
  - LoginForm \"忘记密码?\" 链接接入 ForgotPasswordModal(替换 TODO)
  - LoginPage 移除 edition 业务版本 UI 区分,登录/注册 Tab 全局统一
  - 设置页 Section 组件移入 blocks/ 子目录(git mv 保留历史)
  - auth.ts sendSms URL 修正: /sms/send → /send-sms

  清理:
  - settings/index.ts 移除 4 个未使用 re-export,精简为仅 SettingsPage
  - navbar/index.ts 移除未使用 re-export,精简为仅 NavBar

  文档:
  - WORKLOG.md 新增 2026-06-08 工作日志
  - ARCHITECTURE.md / PROJECT.md 更新设置页目录结构与新组件
  - TODO.md 忘记密码流程标记已完成 + 短信人机验证列入后期优化
This commit is contained in:
2026-06-08 11:57:35 +08:00
parent 952a723448
commit 7032d1c3f2
22 changed files with 1089 additions and 515 deletions

View File

@@ -13,10 +13,11 @@
import { Modal } from 'antd';
import { SettingOutlined } from '@ant-design/icons';
import { useAppContext } from '@/contexts/app-context';
import { ThemeSetting } from './ThemeSetting';
import { StoragePathSetting } from './StoragePathSetting';
import { SystemInfoSetting } from './SystemInfoSetting';
import { UpdateSetting } from './UpdateSetting';
import { ThemeSetting } from './blocks/ThemeSetting';
import { StoragePathSetting } from './blocks/StoragePathSetting';
import { SystemInfoSetting } from './blocks/SystemInfoSetting';
import { UpdateSetting } from './blocks/UpdateSetting';
import { PasswordSetting } from './blocks/PasswordSetting';
interface SettingsPageProps {
open: boolean;
@@ -24,31 +25,32 @@ interface SettingsPageProps {
}
export function SettingsPage({ open, onClose }: SettingsPageProps) {
const { edition } = useAppContext();
const { edition } = useAppContext();
return (
<Modal
open={open}
width={480}
onCancel={onClose}
destroyOnHidden={true}
mask={{ closable: false }}
keyboard={false}
footer={null}
title={
<span className="flex items-center gap-2">
<SettingOutlined />
</span>
}
styles={{ body: { padding: 0, maxHeight: '70vh', overflow: 'auto' } }}
>
<div className="flex flex-col gap-4 p-4">
<ThemeSetting />
<StoragePathSetting edition={edition} />
<SystemInfoSetting />
<UpdateSetting />
</div>
</Modal>
);
return (
<Modal
open={open}
width={480}
onCancel={onClose}
destroyOnHidden={true}
mask={{ closable: false }}
keyboard={false}
footer={null}
title={
<span className="flex items-center gap-2">
<SettingOutlined />
</span>
}
styles={{ body: { padding: 0, maxHeight: '70vh', overflow: 'auto' } }}
>
<div className="flex flex-col gap-4 p-4">
<ThemeSetting />
<StoragePathSetting edition={edition} />
<SystemInfoSetting />
<PasswordSetting />
<UpdateSetting />
</div>
</Modal>
);
}

View File

@@ -0,0 +1,52 @@
// ============================================================
// PasswordSetting — 账号安全区块(密码操作入口)
//
// 提供两种密码操作,用户自选:
// 1. 修改密码 — 通过当前密码验证后设置新密码
// 2. 忘记密码 — 通过手机短信验证码重置密码
// ============================================================
import { useState } from 'react';
import { Card, Button, Typography } from 'antd';
import { LockOutlined, PhoneOutlined } from '@ant-design/icons';
import { ChangePasswordModal } from '@/pages/login/components/ChangePasswordModal';
import { ForgotPasswordModal } from '@/pages/login/components/ForgotPasswordModal';
const { Text } = Typography;
export function PasswordSetting() {
const [changePwdOpen, setChangePwdOpen] = useState(false);
const [forgotPwdOpen, setForgotPwdOpen] = useState(false);
return (
<>
<Card size="small" title="账号安全" className="rounded-md!">
<div className="flex flex-col gap-3">
{/* 修改密码:通过旧密码 */}
<div className="flex items-center justify-between">
<Text type="secondary" className="text-sm">
</Text>
<Button icon={<LockOutlined />} onClick={() => setChangePwdOpen(true)}>
</Button>
</div>
{/* 忘记密码:通过手机验证码重置 */}
<div className="flex items-center justify-between">
<Text type="secondary" className="text-sm">
</Text>
<Button icon={<PhoneOutlined />} onClick={() => setForgotPwdOpen(true)}>
</Button>
</div>
</div>
</Card>
<ChangePasswordModal open={changePwdOpen} onClose={() => setChangePwdOpen(false)} />
<ForgotPasswordModal open={forgotPwdOpen} onClose={() => setForgotPwdOpen(false)} />
</>
);
}

View File

@@ -159,8 +159,14 @@ function PathRow({
}
export function StoragePathSetting({ edition }: StoragePathSettingProps) {
const { outputPath, teamRepoPath, setOutputPath, setTeamRepoPath, clearOutputPath, clearTeamRepoPath } =
useSettings();
const {
outputPath,
teamRepoPath,
setOutputPath,
setTeamRepoPath,
clearOutputPath,
clearTeamRepoPath,
} = useSettings();
const isEnterprise = edition === 'enterprise';

View File

@@ -65,15 +65,8 @@ function renderChangelog(markdown: string) {
}
export function UpdateSetting() {
const {
status,
info,
progress,
error,
checkForUpdates,
downloadUpdate,
installUpdate,
} = useUpdater();
const { status, info, progress, error, checkForUpdates, downloadUpdate, installUpdate } =
useUpdater();
const [detailOpen, setDetailOpen] = useState(false);
@@ -210,15 +203,15 @@ export function UpdateSetting() {
title={`更新详情 — v${info?.version || ''}`}
open={detailOpen}
onCancel={() => setDetailOpen(false)}
footer={
<Button onClick={() => setDetailOpen(false)}></Button>
}
footer={<Button onClick={() => setDetailOpen(false)}></Button>}
width={520}
>
<div className="max-h-96 overflow-y-auto py-2">
{info?.releaseNotes
? renderChangelog(info.releaseNotes)
: <Text type="secondary"></Text>}
{info?.releaseNotes ? (
renderChangelog(info.releaseNotes)
) : (
<Text type="secondary"></Text>
)}
</div>
</Modal>
</Card>

View File

@@ -3,7 +3,3 @@
// ============================================================
export { SettingsPage } from './SettingsPage';
export { ThemeSetting } from './ThemeSetting';
export { StoragePathSetting } from './StoragePathSetting';
export { SystemInfoSetting } from './SystemInfoSetting';
export { UpdateSetting } from './UpdateSetting';