feat: 实现设置页面 + 路由系统 + JWT认证体系 + 公告优化 + 错误类型系统
## 新增功能 ### 设置页面(Router + Modal 叠加) - HashRouter 路由系统,Electron file:// 协议兼容 - 设置页以居中 Modal 叠加首页,仅 X 按钮关闭 - 主题切换 Segmented 控件(与首页 Switch 共享 Context) - 大模型产物存储仓库路径配置(原生文件夹选择对话框) - 企业版额外:团队创作存储仓库路径 - 系统信息展示(平台/版本/版本类型/环境) - 版本更新检查(状态机:idle→checking→no-update/available→downloading→downloaded→error) ### JWT 认证体系 - 双 Token 机制:access_token + refresh_token - Axios 拦截器 401 自动刷新 + 并发请求去重队列 - 启动时 refresh_token 静默续期(自动登录) - 记住密码:Base64 编码存储,表单自动回填 - setTokenRefreshHandler 解耦模式:request.ts 不知晓 auth ### 自定义错误类型系统 - RefreshError 继承 RequestError,type = AUTH_EXPIRED - 全局拦截器捕获 RefreshError → 自动 clearToken + AUTH_REQUIRED - 任何位置 throw new RefreshError() 即可触发登录 Modal ### 公告卡片优化 - 长文本 Tooltip 悬浮预览 - 点击卡片弹出详情 Modal(全文保留换行) ### SettingsContext(类似 Vue Pinia) - 集中式设置状态管理 - localStorage 持久化:ele-heixiu-output-path / ele-heixiu-team-repo-path - useSettings() 全局消费 ## 架构变更 - App.tsx → HashRouter + AppRoutes + LoginPage 弹层 - Layout.tsx = NavBar + <Outlet /> 页面壳 - HomePage.tsx 从 App.tsx 提取 - NavBar 使用 useNavigate() 真实路由导航 - useUpdater 改为模块级单例 IPC 监听器(修复 MaxListenersExceededWarning) - electron/main.ts 新增 FILE_DIALOG IPC handler ## 依赖 - 新增 react-router-dom
This commit is contained in:
@@ -14,7 +14,9 @@ import { useAppContext } from '@/contexts/app-context.ts';
|
||||
import { useTheme } from '@/hooks/use-theme.ts';
|
||||
import { LoginForm } from './components/LoginForm';
|
||||
import { RegisterForm } from './components/RegisterForm';
|
||||
import { useAuthState } from './hooks/use-auth-state';
|
||||
import { useAuthState, getStoredPassword } from './hooks/use-auth-state';
|
||||
import {AuthAPI, LoginResponseBody} from '@/services/modules';
|
||||
import { getDeviceId } from '@/utils/device';
|
||||
import type { LoginFormValues, RegisterFormValues } from './types';
|
||||
|
||||
const { Text } = Typography;
|
||||
@@ -41,14 +43,23 @@ export function LoginPage({ open, onClose }: LoginPageProps) {
|
||||
async (values: LoginFormValues) => {
|
||||
setSubmitting(true);
|
||||
try {
|
||||
// TODO: 接入登录 API
|
||||
console.log('[Login]', values);
|
||||
authState.saveAuth(values.username, values.password);
|
||||
message.success('登录成功(Demo)');
|
||||
login();
|
||||
const auth:LoginResponseBody = await AuthAPI.login({
|
||||
username: values.username,
|
||||
password: values.password,
|
||||
device_id: getDeviceId(),
|
||||
user_ip: '',
|
||||
});
|
||||
|
||||
// 记住偏好(密码 Base64 编码,自动登录靠 refresh_token)
|
||||
authState.saveAuth(values.username, values.password, values.remember, values.autoLogin);
|
||||
|
||||
// 更新全局登录态(内部完成 setToken + emit LOGIN_SUCCESS)
|
||||
login(auth);
|
||||
|
||||
message.success(`登录成功,欢迎 ${auth.display_name || auth.username}`);
|
||||
onClose();
|
||||
} catch (err) {
|
||||
message.error((err as Error).message || '登录失败');
|
||||
message.error((err as Error).message || '登录失败,请检查用户名和密码');
|
||||
} finally {
|
||||
setSubmitting(false);
|
||||
}
|
||||
@@ -62,8 +73,17 @@ export function LoginPage({ open, onClose }: LoginPageProps) {
|
||||
async (values: RegisterFormValues) => {
|
||||
setSubmitting(true);
|
||||
try {
|
||||
console.log('[Register]', values);
|
||||
message.success('注册成功(Demo),请登录');
|
||||
await AuthAPI.register({
|
||||
username: values.username,
|
||||
password: values.password,
|
||||
phone: values.phone,
|
||||
sms_code: values.sms_code,
|
||||
device_id: values.device_id || getDeviceId(),
|
||||
device_label: values.device_label || '',
|
||||
display_name: values.display_name || '',
|
||||
referral_code: values.referral_code || '',
|
||||
});
|
||||
message.success('注册成功,请登录');
|
||||
setActiveTab('login');
|
||||
} catch (err) {
|
||||
message.error((err as Error).message || '注册失败');
|
||||
@@ -75,9 +95,13 @@ export function LoginPage({ open, onClose }: LoginPageProps) {
|
||||
);
|
||||
|
||||
const handleSendSms = useCallback(
|
||||
(phone: string) => {
|
||||
console.log('[SMS]', phone);
|
||||
message.info(`验证码已发送到 ${phone}(Demo)`);
|
||||
async (phone: string) => {
|
||||
try {
|
||||
await AuthAPI.sendSms({ phone });
|
||||
message.success('验证码已发送');
|
||||
} catch (err) {
|
||||
message.error((err as Error).message || '验证码发送失败');
|
||||
}
|
||||
},
|
||||
[message],
|
||||
);
|
||||
@@ -87,7 +111,7 @@ export function LoginPage({ open, onClose }: LoginPageProps) {
|
||||
const loginTab = (
|
||||
<LoginForm
|
||||
initialUsername={authState.username}
|
||||
initialPassword={authState.password}
|
||||
initialPassword={getStoredPassword()}
|
||||
initialRemember={authState.remember}
|
||||
initialAutoLogin={authState.autoLogin}
|
||||
submitting={submitting}
|
||||
|
||||
Reference in New Issue
Block a user