docs: update README with web features, remove dev planning docs

- Add Web 端功能 section covering all features
- Add web/ directory to project structure
- Add Web startup instructions (backend + frontend)
- Remove docs/superpowers/ (internal dev planning docs)

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
2026-05-13 20:53:25 +08:00
parent 7dabb2ce66
commit 0c28031e81
6 changed files with 97 additions and 3933 deletions
+97 -1
View File
@@ -15,6 +15,8 @@
## 快速开始
### 桌面端 (GUI / CLI)
```bash
# 安装依赖
pip install -r requirements.txt
@@ -30,6 +32,31 @@ python headless_api.py data/input/xxx.jpg --barcode 6920584471055 --target 69205
python build_exe.py
```
### Web 端
```bash
# 后端依赖
cd web/backend && pip install -r requirements.txt
# 前端依赖
cd web/frontend && npm install
# 启动后端 (端口 8000)
cd web && python -m uvicorn backend.main:app --host 0.0.0.0 --port 8000 --reload
# 启动前端开发服务器 (端口 5173)
cd web/frontend && npm run dev
# 构建前端到后端静态目录
cd web/frontend && npm run build
# 构建后直接访问 http://localhost:8000 即可
# 生产部署 (仅后端,前端已内嵌)
cd web && python -m uvicorn backend.main:app --host 0.0.0.0 --port 8000
```
**默认账号:** `admin` / `admin123`(首次登录后建议修改密码)
## 项目结构
```
@@ -50,16 +77,85 @@ python build_exe.py
│ │ └── utils/ # 工具(日志、文件、字符串、云端同步、对话框)
│ ├── services/ # 业务服务(订单、OCR、处理器调度)
│ └── ui/ # GUI 模块(主题、日志、快捷键、主窗口)
├── web/ # Web 端
│ ├── backend/
│ │ ├── main.py # FastAPI 入口
│ │ ├── auth/ # JWT 认证(登录、Token、权限)
│ │ ├── routers/ # API 路由(文件、处理、记忆、条码、同步、任务、日志)
│ │ ├── services/ # 后端服务(任务管理、数据库、文件同步)
│ │ └── middleware/ # HTTP 日志中间件
│ └── frontend/
│ ├── src/
│ │ ├── views/ # 页面(Dashboard、Layout、文件管理、任务、日志等)
│ │ ├── stores/ # Pinia 状态管理(auth、processing
│ │ ├── composables/ # 共享逻辑(useDebounce、useFileUtils、useFilePreview
│ │ ├── api.ts # Axios 封装
│ │ └── router/ # Vue Router 路由
│ ├── package.json
│ └── vite.config.ts
├── templates/
│ ├── 银豹-采购单模板.xls # 输出模板(条码/采购量/赠送量/单价)
│ └── 商品资料.xlsx # 单价校验参考数据
├── data/
│ ├── input/ # 输入文件
│ ├── output/ # OCR 输出
── result/ # 最终采购单
── result/ # 最终采购单
│ └── web_data.db # Web 端数据库(SQLite
└── tests/ # 单元测试(191 个)
```
## Web 端功能
基于 Vue 3 + Element Plus + FastAPI 的浏览器端管理界面,与桌面端共享同一个 `data/` 目录。
### 处理中心 (Dashboard)
- **一键全流程**:上传图片或 Excel 后,一键完成 OCR → 标准化 → 合并全流程
- **批量 OCR / 批量处理**:可单独执行 OCR 识别或 Excel 标准化步骤
- **实时进度**:WebSocket 推送任务进度、日志、状态变更
- **多任务监控**:同时查看多个运行中任务的进度和日志
- **任务重试**:失败任务可查看错误详情并一键重试
### 文件管理
- **图片处理**:管理 `data/input/` 中的图片文件,支持上传、预览、批量 OCR、批量生成采购单
- **表格处理**:管理 `data/output/` 中的 Excel 文件,支持上传、预览、批量标准化处理
- **采购单管理**:管理 `data/result/` 中的采购单,支持预览、下载、合并、批量删除
- **实时同步**:页面加载时自动同步磁盘文件到数据库,新文件立即可见
- **清除处理缓存**:删除已处理的输出文件,允许重新处理
### 任务与日志
- **任务历史**:查看所有处理任务的状态、进度、日志,支持按状态和类型筛选
- **HTTP 日志**:记录所有 API 请求,支持按方法和状态码筛选
### 记忆库
- **产品记忆**:自动从 OCR 和处理结果中学习产品信息
- **置信度系统**:根据出现次数自动评估记忆可靠度
- **搜索与管理**:支持搜索、编辑、删除记忆条目
### 条码映射
- **映射规则管理**:添加、编辑、删除条码转换规则
- **批量操作**:支持批量导入和删除映射
### 云端同步
- **Gitea 同步**:通过 Gitea REST API 在多台设备间同步配置文件
- **一键推拉**:选择文件推送或拉取,无需 git 客户端
### 系统配置
- **配置编辑**:在浏览器中编辑系统配置(API 密钥、路径、参数)
- **修改密码**:支持修改 Web 端登录密码
### UI/UX
- **响应式布局**:适配桌面和移动端,小屏幕自动切换为抽屉式导航
- **全局错误处理**:未捕获的 Vue 错误自动显示用户提示
- **表单验证**:修改密码等操作有完整的输入验证
## 供应商智能路由
| 供应商 | 识别特征 | 处理逻辑 |
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
@@ -1,138 +0,0 @@
# Processing Flow + UI/UX Enhancement Plan
> **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development to implement this plan task-by-task.
**Goal:** Enhance task processing flow (retry, multi-task, logs) and add responsive mobile UI.
**Architecture:** Frontend-only changes for most tasks. One backend endpoint for task retry. Processing store refactored from single-task to multi-task model.
**Tech Stack:** Vue 3.4+, TypeScript, Element Plus, Pinia
---
## Phase 2: Processing Flow Enhancement
### Task 1: Multi-task monitoring in processing store
**Files:**
- Modify: `web/frontend/src/stores/processing.ts`
**Description:** Refactor the store from single `currentTask` to a `Map<taskId, TaskInfo>` supporting multiple concurrent tasks. Keep backward compatibility by exposing `currentTask` as the most recent task.
- [ ] Add `activeTasks: ref<Map<string, TaskInfo>>()` state
- [ ] Refactor `connectWebSocket` to register tasks in `activeTasks`
- [ ] Expose `activeTasks` computed (array of active tasks sorted by start time)
- [ ] Keep `currentTask` as computed getter of the latest task
- [ ] Add `removeTask(taskId)` action to dismiss completed/failed tasks
- [ ] Add `retryTask(taskId)` action that calls `POST /api/tasks/{id}/retry`
### Task 2: Backend task retry endpoint
**Files:**
- Modify: `web/backend/routers/tasks.py`
- Modify: `web/backend/services/task_manager.py`
**Description:** Add `POST /api/tasks/{id}/retry` endpoint that reads the original task's stored parameters and re-creates it.
- [ ] Add `metadata: Optional[dict]` field to `Task` dataclass (stores original request params)
- [ ] Store endpoint + body in task metadata when creating tasks in `processing.py`
- [ ] Add `retry_task` method to `TaskManager` that creates a new task from stored metadata
- [ ] Add `POST /api/tasks/{id}/retry` endpoint in `tasks.py`
### Task 3: Dashboard multi-task cards
**Files:**
- Modify: `web/frontend/src/views/Dashboard.vue`
**Description:** Replace the single progress card with a card list showing all active tasks. Each card shows: task name, progress bar, status tag, error message (if failed), retry button (if failed), dismiss button (if completed/failed).
- [ ] Replace single progress card with `v-for` over `activeTasks`
- [ ] Each card: name, progress bar, status tag, message
- [ ] Failed cards: red error panel + "重试" button
- [ ] Completed cards: green success + "查看结果" + dismiss button
- [ ] Show "无活跃任务" placeholder when empty
### Task 4: Expand Dashboard log panel
**Files:**
- Modify: `web/frontend/src/views/Dashboard.vue`
**Description:** Increase log display from 10 to 50 lines. Add "查看全部日志" link to Tasks page.
- [ ] Change `slice(-10)` to `slice(-50)` for log lines
- [ ] Add "查看全部日志" button linking to `/tasks`
- [ ] Increase log panel max-height from 200px to 350px
### Task 5: Error details in Dashboard
**Files:**
- Modify: `web/frontend/src/views/Dashboard.vue`
**Description:** When a task fails, show the error message prominently in the task card.
- [ ] In the task card template, show `task.error` in an `el-alert` with type="error" when status is "failed"
- [ ] Error message should be selectable/copyable
---
## Phase 3: UI/UX Optimization
### Task 6: Mobile sidebar drawer
**Files:**
- Modify: `web/frontend/src/views/Layout.vue`
**Description:** Convert the fixed sidebar to an `el-drawer` on screens < 768px. Add hamburger menu button in topbar.
- [ ] Add `isMobile` ref + resize listener with 768px breakpoint
- [ ] On mobile: hide `el-aside`, show hamburger button in topbar
- [ ] Use `el-drawer` for sidebar navigation on mobile
- [ ] Close drawer on route change
- [ ] Clean up resize listener on unmount
### Task 7: Layout responsive styles
**Files:**
- Modify: `web/frontend/src/views/Layout.vue`
**Description:** Add @media queries for the layout content area.
- [ ] `@media (max-width: 768px)`: reduce content padding, adjust topbar
- [ ] `@media (max-width: 480px)`: further reduce spacing
- [ ] Ensure content doesn't overflow on small screens
### Task 8: Tasks page responsive
**Files:**
- Modify: `web/frontend/src/views/Tasks.vue`
**Description:** Make the Tasks page work on mobile.
- [ ] `@media (max-width: 768px)`: stat cards go 2x2 grid
- [ ] `@media (max-width: 640px)`: stat cards go single column
- [ ] `@media (max-width: 768px)`: table columns hide non-essential ones
- [ ] Filters stack vertically on mobile
### Task 9: Logs page responsive
**Files:**
- Modify: `web/frontend/src/views/Logs.vue`
**Description:** Make the Logs page work on mobile.
- [ ] Same stat card responsive pattern as Tasks
- [ ] `@media (max-width: 768px)`: table columns hide non-essential ones
- [ ] Filters stack vertically on mobile
### Task 10: File views responsive
**Files:**
- Modify: `web/frontend/src/views/files/Images.vue`
- Modify: `web/frontend/src/views/files/Tables.vue`
- Modify: `web/frontend/src/views/files/Orders.vue`
**Description:** Make file list pages work on mobile.
- [ ] `@media (max-width: 768px)`: header-actions wrap to multiple lines
- [ ] Table hides non-essential columns on small screens
- [ ] Action buttons collapse into a dropdown on mobile
@@ -1,256 +0,0 @@
# 日志系统 + 任务历史 + 文件管理 设计文档
> **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:writing-plans to create an implementation plan from this spec.
**Goal:** 为益选 OCR Web 系统添加持久化日志、任务历史和增强文件管理,提升生产环境可观测性和用户体验。
**Architecture:** 单一 SQLite 数据库 (`data/web_data.db`) 存储三类数据,FastAPI 中间件自动采集 HTTP 日志,TaskManager 改造为写入 DB,前端新增两个独立页面。
**Tech Stack:** FastAPI middleware, SQLite (via existing DBPool), Vue 3 + Element Plus, Pinia
---
## 1. 数据库设计
数据库文件: `data/web_data.db`,通过现有 `DBPool` 管理。
### 1.1 `http_logs` 表
```sql
CREATE TABLE IF NOT EXISTS http_logs (
id INTEGER PRIMARY KEY AUTOINCREMENT,
timestamp TEXT NOT NULL, -- ISO 8601
method TEXT NOT NULL, -- GET/POST/PUT/DELETE
path TEXT NOT NULL, -- /api/memory
status_code INTEGER, -- 200, 404, 500
duration_ms REAL, -- 请求耗时(ms)
user TEXT, -- 当前用户名
ip TEXT, -- 客户端 IP
detail TEXT -- 错误详情/备注
);
CREATE INDEX IF NOT EXISTS idx_http_logs_timestamp ON http_logs(timestamp);
CREATE INDEX IF NOT EXISTS idx_http_logs_status ON http_logs(status_code);
```
### 1.2 `task_history` 表
```sql
CREATE TABLE IF NOT EXISTS task_history (
id TEXT PRIMARY KEY, -- 8-char UUID
name TEXT NOT NULL, -- pipeline/ocr-batch/excel/merge/sync-push/sync-pull
status TEXT NOT NULL, -- pending/running/completed/failed
progress INTEGER DEFAULT 0,
message TEXT,
result_files TEXT, -- JSON array of filenames
error TEXT,
log_lines TEXT, -- JSON array of log strings
created_at TEXT NOT NULL, -- ISO 8601
updated_at TEXT NOT NULL, -- ISO 8601
completed_at TEXT -- ISO 8601, null if not done
);
CREATE INDEX IF NOT EXISTS idx_task_history_status ON task_history(status);
CREATE INDEX IF NOT EXISTS idx_task_history_created ON task_history(created_at);
```
### 1.3 `file_metadata` 表
```sql
CREATE TABLE IF NOT EXISTS file_metadata (
id INTEGER PRIMARY KEY AUTOINCREMENT,
filename TEXT NOT NULL,
directory TEXT NOT NULL, -- input/output/result
size INTEGER,
action TEXT NOT NULL, -- upload/delete/clear
user TEXT,
timestamp TEXT NOT NULL, -- ISO 8601
task_id TEXT -- 关联的任务 ID (可选)
);
CREATE INDEX IF NOT EXISTS idx_file_metadata_timestamp ON file_metadata(timestamp);
```
### 1.4 自动清理
30 天过期清理,在服务器启动时执行,之后每天通过 `asyncio` 定时任务执行一次:
```python
async def cleanup_old_records():
cutoff = (datetime.now() - timedelta(days=30)).isoformat()
await db_pool.execute_write("DELETE FROM http_logs WHERE timestamp < ?", cutoff)
await db_pool.execute_write("DELETE FROM task_history WHERE created_at < ?", cutoff)
await db_pool.execute_write("DELETE FROM file_metadata WHERE timestamp < ?", cutoff)
```
---
## 2. 后端架构
### 2.1 新增文件
| 文件 | 职责 |
|------|------|
| `web/backend/services/db_schema.py` | 建表 SQL + `init_db()` + `cleanup_old_records()` |
| `web/backend/middleware/logging.py` | HTTP 请求日志中间件 |
| `web/backend/routers/logs.py` | 日志查询 API |
| `web/backend/routers/tasks.py` | 任务历史 API |
### 2.2 修改文件
| 文件 | 改动 |
|------|------|
| `web/backend/main.py` | lifespan 中调用 `init_db()`,挂载 logging 中间件,注册 logs/tasks 路由 |
| `web/backend/services/task_manager.py` | `update_progress()``_finish()` 写入 task_history 表 |
| `web/backend/routers/files.py` | upload/delete/clear 操作写入 file_metadata 表 |
### 2.3 API 端点
**日志 (`/api/logs`)**
- `GET /api/logs` — 分页查询
- 参数: `page`, `page_size`, `method`, `status_code`, `path`(搜索), `start_date`, `end_date`
- 返回: `{ items: [...], total: number }`
- `GET /api/logs/stats` — 统计概览
- 返回: `{ today_count, error_count, avg_duration_ms, error_rate }`
**任务历史 (`/api/tasks`)**
- `GET /api/tasks` — 分页查询
- 参数: `page`, `page_size`, `status`, `name`(类型筛选), `search`
- 返回: `{ items: [...], total: number }`
- `GET /api/tasks/{task_id}` — 任务详情(含完整 log_lines)
- `POST /api/tasks/{task_id}/retry` — 重试失败任务
- 根据 `name` 字段重新调用对应处理端点
**文件历史 (`/api/files`)**
- `GET /api/files/history` — 文件操作记录
- 参数: `page`, `page_size`, `directory`, `action`
- 返回: `{ items: [...], total: number }`
- `GET /api/files/stats` — 存储统计
- 返回: `{ directories: [{ name, file_count, total_size }] }`
### 2.4 中间件设计
```python
async def logging_middleware(request: Request, call_next):
# 跳过静态资源和 WebSocket
if request.url.path.startswith("/assets") or request.url.path.startswith("/ws"):
return await call_next(request)
start = time.time()
response = await call_next(request)
duration_ms = (time.time() - start) * 1000
# 异步写入日志(不阻塞响应)
asyncio.create_task(write_log(
method=request.method,
path=request.url.path,
status_code=response.status_code,
duration_ms=duration_ms,
user=get_current_user_from_request(request),
ip=request.client.host,
))
return response
```
### 2.5 TaskManager 改造
现有 `TaskManager.update_progress()``_finish()` 方法中增加 DB 写入:
```python
async def update_progress(self, task_id: str, progress: int, message: str):
task = self._tasks[task_id]
task.progress = progress
task.message = message
task.log_lines.append(message)
# 新增:写入 DB
await self._db.execute_write(
"UPDATE task_history SET progress=?, message=?, log_lines=?, updated_at=? WHERE id=?",
progress, message, json.dumps(task.log_lines), datetime.now().isoformat(), task_id
)
await self._broadcast(task)
```
---
## 3. 前端设计
### 3.1 新增页面
**侧边栏导航新增 2 项:**
| 页面 | 路由 | 图标 | 标签 |
|------|------|------|------|
| 任务历史 | `/tasks` | `Timer` | - |
| 日志中心 | `/logs` | `Notebook` | - |
### 3.2 任务历史页面 (`Tasks.vue`)
**布局:**
- 顶部统计卡片行(4 卡片):总任务数 / 成功 / 失败 / 运行中
- 筛选栏:状态下拉(全部/成功/失败/运行中)+ 类型下拉(全部/pipeline/ocr/excel/merge+ 搜索框
- 表格列:任务ID、类型、状态(彩色标签)、进度条、耗时、创建时间、操作
- 操作:查看详情(弹窗显示完整日志流)、重试(仅失败任务)
**详情弹窗:**
- 任务基本信息(类型/状态/耗时/结果文件)
- 终端风格日志流(复用 Dashboard 的 log-box 样式)
- 结果文件列表(可下载)
### 3.3 日志中心页面 (`Logs.vue`)
**布局:**
- 顶部统计卡片行(4 卡片):今日请求 / 错误数 / 平均耗时 / 错误率
- 筛选栏:时间范围选择器(今天/7天/30天)+ 方法筛选(GET/POST/PUT/DELETE+ 状态码筛选(2xx/4xx/5xx+ 路径搜索
- 表格列:时间、方法(彩色标签)、路径、状态码(颜色区分)、耗时、用户
- 点击行展开详情面板(IP 地址、错误信息)
### 3.4 Dashboard 增强
- stats-row 第三列从硬编码 "记忆库 5591" 改为动态存储统计(磁盘用量)
- 文件列表区新增「操作历史」按钮,弹窗显示该目录的 file_metadata 记录
### 3.5 新增文件
| 文件 | 职责 |
|------|------|
| `web/frontend/src/views/Tasks.vue` | 任务历史页面 |
| `web/frontend/src/views/Logs.vue` | 日志中心页面 |
| `web/frontend/src/stores/tasks.ts` | 任务历史状态管理(可选,可用 api 直接调用) |
### 3.6 修改文件
| 文件 | 改动 |
|------|------|
| `web/frontend/src/views/Layout.vue` | navItems 新增 2 项 |
| `web/frontend/src/router/index.ts` | 新增 2 个路由 |
| `web/frontend/src/views/Dashboard.vue` | stats-row 动态化 + 文件历史弹窗 |
---
## 4. 安全与性能
- 日志查询 API 仅限认证用户
- HTTP 日志不记录请求体(避免泄露敏感数据)
- 中间件使用 `asyncio.create_task()` 异步写入,不阻塞响应
- 日志表索引:`timestamp``status_code``path`
- 任务表索引:`status``created_at`
- 自动清理 30 天前的记录,防止数据库无限增长
- 分页查询默认 page_size=50,最大 200
---
## 5. 实施顺序
1. **Phase 1: 数据库 + 后端**
- db_schema.py(建表 + 清理)
- logging 中间件
- task_manager 改造
- files.py 改造
- logs.py + tasks.py 路由
2. **Phase 2: 前端页面**
- Tasks.vue
- Logs.vue
- Layout.vue 路由注册
- Dashboard.vue 增强
3. **Phase 3: 集成测试**
- npm run build
- 端到端验证:操作 → 日志记录 → 任务历史 → 文件历史
@@ -1,211 +0,0 @@
# 前端 Bug 修复 + 代码质量提升设计文档
日期: 2026-05-12
状态: 待审核
## 背景
通过全面审计 Web 前端(Vue 3 + Element Plus),发现了 50+ 个问题。本文档聚焦于前端高优先级 Bug 修复和代码质量改进,分 3 个阶段执行。
## 阶段 1:关键 Bug 修复
### 1.1 修复 fetchUser 未调用
**问题**: `Layout.vue``onMounted` 时未调用 `authStore.fetchUser()`。页面刷新后 `authStore.username` 为空,头像显示 "U" 而非用户名首字母。
**修复**: 在 `Layout.vue``onMounted` 中添加 `await authStore.fetchUser()`
**影响文件**: `web/frontend/src/views/Layout.vue`
### 1.2 修复静默吞错
**问题**: 以下文件的 catch 块完全为空,API 失败时用户无任何反馈:
| 文件 | 函数 | 行号 |
|------|------|------|
| `files/Images.vue` | `loadData()` | 165 |
| `files/Tables.vue` | `loadData()` | 161 |
| `files/Orders.vue` | `loadData()` | 168 |
| `Sync.vue` | `checkStatus()` | 127 |
| `Tasks.vue` | `loadStats()` | 207 |
| `Logs.vue` | `loadStats()` | 164 |
| `files/Images.vue` | `deleteFile()` | 272 |
| `files/Tables.vue` | `deleteFile()` | 250 |
| `files/Orders.vue` | `deleteFile()` | 242 |
**修复**: 所有 catch 块添加 `ElMessage.error()` 提示,使用中文错误消息。
**影响文件**: 上述 6 个 Vue 文件
### 1.3 修复 loading 状态管理
**问题**: `Images.vue``Tables.vue``Orders.vue``loadData()``loading.value = false` 不在 `finally` 块中。异常发生时 loading 转圈卡住。
**修复**: 统一改为 `try { ... } finally { loading.value = false }` 模式。
**影响文件**: `files/Images.vue``files/Tables.vue``files/Orders.vue`
### 1.4 修复内存统计回退逻辑
**问题**: `Memory.vue` 当 API 不返回 stats 时,从 `items.value`(当前页 50 条)计算置信度统计,显示为全局数据,严重误导用户。
**修复**: 移除误导性回退逻辑,当 stats 不可用时显示 "暂无统计数据" 占位。
**影响文件**: `web/frontend/src/views/Memory.vue`
### 1.5 添加全局错误处理
**问题**: `main.ts` 没有 `app.config.errorHandler`,未捕获的 Vue 错误只输出到 console.warn,用户无感知。
**修复**: 在 `main.ts` 注册全局错误处理器:
```typescript
app.config.errorHandler = (err, instance, info) => {
console.error('Vue error:', err, info)
ElMessage.error('操作失败,请稍后重试')
}
```
**影响文件**: `web/frontend/src/main.ts`
---
## 阶段 2:代码质量 — 提取共享逻辑
### 2.1 提取 useDebounce composable
**问题**: `useDebounce` 函数在 4 个文件中完全重复(Memory.vue:166-174, Barcodes.vue:190-198, Tasks.vue:144-152, Logs.vue:107-115)。
**修复**: 创建 `web/frontend/src/composables/useDebounce.ts`
```typescript
import { ref } from 'vue'
export function useDebounce(fn: Function, delay = 300) {
const timer = ref<ReturnType<typeof setTimeout> | null>(null)
return (...args: any[]) => {
if (timer.value) clearTimeout(timer.value)
timer.value = setTimeout(() => fn(...args), delay)
}
}
```
4 个文件改为 `import { useDebounce } from '@/composables/useDebounce'`
**影响文件**:
- 新建: `web/frontend/src/composables/useDebounce.ts`
- 修改: `Memory.vue``Barcodes.vue``Tasks.vue``Logs.vue`
### 2.2 提取文件视图共享逻辑
**问题**: `Images.vue``Tables.vue``Orders.vue` 有大量重复代码:
- `statusType()``statusText()``fmtTime()` 函数完全相同
- 预览对话框模板 + 逻辑 + CSS 完全相同
- 详情对话框模板 + 逻辑完全相同
- 分页、多选、排序模式完全相同
- 批量删除模式完全相同
**修复**: 创建 3 个共享 composable
#### `composables/useFileUtils.ts`
```typescript
export function statusType(status: string): string { ... }
export function statusText(status: string): string { ... }
export function fmtTime(t: string): string { ... }
```
#### `composables/useFilePreview.ts`
```typescript
export function useFilePreview() {
// previewVisible, previewContent, previewTitle, previewLoading
// openPreview(), closePreview()
return { ... }
}
```
#### `composables/useFileSelection.ts`
```typescript
export function useFileSelection(fetchFn: Function) {
// selectedFiles, currentPage, pageSize, total, sortProp, sortOrder
// handleSelectionChange(), handlePageChange(), handleSortChange(), toggleSelectAll()
return { ... }
}
```
3 个文件视图改为使用这些 composable,每个文件预计减少 100-150 行重复代码。
**影响文件**:
- 新建: `composables/useFileUtils.ts``composables/useFilePreview.ts``composables/useFileSelection.ts`
- 修改: `files/Images.vue``files/Tables.vue``files/Orders.vue`
### 2.3 清理死代码
| 文件 | 死代码 | 行号 |
|------|--------|------|
| `Layout.vue` | `navItems` 数组(未被引用) | 175-183 |
| `stores/processing.ts` | `pollTaskStatus` 函数(未被调用) | 124-129 |
| `router/index.ts` | `routeLoadingTimer` 逻辑(无消费者) | 87-94 |
| `Barcodes.vue` | `Plus` 图标导入(未使用) | 186 |
**影响文件**: 上述 4 个文件
---
## 阶段 3:小改进
### 3.1 修改密码表单验证
**问题**: `Layout.vue` 的修改密码对话框无任何验证:无最小长度要求、无确认密码字段。
**修复**:
- 添加 el-form 验证规则:密码最少 6 位
- 添加确认密码字段,验证两次输入一致
- 提交前调用 `formRef.validate()`
**影响文件**: `web/frontend/src/views/Layout.vue`
### 3.2 修复 Layout.vue 冗余代码
**问题**: `navigator.onLine !== false` 冗余(`navigator.onLine` 已经是 boolean)。
**修复**: 改为 `navigator.onLine`
**影响文件**: `web/frontend/src/views/Layout.vue`
### 3.3 后端批量删除端点
**问题**: 前端批量删除是 N+1 API 调用(每个文件 2 次请求),50 个文件 = 100 次 HTTP 请求。
**修复**:
- 后端添加 `POST /api/files/batch-delete` 端点,接受 `files: [{directory, filename}]` 数组
- 前端批量删除改为单次 API 调用
**影响文件**:
- 新建/修改: `web/backend/routers/files.py`
- 修改: `files/Images.vue``files/Tables.vue``files/Orders.vue`
---
## 不在范围内
以下问题记录但不在本次修复范围内:
- 响应式布局(仅 Dashboard 有 @media 查询)
- 键盘快捷键
- 类型感知配置编辑器
- 后端安全问题(路径遍历、登录限流等)
- WebSocket 认证 token 刷新
- 产品记忆批量操作/导出
- 任务取消支持
## 验证标准
1. 页面刷新后头像正确显示用户名首字母
2. API 失败时用户看到错误提示 toast
3. loading 状态不会卡住
4. 统计数据准确或显示占位
5. 未捕获的 Vue 错误有用户提示
6. useDebounce 在 4 个文件中通过 import 使用,无重复定义
7. 3 个文件视图使用共享 composable,每个文件减少 100+ 行
8. 4 处死代码已清理
9. 修改密码表单有验证规则和确认字段
10. 批量删除为单次 API 调用