Skip to content

JavaScript智能待办事项管理器核心功能实现2024:前端开发者完整CRUD操作与任务管理系统开发指南

📊 SEO元描述:2024年最新JavaScript智能待办事项管理器核心功能实现教程,详解任务CRUD操作、任务分类标签、任务提醒功能。包含完整的React+TypeScript实现,适合前端开发者快速掌握现代Web应用开发。

核心关键词:JavaScript任务管理系统2024、React CRUD操作、任务分类标签、任务提醒功能、前端项目实战、TypeScript开发

长尾关键词:JavaScript任务管理怎么实现、React待办事项应用、任务CRUD操作开发、前端项目实战教程、TypeScript项目开发


📚 JavaScript核心功能实现学习目标与核心收获

通过本节JavaScript核心功能实现教程,你将系统性掌握:

  • 任务CRUD操作:掌握完整的任务创建、读取、更新、删除功能实现
  • 任务分类和标签:学会设计和实现灵活的任务分类和标签系统
  • 任务提醒功能:理解基于时间和事件的智能提醒系统设计
  • 状态管理实践:掌握在实际项目中应用状态管理的最佳实践
  • 数据持久化:学会实现本地存储和远程同步的数据管理策略
  • 用户体验优化:掌握提升用户交互体验的前端开发技巧

🎯 适合人群

  • 前端开发工程师的实战项目开发能力提升
  • React开发者的复杂应用开发经验积累
  • 全栈开发者的前端技术栈深度学习
  • 技术面试准备者的项目经验和技能展示

🌟 任务管理系统核心架构设计

任务管理系统的核心在于高效的数据管理和用户交互体验。我们将构建一个基于React+TypeScript的现代化任务管理应用,实现完整的任务生命周期管理,也是现代前端开发的完整实践案例。

核心功能模块架构

  • 🎯 任务管理模块:任务的完整生命周期管理和状态跟踪
  • 🔧 分类标签模块:灵活的任务组织和分类系统
  • 💡 提醒通知模块:智能的时间提醒和事件通知系统
  • 📚 数据同步模块:本地存储和云端同步的数据管理
  • 🚀 用户界面模块:响应式和交互友好的用户界面

💡 架构设计理念:采用模块化设计,每个模块职责单一,通过清晰的接口进行通信,确保代码的可维护性和可扩展性。

数据模型设计

typescript
// 🎉 完整的数据模型定义

// 基础类型定义
export interface BaseEntity {
  id: string;
  createdAt: Date;
  updatedAt: Date;
}

// 用户模型
export interface User extends BaseEntity {
  email: string;
  name: string;
  avatar?: string;
  preferences: UserPreferences;
}

export interface UserPreferences {
  theme: 'light' | 'dark' | 'system';
  language: string;
  timezone: string;
  notifications: NotificationSettings;
  defaultView: 'list' | 'board' | 'calendar';
}

export interface NotificationSettings {
  email: boolean;
  browser: boolean;
  sound: boolean;
  reminderMinutes: number[];
}

// 任务模型
export interface Task extends BaseEntity {
  title: string;
  description?: string;
  status: TaskStatus;
  priority: TaskPriority;
  dueDate?: Date;
  completedAt?: Date;
  categoryId?: string;
  tags: string[];
  subtasks: Subtask[];
  reminders: Reminder[];
  attachments: Attachment[];
  userId: string;
  assignedTo?: string[];
  estimatedTime?: number; // 预估时间(分钟)
  actualTime?: number;    // 实际时间(分钟)
  progress: number;       // 进度百分比 0-100
}

export enum TaskStatus {
  TODO = 'todo',
  IN_PROGRESS = 'in_progress',
  COMPLETED = 'completed',
  CANCELLED = 'cancelled',
  ON_HOLD = 'on_hold'
}

export enum TaskPriority {
  LOW = 'low',
  MEDIUM = 'medium',
  HIGH = 'high',
  URGENT = 'urgent'
}

// 子任务模型
export interface Subtask extends BaseEntity {
  title: string;
  completed: boolean;
  taskId: string;
  order: number;
}

// 分类模型
export interface Category extends BaseEntity {
  name: string;
  color: string;
  icon?: string;
  description?: string;
  userId: string;
  parentId?: string; // 支持嵌套分类
  order: number;
}

// 标签模型
export interface Tag extends BaseEntity {
  name: string;
  color: string;
  userId: string;
  usageCount: number;
}

// 提醒模型
export interface Reminder extends BaseEntity {
  taskId: string;
  type: ReminderType;
  triggerTime: Date;
  message?: string;
  isActive: boolean;
  isSent: boolean;
  repeatPattern?: RepeatPattern;
}

export enum ReminderType {
  ABSOLUTE = 'absolute',     // 绝对时间提醒
  RELATIVE = 'relative',     // 相对时间提醒
  LOCATION = 'location',     // 位置提醒
  RECURRING = 'recurring'    // 重复提醒
}

export interface RepeatPattern {
  type: 'daily' | 'weekly' | 'monthly' | 'yearly' | 'custom';
  interval: number;
  daysOfWeek?: number[];
  endDate?: Date;
  maxOccurrences?: number;
}

// 附件模型
export interface Attachment extends BaseEntity {
  name: string;
  url: string;
  type: string;
  size: number;
  taskId: string;
}

// API响应类型
export interface ApiResponse<T> {
  data: T;
  message: string;
  success: boolean;
  timestamp: Date;
}

export interface PaginatedResponse<T> extends ApiResponse<T[]> {
  pagination: {
    page: number;
    limit: number;
    total: number;
    totalPages: number;
  };
}

// 查询参数类型
export interface TaskQueryParams {
  page?: number;
  limit?: number;
  status?: TaskStatus[];
  priority?: TaskPriority[];
  categoryId?: string;
  tags?: string[];
  search?: string;
  sortBy?: 'createdAt' | 'updatedAt' | 'dueDate' | 'priority' | 'title';
  sortOrder?: 'asc' | 'desc';
  dueDateFrom?: Date;
  dueDateTo?: Date;
}

// 统计数据类型
export interface TaskStatistics {
  total: number;
  completed: number;
  inProgress: number;
  overdue: number;
  completionRate: number;
  averageCompletionTime: number;
  productivityTrend: ProductivityData[];
  categoryDistribution: CategoryStats[];
  priorityDistribution: PriorityStats[];
}

export interface ProductivityData {
  date: Date;
  tasksCompleted: number;
  timeSpent: number;
  efficiency: number;
}

export interface CategoryStats {
  categoryId: string;
  categoryName: string;
  taskCount: number;
  completedCount: number;
  completionRate: number;
}

export interface PriorityStats {
  priority: TaskPriority;
  count: number;
  percentage: number;
}

任务CRUD操作实现

typescript
// 🎉 任务管理服务实现

import { create } from 'zustand';
import { devtools, persist } from 'zustand/middleware';
import { immer } from 'zustand/middleware/immer';

