Files
jiang13-bbs/frontend/lib/userEvents.ts
freefire 6da4309453 feat: 新增管理员用户管理功能与账号封禁体系
此提交完成了全站管理员后台核心功能开发:
1.  新增AdminUserService与管理接口,支持用户列表查询、角色变更与封禁解封
2.  重构认证链路,统一账号封禁错误码与处理逻辑
3.  新增全局强制下线机制,账号被封禁时自动弹出告知弹窗并清理登录态
4.  重构管理员后台路由与布局,统一权限校验逻辑
5.  优化通知轮询逻辑,页面切回前台时立即刷新未读计数
6.  合并原公告、外观管理页到统一后台面板
2026-09-15 02:52:26 +08:00

58 lines
2.4 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import type { User } from "./api";
/**
* 全局用户状态事件总线。
*
* 背景:Header 等客户端组件挂载在持久化的 root layout 上,仅靠
* router.refresh() 刷新 props 在上传/删除头像等场景下存在时序与
* Router Cache 的不确定性。同页内的数据更新必须走显式的状态链路
* (React 状态/事件),storage 事件、轮询等方案对本标签页无效。
*
* 任意位置修改了当前用户资料后 dispatch 一个补丁,Header 等订阅方
* 立即合并更新;router.refresh() 仍照常调用,负责校正服务端渲染区域。
*/
export const USER_UPDATED_EVENT = "j13:user-updated";
export function emitUserUpdate(patch: Partial<User>): void {
if (typeof window === "undefined") return;
window.dispatchEvent(new CustomEvent(USER_UPDATED_EVENT, { detail: patch }));
}
export function onUserUpdate(handler: (patch: Partial<User>) => void): () => void {
if (typeof window === "undefined") return () => {};
const listener = (e: Event) => {
const detail = (e as CustomEvent<Partial<User>>).detail;
if (detail) handler(detail);
};
window.addEventListener(USER_UPDATED_EVENT, listener);
return () => window.removeEventListener(USER_UPDATED_EVENT, listener);
}
/**
* 强制下线事件。
*
* 触发源统一收敛在 lib/api.ts:任何客户端请求收到后端的
* code=account_banned(中间件/refresh/登录)或 /me 的 banned 标记时派发,
* Header 负责复用登出清理链路(清 cookie、游客化、router.refresh),
* AccountGuard 负责弹出封禁告知——状态清理与告知 UI 解耦,
* 避免各请求调用方各自处理导致残留(参考强制下线单一入口原则)。
*/
export type ForceLogoutReason = "banned";
export const FORCE_LOGOUT_EVENT = "j13:force-logout";
export function emitForceLogout(reason: ForceLogoutReason = "banned"): void {
if (typeof window === "undefined") return;
window.dispatchEvent(new CustomEvent(FORCE_LOGOUT_EVENT, { detail: { reason } }));
}
export function onForceLogout(handler: (reason: ForceLogoutReason) => void): () => void {
if (typeof window === "undefined") return () => {};
const listener = (e: Event) => {
const detail = (e as CustomEvent<{ reason: ForceLogoutReason }>).detail;
handler(detail?.reason ?? "banned");
};
window.addEventListener(FORCE_LOGOUT_EVENT, listener);
return () => window.removeEventListener(FORCE_LOGOUT_EVENT, listener);
}