AI 生成: 一个简洁的待办事项 Web 应用,支持添加、完成、删除任务,并使用 localStorage 在浏览器本地保存数据。

This commit is contained in:
admin
2026-09-28 20:05:39 +08:00
commit 3cf7cbe814
4 changed files with 434 additions and 0 deletions
+174
View File
@@ -0,0 +1,174 @@
// Simple Todo App
// 数据保存在 localStorage 中
(function () {
"use strict";
const STORAGE_KEY = "simple-todo-app.tasks";
// DOM 元素
const form = document.getElementById("todo-form");
const input = document.getElementById("todo-input");
const list = document.getElementById("todo-list");
const countEl = document.getElementById("todo-count");
const emptyState = document.getElementById("empty-state");
const clearCompletedBtn = document.getElementById("clear-completed");
/**
* 从 localStorage 读取任务列表
* @returns {Array<{id: string, text: string, completed: boolean}>}
*/
function loadTasks() {
try {
const raw = localStorage.getItem(STORAGE_KEY);
if (!raw) return [];
const parsed = JSON.parse(raw);
return Array.isArray(parsed) ? parsed : [];
} catch (err) {
console.error("读取任务失败:", err);
return [];
}
}
/**
* 保存任务列表到 localStorage
* @param {Array} tasks
*/
function saveTasks(tasks) {
try {
localStorage.setItem(STORAGE_KEY, JSON.stringify(tasks));
} catch (err) {
console.error("保存任务失败:", err);
}
}
// 当前任务状态
let tasks = loadTasks();
/**
* 生成唯一 ID
*/
function generateId() {
return Date.now().toString(36) + Math.random().toString(36).slice(2, 8);
}
/**
* 渲染任务列表
*/
function render() {
list.innerHTML = "";
tasks.forEach(function (task) {
const li = document.createElement("li");
li.className = "todo-item" + (task.completed ? " completed" : "");
li.dataset.id = task.id;
// 复选框
const checkbox = document.createElement("input");
checkbox.type = "checkbox";
checkbox.checked = task.completed;
checkbox.addEventListener("change", function () {
toggleTask(task.id);
});
// 文本
const span = document.createElement("span");
span.className = "todo-text";
span.textContent = task.text;
// 删除按钮
const delBtn = document.createElement("button");
delBtn.className = "delete-btn";
delBtn.type = "button";
delBtn.setAttribute("aria-label", "删除任务");
delBtn.textContent = "✕";
delBtn.addEventListener("click", function () {
deleteTask(task.id);
});
li.appendChild(checkbox);
li.appendChild(span);
li.appendChild(delBtn);
list.appendChild(li);
});
// 空状态
if (tasks.length === 0) {
emptyState.classList.remove("hidden");
} else {
emptyState.classList.add("hidden");
}
// 统计
const remaining = tasks.filter(function (t) {
return !t.completed;
}).length;
countEl.textContent = "共 " + tasks.length + " 项 · 未完成 " + remaining + " 项";
}
/**
* 添加任务
*/
function addTask(text) {
const trimmed = text.trim();
if (!trimmed) return;
tasks.push({
id: generateId(),
text: trimmed,
completed: false
});
saveTasks(tasks);
render();
}
/**
* 切换任务完成状态
*/
function toggleTask(id) {
tasks = tasks.map(function (t) {
return t.id === id ? Object.assign({}, t, { completed: !t.completed }) : t;
});
saveTasks(tasks);
render();
}
/**
* 删除任务
*/
function deleteTask(id) {
tasks = tasks.filter(function (t) {
return t.id !== id;
});
saveTasks(tasks);
render();
}
/**
* 清除已完成任务
*/
function clearCompleted() {
tasks = tasks.filter(function (t) {
return !t.completed;
});
saveTasks(tasks);
render();
}
// 事件绑定
form.addEventListener("submit", function (e) {
e.preventDefault();
addTask(input.value);
input.value = "";
input.focus();
});
clearCompletedBtn.addEventListener("click", clearCompleted);
// 初始渲染
render();
// 自动聚焦输入框
input.focus();
})();