// 任务状态管理
interface TaskState {
  tasks: Task[];
  categories: Category[];
  tags: Tag[];
  loading: boolean;
  error: string | null;
  filters: TaskQueryParams;
  selectedTask: Task | null;
  statistics: TaskStatistics | null;
}

interface TaskActions {
  // 任务CRUD操作
  fetchTasks: (params?: TaskQueryParams) => Promise<void>;
  createTask: (task: Omit<Task, 'id' | 'createdAt' | 'updatedAt'>) => Promise<Task>;
  updateTask: (id: string, updates: Partial<Task>) => Promise<Task>;
  deleteTask: (id: string) => Promise<void>;
  duplicateTask: (id: string) => Promise<Task>;
  
  // 批量操作
  bulkUpdateTasks: (ids: string[], updates: Partial<Task>) => Promise<void>;
  bulkDeleteTasks: (ids: string[]) => Promise<void>;
  
  // 任务状态管理
  toggleTaskStatus: (id: string) => Promise<void>;
  updateTaskProgress: (id: string, progress: number) => Promise<void>;
  
  // 子任务管理
  addSubtask: (taskId: string, subtask: Omit<Subtask, 'id' | 'createdAt' | 'updatedAt' | 'taskId'>) => Promise<void>;
  updateSubtask: (taskId: string, subtaskId: string, updates: Partial<Subtask>) => Promise<void>;
  deleteSubtask: (taskId: string, subtaskId: string) => Promise<void>;
  toggleSubtask: (taskId: string, subtaskId: string) => Promise<void>;
  
  // 分类管理
  fetchCategories: () => Promise<void>;
  createCategory: (category: Omit<Category, 'id' | 'createdAt' | 'updatedAt'>) => Promise<Category>;
  updateCategory: (id: string, updates: Partial<Category>) => Promise<Category>;
  deleteCategory: (id: string) => Promise<void>;
  
  // 标签管理
  fetchTags: () => Promise<void>;
  createTag: (tag: Omit<Tag, 'id' | 'createdAt' | 'updatedAt' | 'usageCount'>) => Promise<Tag>;
  updateTag: (id: string, updates: Partial<Tag>) => Promise<Tag>;
  deleteTag: (id: string) => Promise<void>;
  
  // 筛选和搜索
  setFilters: (filters: Partial<TaskQueryParams>) => void;
  clearFilters: () => void;
  searchTasks: (query: string) => Promise<void>;
  
  // 统计数据
  fetchStatistics: () => Promise<void>;
  
  // UI状态管理
  setSelectedTask: (task: Task | null) => void;
  setLoading: (loading: boolean) => void;
  setError: (error: string | null) => void;
  clearError: () => void;
}

// API服务类
class TaskApiService {
  private baseUrl = '/api/tasks';
  
  async fetchTasks(params: TaskQueryParams = {}): Promise<PaginatedResponse<Task>> {
    const queryString = new URLSearchParams();
    
    Object.entries(params).forEach(([key, value]) => {
      if (value !== undefined && value !== null) {
        if (Array.isArray(value)) {
          value.forEach(v => queryString.append(key, v.toString()));
        } else {
          queryString.append(key, value.toString());
        }
      }
    });
    
    const response = await fetch(`${this.baseUrl}?${queryString}`);
    if (!response.ok) {
      throw new Error(`Failed to fetch tasks: ${response.statusText}`);
    }
    
    return response.json();
  }
  
  async createTask(task: Omit<Task, 'id' | 'createdAt' | 'updatedAt'>): Promise<ApiResponse<Task>> {
    const response = await fetch(this.baseUrl, {
      method: 'POST',
      headers: {
        'Content-Type': 'application/json',
      },
      body: JSON.stringify(task),
    });
    
    if (!response.ok) {
      throw new Error(`Failed to create task: ${response.statusText}`);
    }
    
    return response.json();
  }
  
  async updateTask(id: string, updates: Partial<Task>): Promise<ApiResponse<Task>> {
    const response = await fetch(`${this.baseUrl}/${id}`, {
      method: 'PUT',
      headers: {
        'Content-Type': 'application/json',
      },
      body: JSON.stringify(updates),
    });
    
    if (!response.ok) {
      throw new Error(`Failed to update task: ${response.statusText}`);
    }
    
    return response.json();
  }
  
  async deleteTask(id: string): Promise<ApiResponse<void>> {
    const response = await fetch(`${this.baseUrl}/${id}`, {
      method: 'DELETE',
    });
    
    if (!response.ok) {
      throw new Error(`Failed to delete task: ${response.statusText}`);
    }
    
    return response.json();
  }
  
  async bulkUpdateTasks(ids: string[], updates: Partial<Task>): Promise<ApiResponse<Task[]>> {
    const response = await fetch(`${this.baseUrl}/bulk`, {
      method: 'PUT',
      headers: {
        'Content-Type': 'application/json',
      },
      body: JSON.stringify({ ids, updates }),
    });
    
    if (!response.ok) {
      throw new Error(`Failed to bulk update tasks: ${response.statusText}`);
    }
    
    return response.json();
  }
  
  async fetchStatistics(): Promise<ApiResponse<TaskStatistics>> {
    const response = await fetch(`${this.baseUrl}/statistics`);
    if (!response.ok) {
      throw new Error(`Failed to fetch statistics: ${response.statusText}`);
    }
    
    return response.json();
  }
}

