
平时用过的任务管理工具不少免费的总在功能上差一口气功能全的又基本都绑云服务数据不在自己手里始终不踏实。前前后后换了好几款干脆自己动手折腾了一款能自托管、多端同步的任务管理应用叫 PriTime名字拆开看就是 Priority优先级加 Time时间核心思路就八个字自托管、多端、以优先级驱动时间安排。这个小项目已经跑了大半年平时在 Windows 台式机、手机、平板上都在用数据完全自己掌控同步也不用依赖第三方。如果你也在找一款能自己部署、支持多端访问的任务管理方案这篇文章应该能帮你省不少事。PriTime 不是什么商业产品是我个人维护的开源小项目技术栈选的是 uniapp 做多端前端后端用 Node.js 套 Docker 部署这样既能保证移动端、桌面端、Web 端共用一套代码又能把部署成本压到最低——一台 Windows 机器或者随便一个 Linux 小主机就能跑起来。下面我就按我实际搭建的过程把整体设计思路、核心功能实现、Docker 部署步骤、以及我在多端适配和平时的使用中踩过的坑一条一条理清楚。1. 为什么自己折腾一个任务管理工具从需求倒推设计先聊点动机。市面上的任务管理应用我前前后后试过不少包括一些大厂的、独立开发者的用下来最难受的三点第一数据默认存在别人服务器上哪天服务调整或者团队变动几年积累的任务记录可能就没了第二多端同步基本都会成为付费功能免费额度少得可怜第三绝大多数工具重收集轻执行任务一堆排得满满的优先级和实际可安排的时间段却对不上。PriTime 的设计初衷就是为了解决这三个问题。自托管意味着数据只落在你自己指定的服务器或电脑上数据库文件、备份策略、导出格式都由你说了算多端适配让任务列表在手机、电脑上的体验保持一致而且不需要额外付费任务管理逻辑则强调“今天做哪几件最重要的事”而不是“把所有事都塞进清单里”。当时我在技术选型上花了不少时间最终定下来的是前端用 uniapp 做多端适配后端用 Node.js 的 NestJS 框架提供 API数据存储先上 SQLite等任务量上来再平滑切 PostgreSQL。选择 uniapp 而不是做三套原生客户端核心原因是 uni-app 一次编写可以同时产出 Android、iOS、H5 以及各小程序平台的代码还能打包成 Windows 桌面端对个人开发者来说维护成本是最低的。移动端能多端适配吗uni-app 在这块已经非常成熟我在真机上实测过 Android 和 iOS几乎没有需要单独写平台代码的地方只有通知推送需要按平台差异做适配后面会专门讲。多说一句有些任务管理工具会刻意把界面做得特别复杂恨不得一个页面塞下日历、看板、甘特图。我的看法是个人任务管理的核心场景根本没那么多。PriTime 的界面逻辑非常朴素一个“收件箱”负责快速收集任务一个“今天”视图负责列出应该执行的优先级任务一个“项目”维度负责归类长期目标。没有花哨的图表也没有炫酷的动效真正做到打开就能用用起来不累。2. 整体架构设计与核心功能模块解析PriTime 的架构不复杂但每个模块的取舍都经过实测验证下面拆开讲。2.1 前端多端适配的具体实现方式前端用 uniapp 框架Vue 3 语法配套的 UI 层主要靠自定义组件没有引入太重的第三方 UI 库。为什么不用现成的 UI 库因为任务管理界面里最核心的交互是复选框、优先级按钮、日期选择器、拖拽排序这些功能如果引入重量级组件库体积涨上去不说各端渲染差异还特别难调。自己封装一套轻量组件反而在 Android WebView、iOS WKWebView、Windows 桌面端都能保持一致表现。需要注意一个细节uniapp 里很多 API 是条件编译的比如消息推送Android 端走的是厂商推送通道iOS 端走 APNsH5 端根本没法使用原生推送只能靠 WebSocket 或者定时轮询。我在 PriTime 里的做法是封装了一个统一的推送服务接口各端按平台注入对应的实现同时服务端保留 WebSocket 通道确保 Web 端也能收到任务提醒。多端适配还有一个容易踩坑的地方是安全区。iPhone 的刘海屏、底部 Home Indicator 会遮挡内容需要在 pages.json 里配置 safeArea 相关参数或者用 CSS 的 env(safe-area-inset-bottom) 做兼容。这些细节如果不在开发阶段就处理好上真机之后会出现按钮点不到、列表底部被遮挡的问题排查起来特别费劲。2.2 后端服务与数据存储选型后端接口用 NestJS选择它的一个重要原因是它的模块化结构非常适合这种小中型项目认证模块、任务模块、项目模块、同步模块可以完全解耦以后想加功能比如统计报表直接在对应目录下扩展就行不会影响已有逻辑。数据存储第一步用的 SQLite主要是图省事一个文件搞定所有数据备份直接拷贝文件就行。但 PriTime 的同步机制是基于时间戳的增量同步如果以后数据量上来SQLite 的并发写性能会成为瓶颈。所以我在数据访问层做了抽象所有数据库操作都走 TypeORM 的 Repository 模式切换 PostgreSQL 时只需要改连接配置和实体定义业务代码一行不用动。多说一下同步协议的实现这可能是任务管理工具里最容易被低估的技术点。PriTime 的每次操作都会在后端记录一个 updateTimestamp客户端同步时带上本地最后同步时间服务端把增量数据拉下来合并再返回服务端最新时间戳。这种方案实现简单也不会出现冲突因为任务管理场景下同一任务被多人同时编辑的概率很低个人使用完全够用。唯一要注意的是不同设备的时间戳必须统一用服务器时间不能用本地时间否则会出现新增任务覆盖旧任务的问题。2.3 核心任务管理逻辑优先级四象限和时间块任务管理最怕的就是清单越来越长最后连翻都不想翻。PriTime 借鉴了时间管理里经典的“四象限法则”把任务按“紧急程度”和“重要程度”两个维度拆成四个区重要且紧急、重要不紧急、紧急不重要、不重要不紧急。任务创建时只需要勾选两个维度系统自动算出它属于哪个象限在“今天”视图里按象限分组展示。有了这个分组之后每天打开应用的第一眼就能明确优先处理重要且紧急的然后花整块时间在重要不紧急的事情上。为了避免“任务排满却完不成”的挫败感PriTime 里还加入了时间块设计。每个任务可以估算一个预计用时比如“写周报 45 分钟”系统在最右侧显示当天所有任务的总耗时如果超过了当天可支配时间这个时间也是你自己设置的新增任务时界面会弹出提示。这不是限制而是一个提醒——让你在做计划的时候就把“我可支配的时间”这个硬约束放在前面。实际用下来这个功能比任何花哨的数据统计都管用。3. 自托管部署实操从 Docker 到 Windows 环境下面这部分是全文的重头戏。我自己的主力机是 Windows所以整个部署流程是以 Windows Docker Desktop 为主线来讲的如果你用的是 Linux 服务器或者 NAS思路完全一致只是有些路径写法不同。3.1 部署方案对比为什么选 DockerPriTime 的服务端要跑起来至少需要 Node.js 运行时、数据库、以及一个进程守护工具。如果直接在 Windows 上装 Node.js 裸跑每次开机要手动启动服务还要自己管进程崩溃恢复非常不方便。用 Docker 之后后台服务、数据库、反向代理全都在容器里跑宿主机只需要装一个 Docker Desktop配置好 docker-compose.yml 文件就能一键启动。Docker 对自托管项目还有个额外好处环境隔离。我踩过的一个真实的坑是有一次为了测试其他项目往系统里装了不同版本的 Node.js结果 PriTime 某个依赖的编译产物全乱了折腾了半个下午才定位到是全局 Node 版本冲突。换成 Docker 之后容器内部固定用 Node 20宿主机的环境怎么折腾都不影响服务运行这个体验对比太明显了。3.2 docker-compose.yml 配置详解我直接把目前在用的 docker-compose.yml 贴出来里面已经包含注释按照你自己的实际磁盘路径改一下就能用。version: 3.8 services: pritime-server: image: node:20-alpine container_name: pritime-server restart: unless-stopped working_dir: /app volumes: - /d/docker/pritime/app:/app # 宿主机代码目录Windows 下用盘符绝对路径 - /d/docker/pritime/data:/data # SQLite 数据库文件、日志等持久化数据 environment: - TZAsia/Shanghai - DB_PATH/data/pritime.db - JWT_SECRETchange_this_to_your_secret - PORT3000 ports: - 3000:3000 command: sh -c npm install --production npm run migration:run node dist/main.js pritime-web: image: nginx:alpine container_name: pritime-web restart: unless-stopped depends_on: - pritime-server ports: - 8080:80 volumes: - /d/docker/pritime/web-dist:/usr/share/nginx/html:ro简单解释几个关键点restart: unless-stopped保证 Docker 重启后容器自动拉起Windows 重启之后不用手动干预。/d/docker/pritime/data是宿主机上的持久化目录SQLite 文件放这里。以后升级镜像、重建容器都不会丢数据。command里用了npm install --production因为容器内没有保留源码依赖的习惯每次启动先装生产依赖再跑迁移虽然多花几十秒但胜在干净。JWT_SECRET一定要改成自己的随机字符串这是登录凭证签名用的密钥用默认值等于把后门开着。3.3 Windows 上部署的完整步骤先确保 Windows 已经装好 Docker Desktop并且设置为开机自启。然后按下面步骤操作第一步在 D 盘创建目录D:\docker\pritime底下分别建app、data、web-dist三个子目录。把 PriTime 的后端源码放到D:\docker\pritime\app里前端构建产物放到web-dist目录下。第二步在前端项目根目录执行构建命令输出 H5 产物我一般用npm run build:h5构建完成之后把生成的dist/build/h5目录里的所有文件拷贝到D:\docker\pritime\web-dist。这一步是很多新手会忽略的如果不把前端页面部署到 Nginx 容器里浏览器访问只能看到后端 API页面根本打不开。第三步在D:\docker\pritime下创建上面贴出的docker-compose.yml保存后打开 PowerShell进入该目录执行docker-compose up -d第一次启动会自动拉取 Node 和 Nginx 镜像根据网络情况可能需要等几分钟。启动完成后浏览器访问http://localhost:8080就能看到 PriTime 的 Web 端界面。最后一步验证后端接口是否正常可以在 PowerShell 里执行curl http://localhost:3000/api/health如果返回{status:ok}之类的 JSON 数据说明服务端已经正常运行了。3.4 移动端 PWA 玩法不装 App 也能用PriTime 的前端构建产物是标准的 Web 应用所以在手机上可以直接用浏览器访问但为了获得接近原生 App 的体验我给它加上了 PWA 能力。PWA 全称是渐进式 Web 应用它可以让你的网页像原生应用一样添加到主屏幕全屏显示甚至支持离线访问。打包的步骤很简单在 uniapp 项目的manifest.json里开启 PWA 配置设置应用名称、图标、启动页即可。构建之后Nginx 会自动托管manifest.json和 Service Worker 文件。在 Android 手机上用 Chrome 打开http://你的服务器IP:8080浏览器地址栏会弹出一个“添加到主屏幕”的提示点确认之后手机上就有了 PriTime 的图标点开直接全屏运行外观上和原生应用几乎没有区别。需要注意PWA 只有在 HTTPS 协议下才能完整工作如果部署在局域网里但没用 HTTPSChrome 可能不会提示添加到主屏幕。开发环境可以临时在 Chrome 的 flags 里开启unsafely-treat-insecure-origin-as-secure但真实使用还是建议套一层反向代理做 HTTPS比如用 Caddy 自动申请证书配置非常简便。3.5 数据备份与迁移技巧自托管应用最怕的是数据丢失好在数据全在自己手里备份方案就是常规的文件备份。数据库文件只有pritime.db一个直接复制走就行。但是我建议不要只备份数据库文件最好把web-dist目录也一起备份因为有些旧版本构建产物里的静态资源是按哈希命名的迁移之后如果版本不匹配页面可能报资源加载错误。我自己的习惯是写一个简单脚本每天凌晨把数据库文件拷贝到 NAS 和网盘各一份保留最近七天。脚本用的是 PowerShell 的Copy-Item加上 Windows 任务计划程序定时触发。如果你的服务器是 Linux用 crontab 加 rsync 也是一样的效果。迁移到新机器时只要把整个D:\docker\pritime目录拷贝过去然后在新机器上执行docker-compose up -d所有数据、配置、前端界面直接恢复。4. 常见问题与多端适配排查实录下面这些问题都是我实际使用中碰到过的每个都花了不少时间排查整理成速查表方便你对照解决。4.1 部署与启动类问题现象可能原因解决办法容器启动后立即退出数据库迁移失败或依赖未装齐执行docker-compose logs pritime-server查看完整日志重点看migration:run阶段前端页面能打开但接口报 502后端容器没起来或者端口映射不对先确认 3000 端口映射没被占用再验证浏览器直接访问 3000 端口返回的数据长时间停留在加载页面前端构建产物没有拷贝到 Nginx 目录检查web-dist目录下是否有index.html文件Windows 重启后容器没自动启动Docker Desktop 没有开机自启在 Docker Desktop 设置中开启 “Start Docker Desktop when you sign in”提示容器启动失败时第一件事永远是看日志不要盲目改配置。Docker 的日志输出比大部分报错信息都直接。4.2 多端同步与数据一致性排查任务管理工具最怕的就是两台设备数据不一致。PriTime 的同步机制依赖每次操作的updateTimestamp如果某个客户端的本地时间比服务器时间超前了那么它提交的新任务时间戳就会比服务器上的记录晚后续其他设备同步时就可能重复拉取这条任务造成同一条任务出现在多个设备上的情况。排查方法也很简单把出问题的设备时间和服务器时间都校准到网络时间然后在任一设备上执行一次全量同步观察updateTimestamp是否按照统一时间线递增。如果问题依旧查看服务端日志里是否有多设备同时提交同一任务ID的冲突记录但我在实际使用中从来没遇见过所以概率极低。移动端还有一个特有问题Android 系统在后台会杀掉长时间未使用的应用导致 WebSocket 连接断开任务提醒无法实时送达。解决方式是开启“自启动”权限同时在应用内做掉线重连机制每次回到前台时检查本地与服务器的同步时间差超过五分钟就触发一次静默同步。4.3 uniapp 多端适配的典型坑uniapp 虽然做到了一套代码多端跑但不同平台的底层差异还是会在细节上暴露出来。我遇到的第一个坑是日期选择器。H5 端用的是浏览器自带的input typedateAndroid 端渲染出来的是原生日期选择弹窗iOS 端的样式又不一样。PriTime 里所有日期选择都统一封装成自研组件底层用 picker 实现屏蔽各端差异。第二个坑是键盘弹出遮挡输入框。在 Android 上软键盘弹出时默认会把页面顶上去但如果页面高度设置不当输入框会被键盘完全盖住。解决方案是在输入框获得焦点时调用uni.pageScrollTo把页面滚动到输入框可见区域。这个问题在 Web 端不存在但 Android 真机几乎必现建议提前处理。第三个坑和字体有关。不同平台默认字体渲染差异很大如果用到了自定义图标字体Android 端偶尔会出现图标加载失败的情况。PriTime 的所有图标都是 SVG 组件而不是字体图标彻底避免了这类兼容问题。4.4 局域网内多设备访问的配置如果只想在家里或办公室的局域网内使用那么部署好之后其他设备直接访问http://你的电脑IP:8080就能打开。但是有两个细节要注意第一Windows 防火墙默认会拦截 8080 端口的入站请求需要在“高级安全 Windows Defender 防火墙”里新建一条入站规则允许 TCP 8080第二如果电脑的 IP 是 DHCP 动态分配的建议在路由器管理后台设置 IP 和 MAC 地址绑定避免重启之后 IP 变化导致其他设备访问不了。5. 后续还能怎么扩展到这里PriTime 的核心部署和日常使用基本就能跑通了。如果按耐不住折腾的心可以考虑给它加一些模块。比如任务统计报表从数据库里按周、按月聚合完成率和趋势前端用 ECharts 渲染uniapp 也有对应的小程序版图表库可以接入。再比如把提醒能力升级对接 Webhook把任务变更推送到企业微信、钉钉或者自己的 Telegram Bot实现一个简单的“自动化任务通知”。我给 PriTime 的定位一直是一个“自己能掌控”的任务管理工具而不是一个多么复杂的系统所以很多功能都是按需添加。你在跟随这篇文章部署的过程中如果遇到跟环境相关的报错优先看日志如果是多端样式的问题可以按条件编译逐个平台排查也可以回到 uniapp 社区翻翻别人遇到的同类问题——大部分坑都已经有人踩过并给出方案了。我这个项目会继续保持轻量、开源的方式维护后续还会补上可配置的邮件每日摘要让早上打开邮箱就能知道今天该做什么。