一个桌面应用,用于连接蓝牙 Cpen 设备并获取 TOTP(一次性密码)和设备 ID
这个项目是我们几个同学一起搞的设计主要想法是做一个能跟特定蓝牙设备(Cpen)通信的桌面客户端,能拿到设备的 TOTP 和唯一标识。
用于取代被希沃断头台吓得瑟瑟发抖的u盘们
- 自动扫描附近的蓝牙设备
- 智能识别 Cpen 设备(根据设备名前缀匹配)
- 只保持一个有效连接(不能同时连多个,会冲突)
- 向连接的 Cpen 设备发送
getTotp命令 - 自动设置设备时间(需要先
setTime) - 30 秒缓存机制,避免频繁请求
- 获取设备唯一 ID(UUID)
- 实时显示连接状态
- 手动断开/重新连接
- 云端文件浏览(和本地资源管理器差不多)
- 支持目录切换、新建文件夹
- 文件批量下载(勾选想要的就能下)
- 单文件上传(支持拖拽)
- 上传/下载任务实时进度显示
- 断点续传(暂停后继续不重新来)
- 传输历史记录(方便找之前传过啥)
- 同时管理多个传输任务
- 从云端获取笔记列表
- 笔记内容在线查看
- 迷你悬浮窗口(点击图标就能调出)
- 随时查看TOTP和连接状态
- 不影响其他操作
- 支持多显示器选择
- 一键截取屏幕
- 支持 5 种标注颜色(红、蓝、绿、黄、紫)
- 提供 5 种标注工具:
- 矩形标注
- 圆形标注
- 箭头标注
- 自由绘制
- 文字标注
- 支持标注的创建、编辑、删除
- 支持撤销/重做操作
- 标注数据自动保存和加载
- 图片裁切功能
- 支持亮色/暗色主题
- 自动跟随系统主题(如果用户没手动设置过)
- 用户偏好保存到本地
- Windows键模拟(快捷键调用某些功能)
- 文件和文件夹直接打开
- 自定义下载路径
- Vue 3 - 主要框架
- TypeScript - 类型安全(虽然有些地方还是 any,慢慢改吧)
- Pinia - 状态管理(主要用在蓝牙状态)
- Vue Router - 路由
- Vite - 构建工具(打包速度确实快)
- Tauri - 桌面应用框架
- tokio - 异步运行时(蓝牙操作都是异步的)
- btleplug - 蓝牙通信库(Windows/macOS/Linux 都支持)
- CSS 变量 - 主题切换全靠这个
- Normalize.css - 重置浏览器默认样式
- Remix Icon - 图标库
- Lucide Vue Next - 另一个图标库(有些页面用这个)
- Node.js 18+(我们用的 20,低版本没试过)
- Rust 1.70+(装 Tauri 需要的)
- 系统依赖:
- Windows:没啥特别的,应该都能跑
- macOS:需要 Xcode 命令行工具
- Linux:需要 libwebkit2gtk 之类的,具体看 Tauri 文档
# 安装依赖(第一次运行需要)
npm install
# 启动开发服务器
npm run dev
# 同时启动 Tauri 窗口
npm run tauri dev# 构建前端
npm run build
# 构建桌面应用(打包成安装包)
npm run tauri buildCAMFC-client/
├── src/ # 前端源代码
│ ├── components/ # Vue 组件
│ │ ├── annotate/ # 截图标注相关
│ │ ├── data/ # 数据相关(蓝牙、文件系统、传输)
│ │ ├── file/ # 文件管理组件
│ │ └── layout/ # 布局组件(头部、侧边栏等)
│ ├── composables/ # Vue 组合式函数
│ ├── config/ # 前端配置
│ ├── stores/ # Pinia 状态存储
│ ├── router/ # 路由配置
│ ├── utils/ # 工具函数
│ └── views/ # 页面视图
├── src-tauri/ # Rust 后端代码
│ ├── src/
│ │ ├── bluetooth.rs # 蓝牙底层操作
│ │ ├── cpen_device_manager.rs # 业务逻辑管理器
│ │ ├── download.rs # 下载模块(断点续传)
│ │ ├── upload.rs # 上传模块(断点续传)
│ │ ├── storage.rs # 本地存储管理
│ │ ├── screenshot.rs # 截图功能
│ │ ├── event_emitter.rs # 事件发射器
│ │ ├── config.rs # 后端配置
│ │ ├── lib.rs # Tauri 命令入口
│ │ └── main.rs # 程序入口
│ └── tauri.conf.json # Tauri 配置文件
├── public/ # 静态资源
└── 各种配置文件 # package.json, vite.config.ts 等
最开始用 JavaScript 的 Web Bluetooth API,发现兼容性实在一言难尽。后来改用 Rust 的 btleplug,但异步编程挺烧脑的,尤其是错误处理。 还有就是很多宣称是蓝牙5.x版本的蓝牙适配器是4.0版本的,所以最开始开发的时候发现笔记本可以用5.x但是台式机5.x就不行,最后发现都是拿4.0芯片伪装的,最后换大厂的蓝牙适配器就一切正常了。
蓝牙状态需要在多个组件间共享,开始用 props 传,后来发现太乱。换成 Pinia 好多了,但要注意 store 的初始化时机。
用 CSS 变量实现主题切换,比写两套 CSS 方便。但要注意过渡动画,不然切换时很生硬。
- 设备连接:Cpen 设备有特定服务 UUID,写死在代码里了(如果设备升级可能要改)
- 命令格式:发送给设备的命令是特定字节数组,不能随便改
- 错误处理:蓝牙连接可能随时断开,所有操作都要考虑重试
- 响应式:蓝牙状态变化时要及时更新 UI
- 生命周期:组件卸载时要清理监听器,不然内存泄漏
- 路由:有些页面需要蓝牙已连接才能访问,做了路由守卫
- 某些 Windows 电脑蓝牙驱动有问题,连接不稳定
- 设备距离远了会自动断开,重连逻辑还不够完善
- 暗色主题下某些图标颜色对比度不够(懒得调了)
本项目保留所有权利。未经作者明确授权,禁止任何形式的复制、分发、修改或商业使用。
- 许嘉乐 (@ant-cave)
- 陈欣航 (@cxh09)
- 温子墨 (@lusamaqq)
- 曾楷彬 (@Waple1145)
遇到问题可以:
- 先检查设备蓝牙是否开启
- 确认设备是 Cpen 且电量充足
- 查看开发者工具控制台有没有错误
- 如果还不行...提 issue 吧,我们尽量复现
写于 2026年,某个赶作业的深夜