☰
开源替代WorkBuddy:Electron+SQLite打造三合一桌面工作台
2026/10/27 14:50:31 网站建设 项目流程

三个月前我在技术群里说了句"我要把现在用的那款 WorkBuddy 桌面工作台换掉,自己写一个开源替代品",群友第一反应基本是"你疯了"。但现在这个项目已经发布了 0.4 版本,GitHub 上累计有 200 多个 star,下载量最让我意外的是,有三分之一来自非开发者用户——他们只是想要一个不用订阅、数据能自己掌控的任务工作台。我把项目命名为"魔力工作台",定位就是 WorkBuddy 这类工具的开源替代:任务管理、专注计时、周报生成三合一,数据全部本地存储,支持从原版工具备份文件直接迁移。这篇文章把整个项目的来龙去脉、技术取舍、踩坑记录都摊开讲一遍,适合正在考虑自建效率工具、或者想参考一个真实 Electron 桌面应用项目的人。

1. 为什么放着现成的 workbuddy 不用,非要自己造轮子

1.1 原版最让我头疼的三件事

先说清楚,我在决定自己写之前,正儿八经用了大半年的 WorkBuddy。它确实做得不错,界面干净,任务管理逻辑也顺手。但有三件事让我越来越难受。

第一是订阅和激活机制。原版按年订阅,而且免费版和专业版的功能差距拉得非常大。专业版的核心功能比如多设备同步、高级统计、自动化规则,全都锁在付费墙后面。我换过一次电脑,还要先解绑旧设备再绑定新设备,中间等了半天客服人工处理。这种体验放在 2025 年实在说不过去。

第二是数据封闭。我用原版期间积累了几千条任务记录和完整的工作日志,但想导出来做自己的统计分析时发现,官方只提供全量备份文件,格式不公开,自定义字段还会在备份恢复时丢失。缓存目录位置也藏得深,想换个盘存放完全无从下手。这基本等于我的工作数据被困在一座孤岛上。

第三是扩展能力几乎为零。原版的自动化规则是内置好的那几条,我想加一个"每周五下班前自动汇总本周未完成任务发到我的笔记里"都做不到。我当时的判断是:与其等官方更新,不如自己做一个能完全控制数据格式、能按自己习惯加功能的工作台。

1.2 我能接受开源替代的代价

我当然知道自研是有代价的。自己做就意味着要占用休息时间,要维护文档,还要持续修 bug。当时我给自己算了一笔账:如果按每天平均花 40 分钟维护这个项目,一年下来大概 240 个小时。而原版订阅一年要花几百块,功能还不一定符合我的需求。这 240 个小时换来的,是数据全部掌握在自己手里、功能随时按需调整、永远不会被厂商锁定。对我来说这笔账是划算的。

真正让我下定决心的是某次数据迁移失败。那天我导出备份、换电脑、恢复数据,结果最新一个月的工作日志全部丢失,因为云端同步没有完全覆盖到本地最后那几次修改。我对着空荡荡的界面想了很久,最后冒出来的念头是:与其指望别人,不如自己来。现在回头看,这个决定确实折腾,但完全不后悔。

2. 魔力工作台到底做了什么:任务、专注、回忆三件套

2.1 任务系统的取舍:四象限加项目分组,不做花哨看板

最开始我规划任务系统时,想法特别多,什么甘特图、日历拖拽、看板视图全想塞进去。做到一半我就醒悟了:个人工作台不是团队项目管理工具,用户每天打开它就是为了搞清楚三件事——现在做什么、接下来做什么、这周都做完了什么。

所以最终的任务模型很克制。顶层是项目分组,比如"魔台开发""写作计划""日常琐事";每个项目下是任务列表,任务本身用简单的四象限划分优先级(紧急且重要、重要不紧急、紧急不重要、不紧急也不重要)。没有复杂的看板拖拽,也没有日历视图,因为我的目标用户是自己这种个体工作者,不是需要给跨部门协作做展示的团队。

任务表的核心结构大概是这样的:

CREATE TABLE tasks ( id TEXT PRIMARY KEY, project_id TEXT NOT NULL, title TEXT NOT NULL, priority INTEGER DEFAULT 2, -- 1紧急重要 2重要不紧急 3紧急不重要 4不紧急不重要 status INTEGER DEFAULT 0, -- 0待办 1进行中 2已完成 3已放弃 scheduled_date TEXT, -- YYYY-MM-DD estimated_minutes INTEGER DEFAULT 25, created_at TEXT, completed_at TEXT );

字段尽量精简,够用就好。status用了整数而不是字符串,排序和统计都方便;estimated_minutes直接跟番茄钟模块联动,一个任务默认 25 分钟,在界面上点一下就能直接开始专注计时。

2.2 专注模式:本地番茄钟加动态壁纸提示

