这个待办事项有以下功能:增删查,既新增待办事项,删除待办事项,查看全部,未完成,完成待办事项,当鼠标移动到待办事项上时会显示删除按钮
分为四个部分来实现:ToDoHeader.vue、ToDoMain.vue、ToDoFooter.vue、ToDoList.vue
ToDoHeader.vue
头部部分
<template>
<div>
<div class="header">
<p class="title">待办事项</p>
<input class="new-todo" type="text" placeholder="请填写任务" v-model.trim="name" @keyup.enter="enterName" />
</div>
</div>
</template>
<script setup>
import {
ref
} from 'vue'
const name = ref('')
const emit = defineEmits(['addTodo'])
const enterName = () => {
// 触发自定义事件,为空时弹出警示框
if(name.value===""){
alert("任务名称不能为空")
return
}
emit('addTodo', name.value)
name.value = ''
}
</script>
<style>
.title{
font-size: 20px;
color: red;
height: 40px;
width: 86.5%;
margin: 10px 40px 0 40px;
padding-top: 10px;
text-align: center;
border: 1px solid #ccc;
}
.new-todo{
height: 40px;
width: 80%;
margin: 5px 40px 0 40px;
padding: 4px 0 0 65px;
border: 1px solid #ccc;
}
</style>
ToDoMain.vue
列表总体样式和删除样式
<template>
<div class="main">
<ul class="todo-list">
<li v-for="item in list" :key="item.id" :class="{ completed: item.done }">
<div class="view">
<input class="toggle" type="checkbox" v-model="item.done" />
<label>{{ item.name }}</label>
<button class="destroy" @click="delTodo(item.id)"><div class="overlay">x</div></button>
</div>
</li>
</ul>
</div>
</template>
<script setup>
//声明props
const props = defineProps(['list'])
//声明自定义事件
const emit = defineEmits(['delTodo'])
const delTodo = id => {
id && emit('delToDo', id) // 触发事件
}
</script>
<style>
.view{
position: relative;
height: 40px;
width: 800px;
margin-left: 20px;
padding-top: 15px;
border-bottom: 1px solid #ccc;
}
li{
margin-left: 20px;
}
label{
position: absolute;
left: 70px;
top: 23px;
}
.destroy{
float: right;
border: none;
background-color: white;
}
.overlay{
position: absolute;
top: 26px;
font-size: 40px;
color: black;
/* 字体居中 */
transform: translate(-50%,-50%);
font-size: large;
/* 悬停前完全透明 */
opacity: 0;
transition: 0.3s ease-in-out;
}
.view:hover .overlay{
transition: 0.3s ease-in-out;
/* 悬停时完全不透明 */
opacity: 1;
}
input[type="checkbox"] {
appearance: none;
/*取消默认的复选框样式*/
width: 30px;
/*设置方形宽度为20px*/
height: 30px;
/*设置方形高度为20px*/
border: 1px solid #000;
/*设置边框样式*/
border-radius: 50%;
/*将方形的边框改为圆形*/
}
input[type="checkbox"]:checked {
&:before {
content: '\2713';
/*添加一个对勾,默认颜色是黑色*/
color: greenyellow;
/*将对勾颜色改为白色*/
font-size: 20px;
margin-left: 5px;
/*将对勾左边距改为4px,让它看着像居中*/
}
}
</style>
ToDoList.vue
列表部分
<template>
<ToDoHeader @addTodo="addTodo"></ToDoHeader>
<ToDoMain :list="showList" @delToDo="delToDo"></ToDoMain>
<ToDoFooter :lastLength="lastLength" @updateStatus="updateStatus"></ToDoFooter>
</template>
<script setup>
import ToDoHeader from './ToDoHeader.vue'
import ToDoMain from './ToDoMain.vue'
import ToDoFooter from './ToDoFooter.vue'
import {
computed,
ref
} from 'vue'
const list = ref([{
id: 1,
name: '跑步',
done: false,
},
{
id: 2,
name: '健身',
done: true,
},
])
const addTodo = name => {
list.value.push({
name,
done: false,
id: ~~(Math.random() * 1000)
})
}
const delToDo = id => {
list.value = list.value.filter(item => item.id !== id)
}
const lastLength = computed(() => {
return list.value.filter(item => !item.done).length
})
const status = ref('all')
const showList = computed(() => {
if (status.value === 'all') {
return list.value
}else if (status.value === 'active') {
return list.value.filter(item => !item.done)
}else if (status.value === 'completed') {
return list.value.filter(item => item.done)
}
})
const updateStatus = setStatus => {
status.value = setStatus // 将子组件的状态赋值给父组件
}
</script>
<style>
</style>
ToDoFooter.vue
页脚部分
<template>
<div class="footer">
<span class="todo-count">共<strong>{{ lastLength }}</strong>条未完成任务</span>
<ul class="filters">
<li class="oneli">
<a @click.prevent="emit('updateStatus', 'all')" :class="{ selected: status === 'all' }"
href="#/">全部</a>
</li>
<li>
<a @click.prevent="emit('updateStatus', 'active')" :class="{ selected: status === 'active' }"
href="#/active">未完成</a>
</li>
<li>
<a @click.prevent="emit('updateStatus', 'completed')" :class="{ selected: status === 'completed' }"
href="#/completed">已完成</a>
</li>
</ul>
</div>
</template>
<script setup>
const emit = defineEmits(['updateStatus'])
const props = defineProps(['lastLength', 'status'])
</script>
<style>
.oneli{
margin-left: 50%;
}
.todo-count {
margin-left: 40px;
float: left;
}
ul {
margin-left: -25px;
list-style-type: none;
}
li {
float: left;
margin-left: 4px;
}
a {
border: 1px solid #d3d3d3;
/* 清除a标签默认效果 */
text-decoration: none;
color: black;
font-size: 8px;
}
</style>
最后更改main.js中的代码
main.js
import { createApp } from 'vue'
import App from './components/ToDoList.vue'
createApp(App).mount('#app')
总体效果:
新增效果:
删除效果:
鼠标悬停时会显示删除按钮
查找效果:
全部效果:
未完成效果:
完成效果: