让时间看得见:纯本地自适应日程管理软件“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 将会为您带来全新的自我规划体验!

Logo

一站式 AI 云服务平台

更多推荐