feat: 个人轻量看板工具 MVP
从零实现一个 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)。
This commit is contained in:
@@ -0,0 +1,63 @@
|
||||
/**
|
||||
* 领域类型与常量(T03)
|
||||
*
|
||||
* 设计约定(见 docs/adr.md):
|
||||
* - 列固定为三列,不使用可配置列,因此 status 直接用字面量联合类型而非 enum
|
||||
* - dueDate 仅表示「日期」,格式 YYYY-MM-DD,不含时刻,以此规避时区问题
|
||||
*/
|
||||
|
||||
export const COLUMN_IDS = ['todo', 'doing', 'done'] as const
|
||||
|
||||
export type ColumnId = (typeof COLUMN_IDS)[number]
|
||||
|
||||
/** 优先级:1 = 低,2 = 中,3 = 高 */
|
||||
export type Priority = 1 | 2 | 3
|
||||
|
||||
export interface Task {
|
||||
id: string
|
||||
title: string
|
||||
description: string
|
||||
status: ColumnId
|
||||
priority: Priority
|
||||
tags: string[]
|
||||
/** 仅日期,格式 YYYY-MM-DD;无截止日期时为 null */
|
||||
dueDate: string | null
|
||||
/** 同列内排序用的浮点数,采用相邻中点插入 */
|
||||
sortOrder: number
|
||||
createdAt: string
|
||||
updatedAt: string
|
||||
completedAt: string | null
|
||||
/** 归档时间;null 表示未归档。归档任务默认不在看板上显示 */
|
||||
archivedAt: string | null
|
||||
}
|
||||
|
||||
export interface ColumnDef {
|
||||
readonly id: ColumnId
|
||||
readonly title: string
|
||||
}
|
||||
|
||||
/** 看板列定义,顺序即展示顺序 */
|
||||
export const COLUMNS: readonly ColumnDef[] = [
|
||||
{ id: 'todo', title: '待办' },
|
||||
{ id: 'doing', title: '进行中' },
|
||||
{ id: 'done', title: '已完成' },
|
||||
]
|
||||
|
||||
export const PRIORITY_LABELS: Readonly<Record<Priority, string>> = {
|
||||
1: '低',
|
||||
2: '中',
|
||||
3: '高',
|
||||
}
|
||||
|
||||
/** 新建任务时的默认值 */
|
||||
export const DEFAULT_PRIORITY: Priority = 2
|
||||
|
||||
/** 判断某个值是否为合法的列 id */
|
||||
export function isColumnId(value: unknown): value is ColumnId {
|
||||
return typeof value === 'string' && (COLUMN_IDS as readonly string[]).includes(value)
|
||||
}
|
||||
|
||||
/** 判断某个值是否为合法的优先级 */
|
||||
export function isPriority(value: unknown): value is Priority {
|
||||
return value === 1 || value === 2 || value === 3
|
||||
}
|
||||
Reference in New Issue
Block a user