## 首页整体架构
- 新增 src/pages/home/ — 完整首页模块(10 个组件)
- HomePage / HomeContent:三栏弹性布局,拖动分隔条调整列宽
- BannerCarousel:顶部轮播图(antd Carousel,2280×90 长条图片适配)
- LeftPanel:模型选择器(Menu 分组) + 任务记录(Table 分页),纵向拖拽调整比例
- CenterPanel / ModelInputForm:动态表单,根据模型 param_schema/ui_config 渲染控件
- RightPanel / OutputPreview:任务输出预览(Image/Video)
- 新增 Layout 组件:h-screen flex 布局,首页 Banner + NavBar + Outlet
## 数据层(数据与视图分离)
- 新增 src/services/modules/ — API 模块(banner/models/task/auth)
- TaskAPI 静态方法,TaskInfoItemResponseBody 判别联合类型
- ModelAPI / BannerAPI,const 断言路由常量
- 新增 src/hooks/use-async.ts — 通用异步 Hook
- useAsyncData:竞态处理、条件请求(enabled)、自动错误日志
- useAsyncMutation:数据变更,onSuccess/onError
- getErrorMessage():RequestErrorType → 可读错误信息
- 新增 src/hooks/use-api.ts — 业务 Hook
- useBannerList / useModelList / useTaskList / useTaskDetail / useSubmitTask
## 主题与交互
- 重写暗色令牌:科技感蓝紫(#080C24 底 / #0F1340 面板 / #2A3278 边框)
- 三栏分隔线:token.colorBorderSecondary 常驻 + 手柄竖线 hover 高亮
- 左面板纵向拖拽:模型区/任务区可调比例,ResizeObserver 自适应
- 暗色/亮色同步:tokens.ts + antd-theme.ts + globals.css 三处一致
## 基础设施
- EventBus 新增 TASK_SUBMITTED 事件(替代 Context 数字递增 hack)
- HomeContext 状态管理(选中模型、任务分页、折叠状态)
- 所有组件边框使用 token.colorBorderSecondary(主题自适应)
- Table 滚动高度 ResizeObserver 动态计算(修复分页器遮挡)
75 lines
2.2 KiB
TypeScript
75 lines
2.2 KiB
TypeScript
// ============================================================
|
||
// 事件总线 — 跨组件/跨模块事件通信
|
||
// 用于 axios 拦截器 → App 触发登录弹窗等解耦场景
|
||
// ============================================================
|
||
|
||
type Listener = (...args: unknown[]) => void;
|
||
|
||
const listeners = new Map<string, Set<Listener>>();
|
||
|
||
// ---------- 日志钩子(由 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;
|