// Zustand状态管理实现
export const useTaskStore = create<TaskState & TaskActions>()(
  devtools(
    persist(
      immer((set, get) => ({
        // 初始状态
        tasks: [],
        categories: [],
        tags: [],
        loading: false,
        error: null,
        filters: {},
        selectedTask: null,
        statistics: null,
        
        // API服务实例
        apiService: new TaskApiService(),
        
        // 任务CRUD操作
        fetchTasks: async (params = {}) => {
          set(state => {
            state.loading = true;
            state.error = null;
          });
          
          try {
            const response = await get().apiService.fetchTasks(params);
            set(state => {
              state.tasks = response.data;
              state.loading = false;
            });
          } catch (error) {
            set(state => {
              state.error = error instanceof Error ? error.message : 'Unknown error';
              state.loading = false;
            });
          }
        },
        
        createTask: async (taskData) => {
          set(state => {
            state.loading = true;
            state.error = null;
          });
          
          try {
            const response = await get().apiService.createTask(taskData);
            const newTask = response.data;
            
            set(state => {
              state.tasks.unshift(newTask);
              state.loading = false;
            });
            
            return newTask;
          } catch (error) {
            set(state => {
              state.error = error instanceof Error ? error.message : 'Unknown error';
              state.loading = false;
            });
            throw error;
          }
        },
        
        updateTask: async (id, updates) => {
          set(state => {
            state.loading = true;
            state.error = null;
          });
          
          try {
            const response = await get().apiService.updateTask(id, updates);
            const updatedTask = response.data;
            
            set(state => {
              const index = state.tasks.findIndex(task => task.id === id);
              if (index !== -1) {
                state.tasks[index] = updatedTask;
              }
              
              if (state.selectedTask?.id === id) {
                state.selectedTask = updatedTask;
              }
              
              state.loading = false;
            });
            
            return updatedTask;
          } catch (error) {
            set(state => {
              state.error = error instanceof Error ? error.message : 'Unknown error';
              state.loading = false;
            });
            throw error;
          }
        },
        
        deleteTask: async (id) => {
          set(state => {
            state.loading = true;
            state.error = null;
          });
          
          try {
            await get().apiService.deleteTask(id);
            
            set(state => {
              state.tasks = state.tasks.filter(task => task.id !== id);
              
              if (state.selectedTask?.id === id) {
                state.selectedTask = null;
              }
              
              state.loading = false;
            });
          } catch (error) {
            set(state => {
              state.error = error instanceof Error ? error.message : 'Unknown error';
              state.loading = false;
            });
            throw error;
          }
        },
        
        duplicateTask: async (id) => {
          const originalTask = get().tasks.find(task => task.id === id);
          if (!originalTask) {
            throw new Error('Task not found');
          }
          
          const duplicatedTaskData = {
            ...originalTask,
            title: `${originalTask.title} (Copy)`,
            status: TaskStatus.TODO,
            completedAt: undefined,
            progress: 0,
            subtasks: originalTask.subtasks.map(subtask => ({
              ...subtask,
              completed: false
            }))
          };
          
          // 移除不需要的字段
          delete (duplicatedTaskData as any).id;
          delete (duplicatedTaskData as any).createdAt;
          delete (duplicatedTaskData as any).updatedAt;
          
          return get().createTask(duplicatedTaskData);
        },
        
        bulkUpdateTasks: async (ids, updates) => {
          set(state => {
            state.loading = true;
            state.error = null;
          });
          
          try {
            const response = await get().apiService.bulkUpdateTasks(ids, updates);
            const updatedTasks = response.data;
            
            set(state => {
              updatedTasks.forEach(updatedTask => {
                const index = state.tasks.findIndex(task => task.id === updatedTask.id);
                if (index !== -1) {
                  state.tasks[index] = updatedTask;
                }
              });
              
              state.loading = false;
            });
          } catch (error) {
            set(state => {
              state.error = error instanceof Error ? error.message : 'Unknown error';
              state.loading = false;
            });
            throw error;
          }
        },
        
        bulkDeleteTasks: async (ids) => {
          set(state => {
            state.loading = true;
            state.error = null;
          });
          
          try {
            await Promise.all(ids.map(id => get().apiService.deleteTask(id)));
            
            set(state => {
              state.tasks = state.tasks.filter(task => !ids.includes(task.id));
              
              if (state.selectedTask && ids.includes(state.selectedTask.id)) {
                state.selectedTask = null;
              }
              
              state.loading = false;
            });
          } catch (error) {
            set(state => {
              state.error = error instanceof Error ? error.message : 'Unknown error';
              state.loading = false;
            });
            throw error;
          }
        },
        
        toggleTaskStatus: async (id) => {
          const task = get().tasks.find(t => t.id === id);
          if (!task) return;
          
          const newStatus = task.status === TaskStatus.COMPLETED 
            ? TaskStatus.TODO 
            : TaskStatus.COMPLETED;
          
          const updates: Partial<Task> = {
            status: newStatus,
            completedAt: newStatus === TaskStatus.COMPLETED ? new Date() : undefined,
            progress: newStatus === TaskStatus.COMPLETED ? 100 : task.progress
          };
          
          await get().updateTask(id, updates);
        },
        
        updateTaskProgress: async (id, progress) => {
          const updates: Partial<Task> = {
            progress,
            status: progress === 100 ? TaskStatus.COMPLETED : 
                   progress > 0 ? TaskStatus.IN_PROGRESS : TaskStatus.TODO,
            completedAt: progress === 100 ? new Date() : undefined
          };
          
          await get().updateTask(id, updates);
        },
        
        // 子任务管理
        addSubtask: async (taskId, subtaskData) => {
          const task = get().tasks.find(t => t.id === taskId);
          if (!task) return;
          
          const newSubtask: Subtask = {
            ...subtaskData,
            id: crypto.randomUUID(),
            taskId,
            createdAt: new Date(),
            updatedAt: new Date(),
            order: task.subtasks.length
          };
          
          const updatedSubtasks = [...task.subtasks, newSubtask];
          await get().updateTask(taskId, { subtasks: updatedSubtasks });
        },
        
        updateSubtask: async (taskId, subtaskId, updates) => {
          const task = get().tasks.find(t => t.id === taskId);
          if (!task) return;
          
          const updatedSubtasks = task.subtasks.map(subtask =>
            subtask.id === subtaskId
              ? { ...subtask, ...updates, updatedAt: new Date() }
              : subtask
          );
          
          await get().updateTask(taskId, { subtasks: updatedSubtasks });
        },
        
        deleteSubtask: async (taskId, subtaskId) => {
          const task = get().tasks.find(t => t.id === taskId);
          if (!task) return;
          
          const updatedSubtasks = task.subtasks.filter(subtask => subtask.id !== subtaskId);
          await get().updateTask(taskId, { subtasks: updatedSubtasks });
        },
        
        toggleSubtask: async (taskId, subtaskId) => {
          const task = get().tasks.find(t => t.id === taskId);
          if (!task) return;
          
          const subtask = task.subtasks.find(s => s.id === subtaskId);
          if (!subtask) return;
          
          await get().updateSubtask(taskId, subtaskId, { completed: !subtask.completed });
          
          // 更新主任务进度
          const completedSubtasks = task.subtasks.filter(s => 
            s.id === subtaskId ? !subtask.completed : s.completed
          ).length;
          const totalSubtasks = task.subtasks.length;
          const progress = totalSubtasks > 0 ? Math.round((completedSubtasks / totalSubtasks) * 100) : 0;
          
          await get().updateTaskProgress(taskId, progress);
        },
        
        // 其他方法的实现...
        fetchCategories: async () => {
          // 实现分类获取逻辑
        },
        
        createCategory: async (categoryData) => {
          // 实现分类创建逻辑
          return {} as Category;
        },
        
        updateCategory: async (id, updates) => {
          // 实现分类更新逻辑
          return {} as Category;
        },
        
        deleteCategory: async (id) => {
          // 实现分类删除逻辑
        },
        
        fetchTags: async () => {
          // 实现标签获取逻辑
        },
        
        createTag: async (tagData) => {
          // 实现标签创建逻辑
          return {} as Tag;
        },
        
        updateTag: async (id, updates) => {
          // 实现标签更新逻辑
          return {} as Tag;
        },
        
        deleteTag: async (id) => {
          // 实现标签删除逻辑
        },
        
        setFilters: (filters) => {
          set(state => {
            state.filters = { ...state.filters, ...filters };
          });
        },
        
        clearFilters: () => {
          set(state => {
            state.filters = {};
          });
        },
        
        searchTasks: async (query) => {
          await get().fetchTasks({ ...get().filters, search: query });
        },
        
        fetchStatistics: async () => {
          try {
            const response = await get().apiService.fetchStatistics();
            set(state => {
              state.statistics = response.data;
            });
          } catch (error) {
            set(state => {
              state.error = error instanceof Error ? error.message : 'Unknown error';
            });
          }
        },
        
        setSelectedTask: (task) => {
          set(state => {
            state.selectedTask = task;
          });
        },
        
        setLoading: (loading) => {
          set(state => {
            state.loading = loading;
          });
        },
        
        setError: (error) => {
          set(state => {
            state.error = error;
          });
        },
        
        clearError: () => {
          set(state => {
            state.error = null;
          });
        }
      })),
      {
        name: 'task-store',
        partialize: (state) => ({
          tasks: state.tasks,
          categories: state.categories,
          tags: state.tags,
          filters: state.filters
        })
      }
    ),
    { name: 'task-store' }
  )
);