专注模块做的是全屏番茄钟:开始计时后,界面自动切换到专注状态,显示剩余时间、当前任务、白噪音开关。这个功能本身没什么稀奇,我用 Electron 里一个简单的setInterval加系统通知就实现了。

比较有意思的是"动态壁纸提示"这个细节:专注期间,应用主窗口的标题栏和托盘图标会显示剩余分钟数,这样即使我把窗口最小化去写代码,余光扫到任务栏也能知道番茄钟还剩多久。做这个功能的灵感来自我实际使用中的需求——我真的会因为忘记看计时器而错过休息时间。

2.3 回忆索引:笔记本加周报自动生成

第三个核心模块是"回忆索引",说白了就是两件事:随手记的笔记 + 自动生成的周报。笔记功能解决的是"当时忘了记,事后找不到"的问题,所有笔记按日期归档,支持标签过滤,存的是 Markdown 纯文本。

周报生成是我最满意的功能。每周日下午五点,应用会自动汇总这一周完成的任务,按项目分组,统计总专注时长,然后生成一份 Markdown 格式的周报,存放在用户指定的目录里。我自己的使用习惯是:每周一早上把这份周报里还没做完的事重新拆解进新一周的任务里,相当于自动完成了一次复盘和规划。

周报的核心 SQL 其实就是一次条件聚合:

SELECT project_id, COUNT(*) AS total_tasks, SUM(CASE WHEN priority = 1 THEN 1 ELSE 0 END) AS urgent_count, SUM(estimated_minutes) AS total_estimated FROM tasks WHERE completed_at >= datetime('now', 'start of week', '-1 day', 'weekday 0') AND completed_at < datetime('now', 'start of week', '-1 day', 'weekday 0', '+7 days') GROUP BY project_id;

注意 SQLite 里'start of week', '-1 day', 'weekday 0'这一串,是为了把一周的起点对齐到周日零点,看起来绕,但直接改系统周起始日开关更麻烦。

3. 技术选型背后的几个现实问题

3.1 Electron 还是 Tauri:我为什么选了 Electron

这是整个项目里被问得最多的一个问题。我的答案是:如果只做自己能用的工具,Tauri 挺香;但如果想做一个别人也能轻松部署的桌面应用,Electron 现阶段综合成本更低。

Electron 的主要问题大家都清楚,内存占用高、打包体积大。但它的优势也很明显:运行时能力完整,SQLite 的 Node 原生模块支持成熟,跨平台打包方案 Electron Builder 一条命令搞定三平台安装包。Tauri 的 Rust 后端性能好、体积小,但对我来说引入了一个我不熟悉的技术栈,而且原生 SQLite 在 Tauri 生态里没有 Electron 那套 better-sqlite3 顺手。

我也测过 Wails(Go + Web 前端),数据存储倒是干净,但生态和文档明显比 Electron 薄一圈。最后选型理由很朴素:我是来做功能验证的,不是来折腾工具链的。

对比项ElectronTauri
运行时内存(空应用实测)约 120MB约 30MB
打包体积(简单应用)约 80MB约 10MB
原生 SQLite 支持成熟(better-sqlite3)需自行封装或选 sqlx
跨平台一键打包很成熟较成熟
我的熟悉成本低中

如果你的核心诉求是"只要能跑起来,有人下载就行",Electron 是低风险选择。想清楚了再动手。

3.2 数据存储:SQLite 加明文 JSON 导出

数据层我选的是 SQLite,配合 better-sqlite3 这个同步 API 的库。为什么用同步 API?因为桌面单机应用的数据量根本不需要异步,同步调用反而让代码逻辑简单许多,不用到处 await,也不容易出现并发写库的错乱。

除了数据库,我在应用里做了一个直观的"数据导出"功能,导出格式就是 JSON。选择 JSON 而不是 CSV 或 XML,理由有三个:结构可以嵌套,能完整表达任务、笔记、项目之间的关联;人人能看懂,不用装任何工具就能打开检查;后续做数据迁移或者写脚本处理,JSON 的兼容性最好。

3.3 界面层:Vue 3 组件化的一些实践

界面层我选了 Vue 3,状态管理用了 Pinia。最开始我只用 Vue 自带的reactive就能跑,但任务和计时器之间需要联动,比如开始专注时要锁定当前任务,而且主窗口和托盘菜单状态要同步,手写状态传递很快就乱了。换成 Pinia 之后,跨组件共享状态变得很清晰,一个useTimerStore就能让任务列表、专注页面、系统托盘同时感知计时状态。

组件划分上我坚持一条原则:能用纯组件解决的就不往全局状态里塞。比如四象限视图和列表视图,它们只是对同一个任务数组的不同展示方式,数据源都在 tasks store 里,界面自己维护自己那部分 UI 状态就行。

4. 从零到可用:核心模块的落地细节

