好的, 我们来详细规划第 4 部分: 前端基础结构。我们将这部分分为 3 天, 每天专注于特定的前端基础组件和功能。

第 4 部分:前端基础结构 (3 天)

Day 1: 项目设置和基础组件

  1. 初始化项目 使用 Create React App 或 Vite 创建新的 React 项目:

    npm create vite@latest lightweight-todo-pro -- --template react-ts
    cd lightweight-todo-pro
    npm install
  2. 安装必要的依赖

    npm install react-router-dom @mantine/core @mantine/hooks @mantine/form @mantine/notifications @tabler/icons-react
  3. 设置基础布局组件 在 src/components/Layout.tsx 中:

    import React from 'react';
    import { AppShell, Header, Navbar, Text } from '@mantine/core';
    import { Outlet } from 'react-router-dom';
     
    export const Layout: React.FC = () => {
      return (
        <AppShell
          padding="md"
          navbar={<Navbar width={{ base: 300 }} p="xs">{/* Navbar content */}</Navbar>}
          header={<Header height={60} p="xs"><Text>LightweightTodo Pro</Text></Header>}
        >
          <Outlet />
        </AppShell>
      );
    };
  4. 创建基础路由 在 src/App.tsx 中:

    import React from 'react';
    import { BrowserRouter as Router, Routes, Route } from 'react-router-dom';
    import { MantineProvider } from '@mantine/core';
    import { Layout } from './components/Layout';
    import { Home } from './pages/Home';
    import { Login } from './pages/Login';
    import { Register } from './pages/Register';
     
    const App: React.FC = () => {
      return (
        <MantineProvider withGlobalStyles withNormalizeCSS>
          <Router>
            <Routes>
              <Route path="/" element={<Layout />}>
                <Route index element={<Home />} />
                <Route path="login" element={<Login />} />
                <Route path="register" element={<Register />} />
              </Route>
            </Routes>
          </Router>
        </MantineProvider>
      );
    };
     
    export default App;
  5. 创建占位页面组件 创建 src/pages/Home.tsx, src/pages/Login.tsx, 和 src/pages/Register.tsx,暂时用简单的内容填充。

Day 2: 状态管理和 API 集成

  1. 设置状态管理 使用 React Context API 创建一个简单的状态管理系统。在 src/context/AuthContext.tsx 中:

    import React, { createContext, useState, useContext, useEffect } from 'react';
     
    interface AuthContextType {
      user: any | null;
      login: (userData: any) => void;
      logout: () => void;
    }
     
    const AuthContext = createContext<AuthContextType | undefined>(undefined);
     
    export const AuthProvider: React.FC<{ children: React.ReactNode }> = ({ children }) => {
      const [user, setUser] = useState<any | null>(null);
     
      useEffect(() => {
        // 从本地存储中检查用户会话
        const storedUser = localStorage.getItem('user');
        if (storedUser) {
          setUser(JSON.parse(storedUser));
        }
      }, []);
     
      const login = (userData: any) => {
        setUser(userData);
        localStorage.setItem('user', JSON.stringify(userData));
      };
     
      const logout = () => {
        setUser(null);
        localStorage.removeItem('user');
      };
     
      return (
        <AuthContext.Provider value={{ user, login, logout }}>
          {children}
        </AuthContext.Provider>
      );
    };
     
    export const useAuth = () => {
      const context = useContext(AuthContext);
      if (context === undefined) {
        throw new Error('useAuth must be used within an AuthProvider');
      }
      return context;
    };
  2. 创建 API 服务 在 src/services/api.ts 中:

    import axios from 'axios';
     
    const API_URL = 'http://localhost:3000/api';
     
    const api = axios.create({
      baseURL: API_URL,
      withCredentials: true,
    });
     
    export const login = (email: string, password: string) => 
      api.post('/auth/login', { email, password });
     
    export const register = (username: string, email: string, password: string) => 
      api.post('/auth/register', { username, email, password });
     
    export const logout = () => api.post('/auth/logout');
     
    export const getTodos = () => api.get('/todos');
     
    export const createTodo = (todo: any) => api.post('/todos', todo);
     
    export const updateTodo = (id: string, todo: any) => api.put(`/todos/${id}`, todo);
     
    export const deleteTodo = (id: string) => api.delete(`/todos/${id}`);
     
    export default api;
  3. 实现登录和注册功能 更新 src/pages/Login.tsxsrc/pages/Register.tsx,使用 API 服务和 AuthContext。

