- 数据:Task 增加 deletedAt,schema v2→v3;删除改为软删除,保留 30 天后自动清理 - 回收站:支持恢复、永久删除、二次确认清空;导航栏入口带数量角标 - 批量编辑:多选后统一移动列、改优先级、标签增删、设置截止日、归档 - 合并导入:按 id 匹配、updatedAt 新者胜;覆盖/合并二选一,执行前自动快照 - 标签治理:全局重命名、合并、删除;回收站任务不参与改写 - 看板语义读取统一改用 activeTasks,避免已删除任务泄漏到统计、今日与标签下拉 - UI:回收站图标与备份统一尺寸;批量编辑条统一控件高度并分组 - 测试:705 个单测通过,补充迁移、回收站、批量编辑、合并与标签用例
77 lines
2.3 KiB
TypeScript
77 lines
2.3 KiB
TypeScript
/**
|
||
* 领域类型与常量(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 type MoveDirection = 'left' | 'right' | 'up' | 'down'
|
||
|
||
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
|
||
/**
|
||
* 删除时间;null 表示未删除。
|
||
*
|
||
* 删除是「软删除」:任务仍留在数据里,只是从看板、统计、标签等
|
||
* 所有常规视图里剔除,直到用户在回收站里恢复或永久删除。
|
||
*/
|
||
deletedAt: 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
|
||
}
|