4.1 任务数据的读写与状态流转

任务模块的代码没有太多奇技淫巧,核心是建立一个统一的数据服务层,所有对 tasks 表的操作都走这一个文件。刚开始我习惯在组件里直接写 SQL,后来发现一旦有多个入口修改状态,很容易把 status 和 completed_at 搞得不一致。

现在每次完成任务,只调用一个方法:

export function completeTask(taskId: string) { const updated = db .prepare( `UPDATE tasks SET status = 2, completed_at = ? WHERE id = ?` ) .run(new Date().toISOString(), taskId); if (updated.changes > 0) { taskStore.refresh(); } }

taskStore.refresh()会重新拉取任务列表并触发相关组件的重新渲染。用这种"修改数据库后刷新单例状态"的模式,比在多个组件里手动改本地变量要省心得多,也避免了刷新页面时数据不一致的怪问题。

4.2 计时器模块:闹钟、提醒、跨端锁屏处理

番茄钟用 setInterval 做倒计时是很容易的,难的是各种边界状态。我踩过的一个真实 bug:笔记本合盖休眠后,定时器停了,醒过来发现番茄钟已经超时很久,系统通知也没触发。后来改成在每次计时时记录"目标结束时间",而不是只记剩余秒数,每次心跳检查当前时间是否超过目标结束时间,这样就绕开了休眠期间计时器不执行的死穴。

function tick() { const now = Date.now(); if (now >= focusEndTime) { stopTimer(); sendNotification('专注结束', '休息一下吧'); return; } remainingSeconds.value = Math.ceil((focusEndTime - now) / 1000); }

系统通知用 Electron 的new Notification(),Windows 和 macOS 上都能正常弹出。Linux 上依赖系统通知服务,部分发行版需要手动装libnotify,我后来在 README 里补了一句提示,基本覆盖到大多数情况。

4.3 周报生成:聚合查询加 Markdown 渲染

周报的生成逻辑不复杂,但有一个细节值得展开:周报目录的可配置性。默认生成在文档目录下的"魔力工作台周报"文件夹,但很多用户(包括我自己)习惯把工作数据放在数据盘。所以设置页里加了一个"周报目录"字段,用 Electron 的dialog.showOpenDialog选目录,选择结果直接写入配置文件。

周报的内容结构是固定的:本周完成任务数、按项目分组统计、本周专注总时长、下周建议关注的任务列表。写完后用默认浏览器打开预览,这个功能我用 Electron 的shell.openPath实现,一行代码搞定。

4.4 开机自启与托盘:桌面工具的基本素质

作为一个把自己定位为"替代重量级工具"的桌面应用,开机自启和系统托盘这种"桌面工具基本素质"是必须有的。开机自启用 Electron 的app.setLoginItemSettings,托盘菜单提供了三个核心项:打开主界面、开始/暂停专注、退出应用。

托盘这块我花了些心思:托盘图标会根据当前是否在专注计时而变化,专注时是倒计时数字图标,空闲时是普通 Logo。这个细节对体验提升很大,尤其是对经常把窗口丢掉后台的用户来说,托盘几乎成了第二主界面。

5. 原版数据迁移:我踩过的格式坑

5.1 备份文件的小秘密

为了降低用户的迁移成本,我做了从原版 WorkBuddy 备份文件导入的功能。这个功能比我想象的麻烦得多。原版的导出文件是扩展名.wbbackup,里面是一大段压缩编码的 JSON。解开后发现字段和官方文档里描述的完全对不上:任务标题字段叫wTitle,完成状态是wDone,优先级用 0、1、2 表示,但 0 居然是"高优先级",和直觉正好相反。

时间字段更坑。任务完成时间在备份里存的是 Unix 毫秒时间戳,但有的历史数据是 ISO 字符串,同一个文件里混着两种格式。我当时扫了一遍自己导出的备份文件,光是时间格式就撞上了三个版本。这种问题没有捷径,只能写一个"宽容解析器"去逐个尝试。

5.2 迁移脚本的容错处理

迁移脚本的核心思路是"能救多少救多少"。对每一条导入的记录,尝试解析,解析失败就用默认值补齐,并在迁移报告里明确列出哪些字段是补的。

