好的, 让我们来详细规划第 7 部分: 高级功能实现。我们将这部分分为 3 天, 每天专注于一个高级功能。

第 7 部分:高级功能实现 (3 天)

Day 1: 实现标签功能

  1. 更新 Todo 模型 在 src/types/todo.ts 中:
export interface Todo {
  id: string;
  title: string;
  description: string;
  completed: boolean;
  createdAt: string;
  tags: string[];
}
  1. 创建 TagInput 组件 在 src/components/TagInput.tsx 中:
import React from 'react';
import { MultiSelect } from '@mantine/core';
 
interface TagInputProps {
  value: string[];
  onChange: (value: string[]) => void;
  data: string[];
}
 
export const TagInput: React.FC<TagInputProps> = ({ value, onChange, data }) => {
  return (
    <MultiSelect
      data={data}
      value={value}
      onChange={onChange}
      label="Tags"
      placeholder="Select tags"
      searchable
      creatable
      getCreateLabel={(query) => `+ Create ${query}`}
      onCreate={(query) => {
        const item = { value: query, label: query };
        onChange([...value, query]);
        return item;
      }}
    />
  );
};
  1. 更新 TodoForm 和 TodoEditForm 组件 在这两个组件中添加 TagInput:
import { TagInput } from './TagInput';
 
// 在表单中添加
<TagInput
  value={form.values.tags}
  onChange={(value) => form.setFieldValue('tags', value)}
  data={existingTags}
/>
  1. 更新 TodoList 组件以显示标签 在 src/components/TodoList.tsx 中:
import { Badge } from '@mantine/core';
 
// 在表格中添加标签列
<td>
  {todo.tags.map((tag) => (
    <Badge key={tag} mr={5}>
      {tag}
    </Badge>
  ))}
</td>
  1. 更新 API 服务以支持标签 在 src/services/api.ts 中更新相关函数以包含标签操作。

Day 2: 实现截止日期和提醒功能

  1. 更新 Todo 模型 在 src/types/todo.ts 中:
export interface Todo {
  // ... 其他字段
  dueDate: string | null;
  reminder: string | null;
}
  1. 创建 DateTimePicker 组件 在 src/components/DateTimePicker.tsx 中:
import React from 'react';
import { DatePicker, TimeInput } from '@mantine/dates';
import { Stack } from '@mantine/core';
 
interface DateTimePickerProps {
  value: Date | null;
  onChange: (value: Date | null) => void;
  label: string;
}
 
export const DateTimePicker: React.FC<DateTimePickerProps> = ({ value, onChange, label }) => {
  return (
    <Stack spacing="xs">
      <DatePicker
        label={label}
        value={value}
        onChange={(date) => {
          if (date) {
            const newDateTime = new Date(date);
            if (value) {
              newDateTime.setHours(value.getHours(), value.getMinutes());
            }
            onChange(newDateTime);
          } else {
            onChange(null);
          }
        }}
      />
      <TimeInput
        value={value}
        onChange={(time) => {
          if (time && value) {
            const newDateTime = new Date(value);
            newDateTime.setHours(time.getHours(), time.getMinutes());
            onChange(newDateTime);
          }
        }}
      />
    </Stack>
  );
};
  1. 更新 TodoForm 和 TodoEditForm 组件 在这两个组件中添加 DateTimePicker:
import { DateTimePicker } from './DateTimePicker';
 
// 在表单中添加
<DateTimePicker
  label="Due Date"
  value={form.values.dueDate ? new Date(form.values.dueDate) : null}
  onChange={(value) => form.setFieldValue('dueDate', value ? value.toISOString() : null)}
/>
<DateTimePicker
  label="Reminder"
  value={form.values.reminder ? new Date(form.values.reminder) : null}
  onChange={(value) => form.setFieldValue('reminder', value ? value.toISOString() : null)}
/>
  1. 更新 TodoList 组件以显示截止日期和提醒 在 src/components/TodoList.tsx 中:
import { Text } from '@mantine/core';
 
// 在表格中添加截止日期和提醒列
<td>
  <Text size="sm">{todo.dueDate ? new Date(todo.dueDate).toLocaleString() : '-'}</Text>
</td>
<td>
  <Text size="sm">{todo.reminder ? new Date(todo.reminder).toLocaleString() : '-'}</Text>
</td>
  1. 实现提醒功能 创建一个新的 src/services/reminder.ts 文件:
import { showNotification } from '@mantine/notifications';
 
export const checkReminders = (todos: Todo[]) => {
  const now = new Date();
  todos.forEach((todo) => {
    if (todo.reminder && new Date(todo.reminder) <= now) {
      showNotification({
        title: 'Reminder',
        message: `Todo "${todo.title}" is due soon!`,
        color: 'yellow',
      });
    }
  });
};

App.tsx 中定期检查提醒:

import { useEffect } from 'react';
import { checkReminders } from './services/reminder';
 
// 在App组件中
useEffect(() => {
  const interval = setInterval(() => {
    checkReminders(todos);
  }, 60000); // 每分钟检查一次
 
  return () => clearInterval(interval);
}, [todos]);

Day 3: 实现数据导入/导出功能

  1. 创建 ImportExport 组件 在 src/components/ImportExport.tsx 中:
import React from 'react';
import { Button, Group } from '@mantine/core';
import { exportTodos, importTodos } from '../services/importExport';
 
interface ImportExportProps {
  onImport: () => void;
}
 
export const ImportExport: React.FC<ImportExportProps> = ({ onImport }) => {
  const handleExport = async () => {
    try {
      await exportTodos();
    } catch (error) {
      console.error('Failed to export todos', error);
    }
  };
 
  const handleImport = async () => {
    try {
      await importTodos();
      onImport();
    } catch (error) {
      console.error('Failed to import todos', error);
    }
  };
 
  return (
    <Group>
      <Button onClick={handleExport}>Export Todos</Button>
      <Button onClick={handleImport}>Import Todos</Button>
    </Group>
  );
};
  1. 创建导入/导出服务 在 src/services/importExport.ts 中:
import { getTodos, createTodo } from './api';
 
export const exportTodos = async () => {
  const response = await getTodos();
  const todos = response.data;
  const blob = new Blob([JSON.stringify(todos, null, 2)], { type: 'application/json' });
  const url = URL.createObjectURL(blob);
  const link = document.createElement('a');
  link.href = url;
  link.download = 'todos.json';
  link.click();
  URL.revokeObjectURL(url);
};
 
export const importTodos = async () => {
  return new Promise<void>((resolve, reject) => {
    const input = document.createElement('input');
    input.type = 'file';
    input.accept = 'application/json';
    input.onchange = async (event) => {
      const file = (event.target as HTMLInputElement).files?.[0];
      if (file) {
        try {
          const content = await file.text();
          const todos = JSON.parse(content);
          for (const todo of todos) {
            await createTodo(todo);
          }
          resolve();
        } catch (error) {
          reject(error);
        }
      }
    };
    input.click();
  });
};
  1. 在 Home 组件中添加 ImportExport 组件 在 src/pages/Home.tsx 中:
import { ImportExport } from '../components/ImportExport';
 
// 在Home组件中
const handleImport = () => {
  setRefreshTrigger((prev) => prev + 1);
};
 
// 在JSX中添加
<ImportExport onImport={handleImport} />

这个详细计划涵盖了第 7 部分的高级功能实现, 包括标签功能、截止日期和提醒功能, 以及数据导入/导出功能。每个功能都有具体的实现步骤和相关代码示例。通过这些高级功能, LightweightTodo Pro 应用将更加强大和实用。