# 前端组件化梳理与规划
## 背景与现状
- `static/index.html` 将所有界面(配额通知、对话侧栏、文件/待办/子智能体面板、聊天区、输入区、聚焦文件、个性化抽屉、上下文菜单等)堆在一个根模板里,通过 `v-if`/`v-for` 直接绑定根实例的数据,缺乏可复用组件边界和样式隔离。`static/index.html:35-1094`
- `static/app.js` 中单个 `createApp` 承担了连接管理、消息流、文件树、资源监控、使用额度、工具控制、上传、个性化设置、彩蛋等所有状态/方法,数据段跨越 150+ 行,方法 200+ 个,总体约 4.4k 行,导致维护和测试困难。`static/app.js:186-4362`
- 现有代码依赖 Vue CDN,全局变量(`ICONS`、`TOOL_CATEGORY_ICON_MAP`、`window.requestSocketToken` 等)直接注入;没有构建步骤,也就无法按模块拆分或使用 TypeScript/ESM。
> ✅ Phase 1(脚手架 + 入口切换)已落地,过程总结见 `doc/frontend/phase1_setup.md`。
> ✅ Phase 2(对话侧栏 + 文件面板)已上线,详见 `doc/frontend/phase2_sidebar.md`。
> 以下规划作为 Phase 3+ 的拆分依据。
## 目标与约束
1. 拆分出语义明确的组件,使每个功能模块有独立的模板/逻辑/样式,降低互相影响。
2. 引入现代化构建链路(Vite + Vue 3 SFC),保留当前 API/UI 行为、Socket.IO 逻辑与安全脚本(`security.js`、彩蛋脚本)。
3. 建立状态分层,区分“跨组件共享状态”“本地 UI 状态”“请求数据”,以 Pinia/composable 管理。
4. 支持渐进式迁移:新旧模板可短期共存,确保每阶段都能运行并回归关键流程(登录→聊天→终端→上传→用量面板)。
## 构建与目录规划
1. **构建工具**:采用 Vite(支持 Vue 3、热更新、自动代码分割),后续易拓展 TypeScript/测试。
2. **目录建议**(均位于 `static/` 下):
- `src/main.ts`:入口,挂载 `App.vue`、注入 Pinia、注册全局指令/组件。
- `src/App.vue`:根壳组件,只负责大布局与全局 overlay 容器。
- `src/components/`:按功能域组织(如 `chat/`, `sidebar/`, `panels/`, `overlays/`, `common/`)。
- `src/stores/`:Pinia store(`useConnectionStore`, `useConversationStore`, `useResourceStore`, `useFileStore`, `useUiStore`, `usePersonalizationStore`)。
- `src/composables/`:WebSocket 管理、消息流解析、Toast/Confirm service、复制代码块等工具。
- `src/assets/`:静态 SVG/样式;继续复用 `style.css`,逐步拆为 `src/styles/`。
- `src/utils/request.ts`:封装带 CSRF 的 fetch。
3. **构建输出**:Vite 输出到 `static/dist/`,Flask 模板改为加载 `dist/assets/*.js|css`;保留 CDN 版本作为 fallback,直到迁移完成。
## 组件树(建议)
```
AppShell
├─ GlobalToasts(quota + toast 队列)
├─ ConfirmDialog
├─ EasterEggOverlay + ContextMenu + CopyCodeHandler
├─ ConversationSidebar
├─ WorkspaceLayout
│ ├─ LeftPanel
│ │ ├─ StatusHeader
│ │ ├─ PanelSwitcher
│ │ └─ PanelContent
│ │ ├─ FileTreePanel(含 FileNode)
│ │ ├─ TodoPanel
│ │ └─ SubAgentPanel
│ ├─ ChatArea
│ │ ├─ TokenDrawer
│ │ ├─ MessageList
│ │ │ ├─ MessageItem(User/Assistant/System)
│ │ │ └─ ToolAction / ThinkingBlock / MarkdownBlock
│ │ ├─ ScrollLockToggle
│ │ └─ InputComposer
│ │ ├─ QuickMenu
│ │ │ ├─ ToolToggleMenu
│ │ │ └─ SettingsMenu
│ │ └─ AttachmentButton / ModeSwitch
│ └─ RightFocusPanel
└─ PersonalizationDrawer
```
说明:
- ConversationSidebar 负责搜索/分页/对话操作,接收 `conversations`, `currentConversationId`、触发 `loadConversation/delete/duplicate`。`static/index.html:91-204`
- LeftPanel 根据 `panelMode` 动态渲染文件树(`fileTree`, `expandedFolders`)、待办(`todoList`)、子智能体(`subAgents`)。`static/index.html:217-368`
- ChatArea 内部组件拆分消息渲染、Token/资源卡片、输入框及快捷菜单。`static/index.html:374-820`
- RightFocusPanel 只观察 `focusedFiles`,提供 tabs 和 `downloadFile` 操作。`static/index.html:906-958`
- PersonalizationDrawer 独立组件管理表单、拖拽排序与保存逻辑。`static/index.html:959-1094`
- Toasts/Confirm/ContextMenu/EasterEgg 形成 `overlays/` 组件,避免根实例直接绑定。
## 状态域与 Store 设计
| Store/Composable | 职责 | 关键数据来源 |
| --- | --- | --- |
| `useConnectionStore` | 维护 Socket.IO 实例、连接状态、stop 请求、服务器状态快照;统一事件监听与销毁。| `initSocket` 逻辑 `static/app.js:711-1103` |
| `useConversationStore` | 对话列表、当前对话、消息流、工具状态、压缩/清空等命令;暴露 action(load/create/delete/duplicate/send/compress)。| `loadConversationsList`、`loadConversation`、`fetchAndDisplayHistory`、消息 handlers `static/app.js:1904-3336` |
| `useResourceStore` | Token 统计、容器状态、网络/存储、配额轮询;提供格式化工具。| `loadInitialData`、`updateContainerStatus`、`pollContainerStats`、`fetchUsageQuota` `static/app.js:1595-1870` |
| `useFileStore` | 文件树、聚焦文件、右键菜单、下载逻辑。| `updateFileTree`、`downloadFile/Folder` `static/app.js:615-707`、`static/index.html:906-958` |
| `useTaskStore` | 待办列表、子智能体轮询。| `fetchSubAgents`, `fetchTodoList` `static/app.js:2885-2938` |
| `useUploadStore` | 上传状态、toast 更新、复用安全扫描提示。| `uploadSelectedFile` `static/app.js:2940-3108` |
| `usePersonalizationStore` | 个人页表单、保存/启停逻辑。| `openPersonalPage`~`savePersonalization` `static/app.js:2496-2669` |
| `useUiStore` | 侧栏折叠、面板宽度、QuickMenu/ToolMenu/Settings、toast 队列、confirm/overlay。| `data` 中的 UI 字段 `static/app.js:224-360` + `pushToast/confirmAction` `static/app.js:4200-4334` |
Store 之间通过 action 通信,如 `conversationStore` 发出 `loadConversation` 后通知 `resourceStore` 更新 token;`uiStore` 暴露 toast/confirm 服务供其他 store/组件调用。
## 事件与数据流
1. **WebSocket**:`io('/')` 监听 quota/token/status/conversation/message/easter-egg 等事件(`static/app.js:769-1103`),将原本直接修改 `data` 的逻辑迁移到 store action(例如 `conversationStore.handleAiMessageStart`、`resourceStore.handleQuotaUpdate`)。
2. **REST API**:`loadInitialData` 涵盖 `/api/files`, `/api/focused`, `/api/status`, `/api/conversations/current`, `/api/usage` 等;应抽象为 `apiClient`,集中处理 CSRF/错误提示。
3. **定时轮询**:容器 0.5s、存储 5s、子智能体 5s、配额 10s(可在 store 内封装 start/stop,以路由或可见性驱动)。
4. **命令通道**:`socket.emit('send_command', {...})`、`run_command`、`stop_task` 等保持不变,只是从组件调用 store action。
## 迁移路线(建议)
1. **Phase 0 – 基础设施**
- 初始化 Vite + Vue 3 + Pinia + ESLint + Prettier。
- 配置构建输出与 Flask 静态路径,写入 README 使用说明。
2. **Phase 1 – 壳 + Overlay**
- 实现 `AppShell`、toast/confirm/context-menu/personalization overlay,保持旧版聊天区域引用旧模板,通过 `mount` 在 `#app` 下混用。
- 验证登录、对话加载、toast/confirm 是否正常。
3. **Phase 2 – 侧栏组件**
- 拆 ConversationSidebar、LeftPanel(含 FileTree/Todo/SubAgent)。
- 引入 `useConversationStore`、`useFileStore`、`useTaskStore`。
- 测试对话搜索/切换、文件树展开/右键、待办/子智能体展示。
4. **Phase 3 – ChatArea**
- 拆 TokenDrawer、MessageList、InputComposer、QuickMenu、ToolMenu。
- 落地 WebSocket/消息 store,复用现有渲染逻辑(Markdown、思考块、工具卡片)。
- 回归消息流、工具调用、停止按钮、上传/设置/模式切换。
5. **Phase 4 – RightPanel & Personalization**
- 拆聚焦文件与个人页组件,接入 `usePersonalizationStore`。
- 完成 CSS 抽取,移除旧版 `static/app.js`/`index.html`,保留必要 polyfill。
每个阶段都保持 `main.py`/`web_server.py` 无需改动,仅替换静态入口文件即可。
## 测试与验证
| 阶段 | 核心验证 | 自动化建议 |
| --- | --- | --- |
| Phase 0 | `npm run dev/build` 正常,Flask 引用 `dist`;加载页可看到“正在连接服务器”。 | 添加 `npm run lint`、`npm run typecheck`(若启用 TS)。 |
| Phase 1 | Toast/Confirm/上下文菜单、彩蛋覆盖层可交互;连接断开/重连提示正常。 | 编写 Vitest 针对 toast store 的单测。 |
| Phase 2 | 对话 CRUD、文件树右键下载、待办/子智能体轮询无误;侧栏拖拽宽度保持。 | Playwright 脚本:创建/切换/删除对话,校验 API 调用。 |
| Phase 3 | 消息流畅、工具卡片渲染、上传扫描提示、快捷菜单/设置工作。 | 录制 `socket` mock,用 Jest/Vitest 验证消息 reducer。 |
| Phase 4 | 聚焦文件显示、个人化表单保存;整体构建包大小可控。 | e2e:提交 personalization,再刷新确保状态加载。 |
## 风险与应对
1. **WebSocket 事件错杀**:拆分过程中如未正确解绑监听,会重复触发。→ 在 `useConnectionStore` 中集中注册/清理,配合 `onBeforeUnmount`。
2. **CSS 回归**:`.sidebar`, `.chat-container` 等现有样式都位于 `style.css`,迁移时注意命名冲突,可引入 `postcss-nesting` + CSS Modules,逐步拆分。
3. **Build/Runtime 混用**:迁移初期若需要旧版 `app.js` 支撑未拆组件,可通过 Vite 的 `define` 暴露全局函数,与 Flask 模板条件加载。
4. **依赖外链**:Prism/KaTeX/Marked、Socket.IO 目前从 CDN 引入。组件化后建议使用 npm 依赖,统一由 Vite 打包,避免版本漂移。
## 下一步
Phase 3(进行中):聊天区实时化 + 输入/工具 + Token 面板
1. **连接与消息流(已完成)**:`useConnectionStore` + `useChatStore` + `ChatArea`,加载历史、实时流式输出、发送/停止。
2. **工具卡片与 Quick Menu**(当前待做)
- 抽象 `useToolStore` 管理工具分类、状态、开关(迁移 `toolSettings`)。
- 组件化 Quick Menu(上传、工具开关、设置),接入 Pinia store。
- 渲染工具执行卡片(命令输出、文件操作等),替换 legacy DOM 结构。
3. **Token/Quota/资源面板**
- 新建 `useResourceStore` 轮询 `/api/container-status`、`/api/project-storage`、`/api/usage`,替换 token drawer。
- 组件化 `TokenDrawer`,展示 Token/Quota/CPU/内存/网络/存储卡片。
4. **Toast/Confirm/Context**
- 搬迁 `pushToast/confirmAction/contextMenu`/EasterEgg overlay,形成 `useUiStore` + `GlobalToasts/ConfirmDialog` 组件。
完成 Phase 3 后,再进入 Phase 4:聚焦面板、个性化抽屉、样式统一等。