Tailwind CSSTailwind CSS
Home
  • Tailwind CSS 书籍目录
  • Vue 3 开发实战指南
  • React 和 Next.js 学习
  • TypeScript
  • React开发框架书籍大纲
  • Shadcn学习大纲
  • Swift 编程语言:从入门到进阶
  • SwiftUI 学习指南
  • 函数式编程大纲
  • Swift 异步编程语言
  • Swift 协议化编程
  • SwiftUI MVVM 开发模式
  • SwiftUI 图表开发书籍
  • SwiftData
  • ArkTS编程语言:从入门到精通
  • 仓颉编程语言:从入门到精通
  • 鸿蒙手机客户端开发实战
  • WPF书籍
  • C#开发书籍
learn
  • Java编程语言
  • Kotlin 编程入门与实战
  • /python/outline.html
  • AI Agent
  • MCP (Model Context Protocol) 应用指南
  • 深度学习
  • 深度学习
  • 强化学习: 理论与实践
  • 扩散模型书籍
  • Agentic AI for Everyone
langchain
Home
  • Tailwind CSS 书籍目录
  • Vue 3 开发实战指南
  • React 和 Next.js 学习
  • TypeScript
  • React开发框架书籍大纲
  • Shadcn学习大纲
  • Swift 编程语言:从入门到进阶
  • SwiftUI 学习指南
  • 函数式编程大纲
  • Swift 异步编程语言
  • Swift 协议化编程
  • SwiftUI MVVM 开发模式
  • SwiftUI 图表开发书籍
  • SwiftData
  • ArkTS编程语言:从入门到精通
  • 仓颉编程语言:从入门到精通
  • 鸿蒙手机客户端开发实战
  • WPF书籍
  • C#开发书籍
learn
  • Java编程语言
  • Kotlin 编程入门与实战
  • /python/outline.html
  • AI Agent
  • MCP (Model Context Protocol) 应用指南
  • 深度学习
  • 深度学习
  • 强化学习: 理论与实践
  • 扩散模型书籍
  • Agentic AI for Everyone
langchain
  • 编码实现:任务列表与状态管理

编码实现:任务列表与状态管理

在明确了 TaskMaster 的需求和技术选型后,本节将进入编码实现阶段,重点完成任务列表的展示和状态管理功能。我们将使用 Vue 3 的 Composition API、Pinia 进行状态管理,并结合 Element Plus 的组件库实现用户界面。通过逐步构建任务列表视图和核心逻辑,帮助你掌握 Vue 3 在实际项目中的应用。

目标与功能

实现目标

  • 任务列表展示:显示所有任务,支持按状态过滤。
  • 状态管理:添加、编辑任务及其状态,使用 Pinia 存储数据。
  • 数据持久化:将任务保存到 LocalStorage。

功能分解

  1. 任务数据模型:定义任务结构。
  2. Pinia Store:管理任务状态和操作。
  3. 任务列表组件:展示任务并支持交互。

编码实现

1. 定义任务数据模型

任务将包含以下字段:

  • id:唯一标识。
  • title:任务标题。
  • description:任务描述。
  • status:任务状态(todo、in-progress、done)。
  • dueDate:截止日期。

2. 配置 Pinia Store

stores/task.ts

// src/stores/task.ts
import { defineStore } from 'pinia';
import { ref } from 'vue';

interface Task {
  id: number;
  title: string;
  description: string;
  status: 'todo' | 'in-progress' | 'done';
  dueDate: string; // ISO 日期格式,如 '2025-03-01'
}

export const useTaskStore = defineStore('task', () => {
  const tasks = ref<Task[]>([]);

  // 从 LocalStorage 加载数据
  const loadTasks = () => {
    const savedTasks = localStorage.getItem('tasks');
    if (savedTasks) {
      tasks.value = JSON.parse(savedTasks);
    }
  };

  // 保存到 LocalStorage
  const saveTasks = () => {
    localStorage.setItem('tasks', JSON.stringify(tasks.value));
  };

  // 添加任务
  const addTask = (task: Omit<Task, 'id'>) => {
    tasks.value.push({
      id: Date.now(),
      ...task
    });
    saveTasks();
  };

  // 更新任务
  const updateTask = (id: number, updates: Partial<Task>) => {
    const index = tasks.value.findIndex(t => t.id === id);
    if (index !== -1) {
      tasks.value[index] = { ...tasks.value[index], ...updates };
      saveTasks();
    }
  };

  // 删除任务
  const deleteTask = (id: number) => {
    tasks.value = tasks.value.filter(t => t.id !== id);
    saveTasks();
  };

  // 初始化加载
  loadTasks();

  return { tasks, addTask, updateTask, deleteTask };
});

3. 创建任务列表组件

components/TaskList.vue

