refactor: CSS 拆分 + auth 持久化提取 + 目录整理

- globals.css 拆分为 4 文件:tokens.css(颜色)/ transitions.css(过渡)/ base.css(重置)/ globals.css(入口)
- 提取 auth-persistence.ts 到 services/,解除 AppProvider→pages/login/hooks 反向依赖
- use-auth-state.ts 精简为纯 React Hook,持久化逻辑委托给 services/auth-persistence.ts
- components/common/ → components/ui/(目录名语义更清晰)
- TypeScript + Vite 构建通过

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
2026-06-09 14:20:07 +08:00
parent 53df926973
commit 7982b69219
12 changed files with 511 additions and 439 deletions

View File

@@ -1,76 +1,34 @@
// ============================================================
// useAuthState — 记住账号 / 自动登录 状态管理
// useAuthState — 记住账号 / 自动登录 表单状态 Hook
//
// "记住密码":下次打开页面自动回填用户名(不再存储密码)
// "自动登录"登录成功时标记AppProvider 启动时用 refresh_token 静默续期
//
// 持久化逻辑已提取到 services/auth-persistence.ts
// 此处仅负责 React 组件状态useState + useCallback
//
// 安全:
// - 密码从不持久化存储access_token / refresh_token 由 safeStorage 加密保护)
// - 安全性由 refresh_token + access_token 的 JWT 双重机制 + OS 密钥链保障
// ============================================================
import { useState, useCallback } from 'react';
import {
saveAuth,
clearSavedAuth,
getSavedUsername,
getSavedRemember,
getSavedAutoLogin,
} from '@/services/auth-persistence';
const STORAGE_KEY = 'ele-heixiu-auth';
interface StoredAuth {
/** 上次登录的用户名(表单回填用) */
username: string;
/** 是否记住用户名(勾选后表单自动回填用户名) */
remember: boolean;
/** 是否勾选了自动登录AppProvider 据此决定启动时是否刷新 token */
autoLogin: boolean;
}
function readAuth(): StoredAuth | null {
try {
const raw = localStorage.getItem(STORAGE_KEY);
if (!raw) return null;
return JSON.parse(raw) as StoredAuth;
} catch {
return null;
}
}
function writeAuth(auth: StoredAuth): void {
try {
localStorage.setItem(STORAGE_KEY, JSON.stringify(auth));
} catch {
/* 静默 */
}
}
// ---------- 工具函数(不依赖 ReactAppProvider 可直接调用)----------
/** 读取自动登录标记 */
export function getAutoLoginFlag(): boolean {
return readAuth()?.autoLogin || false;
}
/** 仅清除自动登录标记(保留用户名,下次还能回填表单) */
export function clearAutoLoginFlag(): void {
const auth = readAuth();
if (!auth) return;
writeAuth({ ...auth, autoLogin: false });
}
/** 清除所有保存的认证信息 */
export function clearSavedAuth(): void {
try {
localStorage.removeItem(STORAGE_KEY);
} catch {
/* 静默 */
}
}
export { getAutoLoginFlag, clearAutoLoginFlag, clearSavedAuth } from '@/services/auth-persistence';
// ---------- Hook ----------
export function useAuthState() {
const saved = readAuth();
const [username, setUsername] = useState(saved?.username || '');
const [remember, setRemember] = useState(saved?.remember || false);
const [autoLogin, setAutoLogin] = useState(saved?.autoLogin || false);
const [username, setUsername] = useState(getSavedUsername);
const [remember, setRemember] = useState(getSavedRemember);
const [autoLogin, setAutoLogin] = useState(getSavedAutoLogin);
const handleRememberChange = useCallback((checked: boolean) => {
setRemember(checked);
@@ -93,16 +51,11 @@ export function useAuthState() {
* @param doRemember - 是否勾选"记住用户名"
* @param doAutoLogin - 是否勾选"自动登录"
*/
const saveAuth = useCallback(
const handleSaveAuth = useCallback(
(user: string, _password: string, doRemember: boolean, doAutoLogin: boolean) => {
setUsername(user);
if (doRemember || doAutoLogin) {
const payload: StoredAuth = {
username: user,
remember: doRemember,
autoLogin: doAutoLogin,
};
writeAuth(payload);
saveAuth(user, doRemember, doAutoLogin);
}
},
[],
@@ -115,6 +68,6 @@ export function useAuthState() {
setUsername,
handleRememberChange,
handleAutoLoginChange,
saveAuth,
saveAuth: handleSaveAuth,
};
}