refactor: 替换管理员登录组件并更新会话管理
This commit is contained in:
43
src/stores/admin-profile.ts
Normal file
43
src/stores/admin-profile.ts
Normal file
@@ -0,0 +1,43 @@
|
||||
import type { AdminProfile } from "@/types/api/admin-auth";
|
||||
|
||||
const KEY = "lottery_admin_profile";
|
||||
|
||||
export function readProfile(): AdminProfile | null {
|
||||
if (typeof window === "undefined") {
|
||||
return null;
|
||||
}
|
||||
const raw = window.localStorage.getItem(KEY);
|
||||
if (!raw) {
|
||||
return null;
|
||||
}
|
||||
try {
|
||||
const v = JSON.parse(raw) as AdminProfile;
|
||||
if (
|
||||
typeof v?.id === "number" &&
|
||||
typeof v?.username === "string" &&
|
||||
typeof v?.nickname === "string"
|
||||
) {
|
||||
return {
|
||||
id: v.id,
|
||||
username: v.username,
|
||||
nickname: v.nickname,
|
||||
email: typeof v.email === "string" || v.email === null ? v.email : null,
|
||||
};
|
||||
}
|
||||
} catch {
|
||||
/* ignore */
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
export function writeProfile(p: AdminProfile | null): void {
|
||||
if (typeof window === "undefined") {
|
||||
return;
|
||||
}
|
||||
if (p) {
|
||||
window.localStorage.setItem(KEY, JSON.stringify(p));
|
||||
} else {
|
||||
window.localStorage.removeItem(KEY);
|
||||
}
|
||||
}
|
||||
@@ -1,28 +0,0 @@
|
||||
import { create } from "zustand";
|
||||
|
||||
import { setAdminBearerToken } from "@/lib/admin-auth";
|
||||
import { writeStoredAdminToken } from "@/lib/admin-token-local-storage";
|
||||
|
||||
type AdminSessionState = {
|
||||
/** 已与 `Authorization: Bearer …` 同步的原始片段(不带 `Bearer ` 前缀) */
|
||||
bearerToken: string | null;
|
||||
setBearerToken: (token: string | null) => void;
|
||||
clearBearerToken: () => void;
|
||||
};
|
||||
|
||||
export const useAdminSessionStore = create<AdminSessionState>((set) => ({
|
||||
bearerToken: null,
|
||||
|
||||
setBearerToken: (token) => {
|
||||
const normalized = token?.trim() ? token.trim() : null;
|
||||
setAdminBearerToken(normalized);
|
||||
writeStoredAdminToken(normalized);
|
||||
set({ bearerToken: normalized });
|
||||
},
|
||||
|
||||
clearBearerToken: () => {
|
||||
setAdminBearerToken(null);
|
||||
writeStoredAdminToken(null);
|
||||
set({ bearerToken: null });
|
||||
},
|
||||
}));
|
||||
96
src/stores/admin-session.ts
Normal file
96
src/stores/admin-session.ts
Normal file
@@ -0,0 +1,96 @@
|
||||
/**
|
||||
* 管理端会话:Bearer Token + 登录接口返回的 {@link AdminProfile}。
|
||||
*
|
||||
* - **组件内**:`useAdminProfile()`、`useAdminSessionStore(...)`
|
||||
* - **组件外**(axios、工具函数):`getAdminProfile()`、`useAdminSessionStore.getState()`
|
||||
*/
|
||||
import { create } from "zustand";
|
||||
|
||||
import { setAdminBearerToken } from "@/lib/admin-auth";
|
||||
import { readProfile, writeProfile } from "@/stores/admin-profile";
|
||||
import { readToken, writeToken } from "@/stores/admin-token";
|
||||
import type { AdminProfile } from "@/types/api/admin-auth";
|
||||
|
||||
export type AdminSessionState = {
|
||||
bearerToken: string | null;
|
||||
adminProfile: AdminProfile | null;
|
||||
setBearerToken: (token: string | null) => void;
|
||||
setAdminProfile: (profile: AdminProfile | null) => void;
|
||||
clearSession: () => void;
|
||||
/** 从 localStorage 恢复 Token 与管理员摘要(仅客户端) */
|
||||
rehydrate: () => void;
|
||||
/** @deprecated 使用 {@link clearSession} */
|
||||
clearBearerToken: () => void;
|
||||
};
|
||||
|
||||
export const useAdminSessionStore = create<AdminSessionState>((set, get) => ({
|
||||
bearerToken: null,
|
||||
adminProfile: null,
|
||||
|
||||
setBearerToken: (token) => {
|
||||
const normalized = token?.trim() ? token.trim() : null;
|
||||
setAdminBearerToken(normalized);
|
||||
writeToken(normalized);
|
||||
if (!normalized) {
|
||||
writeProfile(null);
|
||||
set({ bearerToken: null, adminProfile: null });
|
||||
|
||||
return;
|
||||
}
|
||||
set({ bearerToken: normalized });
|
||||
},
|
||||
|
||||
setAdminProfile: (profile) => {
|
||||
writeProfile(profile);
|
||||
set({ adminProfile: profile });
|
||||
},
|
||||
|
||||
clearSession: () => {
|
||||
setAdminBearerToken(null);
|
||||
writeToken(null);
|
||||
writeProfile(null);
|
||||
set({ bearerToken: null, adminProfile: null });
|
||||
},
|
||||
|
||||
rehydrate: () => {
|
||||
if (typeof window === "undefined") {
|
||||
return;
|
||||
}
|
||||
const token = readToken();
|
||||
const profile = readProfile();
|
||||
if (token) {
|
||||
setAdminBearerToken(token);
|
||||
set({ bearerToken: token, adminProfile: profile });
|
||||
} else {
|
||||
setAdminBearerToken(null);
|
||||
set({ bearerToken: null, adminProfile: null });
|
||||
}
|
||||
},
|
||||
|
||||
clearBearerToken: () => {
|
||||
get().clearSession();
|
||||
},
|
||||
}));
|
||||
|
||||
/** React 组件:订阅当前管理员摘要(未登录或未缓存时为 `null`) */
|
||||
export function useAdminProfile(): AdminProfile | null {
|
||||
return useAdminSessionStore((s) => s.adminProfile);
|
||||
}
|
||||
|
||||
/** React 组件:是否已有 Token(已通过登录或 `rehydrate`) */
|
||||
export function useAdminSignedIn(): boolean {
|
||||
return useAdminSessionStore((s) => s.bearerToken !== null);
|
||||
}
|
||||
|
||||
/**
|
||||
* 任意运行上下文读取管理员摘要(不触发重渲染)。
|
||||
* 服务端为 `null`;客户端与页面水合后才有值。
|
||||
*/
|
||||
export function getAdminProfile(): AdminProfile | null {
|
||||
return useAdminSessionStore.getState().adminProfile;
|
||||
}
|
||||
|
||||
/** 非 React 代码读取完整会话状态(含 `setBearerToken`、`clearSession` 等) */
|
||||
export function getAdminSessionState(): AdminSessionState {
|
||||
return useAdminSessionStore.getState();
|
||||
}
|
||||
21
src/stores/admin-token.ts
Normal file
21
src/stores/admin-token.ts
Normal file
@@ -0,0 +1,21 @@
|
||||
const KEY = "lottery_admin_token";
|
||||
|
||||
export function readToken(): string | null {
|
||||
if (typeof window === "undefined") {
|
||||
return null;
|
||||
}
|
||||
const raw = window.localStorage.getItem(KEY)?.trim();
|
||||
|
||||
return raw && raw !== "" ? raw : null;
|
||||
}
|
||||
|
||||
export function writeToken(t: string | null): void {
|
||||
if (typeof window === "undefined") {
|
||||
return;
|
||||
}
|
||||
if (t && t.trim() !== "") {
|
||||
window.localStorage.setItem(KEY, t.trim());
|
||||
} else {
|
||||
window.localStorage.removeItem(KEY);
|
||||
}
|
||||
}
|
||||
10
src/stores/index.ts
Normal file
10
src/stores/index.ts
Normal file
@@ -0,0 +1,10 @@
|
||||
export type { AdminSessionState } from "@/stores/admin-session";
|
||||
export { readProfile, writeProfile } from "@/stores/admin-profile";
|
||||
export {
|
||||
getAdminProfile,
|
||||
getAdminSessionState,
|
||||
useAdminProfile,
|
||||
useAdminSessionStore,
|
||||
useAdminSignedIn,
|
||||
} from "@/stores/admin-session";
|
||||
export { readToken, writeToken } from "@/stores/admin-token";
|
||||
Reference in New Issue
Block a user