// 自定义Hook用于任务操作
export const useTasks = () => {
  const store = useTaskStore();
  
  return {
    // 状态
    tasks: store.tasks,
    loading: store.loading,
    error: store.error,
    selectedTask: store.selectedTask,
    
    // 操作
    fetchTasks: store.fetchTasks,
    createTask: store.createTask,
    updateTask: store.updateTask,
    deleteTask: store.deleteTask,
    toggleTaskStatus: store.toggleTaskStatus,
    
    // 工具方法
    getTaskById: (id: string) => store.tasks.find(task => task.id === id),
    getTasksByStatus: (status: TaskStatus) => store.tasks.filter(task => task.status === status),
    getOverdueTasks: () => store.tasks.filter(task => 
      task.dueDate && new Date(task.dueDate) < new Date() && task.status !== TaskStatus.COMPLETED
    ),
    getTasksByPriority: (priority: TaskPriority) => store.tasks.filter(task => task.priority === priority)
  };
};

任务CRUD操作的核心特点

  • 🎯 完整的生命周期管理:从创建到完成的全流程状态跟踪
  • 🎯 批量操作支持:提高用户操作效率的批量处理功能
  • 🎯 实时状态同步:确保数据一致性的状态管理机制

💼 实现价值:通过完整的CRUD操作实现,为用户提供流畅的任务管理体验,同时确保数据的一致性和可靠性。

任务分类和标签系统实现

typescript
// 🎉 分类和标签管理系统

// 分类管理服务
class CategoryService {
  private baseUrl = '/api/categories';

  async fetchCategories(): Promise<ApiResponse<Category[]>> {
    const response = await fetch(this.baseUrl);
    if (!response.ok) {
      throw new Error(`Failed to fetch categories: ${response.statusText}`);
    }
    return response.json();
  }

  async createCategory(category: Omit<Category, 'id' | 'createdAt' | 'updatedAt'>): Promise<ApiResponse<Category>> {
    const response = await fetch(this.baseUrl, {
      method: 'POST',
      headers: { 'Content-Type': 'application/json' },
      body: JSON.stringify(category),
    });

    if (!response.ok) {
      throw new Error(`Failed to create category: ${response.statusText}`);
    }
    return response.json();
  }

  async updateCategory(id: string, updates: Partial<Category>): Promise<ApiResponse<Category>> {
    const response = await fetch(`${this.baseUrl}/${id}`, {
      method: 'PUT',
      headers: { 'Content-Type': 'application/json' },
      body: JSON.stringify(updates),
    });

    if (!response.ok) {
      throw new Error(`Failed to update category: ${response.statusText}`);
    }
    return response.json();
  }

  async deleteCategory(id: string): Promise<ApiResponse<void>> {
    const response = await fetch(`${this.baseUrl}/${id}`, {
      method: 'DELETE',
    });

    if (!response.ok) {
      throw new Error(`Failed to delete category: ${response.statusText}`);
    }
    return response.json();
  }

  async reorderCategories(categoryIds: string[]): Promise<ApiResponse<Category[]>> {
    const response = await fetch(`${this.baseUrl}/reorder`, {
      method: 'PUT',
      headers: { 'Content-Type': 'application/json' },
      body: JSON.stringify({ categoryIds }),
    });

    if (!response.ok) {
      throw new Error(`Failed to reorder categories: ${response.statusText}`);
    }
    return response.json();
  }
}

// 标签管理服务
class TagService {
  private baseUrl = '/api/tags';

  async fetchTags(): Promise<ApiResponse<Tag[]>> {
    const response = await fetch(this.baseUrl);
    if (!response.ok) {
      throw new Error(`Failed to fetch tags: ${response.statusText}`);
    }
    return response.json();
  }

  async createTag(tag: Omit<Tag, 'id' | 'createdAt' | 'updatedAt' | 'usageCount'>): Promise<ApiResponse<Tag>> {
    const response = await fetch(this.baseUrl, {
      method: 'POST',
      headers: { 'Content-Type': 'application/json' },
      body: JSON.stringify(tag),
    });

    if (!response.ok) {
      throw new Error(`Failed to create tag: ${response.statusText}`);
    }
    return response.json();
  }

  async updateTag(id: string, updates: Partial<Tag>): Promise<ApiResponse<Tag>> {
    const response = await fetch(`${this.baseUrl}/${id}`, {
      method: 'PUT',
      headers: { 'Content-Type': 'application/json' },
      body: JSON.stringify(updates),
    });

    if (!response.ok) {
      throw new Error(`Failed to update tag: ${response.statusText}`);
    }
    return response.json();
  }

  async deleteTag(id: string): Promise<ApiResponse<void>> {
    const response = await fetch(`${this.baseUrl}/${id}`, {
      method: 'DELETE',
    });

    if (!response.ok) {
      throw new Error(`Failed to delete tag: ${response.statusText}`);
    }
    return response.json();
  }

  async searchTags(query: string): Promise<ApiResponse<Tag[]>> {
    const response = await fetch(`${this.baseUrl}/search?q=${encodeURIComponent(query)}`);
    if (!response.ok) {
      throw new Error(`Failed to search tags: ${response.statusText}`);
    }
    return response.json();
  }

  async getPopularTags(limit: number = 10): Promise<ApiResponse<Tag[]>> {
    const response = await fetch(`${this.baseUrl}/popular?limit=${limit}`);
    if (!response.ok) {
      throw new Error(`Failed to fetch popular tags: ${response.statusText}`);
    }
    return response.json();
  }
}

// 分类和标签状态管理
interface CategoryTagState {
  categories: Category[];
  tags: Tag[];
  popularTags: Tag[];
  loading: boolean;
  error: string | null;
}

