介绍4-AI项目智绘日程
让时间看得见:纯本地自适应日程管理软件“TimePlanner”功能全解析与设计美学
在碎片化信息轰炸、多任务并行的现代生活里,我们常常会产生这样的焦虑:
- “今天明明很忙,但怎么感觉什么都没做成?”
- “想做的事情太多,一做计划时间就完全冲突了。”
管理时间的本质不是限制自由,而是让时间可视化。
“TimePlanner (智绘日程)” 就是一款为此打造的前端效率软件。它基于极其前沿的 React 19 + TypeScript + Vite 8.x 现代前端工具链开发,底层采用 IndexedDB 数据库进行本地大容量异步存储。
更为出色的是,它完美支持全自适应跨端适配:
- 电脑端(桌面版):自动铺开为左栏“打卡与实时统计”、右栏“明日日程时间线”的桌面端双栏网格大看板布局,实现高效的集中式排程规划。
- 手机端(移动版):自适应切换为紧凑易用、单手滑动的移动端卡片底栏自适应导航布局,方便您随时随地开启打卡。
它将**实时专注计时(Tracker)与明日时间线规划(Planner)**融为一体,采用极致高端的暗黑毛玻璃设计,为您提供一个最纯粹、最高效的自我管理沙盒。
下面这篇博客,我将带大家全面揭秘“TimePlanner”的核心功能细节、交互手感与背后的底层技术设计内幕。
📸 极简设计美学与主页一览
“TimePlanner”的第一眼体验十分惊艳。它采用了极具科技感与深度氛围的暗黑双渐变美学,背景混合了深蓝色(#080C14)与微光紫色的径向晕染。所有卡片与面板均基于高透亮毛玻璃(Glassmorphism)和微弱的白色边界线条,凸显品质感:
注:在桌面端,应用会自动呈现左边“计时/统计”与右边“明日日程规划”的双栏网格面板;而在移动端,则会自适应切换为底部的 Tab 标签导航,交互体验极其流畅。
🌟 核心功能板块解析
TimePlanner 的设计理念非常清晰:左手记录过去,右手规划未来。
1. ⏱️ 专注打卡计时器(Tracker Widget)
记录时间是管理时间的第一步。应用内置了优雅的实时计时面板:
- 一键专注卡片:预设了丰富多彩的任务模板卡片(例如:绿色的 Coding 编码、蓝色的 Reading 阅读、红色的 Exercise 运动)。点击卡片即可一键启动专注计时。
- 沉浸式计时体验:超大字体的数字停表在屏幕中央伴随呼吸霓虹微光跳动,帮助您快速进入工作流(Flow)。支持随时存入记录或放弃本次计时。

2. 📅 智能明日日程编排(Plan Timeline)
与传统只做“今日待办(Todo)”不同,应用强调**“为明日规划时间块”**。你可以在前一天晚上,将各项活动按具体时间段排入时间线:
- ⚠️ 智能冲突检测(Conflict Detection):如果两个任务的时间段发生重叠(例如 14:00 - 15:30 规划了阅读,而 15:00 - 16:00 规划了开会),系统会实时发出亮红色警告线条,提示您调整时间,杜绝不切实际的“低效安排”。
- 📊 时间饱和度分析(Time Saturation):系统会实时统计任务占您可分配专注时间段的比例,以环形百分比图展示。一旦饱和度过高(例如超过 80%),即提示日程过载,助您保持弹性工作节奏。

3. 📊 专注数据可视化图表(Stats Dashboard)
每一次专注打卡保存后,都会被统计模块自动汇总分析:
- 分类时间环形图(Donut Chart):以多彩的环形比例清晰反馈您今天或本周的时间都花在了哪里,便于审视精力的真实分配。
- 历史总量柱状图(Bar Chart):展示近期每天的累计打卡专注时长,见证自己的每日坚持。

🛠️ 深度架构与关键代码实现
TimePlanner 不仅在外部设计上追求美感,在底层架构设计与状态计算上也体现了高度工程化的前端技术实践。
1. ⚙️ 技术栈选型与工具链
- 前端核心:React 19 (^19.2.7) + TypeScript。引入了 React 19 的高效率状态逻辑,配合类型系统确保底层数学逻辑运算的准确性。
- 构建工具:基于最新的极速构建工具 Vite 8.x 实现毫秒级的热更新(HMR)。
- 超快 Lint 机制:采用基于 Rust 构建的 Oxlint 进行静态语法分析与代码检测,检测速度较常规 ESLint 提升了将近一个数量级。
2. 🗄️ 异构双层存储设计与代码抽象
应用在存储上使用了仓储模式(Repository Pattern)和提供者模式(Provider Pattern)。首先抽象出统一存储物理层的 IStorageProvider 接口:
// src/services/storage/IStorageProvider.ts
export interface TaskTemplate {
id: string;
name: string;
color: string;
createdAt: number;
}
export interface TimeRecord {
id: string;
templateId: string;
startTime: number; // 毫秒时间戳
endTime: number; // 毫秒时间戳
duration: number; // 专注时长(分钟)
note?: string;
dateString: string; // YYYY-MM-DD
}
export interface PlanItem {
id: string;
templateId: string;
startTime: string; // "HH:MM"
endTime: string; // "HH:MM"
isCompleted: boolean;
}
export interface DailyPlan {
id: string; // 以日期为 ID (YYYY-MM-DD)
dateString: string;
items: PlanItem[];
availableStart: string; // 默认开始工作时间 (如 "08:00")
availableEnd: string; // 默认结束工作时间 (如 "22:00")
}
export interface IStorageProvider {
init(): Promise<void>;
getTemplates(): Promise<TaskTemplate[]>;
saveTemplate(template: TaskTemplate): Promise<void>;
deleteTemplate(id: string): Promise<void>;
getRecords(): Promise<TimeRecord[]>;
saveRecord(record: TimeRecord): Promise<void>;
deleteRecord(id: string): Promise<void>;
getPlan(dateString: string): Promise<DailyPlan | null>;
savePlan(plan: DailyPlan): Promise<void>;
exportBackup(): Promise<BackupData>;
importBackup(data: BackupData): Promise<void>;
clearAll(): Promise<void>;
}
为了规避大量打卡记录导致主线程卡顿,应用引入了 IndexedDB 异步非阻塞数据库 作为重量级存储提供者,通过 Promise 对原生的异步监听机制进行了封装:
// src/services/storage/IndexedDBProvider.ts
export class IndexedDBProvider implements IStorageProvider {
private dbName = 'TimePlannerDB';
private dbVersion = 1;
private db: IDBDatabase | null = null;
init(): Promise<void> {
return new Promise((resolve, reject) => {
const request = indexedDB.open(this.dbName, this.dbVersion);
request.onerror = () => reject(request.error);
request.onsuccess = () => {
this.db = request.result;
resolve();
};
// 数据库版本升级,初始化物理结构
request.onupgradeneeded = () => {
const db = request.result;
// 1. 创建任务类别模板 ObjectStore
if (!db.objectStoreNames.contains('templates')) {
db.createObjectStore('templates', { keyPath: 'id' });
}
// 2. 创建专注记录 ObjectStore 并创建多维度复合索引以加速图表分析
if (!db.objectStoreNames.contains('records')) {
const recordStore = db.createObjectStore('records', { keyPath: 'id' });
recordStore.createIndex('dateString', 'dateString', { unique: false });
recordStore.createIndex('templateId', 'templateId', { unique: false });
}
// 3. 创建日程计划 ObjectStore
if (!db.objectStoreNames.contains('plans')) {
db.createObjectStore('plans', { keyPath: 'id' });
}
};
});
}
// 统一通过数据库事务访问具体的 Store
private getStore(storeName: 'templates' | 'records' | 'plans', mode: IDBTransactionMode): IDBObjectStore {
if (!this.db) throw new Error('Database not initialized.');
const transaction = this.db.transaction(storeName, mode);
return transaction.objectStore(storeName);
}
// 异步获取所有打卡历史,保障流畅的渲染响应
getRecords(): Promise<TimeRecord[]> {
return new Promise((resolve, reject) => {
try {
const store = this.getStore('records', 'readonly');
const request = store.getAll();
request.onsuccess = () => resolve(request.result || []);
request.onerror = () => reject(request.error);
} catch (err) {
reject(err);
}
});
}
// ... 封装的 save, delete, exportBackup 事务处理
}
3. 🧠 状态与核心排程计算逻辑 (usePlanner.ts)
在前端状态控制方面,应用通过自定义 Hooks 将渲染状态与业务算法完全解耦。
明日日程规划钩子 usePlanner 内部利用 React 的 useMemo 缓存机制 进行了实时的时间饱和度计算与双重循环笛卡尔积日程冲突判定。此外,它在添加日程项时,实现了智能时间追加算法,自动读取最后一个日程结束时间并顺延添加,省去了用户频繁手动调整时间轴的选择成本:
// src/hooks/usePlanner.ts
export function usePlanner(initialDateString?: string) {
// 智能默认获取明天的日期字符串进行计划渲染
const defaultDate = useMemo(() => {
if (initialDateString) return initialDateString;
const today = new Date();
const tomorrow = new Date(today);
tomorrow.setDate(today.getDate() + 1);
const y = tomorrow.getFullYear();
const m = String(tomorrow.getMonth() + 1).padStart(2, '0');
const d = String(tomorrow.getDate()).padStart(2, '0');
return `${y}-${m}-${d}`;
}, [initialDateString]);
const [dateString, setDateString] = useState<string>(defaultDate);
const [dailyPlan, setDailyPlan] = useState<DailyPlan | null>(null);
const [loading, setLoading] = useState<boolean>(true);
// 1. 实时的冲突检测:只有在计划项变化时才利用 useMemo 触发 O(n^2) 过滤
const conflicts = useMemo(() => {
if (!dailyPlan || !dailyPlan.items) return [];
const blocks = dailyPlan.items.map(item => ({
id: item.id,
startTime: item.startTime,
endTime: item.endTime
}));
return PlannerService.findConflicts(blocks); // 笛卡尔积比对重叠算法
}, [dailyPlan]);
// 2. 实时计划饱和度计算
const saturation = useMemo(() => {
if (!dailyPlan) return { totalMinutes: 0, plannedMinutes: 0, freeMinutes: 0, saturationPercentage: 0 };
return PlannerService.calculateSaturation(
dailyPlan.items,
dailyPlan.availableStart,
dailyPlan.availableEnd
);
}, [dailyPlan]);
// 3. 智能顺延追加日程项算法
const addPlanItem = async (templateId: string) => {
if (!dailyPlan) return;
let startTime = dailyPlan.availableStart;
// 如果之前已经有计划好的事件,自动定位到最后一个事件的结束时间作为新事件的开始时间
if (dailyPlan.items.length > 0) {
const sortedItems = [...dailyPlan.items].sort((a, b) =>
PlannerService.timeToMinutes(a.startTime) - PlannerService.timeToMinutes(b.startTime)
);
const lastItem = sortedItems[sortedItems.length - 1];
if (PlannerService.timeToMinutes(lastItem.endTime) < PlannerService.timeToMinutes(dailyPlan.availableEnd)) {
startTime = lastItem.endTime;
}
}
const startMin = PlannerService.timeToMinutes(startTime);
// 默认新加的任务持续 1 小时,且不超过每日设定的可用时间上限
const endMin = Math.min(startMin + 60, PlannerService.timeToMinutes(dailyPlan.availableEnd));
const endTime = PlannerService.minutesToTime(endMin);
const newItem: PlanItem = {
id: `pi-${Date.now()}-${Math.random().toString(36).substr(2, 4)}`,
templateId,
startTime,
endTime,
isCompleted: false
};
const updated: DailyPlan = {
...dailyPlan,
items: [...dailyPlan.items, newItem]
};
setDailyPlan(updated);
await dbProvider.savePlan(updated);
};
return {
dailyPlan,
conflicts,
saturation,
addPlanItem,
// ... updatePlanItemTime, removePlanItem
};
}
4. 🧮 时间轴冲突与区间重叠算法
这是排程核心业务的算法承载:
// src/services/planner.service.ts
export class PlannerService {
// A. 将 "HH:MM" 格式时间转换为分钟整数表示,方便进行直接大小数值比较
static timeToMinutes(timeStr: string): number {
if (!timeStr || !timeStr.includes(':')) return 0;
const [hours, minutes] = timeStr.split(':').map(Number);
return (hours || 0) * 60 + (minutes || 0);
}
static minutesToTime(minutes: number): string {
const hours = Math.floor(minutes / 60) % 24;
const mins = minutes % 60;
return `${String(hours).padStart(2, '0')}:${String(mins).padStart(2, '0')}`;
}
// B. 判断两个时间区间 [Start_A, End_A] 和 [Start_B, End_B] 是否相交
static isOverlapping(blockA: TimeBlock, blockB: TimeBlock): boolean {
const startA = this.timeToMinutes(blockA.startTime);
const endA = this.timeToMinutes(blockA.endTime);
const startB = this.timeToMinutes(blockB.startTime);
const endB = this.timeToMinutes(blockB.endTime);
if (startA >= endA || startB >= endB) return false;
// 数学区间重叠公式:A 开启在 B 结束前 并且 B 开启在 A 结束前
return startA < endB && startB < endA;
}
// C. 采用笛卡尔积二重循环,遍历寻找出冲突的时间段
static findConflicts(blocks: TimeBlock[]): string[] {
const conflictingIds = new Set<string>();
for (let i = 0; i < blocks.length; i++) {
for (let j = i + 1; j < blocks.length; j++) {
if (this.isOverlapping(blocks[i], blocks[j])) {
conflictingIds.add(blocks[i].id);
conflictingIds.add(blocks[j].id);
}
}
}
return Array.from(conflictingIds);
}
}
🔒 零云端隐私承诺与数据便携
数据主权对于效率类工具而言至关重要。
- 100% 零服务器依赖:TimePlanner 没有任何后端接口,所有配置和打卡记录都通过原生的 LocalStorage 与 IndexedDB 存储于您本机的浏览器沙盒中。数据绝不上云,绝对安全。
- 数据便携性:内置了完整的备份与迁移面板,支持一键将本地所有的打卡记录和模板配置导出为标准的 JSON 备份文件。即使更换浏览器或电脑,也能够秒级导入重连,彻底免除数据丢失的后顾之忧。
📝 结语
“TimePlanner (智绘日程)” 是一款兼具“先进前端工程实践”与“赛博朋克极简美学”的效率工具。它不仅在技术选型上大胆拥抱了 React 19 和 Rust 工具链 Oxlint,更通过 IndexedDB 双层异构架构与精准的线性排程冲突检测,让每一个时间片在客户端得到了极致流畅的处理。
如果您正在寻找一款界面炫酷、无广告、纯净本地化且带有明日规划理念的时间管理应用,那么 TimePlanner 将会为您带来全新的自我规划体验!
更多推荐



所有评论(0)