从零打造个人任务管理系统:一个终端里的效率利器
折
折腾侠
2026/03/28 发布
17约 10 分钟1369 字 / 1297 词00
从零打造个人任务管理系统:一个终端里的效率利器
项目概述
在这个信息爆炸的时代,我们每天都要面对大量的任务和待办事项。虽然市面上有众多任务管理应用,但很多时候我们只需要一个轻量级、快速响应的工具来帮助自己记录和管理日常任务。本项目将带你从零开始,使用 Python 打造一个运行在终端里的个人任务管理系统(Task Manager CLI)。
这个系统不仅能够帮助你理解命令行应用开发的核心概念,还能让你掌握数据存储、用户交互、状态管理等关键技能。完成这个项目后,你将拥有一个真正可用的效率工具,同时积累宝贵的实战经验。
项目功能说明
核心功能
- 任务创建:支持快速添加新任务,可设置优先级和截止日期
- 任务列表:查看所有任务,支持按状态、优先级筛选
- 任务更新:修改任务内容、优先级、状态等信息
- 任务完成:标记任务为已完成状态
- 任务删除:移除不需要的任务
- 数据统计:展示任务完成情况和统计信息
扩展功能
- 任务分类:支持为任务添加标签进行分类
- 搜索功能:根据关键词快速查找任务
- 数据导出:将任务数据导出为 JSON 或 CSV 格式
- 自动备份:定期备份任务数据防止丢失
技术栈选择
编程语言:Python 3.9+
选择 Python 的原因:
- 语法简洁,易于上手
- 丰富的标准库和第三方库支持
- 跨平台兼容性好
- 适合快速原型开发
核心依赖
# requirements.txt
click==8.1.7 # 命令行界面框架
rich==13.7.0 # 终端美化输出
pydantic==2.5.0 # 数据验证和序列化
python-dateutil==2.8.2 # 日期处理
项目结构
task-manager-cli/
├── README.md # 项目说明文档
├── requirements.txt # 依赖列表
├── setup.py # 安装配置
├── taskmanager/
│ ├── __init__.py # 包初始化
│ ├── cli.py # 命令行入口
│ ├── models.py # 数据模型定义
│ ├── storage.py # 数据存储模块
│ ├── commands/ # 命令模块
│ │ ├── __init__.py
│ │ ├── add.py # 添加任务命令
│ │ ├── list.py # 列表命令
│ │ ├── update.py # 更新命令
│ │ ├── complete.py # 完成命令
│ │ └── stats.py # 统计命令
│ └── utils/
│ ├── __init__.py
│ ├── formatters.py # 格式化输出
│ └── validators.py # 输入验证
└── tests/
├── __init__.py
├── test_models.py
└── test_storage.py
核心代码实现
1. 数据模型定义(models.py)
Python
from dataclasses import dataclass, field
from datetime import datetime
from enum import Enum
from typing import Optional, List
import uuid
class Priority(Enum):
LOW = "low"
MEDIUM = "medium"
HIGH = "high"
URGENT = "urgent"
class TaskStatus(Enum):
PENDING = "pending"
IN_PROGRESS = "in_progress"
COMPLETED = "completed"
CANCELLED = "cancelled"
@dataclass
class Task:
"""任务数据模型"""
title: str
description: str = ""
priority: Priority = Priority.MEDIUM
status: TaskStatus = TaskStatus.PENDING
tags: List[str] = field(default_factory=list)
due_date: Optional[datetime] = None
created_at: datetime = field(default_factory=datetime.now)
updated_at: datetime = field(default_factory=datetime.now)
id: str = field(default_factory=lambda: str(uuid.uuid4())[:8])
def to_dict(self) -> dict:
"""转换为字典格式"""
return {
"id": self.id,
"title": self.title,
"description": self.description,
"priority": self.priority.value,
"status": self.status.value,
"tags": self.tags,
"due_date": self.due_date.isoformat() if self.due_date else None,
"created_at": self.created_at.isoformat(),
"updated_at": self.updated_at.isoformat(),
}
@classmethod
def from_dict(cls, data: dict) -> "Task":
"""从字典创建任务"""
return cls(
id=data["id"],
title=data["title"],
description=data.get("description", ""),
priority=Priority(data.get("priority", "medium")),
status=TaskStatus(data.get("status", "pending")),
tags=data.get("tags", []),
due_date=datetime.fromisoformat(data["due_date"]) if data.get("due_date") else None,
created_at=datetime.fromisoformat(data["created_at"]),
updated_at=datetime.fromisoformat(data["updated_at"]),
)
2. 数据存储模块(storage.py)
Python
import json
import os
from pathlib import Path
from typing import List, Optional
from datetime import datetime
from .models import Task
class TaskStorage:
"""任务数据存储管理类"""
def __init__(self, data_dir: Optional[str] = None):
if data_dir is None:
data_dir = Path.home() / ".taskmanager"
self.data_dir = Path(data_dir)
self.data_file = self.data_dir / "tasks.json"
self._ensure_data_dir()
self._tasks: List[Task] = []
self._load()
def _ensure_data_dir(self):
"""确保数据目录存在"""
self.data_dir.mkdir(parents=True, exist_ok=True)
def _load(self):
"""从文件加载任务数据"""
if self.data_file.exists():
with open(self.data_file, "r", encoding="utf-8") as f:
data = json.load(f)
self._tasks = [Task.from_dict(t) for t in data.get("tasks", [])]
def _save(self):
"""保存任务数据到文件"""
data = {
"tasks": [t.to_dict() for t in self._tasks],
"last_updated": datetime.now().isoformat(),
}
with open(self.data_file, "w", encoding="utf-8") as f:
json.dump(data, f, indent=2, ensure_ascii=False)
def add(self, task: Task) -> Task:
"""添加新任务"""
self._tasks.append(task)
self._save()
return task
def get_all(self) -> List[Task]:
"""获取所有任务"""
return self._tasks.copy()
def get_by_id(self, task_id: str) -> Optional[Task]:
"""根据 ID 获取任务"""
for task in self._tasks:
if task.id == task_id:
return task
return None
def update(self, task: Task) -> bool:
"""更新任务"""
for i, t in enumerate(self._tasks):
if t.id == task.id:
task.updated_at = datetime.now()
self._tasks[i] = task
self._save()
return True
return False
def delete(self, task_id: str) -> bool:
"""删除任务"""
for i, task in enumerate(self._tasks):
if task.id == task_id:
self._tasks.pop(i)
self._save()
return True
return False
def backup(self, backup_dir: Optional[str] = None) -> str:
"""创建数据备份"""
if backup_dir is None:
backup_dir = self.data_dir / "backups"
backup_path = Path(backup_dir)
backup_path.mkdir(parents=True, exist_ok=True)
timestamp = datetime.now().strftime("%Y%m%d_%H%M%S")
backup_file = backup_path / f"tasks_backup_{timestamp}.json"
with open(backup_file, "w", encoding="utf-8") as f:
json.dump(
{"tasks": [t.to_dict() for t in self._tasks]},
f,
indent=2,
ensure_ascii=False,
)
return str(backup_file)
3. 命令行入口(cli.py)
Python
import click
from rich.console import Console
from rich.table import Table
from rich.panel import Panel
from .models import Task, Priority, TaskStatus
from .storage import TaskStorage
from .utils.formatters import format_priority, format_status, format_due_date
console = Console()
storage = TaskStorage()
@click.group()
@click.version_option(version="1.0.0")
def cli():
"""📋 Task Manager CLI - 个人任务管理系统
一个轻量级的终端任务管理工具,帮助你高效管理日常任务。
"""
pass
@cli.command()
@click.argument("title")
@click.option("--description", "-d", default="", help="任务描述")
@click.option("--priority", "-p", type=click.Choice(["low", "medium", "high", "urgent"]),
default="medium", help="任务优先级")
@click.option("--due", help="截止日期 (格式:YYYY-MM-DD)")
@click.option("--tags", "-t", multiple=True, help="任务标签")
def add(title, description, priority, due, tags):
"""添加新任务"""
from datetime import datetime
due_date = None
if due:
try:
due_date = datetime.strptime(due, "%Y-%m-%d")
except ValueError:
console.print("[red]❌ 日期格式错误,请使用 YYYY-MM-DD 格式[/red]")
return
task = Task(
title=title,
description=description,
priority=Priority(priority),
due_date=due_date,
tags=list(tags),
)
storage.add(task)
console.print(f"[green]✅ 任务已添加:{task.id} - {task.title}[/green]")
@cli.command()
@click.option("--status", "-s", type=click.Choice(["pending", "in_progress", "completed", "cancelled"]),
help="按状态筛选")
@click.option("--priority", "-p", type=click.Choice(["low", "medium", "high", "urgent"]),
help="按优先级筛选")
@click.option("--tag", "-t", help="按标签筛选")
def list(status, priority, tag):
"""查看任务列表"""
tasks = storage.get_all()
# 筛选
if status:
tasks = [t for t in tasks if t.status.value == status]
if priority:
tasks = [t for t in tasks if t.priority.value == priority]
if tag:
tasks = [t for t in tasks if tag in t.tags]
if not tasks:
console.print("[yellow]📭 暂无任务[/yellow]")
return
# 创建表格
table = Table(title="📋 任务列表")
table.add_column("ID", style="cyan")
table.add_column("标题", style="bold")
table.add_column("优先级", justify="center")
table.add_column("状态", justify="center")
table.add_column("截止日期", justify="center")
table.add_column("标签")
for task in tasks:
table.add_row(
task.id,
task.title[:30] + "..." if len(task.title) > 30 else task.title,
format_priority(task.priority),
format_status(task.status),
format_due_date(task.due_date),
", ".join(task.tags) if task.tags else "-",
)
console.print(table)
@cli.command()
@click.argument("task_id")
def complete(task_id):
"""标记任务为已完成"""
task = storage.get_by_id(task_id)
if not task:
console.print(f"[red]❌ 未找到任务:{task_id}[/red]")
return
task.status = TaskStatus.COMPLETED
storage.update(task)
console.print(f"[green]✅ 任务已完成:{task.title}[/green]")
@cli.command()
@click.argument("task_id")
@click.option("--title", "-t", help="新标题")
@click.option("--description", "-d", help="新描述")
@click.option("--priority", "-p", type=click.Choice(["low", "medium", "high", "urgent"]),
help="新优先级")
def update(task_id, title, description, priority):
"""更新任务信息"""
task = storage.get_by_id(task_id)
if not task:
console.print(f"[red]❌ 未找到任务:{task_id}[/red]")
return
if title:
task.title = title
if description:
task.description = description
if priority:
task.priority = Priority(priority)
storage.update(task)
console.print(f"[green]✅ 任务已更新:{task.title}[/green]")
@cli.command()
@click.argument("task_id")
@click.confirmation_option(prompt="确定要删除这个任务吗?")
def delete(task_id):
"""删除任务"""
if storage.delete(task_id):
console.print(f"[green]✅ 任务已删除:{task_id}[/green]")
else:
console.print(f"[red]❌ 未找到任务:{task_id}[/red]")
@cli.command()
def stats():
"""查看任务统计信息"""
tasks = storage.get_all()
total = len(tasks)
completed = len([t for t in tasks if t.status == TaskStatus.COMPLETED])
pending = len([t for t in tasks if t.status == TaskStatus.PENDING])
in_progress = len([t for t in tasks if t.status == TaskStatus.IN_PROGRESS])
# 按优先级统计
urgent = len([t for t in tasks if t.priority == Priority.URGENT])
high = len([t for t in tasks if t.priority == Priority.HIGH])
panel = Panel(
f"""[bold]📊 任务统计[/bold]
总任务数:[cyan]{total}[/cyan]
已完成:[green]{completed}[/green]
进行中:[yellow]{in_progress}[/yellow]
待处理:[blue]{pending}[/blue]
紧急任务:[red]{urgent}[/red]
高优先级:[orange]{high}[/orange]
完成率:[green]{completed/total*100:.1f}%[/green] (如有任务)
""",
title="📈 统计面板",
)
console.print(panel)
if __name__ == "__main__":
cli()
4. 格式化工具(utils/formatters.py)
Python
from rich.text import Text
from datetime import datetime
from typing import Optional
from ..models import Priority, TaskStatus
def format_priority(priority: Priority) -> Text:
"""格式化优先级显示"""
icons = {
Priority.LOW: "⚪",
Priority.MEDIUM: "🔵",
Priority.HIGH: "🟠",
Priority.URGENT: "🔴",
}
return Text(f"{icons[priority]} {priority.value.upper()}")
def format_status(status: TaskStatus) -> Text:
"""格式化状态显示"""
icons = {
TaskStatus.PENDING: "⏳",
TaskStatus.IN_PROGRESS: "🔄",
TaskStatus.COMPLETED: "✅",
TaskStatus.CANCELLED: "❌",
}
status_text = {
TaskStatus.PENDING: "待处理",
TaskStatus.IN_PROGRESS: "进行中",
TaskStatus.COMPLETED: "已完成",
TaskStatus.CANCELLED: "已取消",
}
return Text(f"{icons[status]} {status_text[status]}")
def format_due_date(due_date: Optional[datetime]) -> str:
"""格式化截止日期显示"""
if not due_date:
return "-"
today = datetime.now()
days_left = (due_date - today).days
if days_left < 0:
return f"[red]过期 {abs(days_left)} 天[/red]"
elif days_left == 0:
return "[red]今天[/red]"
elif days_left == 1:
return "[orange]明天[/orange]"
elif days_left <= 7:
return f"[yellow]{days_left} 天[/yellow]"
else:
return due_date.strftime("%Y-%m-%d")
运行步骤
1. 环境准备
确保已安装 Python 3.9 或更高版本:
Bash
python --version
2. 克隆项目
Bash
git clone https://github.com/yourusername/task-manager-cli.git
cd task-manager-cli
3. 创建虚拟环境
Bash
# macOS/Linux
python -m venv venv
source venv/bin/activate
# Windows
python -m venv venv
venv\Scripts\activate
4. 安装依赖
Bash
pip install -r requirements.txt
5. 安装为命令行工具
Bash
pip install -e .
6. 开始使用
Bash
# 查看帮助
task --help
# 添加任务
task add "完成项目报告" -d "撰写 Q4 项目总结报告" -p high --due 2026-03-30 -t 工作 -t 报告
# 查看任务列表
task list
# 按状态筛选
task list -s pending
# 标记任务完成
task complete abc123
# 更新任务
task update abc123 -p urgent
# 查看统计
task stats
# 删除任务
task delete abc123
7. 数据位置
任务数据默认存储在用户主目录下:
- macOS/Linux: INLINE_CODE_0
- Windows: INLINE_CODE_1
备份文件存储在:INLINE_CODE_2
项目扩展建议
完成基础版本后,你可以考虑以下扩展方向:
- 任务依赖:支持设置任务之间的依赖关系
- 重复任务:支持每日/每周/每月重复的任务
- 时间追踪:记录每个任务的实际耗时
- 云同步:使用 Git 或云存储同步任务数据
- GUI 界面:使用 tkinter 或 PyQt 添加图形界面
- 移动端:使用 Kivy 或 BeeWare 开发移动应用
- API 服务:将核心功能封装为 REST API
- 插件系统:支持第三方插件扩展功能
总结
通过这个项目,你不仅获得了一个实用的个人任务管理工具,还掌握了以下核心技能:
- Python 命令行应用开发
- 数据模型设计与序列化
- 文件存储与数据持久化
- 用户交互与输出美化
- 项目结构与代码组织
这个项目可以作为你学习 Python 开发的起点,也可以作为实际使用的效率工具。更重要的是,它展示了如何将一个想法转化为可运行的软件产品。
现在就开始动手吧,打造属于你自己的效率利器!