用 React + umi 框架 + antd 库构建简单待办事项列表 | 豆包MarsCode AI刷题

142 阅读8分钟

在这篇博客中,我将带大家一步步构建一个简单的待办事项列表应用,使用到的技术栈主要是 React,并结合 umi 框架进行项目搭建,以及借助 antd 库来实现美观的 UI 界面。

一、项目构思

待办事项列表是一个常见的应用场景,用户需要能够方便地添加新的待办事项、对已有的事项进行编辑和删除操作。每个待办事项包含多个重要信息,比如创建时间、具体事项内容、是否完成的状态、截止时间(若未完成)以及完成时间(若已完成)。

演示图:

image.png

为了实现这些功能,我们可以从以下几个方面来考虑:

数据结构设计

首先要确定待办事项的数据结构,在 JavaScript 中,我们可以用对象来表示一个待办事项,例如:

{
  id: 1, // 每个事项独一无二的标识,方便后续操作
  createTime: new Date(), // 创建时间
  task: '完成工作报告', // 具体事项
  isCompleted: false, // 是否完成
  dueTime: new Date('2024-12-10'), // 截止时间(假设)
  completeTime: null // 完成时间,初始化为null
}

这里给每个待办事项定义了一个 id,方便在列表中准确地找到并操作特定的事项。

功能模块划分

  • 添加事项模块:提供一个输入框让用户输入具体事项内容,再设置相关按钮来触发添加操作,添加时自动生成创建时间,并可根据用户需求设置截止时间(也可后续再编辑)。
  • 编辑事项模块:当用户点击某个待办事项的编辑按钮时,弹出一个对话框或者进入编辑页面,允许用户修改具体事项内容、截止时间等信息。
  • 删除事项模块:在每个待办事项旁边设置一个删除按钮,点击即可从列表中删除该事项。
  • 事项列表展示模块:将所有的待办事项以列表的形式展示出来,清晰地显示各项信息,如创建时间、具体事项、是否完成状态等,对于已完成的事项,不显示截止时间,而是显示完成时间。

用户交互设计

用户与应用的交互应该简洁明了。例如,添加事项后能立即在列表中看到新添加的内容;点击编辑按钮能顺利进入编辑状态并保存修改后的结果;删除操作要有确认提示,防止误删。

二、项目环境配置与依赖项安装

安装 umi 框架

首先确保你的电脑已经安装了 Node.js,然后在命令行中执行以下命令来创建一个 umi 项目:

npx create-umi@latest my-todo-list

这里 my-todo-list 是我们项目的名称,你可以根据自己的喜好进行修改。

执行完上述命令后,会自动安装相关的依赖项并创建项目的基本结构。

安装 antd 库

进入项目目录(cd my-todo-list),然后执行以下命令来安装 antd 库:

npm install antd

antd 是一个优秀的 React UI 组件库,它提供了丰富的组件,可以让我们快速搭建出美观且功能齐全的界面。

配置 less-loader(用于 antd 样式定制)

因为 antd 的样式是基于 less 编写的,所以我们需要在项目中配置 less-loader 来正确加载样式。

首先安装 less 和 less-loader:

npm install less less-loader --save-dev

然后在项目的 webpack.config.js 文件(如果没有,可以在 umi 项目根目录下创建一个)中进行如下配置(以下是示例配置,具体可能需要根据项目实际情况调整):

module.exports = {
  module: {
    rules: [
      {
        test: /.less$/,
        use: [
          'style-loader',
          'css-loader',
          'less-loader'
        ]
      }
    ]
  }
};

这样就完成了项目的基本环境配置和主要依赖项的安装,可以开始编写具体的代码来实现待办事项列表的功能了。

三、编写代码实现功能

1. 创建数据存储文件

在项目的 src 目录下创建一个名为 todoData.js 的文件,用于存储待办事项的数据。

// todoData.js
let todoList = [];

export const addTodo = (todo) => {
  todo.id = Date.now(); // 用当前时间戳作为id,确保唯一性
  todo.createTime = new Date();
  todoList.push(todo);
};

