从零实现一个 local-first 的个人任务看板,数据只存在浏览器本地,不依赖后端。 功能 - 固定三列看板(待办 / 进行中 / 已完成),支持拖拽改状态与列内排序 - 快速新增、详情编辑、删除撤销、已完成归档 - 搜索与筛选(关键词 / 优先级 / 标签 / 今日到期 / 逾期) - 今日视图:按紧急度分组(逾期 > 今天到期 > 进行中)+ 统计卡片 - 导出导入 JSON;启动时自动留快照,保留最近 3 份 - 键盘快捷键(N / 「/」/ ? / Esc) - PWA:可安装、离线可用、更新提示 技术要点 - 持久化采用 TaskRepository 端口 + 整份快照读写,schemaVersion 迁移表 显式登记升级规则(当前 v2,v1 -> v2 为归档功能补 archivedAt) - 同列排序用相邻中点插入,间距不足时按列 normalize,避免浮点精度耗尽 - 拖拽只消费 Sortable 的事件,store 保持唯一数据源,不接管数据 - 数据损坏时先备份到 *.corrupt.<时间戳> 再报错,不覆盖用户数据 - 引入 vue-router 拆分看板与今日两个视图(见 docs/adr.md ADR-005) 验证 - 单元测试 437 个(33 个文件),行覆盖率 96.8% - 端到端测试 23 个(Playwright),含真实拖拽与断网离线场景 - npm run build 通过,产物可静态托管 注:部署时需配置 SPA 回落,否则直接访问 /today 会 404(详见 README)。
206 lines
8.3 KiB
Markdown
206 lines
8.3 KiB
Markdown
# 看板 · 个人任务管理
|
||
|
||
一个轻量的个人看板工具:打开即用、数据存在本地、不依赖后端。
|
||
|
||
- 固定三列:**待办 / 进行中 / 已完成**
|
||
- 快速新增、拖拽排序、详情编辑、删除撤销
|
||
- 搜索与筛选(关键词 / 优先级 / 标签 / 今日到期 / 逾期)
|
||
- **今日视图**:按紧急度汇总今天要做的事,含本周完成等统计
|
||
- 键盘快捷键
|
||
- 导出导入 JSON + 启动自动备份
|
||
- **可安装为 PWA,支持离线使用**
|
||
|
||
---
|
||
|
||
## 快速开始
|
||
|
||
```bash
|
||
npm install
|
||
npm run dev
|
||
```
|
||
|
||
打开终端提示的地址即可。首次进入是空看板,在任意列顶部的输入框里输入标题、回车即可创建任务。
|
||
|
||
## 脚本
|
||
|
||
| 命令 | 说明 |
|
||
|---|---|
|
||
| `npm run dev` | 启动开发服务器 |
|
||
| `npm run build` | 类型检查 + 生产构建,产物在 `dist/` |
|
||
| `npm run preview` | 本地预览生产构建 |
|
||
| `npm run typecheck` | 只做类型检查 |
|
||
| `npm run test` | 单元测试(Vitest) |
|
||
| `npm run test:coverage` | 单元测试 + 覆盖率报告 |
|
||
| `npm run test:e2e` | 端到端测试(Playwright,默认驱动系统已安装的 Chrome) |
|
||
| `npm run lint` / `npm run format` | 代码检查与格式化 |
|
||
|
||
## 快捷键
|
||
|
||
| 按键 | 作用 |
|
||
|---|---|
|
||
| `N` | 聚焦「待办」列的快速新增输入框 |
|
||
| `/` | 聚焦搜索框 |
|
||
| `?` | 打开快捷键帮助 |
|
||
| `Esc` | 关闭当前面板 |
|
||
|
||
在输入框内输入时不会触发快捷键。
|
||
|
||
---
|
||
|
||
## 两个视图
|
||
|
||
顶部导航可在两个视图之间切换:
|
||
|
||
| 视图 | 路径 | 组织方式 |
|
||
|---|---|---|
|
||
| 看板 | `/` | 按**状态**:待办 / 进行中 / 已完成 |
|
||
| 今日 | `/today` | 按**紧急度**:已逾期 → 今天到期 → 进行中 |
|
||
|
||
今日视图顶部有四张统计卡片(今日待办 / 逾期 / 今天到期 / 本周完成),下方是可直接勾选完成的待办列表。
|
||
|
||
分组是**互斥**的:一张卡片只会出现在一组里,归属优先级为「逾期 > 今天到期 > 进行中」,所以你能一眼数清今天到底有几件事。已完成和已归档的任务不会出现在今日视图。
|
||
|
||
---
|
||
|
||
## 安装与离线(PWA)
|
||
|
||
应用可以像原生应用一样安装到桌面或手机主屏,安装后支持**完全离线**使用 —— Service Worker 会预缓存页面与静态资源,断网后照常打开,数据本来就在本地。
|
||
|
||
- 桌面 Chrome / Edge:地址栏右侧会出现安装图标
|
||
- 手机:浏览器菜单 →「添加到主屏幕」
|
||
- 安装后长按图标有「今日」快捷方式,直达 `/today`
|
||
|
||
更新策略是 **prompt**:检测到新版本时会在底部弹出提示,由你决定何时刷新,不会打断正在进行的编辑。
|
||
|
||
图标由 `scripts/generate-icons.py`(Pillow)生成,需要改图标时运行:
|
||
|
||
```bash
|
||
python scripts/generate-icons.py
|
||
```
|
||
|
||
---
|
||
|
||
## 数据与备份(重要)
|
||
|
||
**数据只保存在你这台设备的浏览器里**(localStorage,键名 `kanban.tasks`)。没有服务端,也不会自动同步到其它设备。
|
||
|
||
这意味着以下操作会导致数据丢失:
|
||
|
||
- 清理浏览器数据 / 缓存
|
||
- 使用隐私模式
|
||
- 换浏览器或换设备
|
||
- Safari 的 7 天未访问存储清理策略
|
||
|
||
因此工具内置了两道保护:
|
||
|
||
1. **启动自动备份**:每次打开看板时留一份快照,最多保留最近 3 份,可在「备份」面板中一键恢复。
|
||
2. **手动导出**:「备份」面板 → 导出为 JSON,文件名形如 `kanban-2026-10-18.json`。
|
||
|
||
导入是**覆盖式**的:选中文件后会先校验,再显示将被覆盖的任务数量,需要二次确认才会执行。文件不合法时不会改动任何现有数据。
|
||
|
||
> 如果需要在多设备间同步,需要自行接入后端;`TaskRepository` 接口就是为此预留的扩展点(见下)。
|
||
|
||
---
|
||
|
||
## 部署
|
||
|
||
产物是纯静态文件,把 `dist/` 目录交给任意静态托管即可(Nginx、Vercel、Netlify、GitHub Pages 等)。
|
||
|
||
```bash
|
||
npm run build
|
||
# 将 dist/ 上传或指向静态服务器
|
||
```
|
||
|
||
Nginx 示例:
|
||
|
||
```nginx
|
||
server {
|
||
listen 80;
|
||
root /var/www/kanban/dist;
|
||
index index.html;
|
||
location / {
|
||
# 必须配置:/today 是真实路径,刷新时文件不存在,需要回落到 index.html
|
||
try_files $uri $uri/ /index.html;
|
||
}
|
||
}
|
||
```
|
||
|
||
Netlify / Vercel 等平台,在 `dist/` 同级放一个 `_redirects` 或配置即可:
|
||
|
||
```
|
||
/* /index.html 200
|
||
```
|
||
|
||
> **注意**:应用使用前端路由(`/today`),因此**必须**配置 SPA 回落,否则直接访问或刷新 `/today` 会 404。
|
||
> 这不是可选项。若你的托管环境无法配置回落,可以改用 hash 路由(把 `src/router/index.ts` 里的
|
||
> `createWebHistory()` 换成 `createWebHashHistory()`),代价是 URL 变成 `/#/today`。
|
||
|
||
Service Worker 的 `navigateFallback` 已设为 `index.html`,因此**离线状态下**深链 `/today` 也能正常打开,
|
||
无需服务器参与。
|
||
|
||
---
|
||
|
||
## 技术栈
|
||
|
||
Vue 3 · TypeScript · Vite · Pinia · Vue Router · Tailwind CSS v4 · vue-draggable-plus (SortableJS) · dayjs · vite-plugin-pwa (Workbox) · Vitest · Playwright
|
||
|
||
## 目录结构
|
||
|
||
```
|
||
src/
|
||
├── components/ # 看板列、卡片、抽屉、工具栏、导航、PWA 提示等 UI 组件
|
||
├── composables/ # 可复用的组合式逻辑(键盘快捷键)
|
||
├── data/ # 持久化:schema 与迁移、仓库实现、快照
|
||
├── router/ # 路由定义(看板 / 今日)
|
||
├── stores/ # Pinia:任务、筛选、提示
|
||
├── types/ # 领域类型与常量
|
||
├── utils/ # 纯函数:排序、筛选、日期、归档、拖拽解析、今日分组、统计
|
||
└── views/ # 页面级组件(BoardView / TodayView)
|
||
docs/
|
||
├── adr.md # 架构决策记录
|
||
├── tasks.md # 开发任务卡(含验收标准)
|
||
└── tasks.json # 可导入看板的种子数据
|
||
e2e/ # Playwright 端到端测试
|
||
scripts/ # 图标生成等辅助脚本
|
||
```
|
||
|
||
## 架构要点
|
||
|
||
- **单一数据源**:Pinia store 是内存态的唯一来源,所有变更经由 `TaskRepository` 落盘;拖拽也只消费事件,不接管数据。看板与今日视图共享同一份数据。
|
||
- **持久化端口**:`TaskRepository` 采用「整份快照读写」(`load` / `save`),而非细粒度 CRUD。要换成 IndexedDB 或后端 API,只需实现同一接口。
|
||
- **显式迁移**:落盘结构带 `schemaVersion`,升级规则登记在 `MIGRATIONS` 表中。当前版本为 2(v1 → v2 为归档功能补 `archivedAt`)。
|
||
- **损坏数据先备份再报错**:读取失败时把原始内容另存到 `kanban.tasks.corrupt.<时间戳>`,不覆盖用户数据。
|
||
- **视图与数据解耦**:今日视图只做「分组 + 排序」,全部逻辑在 `src/utils/today.ts` 与 `stats.ts` 的纯函数里,组件不持有业务规则。
|
||
|
||
更多取舍与理由见 [docs/adr.md](docs/adr.md)。
|
||
|
||
## 测试
|
||
|
||
- 单元测试:437 个用例,覆盖 `src/` 下的纯逻辑与组件行为,行覆盖率约 97%。
|
||
- 端到端测试:23 个用例,分两组运行 —— 交互类跑 dev server,PWA 类跑生产构建(Service Worker 只在构建产物中生成)。覆盖「新增 → 拖拽 → 完成 → 刷新保持」「导出 → 清空 → 导入还原」「删除撤销」「搜索筛选」「键盘快捷键」「今日视图」「离线打开」等主链路,每个用例使用独立的浏览器上下文。
|
||
|
||
```bash
|
||
npm run test:coverage
|
||
npm run test:e2e
|
||
```
|
||
|
||
### 关于 E2E 使用的浏览器
|
||
|
||
默认驱动**系统已安装的 Chrome**(`channel: 'chrome'`),不下载 Playwright 自带的 Chromium —— 后者约 197MB,在网络受限的环境下容易超时失败。
|
||
|
||
如需改用其它通道:
|
||
|
||
```bash
|
||
PLAYWRIGHT_CHANNEL=msedge npm run test:e2e # 用系统 Edge
|
||
PLAYWRIGHT_CHANNEL=chromium npm run test:e2e # 用自带 Chromium(需先 npx playwright install chromium)
|
||
```
|
||
|
||
## 已知限制
|
||
|
||
- 单用户、单设备,无协作与同步能力(离线可用,但多设备之间不会同步)。
|
||
- 标签是字符串数组,重命名标签需要逐个任务修改。
|
||
- 看板列固定为三列,不支持自定义。
|
||
- 归档阈值固定为「已完成超过 7 天」,暂不可配置。
|
||
- 今日视图的分组规则固定(逾期 > 今天到期 > 进行中),不可自定义。
|
||
- 统计只覆盖本周完成数等基础指标,没有趋势图或历史留存。
|