本文将为您展示如何使用 Node.js 和 HTML5 开发一个完整的任务管理应用,包含前端界面和后端 API。

1. 项目概述

我们将构建一个功能完整的任务管理应用,包含以下特性:

  • 添加、编辑、删除任务
  • 标记任务完成状态
  • 任务分类和过滤
  • 数据持久化存储
  • 响应式 HTML5 界面

2. 环境准备和项目初始化

创建项目结构

# 创建项目目录
mkdir task-manager-app
cd task-manager-app

# 创建目录结构
mkdir -p public/css public/js public/images views routes models config

# 初始化项目
npm init -y

安装依赖包

# 后端依赖
npm install express mongoose cors dotenv

# 开发依赖
npm install --save-dev nodemon concurrently

# 前端依赖(可选,用于构建)
npm install --save-dev webpack webpack-cli css-loader style-loader

3. 项目文件结构

task-manager-app/
├── public/           # 静态文件
│   ├── css/
│   │   └── style.css
│   ├── js/
│   │   └── app.js
│   └── index.html
├── routes/
│   └── tasks.js
├── models/
│   └── Task.js
├── config/
│   └── database.js
├── .env
├── server.js
└── package.json

4. 后端开发

创建 Express 服务器 (server.js)

const express = require('express');
const mongoose = require('mongoose');
const cors = require('cors');
const path = require('path');
require('dotenv').config();

const app = express();
const PORT = process.env.PORT || 3000;

// 中间件
app.use(cors());
app.use(express.json());
app.use(express.static('public'));

// 数据库连接
mongoose.connect(process.env.MONGODB_URI || 'mongodb://localhost:27017/taskmanager', {
    useNewUrlParser: true,
    useUnifiedTopology: true
});

// 路由
app.use('/api/tasks', require('./routes/tasks'));

// 提供前端页面
app.get('/', (req, res) => {
    res.sendFile(path.join(__dirname, 'public', 'index.html'));
});

// 启动服务器
app.listen(PORT, () => {
    console.log(`服务器运行在 http://localhost:${PORT}`);
});

数据库配置 (.env)

MONGODB_URI=mongodb://localhost:27017/taskmanager
PORT=3000
NODE_ENV=development

数据模型 (models/Task.js)

const mongoose = require('mongoose');

const taskSchema = new mongoose.Schema({
    title: {
        type: String,
        required: true,
        trim: true
    },
    description: {
        type: String,
        trim: true
    },
    completed: {
        type: Boolean,
        default: false
    },
    priority: {
        type: String,
        enum: ['low', 'medium', 'high'],
        default: 'medium'
    },
    dueDate: {
        type: Date
    },
    category: {
        type: String,
        default: 'general'
    }
}, {
    timestamps: true
});

module.exports = mongoose.model('Task', taskSchema);

API 路由 (routes/tasks.js)

const express = require('express');
const router = express.Router();
const Task = require('../models/Task');

// 获取所有任务
router.get('/', async (req, res) => {
    try {
        const tasks = await Task.find().sort({ createdAt: -1 });
        res.json(tasks);
    } catch (error) {
        res.status(500).json({ message: error.message });
    }
});

// 创建新任务
router.post('/', async (req, res) => {
    try {
        const task = new Task(req.body);
        const savedTask = await task.save();
        res.status(201).json(savedTask);
    } catch (error) {
        res.status(400).json({ message: error.message });
    }
});

// 更新任务
router.put('/:id', async (req, res) => {
    try {
        const task = await Task.findByIdAndUpdate(
            req.params.id,
            req.body,
            { new: true, runValidators: true }
        );
        if (!task) {
            return res.status(404).json({ message: '任务未找到' });
        }
        res.json(task);
    } catch (error) {
        res.status(400).json({ message: error.message });
    }
});

// 删除任务
router.delete('/:id', async (req, res) => {
    try {
        const task = await Task.findByIdAndDelete(req.params.id);
        if (!task) {
            return res.status(404).json({ message: '任务未找到' });
        }
        res.json({ message: '任务已删除' });
    } catch (error) {
        res.status(500).json({ message: error.message });
    }
});

module.exports = router;

5. 前端开发

HTML5 页面 (public/index.html)

<!DOCTYPE html>
<html lang="zh-CN">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>任务管理器 - Node.js + HTML5 应用</title>
    <link rel="stylesheet" href="css/style.css">
    <link href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.0.0/css/all.min.css" rel="stylesheet">
</head>
<body>
    <div class="container">
        <header class="app-header">
            <h1><i class="fas fa-tasks"></i> 智能任务管理器</h1>
            <p>使用 Node.js 和 HTML5 构建的现代化应用</p>
        </header>

        <main class="app-main">
            <!-- 任务输入表单 -->
            <section class="task-form-section">
                <form id="taskForm" class="task-form">
                    <div class="form-group">
                        <input type="text" id="taskTitle" placeholder="输入任务标题..." required>
                    </div>
                    <div class="form-group">
                        <textarea id="taskDescription" placeholder="任务描述(可选)..."></textarea>
                    </div>
                    <div class="form-row">
                        <div class="form-group">
                            <select id="taskPriority">
                                <option value="low">低优先级</option>
                                <option value="medium" selected>中优先级</option>
                                <option value="high">高优先级</option>
                            </select>
                        </div>
                        <div class="form-group">
                            <input type="date" id="taskDueDate">
                        </div>
                        <div class="form-group">
                            <input type="text" id="taskCategory" placeholder="分类">
                        </div>
                    </div>
                    <button type="submit" class="btn btn-primary">
                        <i class="fas fa-plus"></i> 添加任务
                    </button>
                </form>
            </section>

            <!-- 任务过滤 -->
            <section class="task-filters">
                <div class="filter-buttons">
                    <button class="filter-btn active" data-filter="all">全部任务</button>
                    <button class="filter-btn" data-filter="pending">待完成</button>
                    <button class="filter-btn" data-filter="completed">已完成</button>
                </div>
                <div class="search-box">
                    <input type="text" id="searchInput" placeholder="搜索任务...">
                    <i class="fas fa-search"></i>
                </div>
            </section>

            <!-- 任务统计 -->
            <section class="task-stats">
                <div class="stat-card">
                    <h3 id="totalTasks">0</h3>
                    <p>总任务</p>
                </div>
                <div class="stat-card">
                    <h3 id="pendingTasks">0</h3>
                    <p>待完成</p>
                </div>
                <div class="stat-card">
                    <h3 id="completedTasks">0</h3>
                    <p>已完成</p>
                </div>
            </section>

            <!-- 任务列表 -->
            <section class="task-list-section">
                <h2>任务列表</h2>
                <div id="taskList" class="task-list">
                    <!-- 任务将通过 JavaScript 动态加载 -->
                </div>
                <div id="emptyState" class="empty-state">
                    <i class="fas fa-clipboard-list"></i>
                    <h3>还没有任务</h3>
                    <p>添加第一个任务开始管理您的工作</p>
                </div>
            </section>
        </main>
    </div>

    <!-- 编辑任务模态框 -->
    <div id="editModal" class="modal">
        <div class="modal-content">
            <span class="close">&times;</span>
            <h2>编辑任务</h2>
            <form id="editTaskForm">
                <input type="hidden" id="editTaskId">
                <div class="form-group">
                    <input type="text" id="editTaskTitle" required>
                </div>
                <div class="form-group">
                    <textarea id="editTaskDescription"></textarea>
                </div>
                <div class="form-row">
                    <div class="form-group">
                        <select id="editTaskPriority">
                            <option value="low">低优先级</option>
                            <option value="medium">中优先级</option>
                            <option value="high">高优先级</option>
                        </select>
                    </div>
                    <div class="form-group">
                        <input type="date" id="editTaskDueDate">
                    </div>
                    <div class="form-group">
                        <input type="text" id="editTaskCategory" placeholder="分类">
                    </div>
                </div>
                <div class="modal-actions">
                    <button type="submit" class="btn btn-primary">保存更改</button>
                    <button type="button" class="btn btn-secondary" id="cancelEdit">取消</button>
                </div>
            </form>
        </div>
    </div>

    <script src="js/app.js"></script>
</body>
</html>

CSS 样式 (public/css/style.css)

/* 基础样式重置 */
* {
    margin: 0;
    padding: 0;
    box-sizing: border-box;
}

body {
    font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
    line-height: 1.6;
    color: #333;
    background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
    min-height: 100vh;
}

.container {
    max-width: 1200px;
    margin: 0 auto;
    padding: 20px;
}

/* 头部样式 */
.app-header {
    text-align: center;
    color: white;
    margin-bottom: 2rem;
}

.app-header h1 {
    font-size: 2.5rem;
    margin-bottom: 0.5rem;
    font-weight: 300;
}

.app-header p {
    font-size: 1.1rem;
    opacity: 0.9;
}

/* 主内容区域 */
.app-main {
    background: white;
    border-radius: 15px;
    padding: 2rem;
    box-shadow: 0 10px 30px rgba(0,0,0,0.1);
}

/* 表单样式 */
.task-form {
    background: #f8f9fa;
    padding: 1.5rem;
    border-radius: 10px;
    margin-bottom: 2rem;
}

.form-group {
    margin-bottom: 1rem;
}

.form-row {
    display: grid;
    grid-template-columns: 1fr 1fr 1fr;
    gap: 1rem;
}

input, textarea, select {
    width: 100%;
    padding: 12px;
    border: 2px solid #e9ecef;
    border-radius: 8px;
    font-size: 1rem;
    transition: border-color 0.3s ease;
}

input:focus, textarea:focus, select:focus {
    outline: none;
    border-color: #667eea;
}

textarea {
    resize: vertical;
    min-height: 80px;
}

/* 按钮样式 */
.btn {
    padding: 12px 24px;
    border: none;
    border-radius: 8px;
    font-size: 1rem;
    cursor: pointer;
    transition: all 0.3s ease;
    display: inline-flex;
    align-items: center;
    gap: 8px;
}

.btn-primary {
    background: #667eea;
    color: white;
}

.btn-primary:hover {
    background: #5a6fd8;
    transform: translateY(-2px);
}

.btn-secondary {
    background: #6c757d;
    color: white;
}

.btn-success {
    background: #28a745;
    color: white;
}

.btn-danger {
    background: #dc3545;
    color: white;
}

/* 过滤器和搜索 */
.task-filters {
    display: flex;
    justify-content: space-between;
    align-items: center;
    margin-bottom: 2rem;
    flex-wrap: wrap;
    gap: 1rem;
}

.filter-buttons {
    display: flex;
    gap: 0.5rem;
}

.filter-btn {
    padding: 8px 16px;
    border: 2px solid #e9ecef;
    background: white;
    border-radius: 20px;
    cursor: pointer;
    transition: all 0.3s ease;
}

.filter-btn.active {
    background: #667eea;
    color: white;
    border-color: #667eea;
}

.search-box {
    position: relative;
    min-width: 250px;
}

.search-box input {
    padding-left: 40px;
}

.search-box i {
    position: absolute;
    left: 15px;
    top: 50%;
    transform: translateY(-50%);
    color: #6c757d;
}

/* 统计卡片 */
.task-stats {
    display: grid;
    grid-template-columns: repeat(3, 1fr);
    gap: 1rem;
    margin-bottom: 2rem;
}

.stat-card {
    background: linear-gradient(135deg, #667eea, #764ba2);
    color: white;
    padding: 1.5rem;
    border-radius: 10px;
    text-align: center;
}

.stat-card h3 {
    font-size: 2rem;
    margin-bottom: 0.5rem;
}

/* 任务列表 */
.task-list-section h2 {
    margin-bottom: 1rem;
    color: #333;
}

.task-list {
    display: grid;
    gap: 1rem;
}

.task-item {
    background: white;
    border: 2px solid #e9ecef;
    border-radius: 10px;
    padding: 1.5rem;
    display: flex;
    justify-content: space-between;
    align-items: flex-start;
    transition: all 0.3s ease;
}

.task-item:hover {
    border-color: #667eea;
    transform: translateY(-2px);
    box-shadow: 0 5px 15px rgba(0,0,0,0.1);
}

.task-item.completed {
    opacity: 0.7;
    background: #f8f9fa;
}

.task-item.completed .task-title {
    text-decoration: line-through;
}

.task-content {
    flex: 1;
}

.task-title {
    font-size: 1.2rem;
    font-weight: 600;
    margin-bottom: 0.5rem;
    color: #333;
}

.task-description {
    color: #6c757d;
    margin-bottom: 0.5rem;
}

.task-meta {
    display: flex;
    gap: 1rem;
    font-size: 0.9rem;
    color: #6c757d;
}

.task-priority {
    padding: 4px 8px;
    border-radius: 12px;
    font-size: 0.8rem;
    font-weight: 600;
}

.priority-high { background: #ffe6e6; color: #dc3545; }
.priority-medium { background: #fff3cd; color: #856404; }
.priority-low { background: #e6f3ff; color: #0066cc; }

.task-actions {
    display: flex;
    gap: 0.5rem;
}

/* 空状态 */
.empty-state {
    text-align: center;
    padding: 3rem;
    color: #6c757d;
}

.empty-state i {
    font-size: 4rem;
    margin-bottom: 1rem;
    opacity: 0.5;
}

/* 模态框 */
.modal {
    display: none;
    position: fixed;
    z-index: 1000;
    left: 0;
    top: 0;
    width: 100%;
    height: 100%;
    background-color: rgba(0,0,0,0.5);
}

.modal-content {
    background-color: white;
    margin: 5% auto;
    padding: 2rem;
    border-radius: 15px;
    width: 90%;
    max-width: 600px;
    position: relative;
}

.close {
    position: absolute;
    right: 1rem;
    top: 1rem;
    font-size: 1.5rem;
    cursor: pointer;
    color: #6c757d;
}

.modal-actions {
    display: flex;
    gap: 1rem;
    justify-content: flex-end;
    margin-top: 1.5rem;
}

/* 响应式设计 */
@media (max-width: 768px) {
    .form-row {
        grid-template-columns: 1fr;
    }
    
    .task-filters {
        flex-direction: column;
        align-items: stretch;
    }
    
    .task-stats {
        grid-template-columns: 1fr;
    }
    
    .task-item {
        flex-direction: column;
        gap: 1rem;
    }
    
    .task-actions {
        align-self: stretch;
        justify-content: flex-end;
    }
    
    .modal-actions {
        flex-direction: column;
    }
}

JavaScript 应用逻辑 (public/js/app.js)

class TaskManager {
    constructor() {
        this.tasks = [];
        this.currentFilter = 'all';
        this.searchTerm = '';
        this.init();
    }

    init() {
        this.bindEvents();
        this.loadTasks();
    }

    bindEvents() {
        // 表单提交
        document.getElementById('taskForm').addEventListener('submit', (e) => {
            e.preventDefault();
            this.addTask();
        });

        // 编辑表单提交
        document.getElementById('editTaskForm').addEventListener('submit', (e) => {
            e.preventDefault();
            this.updateTask();
        });

        // 过滤器按钮
        document.querySelectorAll('.filter-btn').forEach(btn => {
            btn.addEventListener('click', (e) => {
                this.setFilter(e.target.dataset.filter);
            });
        });

        // 搜索输入
        document.getElementById('searchInput').addEventListener('input', (e) => {
            this.searchTerm = e.target.value.toLowerCase();
            this.renderTasks();
        });

        // 模态框关闭
        document.querySelector('.close').addEventListener('click', () => {
            this.closeEditModal();
        });

        document.getElementById('cancelEdit').addEventListener('click', () => {
            this.closeEditModal();
        });

        // 点击模态框外部关闭
        window.addEventListener('click', (e) => {
            const modal = document.getElementById('editModal');
            if (e.target === modal) {
                this.closeEditModal();
            }
        });
    }

    async loadTasks() {
        try {
            const response = await fetch('/api/tasks');
            this.tasks = await response.json();
            this.renderTasks();
            this.updateStats();
        } catch (error) {
            console.error('加载任务失败:', error);
            this.showNotification('加载任务失败', 'error');
        }
    }

    async addTask() {
        const form = document.getElementById('taskForm');
        const formData = new FormData(form);
        
        const taskData = {
            title: document.getElementById('taskTitle').value.trim(),
            description: document.getElementById('taskDescription').value.trim(),
            priority: document.getElementById('taskPriority').value,
            category: document.getElementById('taskCategory').value.trim() || 'general',
            dueDate: document.getElementById('taskDueDate').value || null
        };

        if (!taskData.title) {
            this.showNotification('请输入任务标题', 'warning');
            return;
        }

        try {
            const response = await fetch('/api/tasks', {
                method: 'POST',
                headers: {
                    'Content-Type': 'application/json'
                },
                body: JSON.stringify(taskData)
            });

            if (response.ok) {
                const newTask = await response.json();
                this.tasks.unshift(newTask);
                this.renderTasks();
                this.updateStats();
                form.reset();
                this.showNotification('任务添加成功', 'success');
            } else {
                throw new Error('添加任务失败');
            }
        } catch (error) {
            console.error('添加任务失败:', error);
            this.showNotification('添加任务失败', 'error');
        }
    }

    async updateTask() {
        const taskId = document.getElementById('editTaskId').value;
        const taskData = {
            title: document.getElementById('editTaskTitle').value.trim(),
            description: document.getElementById('editTaskDescription').value.trim(),
            priority: document.getElementById('editTaskPriority').value,
            category: document.getElementById('editTaskCategory').value.trim() || 'general',
            dueDate: document.getElementById('editTaskDueDate').value || null
        };

        if (!taskData.title) {
            this.showNotification('请输入任务标题', 'warning');
            return;
        }

        try {
            const response = await fetch(`/api/tasks/${taskId}`, {
                method: 'PUT',
                headers: {
                    'Content-Type': 'application/json'
                },
                body: JSON.stringify(taskData)
            });

            if (response.ok) {
                const updatedTask = await response.json();
                const index = this.tasks.findIndex(task => task._id === taskId);
                if (index !== -1) {
                    this.tasks[index] = updatedTask;
                    this.renderTasks();
                    this.updateStats();
                    this.closeEditModal();
                    this.showNotification('任务更新成功', 'success');
                }
            } else {
                throw new Error('更新任务失败');
            }
        } catch (error) {
            console.error('更新任务失败:', error);
            this.showNotification('更新任务失败', 'error');
        }
    }

    async toggleTaskCompletion(taskId) {
        const task = this.tasks.find(t => t._id === taskId);
        if (!task) return;

        try {
            const response = await fetch(`/api/tasks/${taskId}`, {
                method: 'PUT',
                headers: {
                    'Content-Type': 'application/json'
                },
                body: JSON.stringify({
                    completed: !task.completed
                })
            });

            if (response.ok) {
                const updatedTask = await response.json();
                const index = this.tasks.findIndex(t => t._id === taskId);
                this.tasks[index] = updatedTask;
                this.renderTasks();
                this.updateStats();
                this.showNotification(
                    updatedTask.completed ? '任务标记为已完成' : '任务标记为待完成',
                    'success'
                );
            }
        } catch (error) {
            console.error('更新任务状态失败:', error);
            this.showNotification('更新任务状态失败', 'error');
        }
    }

    async deleteTask(taskId) {
        if (!confirm('确定要删除这个任务吗?')) {
            return;
        }

        try {
            const response = await fetch(`/api/tasks/${taskId}`, {
                method: 'DELETE'
            });

            if (response.ok) {
                this.tasks = this.tasks.filter(task => task._id !== taskId);
                this.renderTasks();
                this.updateStats();
                this.showNotification('任务删除成功', 'success');
            } else {
                throw new Error('删除任务失败');
            }
        } catch (error) {
            console.error('删除任务失败:', error);
            this.showNotification('删除任务失败', 'error');
        }
    }

    openEditModal(task) {
        document.getElementById('editTaskId').value = task._id;
        document.getElementById('editTaskTitle').value = task.title;
        document.getElementById('editTaskDescription').value = task.description || '';
        document.getElementById('editTaskPriority').value = task.priority;
        document.getElementById('editTaskCategory').value = task.category;
        document.getElementById('editTaskDueDate').value = task.dueDate ? 
            new Date(task.dueDate).toISOString().split('T')[0] : '';

        document.getElementById('editModal').style.display = 'block';
    }

    closeEditModal() {
        document.getElementById('editModal').style.display = 'none';
        document.getElementById('editTaskForm').reset();
    }

    setFilter(filter) {
        this.currentFilter = filter;
        
        // 更新按钮状态
        document.querySelectorAll('.filter-btn').forEach(btn => {
            btn.classList.toggle('active', btn.dataset.filter === filter);
        });

        this.renderTasks();
    }

    getFilteredTasks() {
        let filteredTasks = this.tasks;

        // 应用状态过滤器
        if (this.currentFilter === 'pending') {
            filteredTasks = filteredTasks.filter(task => !task.completed);
        } else if (this.currentFilter === 'completed') {
            filteredTasks = filteredTasks.filter(task => task.completed);
        }

        // 应用搜索过滤器
        if (this.searchTerm) {
            filteredTasks = filteredTasks.filter(task =>
                task.title.toLowerCase().includes(this.searchTerm) ||
                task.description.toLowerCase().includes(this.searchTerm) ||
                task.category.toLowerCase().includes(this.searchTerm)
            );
        }

        return filteredTasks;
    }

    renderTasks() {
        const taskList = document.getElementById('taskList');
        const emptyState = document.getElementById('emptyState');
        const filteredTasks = this.getFilteredTasks();

        if (filteredTasks.length === 0) {
            taskList.style.display = 'none';
            emptyState.style.display = 'block';
            return;
        }

        taskList.style.display = 'grid';
        emptyState.style.display = 'none';

        taskList.innerHTML = filteredTasks.map(task => `
            <div class="task-item ${task.completed ? 'completed' : ''}">
                <div class="task-content">
                    <div class="task-title">${this.escapeHtml(task.title)}</div>
                    ${task.description ? `<div class="task-description">${this.escapeHtml(task.description)}</div>` : ''}
                    <div class="task-meta">
                        <span class="task-priority priority-${task.priority}">
                            ${this.getPriorityText(task.priority)}
                        </span>
                        ${task.category ? `<span><i class="fas fa-tag"></i> ${this.escapeHtml(task.category)}</span>` : ''}
                        ${task.dueDate ? `<span><i class="fas fa-calendar"></i> ${new Date(task.dueDate).toLocaleDateString()}</span>` : ''}
                        <span><i class="fas fa-clock"></i> ${new Date(task.createdAt).toLocaleDateString()}</span>
                    </div>
                </div>
                <div class="task-actions">
                    <button class="btn ${task.completed ? 'btn-secondary' : 'btn-success'}" 
                            onclick="taskManager.toggleTaskCompletion('${task._id}')">
                        <i class="fas ${task.completed ? 'fa-undo' : 'fa-check'}"></i>
                        ${task.completed ? '标记未完成' : '标记完成'}
                    </button>
                    <button class="btn btn-primary" 
                            onclick="taskManager.openEditModal(${this.escapeHtml(JSON.stringify(task))})">
                        <i class="fas fa-edit"></i> 编辑
                    </button>
                    <button class="btn btn-danger" 
                            onclick="taskManager.deleteTask('${task._id}')">
                        <i class="fas fa-trash"></i> 删除
                    </button>
                </div>
            </div>
        `).join('');
    }

    updateStats() {
        const totalTasks = this.tasks.length;
        const completedTasks = this.tasks.filter(task => task.completed).length;
        const pendingTasks = totalTasks - completedTasks;

        document.getElementById('totalTasks').textContent = totalTasks;
        document.getElementById('completedTasks').textContent = completedTasks;
        document.getElementById('pendingTasks').textContent = pendingTasks;
    }

    getPriorityText(priority) {
        const priorityMap = {
            'low': '低优先级',
            'medium': '中优先级',
            'high': '高优先级'
        };
        return priorityMap[priority] || priority;
    }

    escapeHtml(unsafe) {
        if (typeof unsafe !== 'string') return unsafe;
        return unsafe
            .replace(/&/g, "&amp;")
            .replace(/</g, "&lt;")
            .replace(/>/g, "&gt;")
            .replace(/"/g, "&quot;")
            .replace(/'/g, "&#039;");
    }

    showNotification(message, type = 'info') {
        // 创建通知元素
        const notification = document.createElement('div');
        notification.className = `notification notification-${type}`;
        notification.innerHTML = `
            <div class="notification-content">
                <i class="fas ${this.getNotificationIcon(type)}"></i>
                <span>${message}</span>
            </div>
        `;

        // 添加样式
        notification.style.cssText = `
            position: fixed;
            top: 20px;
            right: 20px;
            background: ${this.getNotificationColor(type)};
            color: white;
            padding: 1rem 1.5rem;
            border-radius: 8px;
            box-shadow: 0 5px 15px rgba(0,0,0,0.2);
            z-index: 10000;
            transform: translateX(400px);
            transition: transform 0.3s ease;
        `;

        document.body.appendChild(notification);

        // 显示动画
        setTimeout(() => {
            notification.style.transform = 'translateX(0)';
        }, 100);

        // 自动隐藏
        setTimeout(() => {
            notification.style.transform = 'translateX(400px)';
            setTimeout(() => {
                if (notification.parentNode) {
                    notification.parentNode.removeChild(notification);
                }
            }, 300);
        }, 3000);
    }

    getNotificationIcon(type) {
        const icons = {
            'success': 'fa-check-circle',
            'error': 'fa-exclamation-circle',
            'warning': 'fa-exclamation-triangle',
            'info': 'fa-info-circle'
        };
        return icons[type] || 'fa-info-circle';
    }

    getNotificationColor(type) {
        const colors = {
            'success': '#28a745',
            'error': '#dc3545',
            'warning': '#ffc107',
            'info': '#17a2b8'
        };
        return colors[type] || '#17a2b8';
    }
}

// 初始化应用
const taskManager = new TaskManager();

6. 更新 package.json 脚本

{
  "name": "task-manager-app",
  "version": "1.0.0",
  "description": "Node.js + HTML5 任务管理应用",
  "main": "server.js",
  "scripts": {
    "start": "node server.js",
    "dev": "nodemon server.js",
    "build": "node server.js",
    "test": "echo \"Error: no test specified\" && exit 1"
  },
  "keywords": ["nodejs", "html5", "express", "mongodb", "task-manager"],
  "author": "Your Name",
  "license": "MIT",
  "dependencies": {
    "cors": "^2.8.5",
    "dotenv": "^16.3.1",
    "express": "^4.18.2",
    "mongoose": "^7.4.0"
  },
  "devDependencies": {
    "nodemon": "^3.0.1"
  }
}

7. 运行应用

启动 MongoDB

确保 MongoDB 服务正在运行:

# 如果使用本地 MongoDB
mongod

# 或者使用 MongoDB Atlas 云服务

启动应用

# 安装依赖
npm install

# 开发模式
npm run dev

# 生产模式
npm start

应用将在 http://localhost:3000 运行

8. 功能特性

完整的 CRUD 操作 - 创建、读取、更新、删除任务
任务状态管理 - 标记完成/未完成状态
优先级系统 - 高、中、低三个优先级
分类和搜索 - 按分类筛选和全文搜索
响应式设计 - 适配桌面和移动设备
数据持久化 - MongoDB 数据库存储
实时统计 - 任务完成情况统计
现代化 UI - 美观的用户界面和交互动画

9. 扩展建议

  1. 用户认证 - 添加用户注册和登录功能
  2. 文件上传 - 支持任务附件
  3. 数据导出 - 导出任务数据为 CSV 或 PDF
  4. 提醒功能 - 任务到期提醒
  5. 团队协作 - 多用户任务共享和协作
  6. 数据备份 - 自动备份和恢复功能

这个完整的教程展示了如何使用 Node.js 和 HTML5 开发一个功能丰富的现代化 Web 应用,涵盖了前后端开发的所有关键方面。

Logo

Agent 垂直技术社区,欢迎活跃、内容共建。

更多推荐