export const editTodo = (id, updatedTodo) => {
  const index = todoList.findIndex(todo => todo.id === id);
  if (index!== -1) {
    todoList[index] = {...todoList[index],...updatedTodo };
  }
};

export const deleteTodo = (id) => {
  todoList = todoList.filter(todo => todo.id!== id);
};

export const getTodoList = () => todoList;

在这个文件中,我们定义了一个数组 todoList 来存储所有的待办事项。然后提供了几个函数来实现添加、编辑、删除待办事项以及获取待办事项列表的功能。

  • addTodo 函数:接收一个待办事项对象作为参数,给它添加 id 和 createTime 属性后,将其添加到 todoList 数组中。
  • editTodo 函数:根据传入的 id 找到对应的待办事项,然后用更新后的待办事项对象替换原来的那个。
  • deleteTodo 函数:根据 id 过滤掉要删除的待办事项,从而实现从列表中删除的操作。
  • getTodoList 函数:简单地返回当前的 todoList 数组,以便在其他组件中获取待办事项列表进行展示。

2. 创建待办事项列表组件

在 src 目录下创建一个名为 TodoList.js 的组件文件。

// TodoList.js
import React, { useState, useEffect } from 'react';
import * as data from './todoData.js';
import { Table, Button, Modal, Input, DatePicker } from 'antd';

const { Column } = Table;

const TodoList = () => {
  const [todoList, setTodoList] = useState([]);
  const [isEditing, setIsEditing] = useState(false);
  const [editingTodo, setEditingTodo] = useState({});
  const [visible, setVisible] = useState(false);

  useEffect(() => {
    const list = data.getTodoList();
    setTodoList(list);
  }, []);

  const handleAddTodo = () => {
    setVisible(true);
  };

  const handleSaveTodo = () => {
    const newTodo = {
      task: editingTodo.task,
      isCompleted: false,
      dueTime: editingTodo.dueTime
    };
    data.addTodo(newTodo);
    setVisible(false);
    setIsEditing(false);
    setEditingTodo({});
    setTodoList(data.getTodoList());
  };

  const handleEditTodo = (id) => {
    const todo = todoList.find(todo => todo.id === id);
    setIsEditing(true);
    setEditingTodo(todo);
    setVisible(true);
  };

  const handleDeleteTodo = (id) => {
    Modal.confirm({
      title: '确认删除',
      content: '确定要删除该待办事项吗?',
      onOk: () => {
        data.deleteTodo(id);
        setTodoList(data.getTodoList());
      }
    });
  };

  const columns = [
    {
      title: '创建时间',
      dataIndex: 'createTime',
      key: 'createTime',
      render: (text) => text.toLocaleString()
    },
    {
      title: '具体事项',
      dataIndex: 'task',
      key: 'task'
    },
    {
      title: '是否完成',
      dataIndex: 'isCompleted',
      key: 'isCompleted',
      render: (text) => text? '已完成' : '未完成'
    },
    {
      title: '截止时间',
      dataIndex: 'dueTime',
      key: 'dueTime',
      render: (text, record) => record.isCompleted? '' : text.toLocaleString()
    },
    {
      title: '完成时间',
      dataIndex: 'completeTime',
      key: 'completeTime',
      render: (text) => text? text.toLocaleString() : ''
    },
    {
      title: '操作',
      key: 'action',
      render: (text, record) => (
        <span>
          {!record.isCompleted && <Button type="link" onClick={() => handleEditTodo(record.id)}>编辑</Button>}
          <Button type="link" onClick={() => handleDeleteTodo(record.id)}>删除</Button>
        </span>
      )
    }
  ];

  return (
    <div>
      <Button type="primary" onClick={handleAddTodo}>添加待办事项</Button>
      <Table dataSource={todoList} columns={columns} />
      <Modal
        title={isEditing? '编辑待办事项' : '添加待办事项'}
        visible={visible}
        onOk={handleSaveTodo}
        onCancel={() => {
          setVisible(false);
          setIsEditing(false);
          setEditingTodo({});
        }}
      >
        <Input
          placeholder="请输入具体事项"
          value={editingTodo.task}
          onChange={(e) => setEditingTodo({...editingTodo, task: e.target.value })}
        />
        <DatePicker
          placeholder="请选择截止时间"
          value={editingTodo.dueTime}
          onChange={(date) => setEditingTodo({...editingTodo, dueTime: date })}
        />
      </Modal>
    </div>
  );
};

