# Changelog: Vuetify 前端审计修复 **日期**: 2026-05-31 **主题**: 5个严重问题修复 + DRY 重构 + 类型安全 ## 变更摘要 ### 1. 提取 useSnackbar composable(DRY 重构) - **新建** `src/composables/useSnackbar.ts` — 统一 snackbar 包装函数 - **新建** `src/types/global.d.ts` — `Window.$snackbar` 全局类型声明 - **修改 8 个页面** — 删除各自重复的 `function snackbar(...)` 定义,改为 `import { useSnackbar }` - **修改** `App.vue` — `(window as any).$snackbar` → `window.$snackbar` - **修改** `useWebSocket.ts` — 同上 ### 2. API client FormData 支持(🔴 严重 #1 + 🟠 #12) - **修改** `src/api/index.ts`: - `body instanceof FormData` 时不设 `Content-Type`(浏览器自动加 boundary) - 新增 `http.upload()` 方法 - **修改** `FilesPage.vue` `doUpload()`: - `fetch('/api/sync/upload')` → `http.upload('/sync/upload', form)` - 修复:文件上传现在自动携带 JWT Authorization header ### 3. 修复 6 个静默 catch 块(🔴 严重 #3) - `FilesPage.vue` — `loadServers()` / `browse()` catch 加 snackbar 错误提示 - `SettingsPage.vue` — `loadSettings()` / `loadAllowlist()` catch 加 snackbar 错误提示 - `DashboardPage.vue` — `loadStats()` / `loadServers()` catch 加 snackbar 错误提示 ### 4. SettingsPage API Key 密码验证对话框(🔴 严重 #2) - **新增** `` 密码输入对话框(与 TOTP Disable 对话框模式一致) - `revealApiKey()` → 先弹密码对话框 → `doRevealApiKey()` 用真实密码调用后端 - 修复:不再发送空密码 `{ password: '' }` 绕过 re-auth 保护 ### 5. DashboardPage 数据补全(🔴 严重 #5) - **新增** `loadAlerts()` — 从 `GET /alert-history/?limit=5` 加载告警历史 - **新增** `loadSummary()` — 从 `GET /servers/?per_page=500` 计算: - Agent 覆盖率(有 agent_version 的服务器比例) - CPU 平均值(从 system_info.cpu_usage) - 内存平均值(从 system_info.mem_usage) - **新增** WebSocket 实时刷新 — 监听 ws.alerts 变化自动刷新 stats + alerts ### 6. 消除 as any 类型断言(🔴 严重 #4) - **新建** `src/types/api.ts` — 14 个 API 响应 interface: - `PaginatedResponse`, `SettingsResponse`, `AllowlistResponse`, `BrowseResponse` - `FileEntry`, `ServerApiItem`, `AlertLogItem`, `AlertStatsResponse` - `CommandLogItem`, `RetryItem`, `ScriptItem`, `PushItem`, `ScheduleItem`, `AuditItem` - **修改 12 个页面** — 所有 `(res as any).items` 改为 `http.get>()` - **结果**: `as any` 从 33 个降到 0 个(src/ 目录完全清零) ## 涉及文件 | 文件 | 操作 | |------|------| | `src/composables/useSnackbar.ts` | 新建 | | `src/types/global.d.ts` | 新建 | | `src/types/api.ts` | 新建 | | `src/api/index.ts` | 修改 | | `src/App.vue` | 修改 | | `src/composables/useWebSocket.ts` | 修改 | | `src/pages/FilesPage.vue` | 修改 | | `src/pages/SettingsPage.vue` | 修改 | | `src/pages/DashboardPage.vue` | 修改 | | `src/pages/CredentialsPage.vue` | 修改 | | `src/pages/RetriesPage.vue` | 修改 | | `src/pages/PushPage.vue` | 修改 | | `src/pages/ScriptsPage.vue` | 修改 | | `src/pages/SchedulesPage.vue` | 修改 | | `src/pages/ServersPage.vue` | 修改 | | `src/pages/CommandsPage.vue` | 修改 | | `src/pages/AlertsPage.vue` | 修改 | | `src/pages/AuditPage.vue` | 修改 | | `src/pages/TerminalPage.vue` | 修改 | ## 验证 - ✅ `vue-tsc --noEmit` — TypeScript 零错误 - ✅ `vite build` — 生产构建通过 (1.53s, Monaco 懒加载) - ✅ 开发服务器正常启动 --- ## Phase 2: Composables + UX + 核心功能 (同日) ### Composables 提取 (Step 7) - **新建** `src/composables/useServerList.ts` — 5 个页面复用 - **新建** `src/composables/useServerPagination.ts` — ServersPage/DashboardPage 复用 - **新建** `src/utils/status.ts` — statusChipColor/statusLabel/formatRelativeTime ### 表单验证 + 空状态 + Loading (Step 8) - **新建** `src/utils/validation.ts` — 8 个验证规则工厂 - **12 个空状态** — 9 个文件的 v-data-table 加 `