function parseDate(raw: any): string | null { if (typeof raw === 'number') { return new Date(raw).toISOString(); } if (typeof raw === 'string') { const timestamp = Number(raw); if (!Number.isNaN(timestamp)) { return new Date(timestamp).toISOString(); } return raw; // 已经是可读格式 } return null; }

迁移完成后,应用会生成一份完整的迁移报告,告诉用户:导入了多少任务、多少笔记,其中多少条任务的完成时间无法识别因此默认为"未完成"。我把报告同时写入一份 Markdown 文件存到桌面,方便用户核对。这个细节在 GitHub 上被很多人好评,因为迁移最怕的就是"无声失败"。

5.3 手动修正清单

除了脚本自动处理的部分,还有一小撮数据需要手动修正。我把比较典型的字段映射整理成了一张表放在文档里,方便有需要的人对照:

原版备份字段魔力工作台字段注意事项
wTitletitle如果为空,任务会被标记为"未命名任务"
wDonestatustrue 映射为"已完成",false 映射为"待办"
wPrioritypriority0 映射为紧急重要,1 映射为重要不紧急
wCreatedTScreated_at兼容时间戳和 ISO 字符串两种格式
wNotenote_content保留原始 Markdown 语法,不做转换

迁移功能的第二个版本还做了可重复导入:如果用户发现上次导入漏了某些任务,再次导入时不会产生重复记录。实现方式很简单,给每条任务加一个source_key字段,和原备份里的 ID 绑定,导入前先查一遍source_key是否存在。

6. 开源之后的三件事:打包、文档、社区反馈

6.1 跨平台打包的经验:免安装版与安装版

项目开源之后,第一个涌进来的需求是"我怎么装"。这让我意识到打包体验直接决定了用户第一印象。Electron Builder 默认给每个平台产出一堆安装包,但个人用户的接受度差异很大:Windows 用户喜欢免安装的绿色版,macOS 用户要 dmg,Linux 用户则希望有 AppImage。

我最后的打包方案是三平台各出两个版本:Windows 出 NSIS 安装包 + 免安装 zip,macOS 出 dmg + zip,Linux 出 AppImage + deb。额外注意的一点是签名问题——我没有苹果开发者证书,macOS 用户第一次打开 dmg 会被 Gatekeeper 拦截,需要右键选择"打开"。这个问题我在下载页里用了一整段说明,还配了步骤截图,大幅减少了"装不上"的求助。

6.2 文档怎么写才有人看得懂

开源仓库如果不认真写 README,功能做得再好也很难有人用。我吃过这个亏:最初版 README 只有一句话"这是一个 workbuddy 替代品",结果一周过去了下载量不到二十次。后来我把 README 重写了一遍,分成了五个部分:这个项目是什么、和原版的区别是什么、截图展示、快速安装指引、常见问题。

真正带来下载量暴增的是我在 README 里加了一节"迁移指南",教用户如何从原版导出备份然后导入到本工具。很多人就是被这个"可以无缝替换原版"的承诺打动的,毕竟效率工具迁移最怕的就是历史数据带不走。

6.3 一个让我改版的真实案例

开源之后收到最有价值的反馈来自一位设计师用户。他说自己每天的工作记录在多个分类之间来回切换,但应用只有"全局笔记"和"任务笔记"两种入口,没有"给单条任务挂一段笔记"的能力。这个诉求原版有,我确实漏了。

我选择了一个轻量方案:在任务详情页增加"关联笔记"区域,点击后可以创建一条和任务绑定的笔记,或者把已有笔记关联进来。这个改动不复杂,但涉及任务表、笔记表、关联表三张表的联动。改完之后我发现自己的使用习惯也变了,现在开会时想起某个任务的背景资料,我会直接翻任务详情页,不用再满世界找笔记。

这个案例让我意识到,开源项目的需求收集不能只看 star 数,真正的价值来自用户真实使用场景的映射。每次有人提需求,我都会先问自己三个问题:这个需求是只有少数人要,还是大多数人都会碰到?改动会不会破坏现有数据结构?能不能在半天内实现?符合这三条的就顺手做了,否则我会明确告诉需求方目前版本不适合,而不是含糊其辞。

最后再分享一个小技巧

做数据本地化的桌面工具,最怕的就是用户硬盘出问题导致数据全没了。所以我强烈建议任何类似项目都把"自动备份"做成默认功能,而不是放一个"备份"按钮等着用户自己点。魔力工作台的默认方案是:每次应用退出时,把 SQLite 数据库文件复制一份到备份目录,保留最近 14 天版本,每天第一次启动时清理过期备份。用户完全无感知,但数据安全等级比"用户手动点备份"高了好几个量级。

相关代码其实只有几行:

function backupDatabase() { const backupDir = path.join(app.getPath('userData'), 'backups'); const fileName = `magic-workbench-${new Date().toISOString().slice(0, 10)}.db`; fs.copyFileSync(dbPath, path.join(backupDir, fileName)); cleanOldBackups(backupDir, 14); // 只保留最近14天的备份 }

如果你和我一样,准备从一个商业工具迁移到一个自己掌控的工具,我的建议是先做一轮完整备份,再跑迁移脚本,然后用半个月时间并行使用,确认新工具真的能覆盖日常工作流之后,再彻底切换。效率工具的替代从来不是一蹴而就的,但数据自由这件事,值得投入那点折腾的时间。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询