console
const { createApp, ref, computed, onMounted, watch } = Vue;
createApp({
setup() {
const tasks = ref([
{ id: 1, text: '学习Vue 3', completed: false },
{ id: 2, text: '完成项目', completed: false },
{ id: 3, text: '提交报告', completed: true },
]);
const newTask = ref('');
const filter = ref('all');
const editingTaskIndex = ref(-1);
const editedTaskText = ref('');
const filteredTasks = computed(() => {
switch(filter.value) {
case 'active':
return tasks.value.filter(task => !task.completed);
case 'completed':
return tasks.value.filter(task => task.completed);
default:
return tasks.value;
}
});
const completedTasks = computed(() => {
return tasks.value.filter(task => task.completed);
});
const addTask = () => {
if (newTask.value.trim() === '') return;
tasks.value.push({
id: Date.now(),
text: newTask.value,
completed: false
});
newTask.value = '';
};
const toggleTask = (index) => {
tasks.value[index].completed = !tasks.value[index].completed;
};
const removeTask = (index) => {
tasks.value.splice(index, 1);
};
const clearCompleted = () => {
tasks.value = tasks.value.filter(task => !task.completed);
};
const startEditing = (index) => {
editingTaskIndex.value = index;
editedTaskText.value = tasks.value[index].text;
};
const finishEditing = () => {
if (editedTaskText.value.trim() === '') {
removeTask(editingTaskIndex.value);
} else {
tasks.value[editingTaskIndex.value].text = editedTaskText.value;
}
editingTaskIndex.value = -1;
};
onMounted(() => {
const savedTasks = localStorage.getItem('vue3-tasks');
if (savedTasks) {
tasks.value = JSON.parse(savedTasks);
}
});
watch(tasks, (newTasks) => {
localStorage.setItem('vue3-tasks', JSON.stringify(newTasks));
}, { deep: true });
return {
tasks,
newTask,
filter,
filteredTasks,
completedTasks,
addTask,
toggleTask,
removeTask,
clearCompleted,
startEditing,
finishEditing,
editingTaskIndex,
editedTaskText
};
}
}).mount('#app');
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Vue 3 待办事项应用</title>
<script src="https://cdn.tailwindcss.com"></script>
<link href="https://cdn.jsdelivr.net/npm/font-awesome@4.7.0/css/font-awesome.min.css" rel="stylesheet">
<script src="https://cdn.jsdelivr.net/npm/vue@3.2.47/dist/vue.global.prod.min.js"></script>
<script>
tailwind.config = {
theme: {
extend: {
colors: {
primary: '#4CAF50',
secondary: '#f44336',
neutral: '#f9f9f9',
},
fontFamily: {
sans: ['Inter', 'system-ui', 'sans-serif'],
},
},
}
}
</script>
<link rel="stylesheet" href="style.css">
</head>
<body class="bg-gray-50 min-h-screen">
<div id="app" class="max-w-md mx-auto px-4 py-8">
<div class="bg-white rounded-xl shadow-lg p-6 transform transition-all duration-300 hover:shadow-xl">
<h1 class="text-[clamp(1.5rem,3vw,2rem)] font-bold text-gray-800 mb-6 flex items-center">
<i class="fa fa-tasks text-primary mr-2"></i>
<span>待办事项</span>
</h1>
<div class="flex items-center mb-6">
<input
v-model="newTask"
@keyup.enter="addTask"
type="text"
placeholder="添加新任务..."
class="task-input"
>
<button
@click="addTask"
class="btn btn-primary ml-3"
>
<i class="fa fa-plus mr-1"></i> 添加
</button>
</div>
<div class="mb-4">
<div class="flex justify-between items-center mb-2">
<span class="text-sm text-gray-500">
共 {{ tasks.length }} 个任务,已完成 {{ completedTasks.length }} 个
</span>
<div class="flex space-x-2">
<button
@click="filter = 'all'"
:class="['filter-btn', filter === 'all' ? 'active' : '']"
>
全部
</button>
<button
@click="filter = 'active'"
:class="['filter-btn', filter === 'active' ? 'active' : '']"
>
未完成
</button>
<button
@click="filter = 'completed'"
:class="['filter-btn', filter === 'completed' ? 'active' : '']"
>
已完成
</button>
</div>
</div>
<ul class="task-list space-y-2 max-h-[300px] overflow-y-auto pr-1">
<li
v-for="(task, index) in filteredTasks"
:key="task.id"
class="task-item"
:class="{ 'completed': task.completed }"
>
<div class="flex items-center">
<input
type="checkbox"
:checked="task.completed"
@change="toggleTask(index)"
class="mr-3 h-5 w-5 text-primary focus:ring-primary rounded"
>
<span>{{ task.text }}</span>
</div>
<button
@click="removeTask(index)"
class="btn btn-secondary p-1.5"
>
<i class="fa fa-trash-o"></i>
</button>
</li>
</ul>
<div v-if="filteredTasks.length === 0" class="empty-state text-center py-8 text-gray-400">
<i class="fa fa-check-circle text-4xl mb-2 block"></i>
<p>没有任务了,休息一下吧!</p>
</div>
</div>
<button
v-if="tasks.length > 0"
@click="clearCompleted"
class="btn btn-secondary w-full"
>
<i class="fa fa-trash mr-1"></i> 清除已完成任务
</button>
</div>
<footer class="mt-8 text-center text-gray-400 text-sm">
<p>双击任务可以编辑内容</p>
</footer>
</div>
<script src="script.js"></script>
</body>
</html>