# React 前端(src) ## 结构总览 ``` src/ ├── App.tsx # 全局主题(AntD ConfigProvider,明暗主题) ├── main.tsx # 入口 ├── i18n.ts # 国际化:Locale / AppErrorInfo / translate / formatAppError / toAppError ├── i18n/ │ ├── en-US.ts # 英文文案表 │ └── zh-CN.ts # 中文文案表(TranslationKey 基准) ├── pages/ │ └── HomePage.tsx # 唯一主页面:侧栏 + 标签页 + 各面板 + 设置对话框(FooterActions) ├── components/ │ ├── host/HostListSidebar.tsx # 主机列表:搜索、分组、折叠、右键操作 │ ├── host/HostEditorDialog.tsx # 主机编辑对话框(含分组) │ ├── terminal/TerminalTabs.tsx # 会话标签栏 │ ├── terminal/TerminalPane.tsx # 会话面板容器 │ ├── terminal/XtermView.tsx # xterm.js 渲染封装(应用终端主题) │ ├── terminal/terminalThemes.ts # 6 套 xterm 色板(light/dark/OneDark/Dracula/Solarized×2) │ ├── status/ServerStatusPanel.tsx # 监控面板(CPU/内存/磁盘 + 网卡选择 + 60s 趋势) │ ├── sftp/SftpPanel.tsx # SFTP 面板容器(目录 + 传输队列) │ ├── sftp/FileExplorer.tsx # 远程文件浏览(右键菜单操作) │ ├── sftp/TransferQueue.tsx # 传输任务队列(取消/重试) │ └── shell/EmptyState.tsx # 无会话空态页 ├── stores/ # Zustand stores(无业务逻辑在组件内) │ ├── host.ts # 主机 CRUD + 搜索过滤 + 分组聚合/重命名/删除 │ ├── session.ts # 会话状态机(后端权威状态,无回写/无超时改写) │ ├── monitor.ts # 监控快照 + 网卡选择 + 趋势窗口 + 长任务 + 事件缓存补投 │ ├── sftp.ts # SFTP 目录 + 传输任务 + 事件缓存补投 │ ├── layout.ts # 侧栏宽度 + 分组折叠 + 监视条折叠 │ ├── theme.ts # 应用明暗主题 │ ├── locale.ts # 界面语言(跟随系统 + 手动切换 + localStorage) │ ├── log-level.ts # 日志等级(invoke set_log_level 同步后端) │ ├── logs.ts # 日志查看器投影(get_recent_logs 轮询 + export_logs 导出) │ └── terminal-theme.ts # SSH 终端主题偏好(跨会话共享) ├── types/ # TS 类型定义(与 Rust 模型 camelCase 对齐) │ ├── host.ts · session.ts · monitor.ts · sftp.ts └── test/ # 单测 + 集成测试 + mocks ``` ## Store 职责(Zustand,不可变更新) | Store | 状态 | 职责 | | --- | --- | --- | | host | hosts、loading、error、searchQuery、selectedHostId | 主机列表 CRUD,`filterHosts` / `groupHosts` 纯函数(搜索匹配名称/地址/分组),`renameGroup` / `deleteGroup` 逐个保存受影响主机 | | session | sessions: Map、activeView、statusMessage | 会话生命周期,`applySessionStatus/Progress` 消费后端权威事件;Connected 后初始化该会话 SFTP 根目录;closeSession 只请求后端 teardown 后清理本地投影(含 monitor/sftp 的 clearSession);事件监听入口 `initListeners` | | monitor | snapshots、selectedInterfaces、networkTrends、tasks、sessionTaskMap、pendingTaskEvents | 监控快照缓存、网卡选择(只接受最新快照候选)、所选网卡 60s 趋势窗口(TREND_WINDOW_MILLIS)、长任务状态机、事件缓存补投 | | sftp | sessionStates: Map、pendingTaskEvents | 目录浏览、任务队列、进度更新,监听 `sftp:progress` / `sftp:task_status`,同样带事件缓存补投 | | layout | sidebarWidth、collapsedGroups、monitorCollapsed | 侧栏拖拽宽度(clamp 到视口)、分组折叠状态持久化、监视条折叠 | | theme | theme: 'light' | 'dark' | 应用主题切换(DOM data-theme + localStorage + 系统偏好) | | locale | locale: 'zh-CN' | 'en-US' | `detectLocale` 跟随系统语言,`initLocale` 优先本地存储,`setLocale` 持久化 | | log-level | logLevel: 'error'..'trace' | `setLogLevel` invoke `set_log_level` 同步后端日志过滤器并持久化 | | logs | lines、loadError、exportError | 日志查看器投影:`load` invoke `get_recent_logs`(非数组响应防御回退空列表),`export` invoke `export_logs` 复制日志文件 | | terminal-theme | terminalTheme | 跨会话共享的 SSH 终端主题偏好(localStorage) | ## 关键流程 ### 会话打开 ``` HomePage 点击主机 → sessionStore.openSession(hostId) → invoke('open_session') → 写入 sessions + activeView → sftpStore.listDir('/') + monitorStore.startMonitoring(监控失败不阻断主流程) → initListeners 注册:session:status / session:progress / monitor:snapshot / task:status → Connected 事件到达 → 自动初始化 SFTP 根目录 ``` ### 会话关闭 ``` closeSession(sessionId) → invoke('close_session')(后端统一 teardown Terminal/Monitoring/SFTP) → 移除本地 sessions 投影 + 重置 activeView → monitorStore.clearSession / sftpStore.clearSession(只清投影,不再发停止命令) ``` ### 终端渲染(xterm.js) - `XtermView` 持有 xterm Terminal + FitAddon,只负责渲染;应用 `terminalTheme` store 的色板 - 键盘输入 → `writeTerminal(sessionId, data)` → invoke `write_terminal` - 后端 `terminal:data` → xterm 写入缓冲区 - 尺寸变化 → FitAddon → `resizeTerminal` → invoke `resize_terminal` ### 监控面板 - `monitor:snapshot` 事件 → applySnapshot → `ServerStatusPanel` 直接消费 - 快照内网卡列表驱动选择器;所选网卡的 RX/TX 速率样本按 60s 窗口保留为趋势;切换网卡或候选变化时重置趋势 - `task:status` → applyTaskStatus(未知任务缓存,latest-wins,元数据到达后补投) - 前端不做任何聚合,一次更新一个完整 payload ### SFTP - `SftpPanel` 展示 FileExplorer + TransferQueue;FileExplorer 支持右键菜单(下载/重命名等操作入口) - 上传/下载经 Tauri dialog 选文件 → `sftp_upload / sftp_download` 返回 TransferTask - `sftp:progress`(~500ms)更新进度条(终态任务忽略进度回退),`sftp:task_status` 驱动任务状态机;Done 时进度强制为总大小;Failed 展示本地化错误文案 - 快速完成/快速失败事件先于 invoke 返回时缓存,任务元数据写入后补投,避免任务卡 Pending ### 设置对话框(FooterActions) - 侧栏底部齿轮入口,三个分区:通用(语言切换)、终端(6 套主题卡片预览选择)、日志(五档等级下拉 + 内嵌日志查看器) - 语言切换即时生效(所有文案经 `translate(locale, key)` 渲染);日志等级写入后立即 invoke 同步后端 - 日志查看器(LogViewer):挂载即加载并每 2 秒轮询 `get_recent_logs`(卸载停止,latest-wins 丢弃迟到响应),刷新按钮手动重拉,导出经 `export_logs` 由后端弹出保存对话框并复制日志文件(目标路径不经 IPC 边界,防重入);日志行纯文本展示不解析 ## 测试策略(前端) - **单测**:stores 状态流转(stores.test.ts,含事件缓存补投、latest-wins、clearSession 清理)、i18n(translate/formatAppError/toAppError)、log-level(读取回退)、terminal-theme(读取回退)、release-notes / release-workflow(发布说明脚本)、application-name、smoke - **组件测试**:React Testing Library(components.test.tsx、xterm-view.test.tsx),无业务逻辑断言 - **集成测试**:homepage.integration.test.tsx —— invoke/event 契约验证 - **Mock**:src/test/mocks/(tauri.ts、event.ts、dialog.ts),jsdom 环境(setup.ts) - **E2E**:Playwright + `__TAURI_TEST__` 桥接(e2e/app.spec.ts),mock 后端事件驱动,覆盖分组/搜索/折叠、监视条折叠、失败重试、完整闭环 ## 注意 - 组件内不允许业务逻辑;只消费 store 状态与事件 - 事件监听由各 store 的 `initListeners` 注册(session / monitor / sftp),统一在 HomePage useEffect 的 cleanup 中销毁 - 前端类型必须与 Rust serde camelCase 输出严格一致;错误展示统一走 `formatAppError`(本地化 code + 保留 detail)