interface CategoryTagActions {
  // 分类操作
  fetchCategories: () => Promise<void>;
  createCategory: (category: Omit<Category, 'id' | 'createdAt' | 'updatedAt'>) => Promise<Category>;
  updateCategory: (id: string, updates: Partial<Category>) => Promise<Category>;
  deleteCategory: (id: string) => Promise<void>;
  reorderCategories: (categoryIds: string[]) => Promise<void>;

  // 标签操作
  fetchTags: () => Promise<void>;
  createTag: (tag: Omit<Tag, 'id' | 'createdAt' | 'updatedAt' | 'usageCount'>) => Promise<Tag>;
  updateTag: (id: string, updates: Partial<Tag>) => Promise<Tag>;
  deleteTag: (id: string) => Promise<void>;
  searchTags: (query: string) => Promise<Tag[]>;
  fetchPopularTags: () => Promise<void>;

  // 工具方法
  getCategoryById: (id: string) => Category | undefined;
  getTagById: (id: string) => Tag | undefined;
  getCategoryTree: () => CategoryTreeNode[];
  getTagsByUsage: () => Tag[];
}

interface CategoryTreeNode extends Category {
  children: CategoryTreeNode[];
  level: number;
}

export const useCategoryTagStore = create<CategoryTagState & CategoryTagActions>()(
  devtools(
    persist(
      immer((set, get) => ({
        // 初始状态
        categories: [],
        tags: [],
        popularTags: [],
        loading: false,
        error: null,

        // 服务实例
        categoryService: new CategoryService(),
        tagService: new TagService(),

        // 分类操作
        fetchCategories: async () => {
          set(state => {
            state.loading = true;
            state.error = null;
          });

          try {
            const response = await get().categoryService.fetchCategories();
            set(state => {
              state.categories = response.data;
              state.loading = false;
            });
          } catch (error) {
            set(state => {
              state.error = error instanceof Error ? error.message : 'Unknown error';
              state.loading = false;
            });
          }
        },

        createCategory: async (categoryData) => {
          set(state => {
            state.loading = true;
            state.error = null;
          });

          try {
            const response = await get().categoryService.createCategory(categoryData);
            const newCategory = response.data;

            set(state => {
              state.categories.push(newCategory);
              state.loading = false;
            });

            return newCategory;
          } catch (error) {
            set(state => {
              state.error = error instanceof Error ? error.message : 'Unknown error';
              state.loading = false;
            });
            throw error;
          }
        },

        updateCategory: async (id, updates) => {
          try {
            const response = await get().categoryService.updateCategory(id, updates);
            const updatedCategory = response.data;

            set(state => {
              const index = state.categories.findIndex(cat => cat.id === id);
              if (index !== -1) {
                state.categories[index] = updatedCategory;
              }
            });

            return updatedCategory;
          } catch (error) {
            set(state => {
              state.error = error instanceof Error ? error.message : 'Unknown error';
            });
            throw error;
          }
        },

        deleteCategory: async (id) => {
          try {
            await get().categoryService.deleteCategory(id);

            set(state => {
              state.categories = state.categories.filter(cat => cat.id !== id);
            });
          } catch (error) {
            set(state => {
              state.error = error instanceof Error ? error.message : 'Unknown error';
            });
            throw error;
          }
        },

        reorderCategories: async (categoryIds) => {
          try {
            const response = await get().categoryService.reorderCategories(categoryIds);

            set(state => {
              state.categories = response.data;
            });
          } catch (error) {
            set(state => {
              state.error = error instanceof Error ? error.message : 'Unknown error';
            });
            throw error;
          }
        },

        // 标签操作
        fetchTags: async () => {
          try {
            const response = await get().tagService.fetchTags();
            set(state => {
              state.tags = response.data;
            });
          } catch (error) {
            set(state => {
              state.error = error instanceof Error ? error.message : 'Unknown error';
            });
          }
        },

        createTag: async (tagData) => {
          try {
            const response = await get().tagService.createTag(tagData);
            const newTag = response.data;

            set(state => {
              state.tags.push(newTag);
            });

            return newTag;
          } catch (error) {
            set(state => {
              state.error = error instanceof Error ? error.message : 'Unknown error';
            });
            throw error;
          }
        },

        updateTag: async (id, updates) => {
          try {
            const response = await get().tagService.updateTag(id, updates);
            const updatedTag = response.data;

            set(state => {
              const index = state.tags.findIndex(tag => tag.id === id);
              if (index !== -1) {
                state.tags[index] = updatedTag;
              }
            });

            return updatedTag;
          } catch (error) {
            set(state => {
              state.error = error instanceof Error ? error.message : 'Unknown error';
            });
            throw error;
          }
        },

        deleteTag: async (id) => {
          try {
            await get().tagService.deleteTag(id);

            set(state => {
              state.tags = state.tags.filter(tag => tag.id !== id);
            });
          } catch (error) {
            set(state => {
              state.error = error instanceof Error ? error.message : 'Unknown error';
            });
            throw error;
          }
        },

        searchTags: async (query) => {
          try {
            const response = await get().tagService.searchTags(query);
            return response.data;
          } catch (error) {
            set(state => {
              state.error = error instanceof Error ? error.message : 'Unknown error';
            });
            return [];
          }
        },

        fetchPopularTags: async () => {
          try {
            const response = await get().tagService.getPopularTags();
            set(state => {
              state.popularTags = response.data;
            });
          } catch (error) {
            set(state => {
              state.error = error instanceof Error ? error.message : 'Unknown error';
            });
          }
        },

        // 工具方法
        getCategoryById: (id) => {
          return get().categories.find(cat => cat.id === id);
        },

        getTagById: (id) => {
          return get().tags.find(tag => tag.id === id);
        },

        getCategoryTree: () => {
          const categories = get().categories;
          const categoryMap = new Map<string, CategoryTreeNode>();
          const rootCategories: CategoryTreeNode[] = [];

          // 创建节点映射
          categories.forEach(category => {
            categoryMap.set(category.id, {
              ...category,
              children: [],
              level: 0
            });
          });

          // 构建树结构
          categories.forEach(category => {
            const node = categoryMap.get(category.id)!;

            if (category.parentId) {
              const parent = categoryMap.get(category.parentId);
              if (parent) {
                node.level = parent.level + 1;
                parent.children.push(node);
              }
            } else {
              rootCategories.push(node);
            }
          });

          // 按order排序
          const sortByOrder = (nodes: CategoryTreeNode[]) => {
            nodes.sort((a, b) => a.order - b.order);
            nodes.forEach(node => sortByOrder(node.children));
          };

          sortByOrder(rootCategories);
          return rootCategories;
        },

        getTagsByUsage: () => {
          return [...get().tags].sort((a, b) => b.usageCount - a.usageCount);
        }
      })),
      {
        name: 'category-tag-store',
        partialize: (state) => ({
          categories: state.categories,
          tags: state.tags,
          popularTags: state.popularTags
        })
      }
    ),
    { name: 'category-tag-store' }
  )
);

