Vue每日一题 实现一个待办事项列表(Todo List)
题目描述
使用Vue 3组合式API实现一个功能完整的待办事项列表,需要包含以下功能:
1.
添加新的待办事项
2.
标记待办事项为已完成/未完成
3.
删除待办事项
4.
显示待办事项总数和已完成数量
5.
筛选显示:全部、未完成、已完成
要求
使用Vue 3组合式API(Composition API)
数据需要响应式更新
样式简洁美观
代码结构清晰
题解
点击查看代码
<template>
<div class="todo-app">
<h1>Vue待办事项列表</h1>
<!-- 添加新待办 -->
<div class="add-todo">
<input
v-model="newTodo"
@keyup.enter="addTodo"
placeholder="输入新的待办事项..."
class="todo-input"
/>
<button @click="addTodo" class="add-btn">添加</button>
</div>
<!-- 统计信息 -->
<div class="todo-stats">
<span>总计: {{ todos.length }}</span>
<span>已完成: {{ completedCount }}</span>
<span>未完成: {{ uncompletedCount }}</span>
</div>
<!-- 筛选按钮 -->
<div class="filter-buttons">
<button
v-for="filter in filters"
:key="filter.key"
@click="currentFilter = filter.key"
:class="{ active: currentFilter === filter.key }"
class="filter-btn"
>
{{ filter.label }}
</button>
</div>
<!-- 待办列表 -->
<ul class="todo-list">
<li
v-for="todo in filteredTodos"
:key="todo.id"
:class="{ completed: todo.completed }"
class="todo-item"
>
<input
type="checkbox"
v-model="todo.completed"
class="todo-checkbox"
/>
<span class="todo-text">{{ todo.text }}</span>
<button @click="deleteTodo(todo.id)" class="delete-btn">删除</button>
</li>
</ul>
<div v-if="filteredTodos.length === 0" class="empty-state">
{{ getEmptyMessage() }}
</div>
</div>
</template>
<script setup>
import { ref, computed, reactive } from 'vue'
// 响应式数据
const newTodo = ref('')
const currentFilter = ref('all')
const todos = ref([])
let nextId = 1
// 筛选选项
const filters = reactive([
{ key: 'all', label: '全部' },
{ key: 'uncompleted', label: '未完成' },
{ key: 'completed', label: '已完成' }
])
// 计算属性
const completedCount = computed(() =>
todos.value.filter(todo => todo.completed).length
)
const uncompletedCount = computed(() =>
todos.value.filter(todo => !todo.completed).length
)
const filteredTodos = computed(() => {
switch (currentFilter.value) {
case 'completed':
return todos.value.filter(todo => todo.completed)
case 'uncompleted':
return todos.value.filter(todo => !todo.completed)
default:
return todos.value
}
})
// 方法
const addTodo = () => {
if (newTodo.value.trim() === '') {
alert('请输入待办事项内容!')
return
}
todos.value.push({
id: nextId++,
text: newTodo.value.trim(),
completed: false
})
newTodo.value = ''
}
const deleteTodo = (id) => {
const index = todos.value.findIndex(todo => todo.id === id)
if (index > -1) {
todos.value.splice(index, 1)
}
}
const getEmptyMessage = () => {
switch (currentFilter.value) {
case 'completed':
return '暂无已完成的待办事项'
case 'uncompleted':
return '恭喜!所有待办事项都已完成'
default:
return '暂无待办事项,快来添加一个吧!'
}
}
</script>
<style scoped>
.todo-app {
max-width: 600px;
margin: 50px auto;
padding: 20px;
font-family: 'Arial', sans-serif;
}
h1 {
text-align: center;
color: #2c3e50;
margin-bottom: 30px;
}
.add-todo {
display: flex;
gap: 10px;
margin-bottom: 20px;
}
.todo-input {
flex: 1;
padding: 12px;
border: 2px solid #ddd;
border-radius: 6px;
font-size: 16px;
}
.todo-input:focus {
outline: none;
border-color: #3498db;
}
.add-btn {
padding: 12px 20px;
background-color: #3498db;
color: white;
border: none;
border-radius: 6px;
cursor: pointer;
font-size: 16px;
}
.add-btn:hover {
background-color: #2980b9;
}
.todo-stats {
display: flex;
gap: 20px;
margin-bottom: 20px;
padding: 10px;
background-color: #f8f9fa;
border-radius: 6px;
}
.todo-stats span {
font-weight: bold;
color: #555;
}
.filter-buttons {
display: flex;
gap: 10px;
margin-bottom: 20px;
}
.filter-btn {
padding: 8px 16px;
border: 2px solid #ddd;
background-color: white;
border-radius: 20px;
cursor: pointer;
transition: all 0.3s;
}
.filter-btn:hover {
border-color: #3498db;
}
.filter-btn.active {
background-color: #3498db;
color: white;
border-color: #3498db;
}
.todo-list {
list-style: none;
padding: 0;
}
.todo-item {
display: flex;
align-items: center;
gap: 12px;
padding: 15px;
border: 1px solid #eee;
border-radius: 6px;
margin-bottom: 10px;
transition: all 0.3s;
}
.todo-item:hover {
box-shadow: 0 2px 8px rgba(0,0,0,0.1);
}
.todo-item.completed {
background-color: #f8f9fa;
opacity: 0.8;
}
.todo-checkbox {
width: 18px;
height: 18px;
cursor: pointer;
}
.todo-text {
flex: 1;
font-size: 16px;
}
.todo-item.completed .todo-text {
text-decoration: line-through;
color: #999;
}
.delete-btn {
padding: 6px 12px;
background-color: #e74c3c;
color: white;
border: none;
border-radius: 4px;
cursor: pointer;
font-size: 14px;
}
.delete-btn:hover {
background-color: #c0392b;
}
.empty-state {
text-align: center;
color: #999;
font-style: italic;
padding: 40px;
}
</style>

浙公网安备 33010602011771号