Files
Alfred/docs/development/frontend.md
lanyuanxiaoyao b1dec691e9 refactor(web): 前端目录重构 — consoles/pages → layouts/features + shared
- consoles/admin/ → layouts/admin-layout/
- consoles/workbench/ → layouts/workbench-layout/ + features/chat/
- pages/ → features/ (dashboard, models, projects, not-found)
- components/ → shared/components/
- hooks/ → shared/hooks/
- utils/ → shared/utils/
- 更新所有 import 路径 (src/web/ + tests/web/)
- 更新开发文档 (README.md, frontend.md, architecture.md)
2026-06-02 23:17:28 +08:00

162 lines
10 KiB
Markdown
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.
# 前端开发
开发规范见 [开发规范文档](README.md)。
## 布局架构
两个布局入口共享 ConsoleShell`src/web/shared/components/ConsoleShell/`
- **AdminLayout**`src/web/layouts/admin-layout/`):路由 `/`(总览)、`/projects``/models`
- **WorkbenchLayout**`src/web/layouts/workbench-layout/`):路由 `/workbench/:projectId``/workbench/:projectId/chat``WorkbenchProjectGate` 从 URL 读 projectId通过 `ProjectContext` 提供项目上下文,仅 active 项目渲染。
ConsoleShell 包含:`XProvider(zhCN + zhCN_X)` + `AntApp` + `Layout`(Header/Sider/Content) + 主题切换(明亮/黑暗/系统)+ 侧边栏折叠。Header 显示品牌名、版本号和布局标题。
`Sidebar``src/web/shared/components/Sidebar/`)纯展示组件,通过 `menuItems` props 接收配置。
## 功能模块
| 功能模块 | 路径 | 说明 |
| -------- | --------------------- | --------------------------- |
| 仪表盘 | `features/dashboard/` | 总览页面 |
| 项目管理 | `features/projects/` | 项目 CRUD、归档、搜索 |
| 模型管理 | `features/models/` | 供应商/模型管理、连通性测试 |
| 聊天 | `features/chat/` | 会话管理、消息渲染、AI 对话 |
## 页面
| 页面 | 路径 | 入口 |
| -------- | ---------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| 总览 | `/` | `features/dashboard/index.tsx` |
| 项目管理 | `/projects` | `features/projects/index.tsx` — ProjectToolbar(Tab 切换 active/archived + 搜索 + 新建) + ProjectTable + ProjectFormModal。支持创建/编辑/归档/恢复/删除,仅 active 项目可跳转工作台。 |
| 模型管理 | `/models` | `features/models/index.tsx` — antd Tabs 切换供应商/模型视图。模型表单和表格使用 `GET /api/providers/options`。供应商表单支持预保存连通性测试(`POST /api/providers/test`),新建时 type 默认 `openai-compatible`,测试 `ok: false` 展示失败但不阻止保存。 |
| 聊天室 | `/workbench/:id` | `features/chat/index.tsx` |
| 404 | `*` | `features/not-found/index.tsx` |
### 聊天页面
`ChatPage` = `Conversations`@ant-design/x+ `ChatPanel`
- **Conversations**会话侧边栏TanStack Query 管理会话列表,支持创建/选中/删除menu dropdown
- **ChatPanel**`useChat`@ai-sdk/react+ `DefaultChatTransport`ai 包)与后端 SSE 通信。按 `part.type` 分派渲染TextPartXMarkdown、ReasoningPart、ToolPart四态。支持编辑重发、重新生成、复制。
- **Sender**@ant-design/x输入框 + 发送/停止按钮 + 模型 Selectfooter slot
## 共享代码
### 共享组件
| 组件 | 路径 | 说明 |
| ------------- | ------------------------------------- | --------------------------------- |
| ConsoleShell | `shared/components/ConsoleShell/` | 全局布局外壳Provider + Layout |
| Sidebar | `shared/components/Sidebar/` | 侧边栏纯展示组件 |
| ErrorBoundary | `shared/components/ErrorBoundary.tsx` | 生产环境错误边界 |
### 共享 Hooks
| Hook | 路径 | 说明 |
| ----------------------- | --------------------------------------- | ----------------------------------------- |
| `use-meta.ts` | `shared/hooks/use-meta.ts` | `/api/meta`30s 轮询5s staleTime |
| `use-providers.ts` | `shared/hooks/use-providers.ts` | 供应商 CRUD + test connection |
| `use-models.ts` | `shared/hooks/use-models.ts` | 模型 CRUD + test connection |
| `use-projects.ts` | `shared/hooks/use-projects.ts` | 项目 CRUD + archive/restore |
| `use-conversations.ts` | `shared/hooks/use-conversations.ts` | 会话和消息 fetch 函数(不含 Query hooks |
| `use-logger` | `shared/hooks/use-logger.ts` | Logger hook组件内使用 |
| `use-theme-preference` | `shared/hooks/use-theme-preference.ts` | 主题偏好 localStorage 持久化 |
| `use-sidebar-collapsed` | `shared/hooks/use-sidebar-collapsed.ts` | 侧边栏折叠 localStorage 持久化 |
| `use-is-dark` | `shared/hooks/use-is-dark.ts` | 当前是否暗色主题 |
### 共享工具函数
| 文件 | 导出 |
| --------------- | --------------------------------------------------------------------------------------------- |
| `utils/api.ts` | `handleResponse(response, extract)``handleVoidResponse(response)` |
| `utils/time.ts` | `formatCountdown``formatDurationUnit``formatRelativeTime``isOlderThan``subtractHours` |
## 更新触发条件
修改前端技术栈、组件边界、数据流、样式规则、测试环境、前端验证方式、运行时代码结构、页面组成、组件索引、hooks/工具清单、目录结构或功能模块归属时,必须更新本文档。
## 日志模块
### Logger 接口
`src/web/shared/utils/logger.ts` 提供与后端镜像的 Logger 抽象:
```typescript
export interface Logger {
child(bindings: Record<string, unknown>): Logger;
debug(message: string, data?: unknown): void;
error(message: string, data?: unknown): void;
info(message: string, data?: unknown): void;
setLevel(level: LogLevel): void;
warn(message: string, data?: unknown): void;
}
```
### 实现
| 实现 | 工厂函数 | 用途 |
| ----------------------- | ------------------------------------------------ | ----------------------------------------------------------------------------------------- |
| `DefaultLogger` + Sinks | `useLogger(bindings?)` / `createDefaultLogger()` | 组件内使用ConsoleSink + AntdMessageSink 双流;传入 bindings 自动创建带作用域的子 Logger |
| `ConsoleLogger` | `createConsoleLogger()` | 非组件纯函数ErrorBoundary、工具函数仅 ConsoleSink |
| `NoopLogger` | `createNoopLogger()` | 测试中不需要日志的场景 |
| `MemoryLogger` | `createMemoryLogger()` | 测试断言日志条目 |
### 使用方式
**组件内(推荐):**
```typescript
import { useLogger } from "../../shared/hooks/use-logger";
function MyComponent() {
const logger = useLogger({ component: "MyComponent" });
logger.info("数据加载完成", { count: 42 });
logger.warn("即将超时");
logger.error("操作失败", { error: new Error("...") });
}
```
**非组件纯函数:**
```typescript
import { createConsoleLogger } from "../../shared/utils/logger";
const logger = createConsoleLogger();
logger.debug("调试信息");
```
**作用域绑定:**
组件内直接通过 `useLogger``bindings` 参数传入hook 内部保证引用稳定(值不变时多次渲染返回同一 Logger
```typescript
const logger = useLogger({ component: "ChatPanel", page: "workbench" });
logger.info("页面加载"); // [Alfred:INFO] 页面加载 [component=ChatPanel][page=workbench]
```
非组件场景仍可使用 `logger.child()`
```typescript
const pageLogger = logger.child({ page: "projects" });
pageLogger.info("页面加载"); // [Alfred:INFO] 页面加载 [page=projects]
```
### notification 红线
- `AntdMessageSink` 仅对 **warn**`message.warning`)和 **error**`message.error`)触发用户可见通知。
- `debug``info` 级别绝不对用户弹出 notification仅在开发者控制台输出。
- 错误详情通过 `data` 参数传入(如 `logger.error("提交失败", { error })``data` 不经序列化透传,保留 Error 堆栈展开能力。
### 生产环境行为
生产环境(`import.meta.env["PROD"]`)自动将 ConsoleSink 最小级别设为 `warn`,屏蔽 debug/info 输出。`useLogger()``createConsoleLogger()` 自动处理此逻辑,调用方无需关心环境判断。
### ErrorBoundary 特殊说明
`ErrorBoundary` 是 class 组件,无法使用 `useLogger()` hook。它以 `createConsoleLogger()` 直接创建独立的 ConsoleLogger 实例,仅输出到控制台不触发用户通知。
### 测试
- 单元测试使用 `createMemoryLogger()` 断言日志记录,使用 `createNoopLogger()` 静默无关日志。
- `createDefaultLogger(sinks, isProduction)` 接受 `isProduction` 参数,测试中可显式控制级别过滤行为,不依赖 `import.meta.env`