// 自定义Hook
export const useCategoriesAndTags = () => {
  const store = useCategoryTagStore();

  return {
    // 状态
    categories: store.categories,
    tags: store.tags,
    popularTags: store.popularTags,
    loading: store.loading,
    error: store.error,

    // 操作
    fetchCategories: store.fetchCategories,
    createCategory: store.createCategory,
    updateCategory: store.updateCategory,
    deleteCategory: store.deleteCategory,

    fetchTags: store.fetchTags,
    createTag: store.createTag,
    updateTag: store.updateTag,
    deleteTag: store.deleteTag,
    searchTags: store.searchTags,

    // 工具方法
    getCategoryTree: store.getCategoryTree,
    getTagsByUsage: store.getTagsByUsage,
    getCategoryById: store.getCategoryById,
    getTagById: store.getTagById
  };
};

智能提醒系统实现

typescript
// 🎉 智能提醒系统

// 提醒服务
class ReminderService {
  private baseUrl = '/api/reminders';
  private notificationPermission: NotificationPermission = 'default';

  constructor() {
    this.requestNotificationPermission();
  }

  async requestNotificationPermission(): Promise<NotificationPermission> {
    if ('Notification' in window) {
      this.notificationPermission = await Notification.requestPermission();
    }
    return this.notificationPermission;
  }

  async createReminder(reminder: Omit<Reminder, 'id' | 'createdAt' | 'updatedAt'>): Promise<ApiResponse<Reminder>> {
    const response = await fetch(this.baseUrl, {
      method: 'POST',
      headers: { 'Content-Type': 'application/json' },
      body: JSON.stringify(reminder),
    });

    if (!response.ok) {
      throw new Error(`Failed to create reminder: ${response.statusText}`);
    }
    return response.json();
  }

  async updateReminder(id: string, updates: Partial<Reminder>): Promise<ApiResponse<Reminder>> {
    const response = await fetch(`${this.baseUrl}/${id}`, {
      method: 'PUT',
      headers: { 'Content-Type': 'application/json' },
      body: JSON.stringify(updates),
    });

    if (!response.ok) {
      throw new Error(`Failed to update reminder: ${response.statusText}`);
    }
    return response.json();
  }

  async deleteReminder(id: string): Promise<ApiResponse<void>> {
    const response = await fetch(`${this.baseUrl}/${id}`, {
      method: 'DELETE',
    });

    if (!response.ok) {
      throw new Error(`Failed to delete reminder: ${response.statusText}`);
    }
    return response.json();
  }

  // 显示浏览器通知
  showNotification(title: string, options: NotificationOptions = {}): Notification | null {
    if (this.notificationPermission === 'granted') {
      return new Notification(title, {
        icon: '/favicon.ico',
        badge: '/badge.png',
        ...options
      });
    }
    return null;
  }

  // 计算下次提醒时间
  calculateNextReminderTime(reminder: Reminder): Date | null {
    if (!reminder.repeatPattern) {
      return null;
    }

    const { type, interval, daysOfWeek, endDate, maxOccurrences } = reminder.repeatPattern;
    const now = new Date();
    const triggerTime = new Date(reminder.triggerTime);

    switch (type) {
      case 'daily':
        const nextDaily = new Date(triggerTime);
        nextDaily.setDate(nextDaily.getDate() + interval);
        return nextDaily > now ? nextDaily : null;

      case 'weekly':
        if (daysOfWeek && daysOfWeek.length > 0) {
          const nextWeekly = new Date(triggerTime);
          const currentDay = nextWeekly.getDay();
          const nextDay = daysOfWeek.find(day => day > currentDay) || daysOfWeek[0];

          if (nextDay > currentDay) {
            nextWeekly.setDate(nextWeekly.getDate() + (nextDay - currentDay));
          } else {
            nextWeekly.setDate(nextWeekly.getDate() + (7 - currentDay + nextDay));
          }

          return nextWeekly > now ? nextWeekly : null;
        }
        break;

      case 'monthly':
        const nextMonthly = new Date(triggerTime);
        nextMonthly.setMonth(nextMonthly.getMonth() + interval);
        return nextMonthly > now ? nextMonthly : null;

      case 'yearly':
        const nextYearly = new Date(triggerTime);
        nextYearly.setFullYear(nextYearly.getFullYear() + interval);
        return nextYearly > now ? nextYearly : null;
    }

    return null;
  }

  // 检查并触发到期的提醒
  checkAndTriggerReminders(reminders: Reminder[], tasks: Task[]): void {
    const now = new Date();

    reminders.forEach(reminder => {
      if (!reminder.isActive || reminder.isSent) return;

      const triggerTime = new Date(reminder.triggerTime);
      if (triggerTime <= now) {
        const task = tasks.find(t => t.id === reminder.taskId);
        if (task) {
          this.triggerReminder(reminder, task);
        }
      }
    });
  }

  private triggerReminder(reminder: Reminder, task: Task): void {
    const title = `任务提醒: ${task.title}`;
    const message = reminder.message || `任务 "${task.title}" 需要您的关注`;

    // 显示浏览器通知
    const notification = this.showNotification(title, {
      body: message,
      tag: `reminder-${reminder.id}`,
      requireInteraction: true,
      actions: [
        { action: 'complete', title: '标记完成' },
        { action: 'snooze', title: '稍后提醒' }
      ]
    });

    if (notification) {
      notification.onclick = () => {
        window.focus();
        // 导航到任务详情页
        window.location.href = `/tasks/${task.id}`;
      };

      notification.onclose = () => {
        // 标记提醒已发送
        this.updateReminder(reminder.id, { isSent: true });
      };
    }

    // 播放提醒声音(如果用户启用)
    this.playReminderSound();

    // 如果是重复提醒,计算下次提醒时间
    if (reminder.repeatPattern) {
      const nextTime = this.calculateNextReminderTime(reminder);
      if (nextTime) {
        this.createReminder({
          ...reminder,
          triggerTime: nextTime,
          isSent: false
        });
      }
    }
  }

  private playReminderSound(): void {
    try {
      const audio = new Audio('/sounds/notification.mp3');
      audio.volume = 0.5;
      audio.play().catch(error => {
        console.warn('Failed to play reminder sound:', error);
      });
    } catch (error) {
      console.warn('Reminder sound not available:', error);
    }
  }
}

// 提醒状态管理
interface ReminderState {
  reminders: Reminder[];
  loading: boolean;
  error: string | null;
  notificationPermission: NotificationPermission;
}

interface ReminderActions {
  fetchReminders: (taskId?: string) => Promise<void>;
  createReminder: (reminder: Omit<Reminder, 'id' | 'createdAt' | 'updatedAt'>) => Promise<Reminder>;
  updateReminder: (id: string, updates: Partial<Reminder>) => Promise<Reminder>;
  deleteReminder: (id: string) => Promise<void>;

