LRAgent 桌面客户端
LRAgent 桌面客户端基于 Electron v28 构建,在网页版基础上扩展了丰富的本地系统能力。
与网页版的区别
| 功能 | 网页版 | 桌面端 |
|---|---|---|
| 系统托盘常驻 | - | ✅ |
| 全局快捷键唤起 | - | ✅ Ctrl+Shift+L |
| 拖拽文件导入 | - | ✅ |
| 离线数据缓存 | - | ✅ |
| 本地文件系统读写 | - | ✅ |
| 原生文件对话框 | - | ✅ |
| 导出为PDF | - | ✅ |
| Windows原生通知 | - | ✅ |
| 自动检查更新 | - | ✅ |
| 系统信息查看 | - | ✅ |
| 剪贴板图片读写 | - | ✅ |
| 窗口最小化到托盘 | - | ✅ |
| NSIS安装包分发 | - | ✅ |
快速启动
开发模式
cd C:\bossagents
npm run electron:dev
此命令会自动:
- 检测 Vite 开发服务器是否在 5173 端口运行
- 如未运行则自动启动 Vite
- 清除
ELECTRON_RUN_AS_NODE环境变量(关键步骤) - 启动 Electron 桌面窗口
生产模式(安装包)
npm run electron:build
生成的安装包位于 release/LRAgent-Setup-{version}-win-x64.exe。
仅预览(不打包)
npm run electron:preview
快捷键
| 快捷键 | 功能 |
|--------|------|
| Ctrl+Shift+L | 显示/隐藏主窗口 |
| Ctrl+Shift+N | 快速创建 Part |
| Ctrl+Shift+I | 数据质量检查 |
| Ctrl+O | 导入文件 |
| Ctrl+S | 导出文件 |
| Ctrl+F | 查找 Part |
| F11 | 全屏切换 |
系统托盘
最小化窗口后,LRAgent 会缩小到系统托盘区域:
- 单击托盘图标:显示右键菜单
- 双击托盘图标:恢复主窗口
- 托盘菜单包含:显示窗口、快速创建Part、数据质量检查、离线模式切换、关于、退出
离线模式
桌面端支持离线数据缓存:
- 前端通过
window.lragent.offlineCacheSave(key, data)保存数据 - 通过
window.lragent.offlineCacheLoad(key)读取缓存 - 通过
window.lragent.offlineCacheList()列出所有缓存条目 - 缓存存储在
%APPDATA%/lragent/offline-cache/目录 - 菜单「工具 → 清除缓存」可一键清空
前端 API
桌面端通过 window.lragent 对象暴露以下 API(仅在桌面端可用):
文件系统
// 判断是否在桌面端
if (window.lragent?.isDesktop) {
// 读取文本文件
const result = await window.lragent.readFile('/path/to/file.json')
// 写入文件
await window.lragent.writeFile('/path/to/output.json', JSON.stringify(data))
// 读取二进制文件(返回 base64)
const binary = await window.lragent.readBinaryFile('/path/to/image.png')
// 检查文件是否存在
const exists = await window.lragent.fileExists('/path/to/file')
// 获取文件信息
const stat = await window.lragent.fileStat('/path/to/file')
// 列出目录内容
const dir = await window.lragent.listDir('/path/to/dir')
// 创建目录
await window.lragent.mkdir('/path/to/new/dir')
// 删除文件
await window.lragent.deleteFile('/path/to/file')
// 原生文件选择对话框
const { filePaths } = await window.lragent.selectOpenFile({
filters: [{ name: 'Excel', extensions: ['xlsx', 'xls'] }]
})
// 保存文件对话框
const { filePath } = await window.lragent.selectSaveFile({
filters: [{ name: 'JSON', extensions: ['json'] }]
})
// 选择目录
const { filePath: dirPath } = await window.lragent.selectDirectory()
}
剪贴板
const text = await window.lragent.readClipboardText()
await window.lragent.writeClipboardText('复制内容')
const imageDataURL = await window.lragent.readClipboardImage() // 返回 data:image/png;base64,... 或 null
系统信息
const info = await window.lragent.getSystemInfo()
// 返回: { platform, arch, cpuModel, cpuCores, totalMemGB, freeMemGB, uptime, hostname, electronVer, chromeVer, nodeVer }
通知
await window.lragent.sendNotification('LRAgent', 'Part 创建成功')
窗口控制
await window.lragent.windowMinimize()
await window.lragent.windowMaximize() // 切换最大化
await window.lragent.windowClose()
await window.lragent.windowSetTitle('LRAgent - Part 详情')
离线缓存
await window.lragent.offlineCacheSave('parts-list', partsData)
const cached = await window.lragent.offlineCacheLoad('parts-list')
const allKeys = await window.lragent.offlineCacheList()
事件监听
// 文件拖拽到窗口
window.lragent.onFilesDropped((filePaths) => {
console.log('拖入文件:', filePaths)
})
// 菜单导入文件
window.lragent.onFilesImported((filePaths) => {
console.log('导入文件:', filePaths)
})
// 菜单导入目录
window.lragent.onDirectoryImported((dirPath) => {
console.log('导入目录:', dirPath)
})
// 快捷操作(托盘菜单/全局快捷键触发)
window.lragent.onQuickAction((action) => {
// action: 'create-part' | 'inspect-quality' | 'change-impact' | 'search' | 'local-db'
if (action === 'create-part') openCreatePartDialog()
if (action === 'inspect-quality') runQualityInspection()
})
// 离线模式切换
window.lragent.onOfflineMode((enabled) => {
console.log('离线模式:', enabled ? '开启' : '关闭')
})
// 缓存已清除
window.lragent.onCacheCleared(() => {
console.log('缓存已清除')
})
// 导出完成
window.lragent.onExportComplete((info) => {
console.log('导出完成:', info.type, info.path)
})
其他
const version = await window.lragent.getAppVersion() // '0.1.0'
const dataPath = await window.lragent.getAppPath('userData') // 用户数据目录
const locale = await window.lragent.getAppLocale() // 'zh-CN'
await window.lragent.openExternal('https://eastaiai.com') // 打开外部链接
await window.lragent.openPath('C:\\Users\\...') // 用系统程序打开
await window.lragent.showItemInFolder('C:\\file.txt') // 在资源管理器中显示
await window.lragent.checkForUpdates() // 检查GitHub更新
目录结构
C:\bossagents\
├── electron/ # Electron 启动脚本
│ ├── run.js # 启动器(自动管理Vite + ELECTRON_RUN_AS_NODE)
│ ├── main.js # 主进程(备用,与electron-app/main.js同步)
│ └── preload.js # 预加载脚本(备用)
├── electron-app/ # 独立Electron应用目录
│ ├── package.json # 独立package.json(避免node_modules干扰)
│ ├── main.js # 主进程(窗口、托盘、快捷键、IPC)
│ ├── preload.js # 安全桥接层(contextBridge API)
│ └── installer.nsh # NSIS自定义安装脚本
├── electron-builder.yml # 打包配置
├── dist/ # Vite构建产物(打包时包含)
└── release/ # 安装包输出目录
└── LRAgent-Setup-0.1.0-win-x64.exe
打包配置
electron-builder.yml 支持:
- Windows: NSIS 安装包(支持自定义安装路径、中文界面、桌面快捷方式)
- macOS: DMG 镜像(x64 + arm64)
- Linux: AppImage + deb
- 自动更新: GitHub Release 发布后自动检测
故障排查
1. ELECTRON_RUN_AS_NODE 导致启动失败
症状: TypeError: Cannot read properties of undefined (reading 'handle')
原因: npm/pnpm 在执行脚本时设置 ELECTRON_RUN_AS_NODE=1,导致 Electron 以普通 Node.js 模式运行。
解决: electron/run.js 已自动处理此问题。如手动启动需确保:
$env:ELECTRON_RUN_AS_NODE=''
& "node_modules\electron\dist\electron.exe" "electron-app" --dev
2. Vite 未启动导致白屏
症状: 窗口打开但显示 ERR_CONNECTION_REFUSED
解决: 先启动 Vite 再启动 Electron,或使用 npm run electron:dev(自动管理)
3. Electron v43 不兼容
原因: Electron v43 要求 ESM 导入(import from 'electron/main'),与 CJS 项目不兼容。
解决: 当前使用 Electron v28 LTS(CJS 兼容),package.json 已锁定版本。
4. 安装包缺少图标
现状: 使用 Electron 默认图标。如需自定义,将 icon.ico 放入 electron-app/ 目录,并在 electron-builder.yml 中配置 win.icon。
5. better-sqlite3 原生模块重建失败
原因: electron-builder 默认会 rebuild 原生模块,需要 Python + Visual Studio Build Tools。
解决: 打包时加 --config.npmRebuild=false 跳过重建:
npx electron-builder --win --x64 --config.npmRebuild=false
版本历史
- v0.1.0 (2026-08-16)
- 初始桌面客户端
- 系统托盘 + 全局快捷键
- 拖拽文件导入
- 离线数据缓存
- 本地文件系统 API
- NSIS 安装包
- 自动更新检测
BossAgents