--- last_reviewed: 2026-06-16 --- # src/web/static/js — 前端逻辑模块 ## 概述 财务报销自动化系统的前端入口脚本,按功能拆分为多个模块文件,通过 `App` 共享状态对象通信。采用原生 JavaScript 编写,通过 HTTP 请求与 Flask 后端通信,使用 SSE 接收处理进度。 ## 文件结构 | 文件 | 职责 | |------|------| | `state.js` | 全局状态管理 (`App` 对象,含处理状态机) | | `utils.js` | 工具函数 (HTML 转义、会话管理) | | `chat.js` | 聊天窗口消息管理(含文件消息、处理进度、三点动画、LLM 流式气泡) | | `upload.js` | 文件上传(含拖拽、config.json 解析) | | `config.js` | 配置收集与校验(基于状态机的自动触发) | | `process.js` | 发票处理流程与 SSE 监听(含 file_progress、llm_stream 事件处理) | | `agent.js` | Agent 交互(含 supplement 流程、强制提交、用户文字补充、自动提交) | | `sync.js` | 移动端同步轮询(状态感知模式) | | `index.js` | 入口初始化 | ## 加载顺序 ``` state.js → utils.js → chat.js → upload.js → config.js → process.js → sync.js → index.js ``` ## 模块依赖关系 ``` state.js (无依赖) │ ├── utils.js ────────────────┐ │ │ ├── chat.js ─────────────────┐ │ │ │ │ ├── upload.js ──────────────┐ │ │ │ │ │ │ ├── config.js ─────────────┐ │ │ │ │ │ │ │ │ ├── process.js ───────────┐ │ │ │ │ │ │ │ │ │ │ ├── sync.js ──────────┐ │ │ │ │ │ │ │ │ │ │ │ │ └── index.js ────────┘ │ │ │ │ │ │ ``` ## 处理状态机 `App.processState` 控制何时触发自动处理,避免重复执行: ```mermaid stateDiagram-v2 [*] --> idle idle --> processing: 配置完整 + 有文件 processing --> done: 处理完成 processing --> awaiting_supplement: Agent 要求补充材料 done --> idle: 有新文件 / forceStart = true done --> done: 无新文件(保持,不重复) awaiting_supplement --> idle: 用户补充文件(只上传新文件) idle --> processing: idle + newFilenames > 0(走 supplement 流程) idle --> awaiting_files: AI 判断文件不足(未来功能) awaiting_files --> idle: 用户补充文件 awaiting_files --> processing: 用户说"直接开始" (forceStart = true) ``` 状态字段说明: | 字段 | 类型 | 说明 | |------|------|------| | `processState` | string | 当前处理状态:idle / processing / done / awaiting_files / awaiting_supplement | | `lastProcessedFileCount` | number | 上次处理时的文件数,用于检测是否有新文件 | | `forceStart` | boolean | 用户强制开始标记(兼容"文件不足也要处理"的场景) | | `newFilenames` | array | 待补充上传的文件名列表,用于区分首次处理和补充处理 | ## 核心流程 ### 1. 文件上传 ``` 用户上传文件 → addFileMessage() → 注册到 App.fileMessageMap → promptMissingConfig() → processState 为 done 时重置为 idle(支持增量上传后重新处理) ``` ### 2. 配置收集 ``` promptMissingConfig() → 检查 CONFIG_FIELDS → 有缺失则提示 → 用户输入 → sendUserMessage() → 递归直到完整 ``` ### 3. 自动触发检查 (checkAutoStart) ``` checkAutoStart() → 状态机判断: - processing → 跳过 - done + 无新文件 → 跳过(修复重复执行问题) - done + 有新文件 → 走 supplement 流程(不再走 startProcess) - idle + newFilenames > 0 → 走 supplement 流程 - idle + 配置完整 + 有文件 → 走 startProcess(首次处理) - awaiting_files + forceStart → 触发 ``` ### 4. 发票处理(首次) ``` startProcess() → ensureSession → 上传全部文件 → /api/agent/process → SSE 监听 → 收到 file_progress(processing) → setFileProcessing()(三点动画) → 收到 file_progress(done) → setFileDone()(展示提取摘要) → 收到 file_progress(cached) → setFileCached() → 收到 file_progress(error) → setFileError() → 收到 llm_stream(start) → handleLLMStream() 创建流式气泡 → 收到 llm_stream(chunk) → handleLLMStream() 逐字追加 AI 输出 → 收到 llm_stream(end) → handleLLMStream() 关闭流式气泡 → 收到 done → 设置 processState = 'done' + 记录文件数 → 显示汇总结果 ``` ### 5. 补充文件处理(新增) 当 Agent 返回 `awaiting_supplement` 状态时,用户补充文件的流程: ``` 用户上传补充文件 → handleFiles() 检测 processState === 'awaiting_supplement' → _uploadNewFiles() 只上传新文件(不再重新上传所有文件) → App.newFilenames = [新文件名列表] → App.processState = 'idle' → syncFiles() 3 秒后触发 checkAutoStart() → checkAutoStart() 检测到 idle + newFilenames > 0 → handleSupplementUpload(filenames) → POST /api/agent/supplement/(后端会重新 extract_invoices,旧文件走缓存) → 新文件走 LLM 提取,旧文件命中 JSON 缓存直接返回 → run_agent_round() 重新分析 → SSE 返回 done → 根据结果进入 awaiting_supplement 或 done 状态 ``` **关键区别**:补充文件走 `/api/agent/supplement/` 而非 `/api/agent/process/`,后者在已有 agent_session 时会跳过提取阶段。 ## 数据流 ### 配置数据流 ``` config.json → parseConfigFile → App.sessionConfig → startProcess → /api/agent/process ``` ### 发票数据流(首次处理) ``` 发票文件 → startProcess → /api/upload → /api/agent/process → SSE file_events.log → 文件消息实时更新 ``` ### 发票数据流(补充文件) ``` 新发票文件 → _uploadNewFiles() → /api/upload → /api/agent/supplement → extract_invoices()(旧文件走缓存,新文件走 LLM) → run_agent_round() → SSE file_events.log → 文件消息实时更新 ``` ### 文件消息状态机 ``` addFileMessage(filename) → file_progress(processing) → 三点动画(同一行) → file_progress(done/cached/error) → 详情展开 + 状态图标 ``` ## 变更记录 - **2026-06-16**:修复补充文件重复上传问题 — 新增 `newFilenames` 状态字段,补充文件时只上传新文件并走 `/api/agent/supplement/` 接口。`upload.js` 新增 `_uploadNewFiles()` 辅助函数。`config.js` 的 `checkAutoStart()` 在 `done` 和 `idle` 状态下有 `newFilenames` 时走 supplement 流程而非 `startProcess()` - **2026-06-16**:新增 LLM 流式思考展示 — `chat.js` 新增 `handleLLMStream` 状态机(start/chunk/end/error 四阶段),`process.js` 的 SSE 监听增加 `llm_stream` 事件分支,AI 思考过程以聊天气泡逐字展示 - **2026-06-16**:引入处理状态机 — 新增 `processState` / `lastProcessedFileCount` / `forceStart` 状态字段,修复 `syncFiles` 轮询每 3 秒重复触发处理的 bug。`checkAutoStart` 改为基于状态机判断,done 状态下只有检测到新文件才重新触发。为未来 AI 判断文件是否充足预留了 `awaiting_files` 状态和 `forceStart` 标记 - **2026-06-12**:上传区域可折叠 — 新增 `toggleUploadPanel()` 函数,配合 CSS `max-height` 动画实现面板收起/展开 - **2026-06-12**:文件消息框对齐到用户侧 — `addFileMessage` 的 wrapper 新增 `user` 类,上传的文件消息显示在右侧(用户侧),与 agent 消息区分 - **2026-06-12**:配置完成增加互动提示 — `promptMissingConfig` 在配置完整时发送系统消息告知用户即将开始处理发票 - **2026-06-14**:新增文件进度实时反馈 — 后端 extractor 在每处理一个文件时产生 SSE 进度事件,前端根据文件名定位 DOM 并更新状态(处理中三点动画、完成摘要展示、缓存/错误状态) - **2026-06-14**:移除发票编辑和财务提交流程 — 删除 table.js、financial.js 及相关 HTML 区域,后续开发不再支持手动编辑发票信息 - **2026-06-12**:模块拆分重构 — 将 index.js (625 行) 拆分为 10 个独立模块,引入 `App` 共享状态对象 - **2026-06-12**:修复配置收集流程的异步时序问题 — parseConfigFile 改为 Promise,拆分 checkAutoStart 为静默检查/配置提示两个函数