  // 智能提醒功能
  createSmartReminder: (taskId: string, type: 'before_due' | 'daily' | 'weekly') => Promise<Reminder>;
  snoozeReminder: (id: string, minutes: number) => Promise<void>;
  checkPendingReminders: () => void;

  // 通知权限管理
  requestNotificationPermission: () => Promise<NotificationPermission>;

  // 工具方法
  getRemindersByTask: (taskId: string) => Reminder[];
  getActiveReminders: () => Reminder[];
  getOverdueReminders: () => Reminder[];
}

export const useReminderStore = create<ReminderState & ReminderActions>()(
  devtools(
    immer((set, get) => ({
      // 初始状态
      reminders: [],
      loading: false,
      error: null,
      notificationPermission: 'default',

      // 服务实例
      reminderService: new ReminderService(),

      fetchReminders: async (taskId) => {
        set(state => {
          state.loading = true;
          state.error = null;
        });

        try {
          const url = taskId ? `/api/reminders?taskId=${taskId}` : '/api/reminders';
          const response = await fetch(url);

          if (!response.ok) {
            throw new Error(`Failed to fetch reminders: ${response.statusText}`);
          }

          const data = await response.json();

          set(state => {
            state.reminders = data.data;
            state.loading = false;
          });
        } catch (error) {
          set(state => {
            state.error = error instanceof Error ? error.message : 'Unknown error';
            state.loading = false;
          });
        }
      },

      createReminder: async (reminderData) => {
        try {
          const response = await get().reminderService.createReminder(reminderData);
          const newReminder = response.data;

          set(state => {
            state.reminders.push(newReminder);
          });

          return newReminder;
        } catch (error) {
          set(state => {
            state.error = error instanceof Error ? error.message : 'Unknown error';
          });
          throw error;
        }
      },

      updateReminder: async (id, updates) => {
        try {
          const response = await get().reminderService.updateReminder(id, updates);
          const updatedReminder = response.data;

          set(state => {
            const index = state.reminders.findIndex(r => r.id === id);
            if (index !== -1) {
              state.reminders[index] = updatedReminder;
            }
          });

          return updatedReminder;
        } catch (error) {
          set(state => {
            state.error = error instanceof Error ? error.message : 'Unknown error';
          });
          throw error;
        }
      },

      deleteReminder: async (id) => {
        try {
          await get().reminderService.deleteReminder(id);

          set(state => {
            state.reminders = state.reminders.filter(r => r.id !== id);
          });
        } catch (error) {
          set(state => {
            state.error = error instanceof Error ? error.message : 'Unknown error';
          });
          throw error;
        }
      },

      createSmartReminder: async (taskId, type) => {
        const task = useTaskStore.getState().tasks.find(t => t.id === taskId);
        if (!task) {
          throw new Error('Task not found');
        }

        let triggerTime: Date;
        let repeatPattern: RepeatPattern | undefined;

        switch (type) {
          case 'before_due':
            if (!task.dueDate) {
              throw new Error('Task has no due date');
            }
            triggerTime = new Date(task.dueDate);
            triggerTime.setHours(triggerTime.getHours() - 1); // 1小时前提醒
            break;

          case 'daily':
            triggerTime = new Date();
            triggerTime.setHours(9, 0, 0, 0); // 每天上午9点
            triggerTime.setDate(triggerTime.getDate() + 1); // 从明天开始
            repeatPattern = {
              type: 'daily',
              interval: 1
            };
            break;

          case 'weekly':
            triggerTime = new Date();
            triggerTime.setHours(9, 0, 0, 0);
            triggerTime.setDate(triggerTime.getDate() + (7 - triggerTime.getDay() + 1)); // 下周一
            repeatPattern = {
              type: 'weekly',
              interval: 1,
              daysOfWeek: [1] // 周一
            };
            break;
        }

        const reminderData: Omit<Reminder, 'id' | 'createdAt' | 'updatedAt'> = {
          taskId,
          type: repeatPattern ? ReminderType.RECURRING : ReminderType.ABSOLUTE,
          triggerTime,
          isActive: true,
          isSent: false,
          repeatPattern
        };

        return get().createReminder(reminderData);
      },

      snoozeReminder: async (id, minutes) => {
        const reminder = get().reminders.find(r => r.id === id);
        if (!reminder) return;

        const newTriggerTime = new Date();
        newTriggerTime.setMinutes(newTriggerTime.getMinutes() + minutes);

        await get().updateReminder(id, {
          triggerTime: newTriggerTime,
          isSent: false
        });
      },

      checkPendingReminders: () => {
        const reminders = get().reminders;
        const tasks = useTaskStore.getState().tasks;

        get().reminderService.checkAndTriggerReminders(reminders, tasks);
      },

      requestNotificationPermission: async () => {
        const permission = await get().reminderService.requestNotificationPermission();

        set(state => {
          state.notificationPermission = permission;
        });

        return permission;
      },

      getRemindersByTask: (taskId) => {
        return get().reminders.filter(r => r.taskId === taskId);
      },

      getActiveReminders: () => {
        return get().reminders.filter(r => r.isActive && !r.isSent);
      },

      getOverdueReminders: () => {
        const now = new Date();
        return get().reminders.filter(r =>
          r.isActive &&
          !r.isSent &&
          new Date(r.triggerTime) <= now
        );
      }
    })),
    { name: 'reminder-store' }
  )
);

// 自定义Hook
export const useReminders = () => {
  const store = useReminderStore();

  // 定期检查提醒
  React.useEffect(() => {
    const interval = setInterval(() => {
      store.checkPendingReminders();
    }, 60000); // 每分钟检查一次

    return () => clearInterval(interval);
  }, [store.checkPendingReminders]);

  return {
    reminders: store.reminders,
    loading: store.loading,
    error: store.error,
    notificationPermission: store.notificationPermission,

    fetchReminders: store.fetchReminders,
    createReminder: store.createReminder,
    updateReminder: store.updateReminder,
    deleteReminder: store.deleteReminder,
    createSmartReminder: store.createSmartReminder,
    snoozeReminder: store.snoozeReminder,
    requestNotificationPermission: store.requestNotificationPermission,

    getRemindersByTask: store.getRemindersByTask,
    getActiveReminders: store.getActiveReminders,
    getOverdueReminders: store.getOverdueReminders
  };
};

分类标签和提醒系统的核心特点

  • 🎯 灵活的组织系统:支持嵌套分类和多标签的任务组织方式
  • 🎯 智能提醒机制:基于时间、重复模式的多样化提醒功能
  • 🎯 用户体验优化:浏览器通知、声音提醒等多感官提醒方式

💼 功能价值:通过完善的分类标签系统和智能提醒功能,帮助用户更好地组织和管理任务,提升工作效率和时间管理能力。


📚 JavaScript核心功能实现学习总结与下一步规划

✅ 本节核心收获回顾