Day 3: Todo 列表和表单组件

  1. 创建 Todo 列表组件 在 src/components/TodoList.tsx 中:

    import React, { useEffect, useState } from 'react';
    import { Table, Button, Group } from '@mantine/core';
    import { getTodos, deleteTodo } from '../services/api';
     
    export const TodoList: React.FC = () => {
      const [todos, setTodos] = useState([]);
     
      useEffect(() => {
        fetchTodos();
      }, []);
     
      const fetchTodos = async () => {
        try {
          const response = await getTodos();
          setTodos(response.data);
        } catch (error) {
          console.error('Failed to fetch todos', error);
        }
      };
     
      const handleDelete = async (id: string) => {
        try {
          await deleteTodo(id);
          fetchTodos();
        } catch (error) {
          console.error('Failed to delete todo', error);
        }
      };
     
      return (
        <Table>
          <thead>
            <tr>
              <th>Title</th>
              <th>Description</th>
              <th>Actions</th>
            </tr>
          </thead>
          <tbody>
            {todos.map((todo: any) => (
              <tr key={todo.id}>
                <td>{todo.title}</td>
                <td>{todo.description}</td>
                <td>
                  <Group>
                    <Button color="blue">Edit</Button>
                    <Button color="red" onClick={() => handleDelete(todo.id)}>Delete</Button>
                  </Group>
                </td>
              </tr>
            ))}
          </tbody>
        </Table>
      );
    };
  2. 创建 Todo 表单组件 在 src/components/TodoForm.tsx 中:

    import React from 'react';
    import { useForm } from '@mantine/form';
    import { TextInput, Textarea, Button, Box } from '@mantine/core';
    import { createTodo } from '../services/api';
     
    export const TodoForm: React.FC = () => {
      const form = useForm({
        initialValues: {
          title: '',
          description: '',
        },
      });
     
      const handleSubmit = async (values: any) => {
        try {
          await createTodo(values);
          form.reset();
          // 可以在这里添加一个回调函数来刷新Todo列表
        } catch (error) {
          console.error('Failed to create todo', error);
        }
      };
     
      return (
        <Box mx="auto">
          <form onSubmit={form.onSubmit(handleSubmit)}>
            <TextInput
              required
              label="Title"
              placeholder="Enter todo title"
              {...form.getInputProps('title')}
            />
            <Textarea
              mt="md"
              label="Description"
              placeholder="Enter todo description"
              {...form.getInputProps('description')}
            />
            <Button type="submit" mt="md">
              Add Todo
            </Button>
          </form>
        </Box>
      );
    };
  3. 整合到主页 更新 src/pages/Home.tsx

    import React from 'react';
    import { Container, Title } from '@mantine/core';
    import { TodoList } from '../components/TodoList';
    import { TodoForm } from '../components/TodoForm';
     
    export const Home: React.FC = () => {
      return (
        <Container>
          <Title order={2}>My Todos</Title>
          <TodoForm />
          <TodoList />
        </Container>
      );
    };
  4. 添加导航组件 在 src/components/Navigation.tsx 中:

    import React from 'react';
    import { Link } from 'react-router-dom';
    import { Button, Group } from '@mantine/core';
    import { useAuth } from '../context/AuthContext';
     
    export const Navigation: React.FC = () => {
      const { user, logout } = useAuth();
     
      return (
        <Group>
          {user ? (
            <>
              <Button component={Link} to="/">Home</Button>
              <Button onClick={logout}>Logout</Button>
            </>
          ) : (
            <>
              <Button component={Link} to="/login">Login</Button>
              <Button component={Link} to="/register">Register</Button>
            </>
          )}
        </Group>
      );
    };

    然后将 Navigation 组件添加到 Layout 组件中。

这个三天的计划涵盖了前端基础结构的主要部分,包括项目设置、路由、状态管理、API 集成以及基本的 Todo CRUD 操作。接下来,我们可以继续完善用户界面,添加更多功能,并进行进一步的优化。