Node.js+Html5+MongoDB项目开发
·
本文将为您展示如何使用 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">×</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, "&")
.replace(/</g, "<")
.replace(/>/g, ">")
.replace(/"/g, """)
.replace(/'/g, "'");
}
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. 扩展建议
- 用户认证 - 添加用户注册和登录功能
- 文件上传 - 支持任务附件
- 数据导出 - 导出任务数据为 CSV 或 PDF
- 提醒功能 - 任务到期提醒
- 团队协作 - 多用户任务共享和协作
- 数据备份 - 自动备份和恢复功能
这个完整的教程展示了如何使用 Node.js 和 HTML5 开发一个功能丰富的现代化 Web 应用,涵盖了前后端开发的所有关键方面。
更多推荐



所有评论(0)