// ============================================================ // 事件总线 — 跨组件/跨模块事件通信 // 用于 axios 拦截器 → App 触发登录弹窗等解耦场景 // ============================================================ type Listener = (...args: unknown[]) => void; const listeners = new Map>(); // ---------- 日志钩子(由 logger 模块注册,避免循环依赖)---------- type EventLogListener = (event: string, ...args: unknown[]) => void; let eventLogListener: EventLogListener | null = null; /** * 注册事件日志监听器(由 logger 模块调用) * 每次 emit() 时回调,用于审计追踪 */ export function setEventLogListener(fn: EventLogListener): void { eventLogListener = fn; } // ---------- API ---------- /** 监听事件 */ export function on(event: string, fn: Listener): void { if (!listeners.has(event)) { listeners.set(event, new Set()); } listeners.get(event)!.add(fn); } /** 取消监听 */ export function off(event: string, fn: Listener): void { listeners.get(event)?.delete(fn); } /** 触发事件 */ export function emit(event: string, ...args: unknown[]): void { // ① 通知日志监听器(在触发业务监听器之前记录) if (eventLogListener) { try { eventLogListener(event, ...args); } catch { /* 日志钩子异常不影响事件投递 */ } } // ② 通知业务监听器 listeners.get(event)?.forEach((fn) => { try { fn(...args); } catch (e) { console.error(`[EventBus] ${event} 回调出错:`, e); } }); } // ============================================================ // 预定义事件名 // ============================================================ export const EVENTS = { /** 需要登录(axios 返回 401 时触发) */ AUTH_REQUIRED: 'auth:required', /** 显示登录弹窗(用户点击登录按钮触发) */ SHOW_LOGIN: 'auth:show-login', /** 登录成功 */ LOGIN_SUCCESS: 'auth:login-success', /** 退出登录 */ LOGOUT: 'auth:logout', /** 任务提交成功(→ 刷新任务列表) */ TASK_SUBMITTED: 'task:submitted', } as const;