<template>
  <div>
    <el-input
      v-model="newTaskTitle"
      placeholder="输入任务标题"
      @keyup.enter="addNewTask"
      style="margin-bottom: 20px;"
    />
    <el-table :data="filteredTasks" style="width: 100%;">
      <el-table-column prop="title" label="任务标题" />
      <el-table-column prop="description" label="描述" />
      <el-table-column prop="dueDate" label="截止日期" />
      <el-table-column label="状态" width="150">
        <template #default="{ row }">
          <el-select v-model="row.status" @change="updateTaskStatus(row)">
            <el-option label="待办" value="todo" />
            <el-option label="进行中" value="in-progress" />
            <el-option label="已完成" value="done" />
          </el-select>
        </template>
      </el-table-column>
      <el-table-column label="操作" width="100">
        <template #default="{ row }">
          <el-button type="danger" size="small" @click="deleteTask(row.id)">删除</el-button>
        </template>
      </el-table-column>
    </el-table>
    <el-select v-model="filterStatus" placeholder="筛选状态" style="margin-top: 20px;">
      <el-option label="全部" value="all" />
      <el-option label="待办" value="todo" />
      <el-option label="进行中" value="in-progress" />
      <el-option label="已完成" value="done" />
    </el-select>
  </div>
</template>

<script lang="ts">
import { defineComponent, ref, computed } from 'vue';
import { useTaskStore } from '@/stores/task';
import { ElMessage } from 'element-plus';

export default defineComponent({
  setup() {
    const taskStore = useTaskStore();
    const newTaskTitle = ref('');
    const filterStatus = ref('all');

    const filteredTasks = computed(() => {
      if (filterStatus.value === 'all') return taskStore.tasks;
      return taskStore.tasks.filter(task => task.status === filterStatus.value);
    });

    const addNewTask = () => {
      if (newTaskTitle.value.trim()) {
        taskStore.addTask({
          title: newTaskTitle.value,
          description: '',
          status: 'todo',
          dueDate: new Date().toISOString().split('T')[0] // 默认今天
        });
        ElMessage.success('任务添加成功');
        newTaskTitle.value = '';
      }
    };

    const updateTaskStatus = (task: { id: number; status: string }) => {
      taskStore.updateTask(task.id, { status: task.status });
      ElMessage.info('任务状态已更新');
    };

    const deleteTask = (id: number) => {
      taskStore.deleteTask(id);
      ElMessage.success('任务已删除');
    };

    return {
      newTaskTitle,
      filterStatus,
      filteredTasks,
      addNewTask,
      updateTaskStatus,
      deleteTask
    };
  }
});
</script>

<style scoped>
.el-table {
  margin-bottom: 20px;
}
</style>

4. 集成到主应用

App.vue

<template>
  <div class="app-container">
    <h1>TaskMaster</h1>
    <TaskList />
  </div>
</template>

<script lang="ts">
import { defineComponent } from 'vue';
import TaskList from './components/TaskList.vue';

export default defineComponent({
  components: { TaskList },
  setup() {}
});
</script>

<style scoped>
.app-container {
  max-width: 1200px;
  margin: 0 auto;
  padding: 20px;
}
</style>

main.ts

import { createApp } from 'vue';
import { createPinia } from 'pinia';
import ElementPlus from 'element-plus';
import 'element-plus/dist/index.css';
import App from './App.vue';

const app = createApp(App);
app.use(createPinia());
app.use(ElementPlus);
app.mount('#app');

实现效果

  • 任务添加:输入标题,按回车创建新任务,默认状态为“待办”。
  • 状态切换:通过下拉框更改任务状态,实时更新并保存。
  • 列表过滤:选择状态筛选任务,动态刷新表格。
  • 数据持久化:刷新页面后任务数据保留。

代码分析

Pinia Store

  • 状态管理:tasks 使用 ref 存储任务数组,支持响应式更新。
  • 持久化:loadTasks 和 saveTasks 实现 LocalStorage 读写。
  • 操作方法:addTask、updateTask 和 deleteTask 确保数据一致性。

TaskList 组件

  • UI 组件:使用 el-table 和 el-select 实现列表和筛选。
  • 响应式计算:filteredTasks 通过 computed 动态过滤。
  • 用户反馈:ElMessage 提供操作提示。

测试验证

手动测试

  1. 添加任务,检查列表更新和 LocalStorage。
  2. 更改状态,验证筛选功能。
  3. 删除任务,确认数据移除。

单元测试(可选)

// tests/TaskList.test.ts
import { describe, it, expect } from 'vitest';
import { mount } from '@vue/test-utils';
import TaskList from '@/components/TaskList.vue';
import { createPinia, setActivePinia } from 'pinia';

describe('TaskList', () => {
  it('adds a new task', async () => {
    setActivePinia(createPinia());
    const wrapper = mount(TaskList);
    const input = wrapper.find('input');
    await input.setValue('New Task');
    await input.trigger('keyup.enter');
    expect(wrapper.text()).toContain('New Task');
  });
});

注意事项

  1. 数据校验:
    • 当前仅检查标题非空,后续可添加更多验证。
  2. 性能:
    • 大量任务可能需优化(如虚拟滚动)。
  3. 错误处理:
    • LocalStorage 存储失败时需提示用户。

总结

本节实现了 TaskMaster 的任务列表和状态管理功能,利用 Vue 3、Pinia 和 Element Plus 完成了核心需求。通过响应式数据和组件化设计,你已掌握了基本的编码实践。下一节将扩展功能,添加任务编辑和排序,完善应用体验!

Last Updated:: 2/24/25, 3:35 PM