export default TodoList;

下面来详细讲解一下这个组件的代码:

  • 首先导入了必要的 React 钩子函数 useState 和 useEffect,以及从 todoData.js 文件中导入了操作待办事项数据的函数,还从 antd 库中导入了需要用到的组件,如 TableButtonModalInputDatePicker 等。

  • 在组件内部,使用 useState 钩子函数定义了几个状态:

    • todoList:用于存储从 todoData.js 获取到的待办事项列表,初始值为空数组,通过 useEffect 钩子在组件挂载时获取并设置初始值。
    • isEditing:用于标识当前是否处于编辑某个待办事项的状态,初始值为 false
    • editingTodo:用于存储正在编辑的待办事项对象,初始值为空对象。
    • visible:用于控制添加 / 编辑待办事项的模态框是否可见,初始值为 false
  • useEffect 钩子函数:在组件挂载时,调用 getTodoList 函数获取待办事项列表,并将其设置为 todoList 状态的值,这样组件一开始就能展示已有的待办事项。

  • handleAddTodo 函数:当点击 “添加待办事项” 按钮时,设置 visible 状态为 true,从而显示添加待办事项的模态框。

  • handleSaveTodo 函数:在添加或编辑待办事项后,点击模态框的 “确定” 按钮时调用。它首先根据当前 editingTodo 状态构建一个新的待办事项对象(如果是编辑,这里的 editingTodo 已经包含了更新后的信息),然后调用 addTodo 函数添加新事项(如果是编辑则是更新操作),接着隐藏模态框,重置编辑相关的状态,并重新获取最新的待办事项列表设置给 todoList 状态。

  • handleEditTodo 函数:当点击某个待办事项的 “编辑” 按钮时,根据传入的 id 找到对应的待办事项,设置 isEditingeditingTodo 和 visible 状态,从而进入编辑模式并显示模态框。

  • handleDeleteTodo 函数:当点击某个待办事项的 “删除” 按钮时,弹出一个确认对话框,在用户确认后,调用 deleteTodo 函数删除对应的待办事项,并重新获取最新的待办事项列表设置给 todoList 状态。

  • columns 数组:定义了用于展示待办事项列表的表格列信息。每一列对应一个待办事项的属性,通过 render 函数可以对属性值进行格式化展示,比如将日期格式化为本地字符串格式,根据 isCompleted 状态显示不同的文本等。

3. 在入口文件中使用待办事项列表组件

最后,在项目的入口文件(通常是 src/index.js)中引入并使用 TodoList.js 组件。

// index.js
import React from 'react';
import ReactDOM from 'react-dom';
import TodoList from './TodoList.js';
import 'antd/dist/antd.css';

ReactDOM.render(
  <TodoList />,
  document.getElementById('root')
);

这里首先导入了 ReactReactDOMTodoList 组件以及 antd 的样式文件。然后使用 ReactDOM.render 函数将 TodoList 组件渲染到 id 为 root 的 DOM 元素上,这样我们的待办事项列表应用就可以在浏览器中正常展示并使用了。

image.png

通过以上步骤,我们就成功地使用 React、umi 框架和 antd 库构建了一个简单的待办事项列表应用,用户可以方便地添加、编辑和删除待办事项,并能清晰地查看各项相关信息。当然,这只是一个基础的实现,你还可以根据自己的需求进一步扩展和优化这个应用,比如添加更多的筛选功能、设置提醒功能等等。