14 KiB
前端开发
开发规范见 开发规范文档。
布局架构
两个布局入口共享 ConsoleShell(src/web/shared/components/ConsoleShell/):
- AdminLayout(
src/web/layouts/admin-layout/):路由/(总览)、/projects、/models、/models/providers。 - 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),主题配置由 shared/theme/theme-config.ts 的 buildThemeConfig(effectiveTheme) 集中构建(含 cssVar、borderRadius、controlHeight、components.Layout 配色);侧边栏折叠。主题切换已迁移至设置页(/settings)。Header 显示品牌名、版本号和布局标题。
Sidebar(src/web/shared/components/Sidebar/)纯展示组件,通过 menuItems props 接收配置。
功能模块
| 功能模块 | 路径 | 说明 |
|---|---|---|
| 仪表盘 | features/dashboard/ |
总览页面 |
| 项目管理 | features/projects/ |
项目 CRUD、归档、搜索、排序 |
| 模型管理 | features/models/ |
供应商/模型管理、连通性测试 |
| 聊天 | features/chat/ |
会话管理、消息渲染、AI 对话 |
| 收集箱 | features/inbox/ |
素材 CRUD、持久化、列表管理 |
| 设置 | features/settings/ |
平台业务设置,卡片式布局 |
页面
| 页面 | 路径 | 入口 |
|---|---|---|
| 总览 | / |
features/dashboard/index.tsx |
| 项目管理 | /projects |
features/projects/index.tsx — FilterToolbar(状态 Select + 搜索 + 新建/归档恢复删除) + ProjectTable + ProjectFormModal。支持创建/编辑/归档/恢复/删除、列表排序、URL 同步筛选参数。 |
| 模型管理 | /models 和 /models/providers |
独立路由页面:ModelListPage.tsx(FilterToolbar + ModelTable) + ProviderListPage.tsx(FilterToolbar + ProviderTable)。模型支持供应商/能力筛选和列表排序,供应商支持类型筛选和列表排序。模型表单使用 GET /api/providers/options。供应商表单支持预保存连通性测试(POST /api/providers/test)。 |
| 设置 | /settings |
features/settings/index.tsx — 卡片式布局分区管理平台业务设置。当前包含"主题配置"卡片(Segmented 切换系统/明亮/黑暗),使用 useSettings hook 通过 GET/PUT /api/settings 持久化,悲观更新策略。 |
| 聊天室 | /workbench/:id |
features/chat/index.tsx |
| 收集箱 | /workbench/:id/inbox |
features/inbox/index.tsx — 协调层(selectedId + modalOpen)+ MaterialSidebar(列表容器)+ MaterialDetailPanel(详情容器)+ AddMaterialModal。素材 CRUD 通过 TanStack Query hooks 接入后端 API。 |
| 404 | * |
features/not-found/index.tsx |
聊天页面
ChatPage = ConversationSidebar(自定义组件)+ ChatPanel。
- ConversationSidebar:会话侧边栏数据加载层(useQuery + 错误处理)。内部渲染
ConversationList(搜索框 + OverlayScrollbars 滚动 + 日期分组 + ConversationCard 列表)。对话按updatedAt分组(今天/昨天/本周/本月/更早),支持搜索过滤和 hover 删除(Popconfirm)。 - ChatPanel:
useChat(@ai-sdk/react)+DefaultChatTransport(ai 包)与后端 SSE 通信。按part.type分派渲染:TextPart(markdown-to-jsx 含自定义 overrides:CodeBlock 提供 Shiki 语法高亮和复制按钮、MarkdownTable 提供类 antd 表格样式)、ReasoningPart、ToolPart(四态)。支持编辑重发、重新生成、复制。 - Sender(@ant-design/x):输入框 + 发送/停止按钮 + 模型 Select(footer slot)。
共享代码
共享组件
| 组件 | 路径 | 说明 |
|---|---|---|
| ConsoleShell | shared/components/ConsoleShell/ |
全局布局外壳(Provider + Layout) |
| FilterToolbar | shared/components/FilterToolbar.tsx |
统一筛选工具条(Select 筛选 + 搜索 + 重置 + 操作按钮) |
| Sidebar | shared/components/Sidebar/ |
侧边栏纯展示组件 |
| SidebarGroup | shared/components/SidebarGroup/ |
可折叠日期分组(聊天室和收集箱共用) |
| ErrorBoundary | shared/components/ErrorBoundary.tsx |
生产环境错误边界 |
共享 Hooks
| Hook | 路径 | 说明 |
|---|---|---|
use-page-search-params |
shared/hooks/usePageSearchParams.ts |
URL 查询参数同步(筛选/分页/排序),批量更新 setParams 避免闭包覆盖 |
use-confirm-action |
shared/hooks/useConfirmAction.ts |
包装异步操作 + toast 成功/失败通知 |
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 + API 同步) |
use-settings |
shared/hooks/use-settings.ts |
平台设置读写(react-query: GET/PUT /api/settings) |
use-sidebar-collapsed |
shared/hooks/use-sidebar-collapsed.ts |
侧边栏折叠 localStorage 持久化 |
use-is-dark |
shared/hooks/use-is-dark.ts |
当前是否暗色主题 |
共享主题配置
| 文件 | 导出 | |
|---|---|---|
theme/theme-config.ts |
buildThemeConfig(effectiveTheme) — 构建 antd ThemeConfig(algorithm、cssVar、token、components.Layout) |
|
use-current-project |
shared/hooks/use-current-project.ts |
当前工作台项目 + ProjectContext(需在 ProjectProvider 内) |
use-materials.ts |
shared/hooks/use-materials.ts |
素材 CRUD(create/delete/fetch/list + Query hooks) |
共享工具函数
| 文件 | 导出 |
|---|---|
utils/api.ts |
handleResponse(response, extract)、handleVoidResponse(response) |
utils/format.ts |
formatDatetime(iso: string) — 格式化 ISO 时间字符串为 YYYY-MM-DD HH:mm |
utils/time.ts |
formatCountdown、formatDurationUnit、formatRelativeTime、isOlderThan、subtractHours |
utils/date-group.ts |
getDateGroup、groupByDate、GROUP_LABELS、GROUP_ORDER、DateGroup、DateGroupData |
更新触发条件
修改前端技术栈、组件边界、数据流、样式规则、测试环境、前端验证方式、运行时代码结构、页面组成、组件索引、hooks/工具清单、目录结构或功能模块归属时,必须更新本文档。管理页面 CRUD 通用模式的详细约定见 crud.md。
日志模块
Logger 接口
src/web/shared/utils/logger.ts 提供与后端镜像的 Logger 抽象:
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() |
测试断言日志条目 |
使用方式
组件内(推荐):
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("...") });
}
非组件纯函数:
import { createConsoleLogger } from "../../shared/utils/logger";
const logger = createConsoleLogger();
logger.debug("调试信息");
作用域绑定:
组件内直接通过 useLogger 的 bindings 参数传入,hook 内部保证引用稳定(值不变时多次渲染返回同一 Logger):
const logger = useLogger({ component: "ChatPanel", page: "workbench" });
logger.info("页面加载"); // [Alfred:INFO] 页面加载 [component=ChatPanel][page=workbench]
非组件场景仍可使用 logger.child():
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。