通过本节JavaScript核心功能实现的学习,你已经掌握:

  1. 完整的CRUD操作:掌握了任务管理系统的完整数据操作流程和状态管理
  2. 分类标签系统:学会了设计和实现灵活的任务组织和分类管理功能
  3. 智能提醒系统:理解了基于时间和事件的提醒机制设计和实现方法
  4. 状态管理实践:掌握了在复杂应用中使用Zustand进行状态管理的最佳实践
  5. TypeScript应用:学会了在实际项目中应用TypeScript提升代码质量和开发效率

🎯 核心功能开发下一步

  1. 用户界面实现:基于核心功能开发用户友好的界面组件
  2. 数据持久化优化:实现更完善的本地存储和云端同步机制
  3. 性能优化实践:对核心功能进行性能分析和优化
  4. 测试用例编写:为核心功能编写完整的单元测试和集成测试

🔗 相关学习资源

  • React状态管理:深入学习Redux、Zustand等状态管理库的高级用法
  • TypeScript进阶:掌握TypeScript的高级类型和泛型编程
  • Web API应用:学习Notification API、Service Worker等现代Web API
  • 数据库设计:学习关系型数据库设计和NoSQL数据库应用

💪 实践建议

  1. 完善功能细节:继续完善任务管理的各种边界情况和用户体验细节
  2. 添加单元测试:为核心功能编写完整的测试用例确保代码质量
  3. 性能监控:添加性能监控和错误追踪机制
  4. 用户反馈收集:建立用户反馈收集和处理机制

🔍 常见问题FAQ

Q1: 如何设计高效的任务状态管理?

A: 使用不可变数据结构,合理设计状态结构避免深层嵌套,使用选择器模式优化数据访问,实现乐观更新提升用户体验,同时保持数据的一致性和可预测性。

Q2: 分类和标签系统如何平衡灵活性和性能?

A: 采用扁平化的标签设计避免复杂的层级关系,使用索引优化查询性能,实现标签的懒加载和缓存机制,同时提供标签使用统计帮助用户管理标签。

Q3: 提醒系统如何处理时区和本地化问题?

A: 统一使用UTC时间存储,在显示时转换为用户本地时区,支持用户设置首选时区,提醒时间计算考虑夏令时变化,提供多语言的提醒消息模板。

Q4: 如何优化大量任务数据的性能?

A: 实现虚拟滚动处理大列表,使用分页和懒加载减少初始加载时间,实现智能缓存策略,使用Web Worker处理复杂计算,优化数据库查询和索引。

Q5: 离线功能如何实现数据同步?

A: 使用Service Worker实现离线缓存,设计冲突解决策略处理数据冲突,实现增量同步减少数据传输,提供同步状态指示器,支持手动触发同步操作。


🛠️ 核心功能实现最佳实践指南

数据管理最佳实践

1. 状态结构设计

typescript
// 良好的状态结构设计
interface AppState {
  // 按功能模块组织
  tasks: {
    items: Task[];
    loading: boolean;
    error: string | null;
    filters: TaskFilters;
    pagination: PaginationInfo;
  };

  // 规范化数据结构
  categories: {
    byId: Record<string, Category>;
    allIds: string[];
    tree: CategoryTreeNode[];
  };

  // UI状态分离
  ui: {
    selectedTaskId: string | null;
    sidebarOpen: boolean;
    activeView: 'list' | 'board' | 'calendar';
  };
}

2. 错误处理策略

typescript
// 统一的错误处理
class ErrorHandler {
  static handle(error: unknown, context: string): string {
    console.error(`Error in ${context}:`, error);

    if (error instanceof Error) {
      // 网络错误
      if (error.message.includes('fetch')) {
        return '网络连接失败,请检查网络设置';
      }

      // 权限错误
      if (error.message.includes('401')) {
        return '登录已过期,请重新登录';
      }

      // 验证错误
      if (error.message.includes('validation')) {
        return '输入数据格式不正确,请检查后重试';
      }

      return error.message;
    }

    return '发生未知错误,请稍后重试';
  }

  static async withErrorHandling<T>(
    operation: () => Promise<T>,
    context: string
  ): Promise<T> {
    try {
      return await operation();
    } catch (error) {
      const message = this.handle(error, context);
      throw new Error(message);
    }
  }
}

3. 性能优化技巧

typescript
// 使用React.memo优化组件渲染
const TaskItem = React.memo(({ task, onUpdate }: TaskItemProps) => {
  // 使用useCallback避免不必要的重新渲染
  const handleToggle = useCallback(() => {
    onUpdate(task.id, { status: task.status === 'completed' ? 'todo' : 'completed' });
  }, [task.id, task.status, onUpdate]);

  return (
    <div className="task-item">
      <input
        type="checkbox"
        checked={task.status === 'completed'}
        onChange={handleToggle}
      />
      <span>{task.title}</span>
    </div>
  );
});

// 使用useMemo优化计算
const TaskList = ({ tasks, filters }: TaskListProps) => {
  const filteredTasks = useMemo(() => {
    return tasks.filter(task => {
      if (filters.status && task.status !== filters.status) return false;
      if (filters.category && task.categoryId !== filters.category) return false;
      if (filters.search && !task.title.toLowerCase().includes(filters.search.toLowerCase())) return false;
      return true;
    });
  }, [tasks, filters]);

  return (
    <div>
      {filteredTasks.map(task => (
        <TaskItem key={task.id} task={task} onUpdate={updateTask} />
      ))}
    </div>
  );
};

用户体验优化

1. 乐观更新

typescript
// 乐观更新实现
const useOptimisticUpdate = () => {
  const updateTaskOptimistically = async (id: string, updates: Partial<Task>) => {
    // 立即更新UI
    const originalTask = getTaskById(id);
    updateTaskInStore(id, updates);

    try {
      // 发送API请求
      await updateTaskAPI(id, updates);
    } catch (error) {
      // 失败时回滚
      if (originalTask) {
        updateTaskInStore(id, originalTask);
      }
      throw error;
    }
  };

  return { updateTaskOptimistically };
};

2. 加载状态管理

typescript
// 细粒度的加载状态
interface LoadingState {
  global: boolean;
  tasks: {
    fetching: boolean;
    creating: boolean;
    updating: Record<string, boolean>;
    deleting: Record<string, boolean>;
  };
  categories: {
    fetching: boolean;
    creating: boolean;
  };
}

// 使用加载状态
const TaskItem = ({ task }: { task: Task }) => {
  const { loading } = useTaskStore();
  const isUpdating = loading.tasks.updating[task.id];

  return (
    <div className={`task-item ${isUpdating ? 'updating' : ''}`}>
      {isUpdating && <Spinner size="small" />}
      {/* 任务内容 */}
    </div>
  );
};

"核心功能的实现质量直接决定了整个应用的用户体验。通过合理的架构设计、完善的错误处理、优化的性能表现,我们可以构建出既强大又易用的任务管理系统。记住,好的代码不仅要功能完整,更要易于维护和扩展。"