效果图
这里是参考的b站上面尚硅谷张天禹老师的课程
https://www.bilibili.com/video/BV1Zy4y1K7SH/?vd_source=1b08984208faca5ecf70d67f06a3962a
如有侵权还请速速联系
App.vue
这里用到的是父组件给子组件传参 :todos = “todos”,todos是值的名字,引号里面的是要传递的值
注意:这里不仅可以传递数据,还可以传递方法 :checkAllTodo=“checkAllTodo”
这里用到了filter() 过滤 留下满足条件的内容,该方法不会改变原数组,所以需要重新赋值
<template>
<div class="content">
<MyHeader :addTodo="addTodo"></MyHeader>
<MyList :todos="todos" :checkTodo="checkTodo" :deleteTodo="deleteTodo" ></MyList>
<MyFooter :todos="todos" :checkAllTodo="checkAllTodo" :clearAllTodo="clearAllTodo"></MyFooter>
</div>
</template>
<script>
import MyHeader from './components/MyHeader.vue'
import MyFooter from './components/MyFooter.vue'
import MyList from './components/MyList.vue'
export default {
name: 'App',
components: {
MyHeader,
MyFooter,
MyList
},
data() {
return {
todos: [{
id: '001',
title: '打代码',
done: false
},
{
id: '002',
title: '睡觉',
done: true
},
{
id: '003',
title: '吃饭',
done: false
}
]
}
},
methods: {
// 数据在哪里,数据操作的方法就在那里
// 添加todo
addTodo(todoObj) {
// console.log('我是app组件我收到了数据',todoObj)
this.todos.unshift(todoObj)
},
// 勾选or取消一个todo
checkTodo(id){
this.todos.forEach((todo)=>{
if(todo.id === id){
todo.done = !todo.done
}
})
},
// 删除todo
deleteTodo(id){
// filter不会改变原数组 ,留下满足条件的
this.todos = this.todos.filter((todo)=>{
return todo.id !== id
})
},
// 全选
checkAllTodo(done){
this.todos.forEach((todo)=>{
todo.done = done
})
},
// 删除已完成
clearAllTodo(){
this.todos = this.todos.filter((todo)=>{
return !todo.done
})
}
}
}
</script>
<style>
.content {
width: 600px;
border: 1px solid #ddd;
padding: 10px;
margin: 0 auto;
border-radius: 10px;
}
</style>
组件MyHeader
这里主要是来实现添加功能的 add()
注意:这里便是子组件给父组件传参,通过props的话需要父组件提前声明函数并传递子组件 addTodo(),然后子组件接收,调用即可
这里用到了trim()方法,去除字符串前后的空格,这个方法在表单校验时很常用
还用到了uuid可以生成全球唯一的字符串,可以考虑做测试的时候用 nanoid是这个的精简版
<template>
<div class="headerContent">
<input type="text" placeholder="请输入您的任务名称,按回车确认" v-model="title" @keydown.enter="add">
</div>
</template>
<script>
// 分别暴露
import {nanoid} from 'nanoid'
export default {
name: 'MyHeader',
data() {
return {
title: ''
}
},
props:['addTodo'],
methods: {
add(){
// console.log(e.target.value)
// console.log(this.title)
// 将用户的输入包装成一个todo对象
// uuid生成唯一字符串编码,比较大 变种nanoid - 精简版
// 校验数据
// trim()去掉空格
if(this.title.trim()){
const todoObj = {id:nanoid(),title:this.title,done:false}
// 通知app组件去添加一个对象
this.addTodo(todoObj)
// 清空内容
this.title = ""
}else{
alert('输入内容不能为空')
return
}
}
}
}
</script>
<style scoped>
.headerContent {
width: 100%;
border: 1px solid #ddd;
padding: 5px 10px;
box-sizing: border-box;
}
.headerContent input {
width: 100%;
height: 30px;
box-sizing: border-box;
outline: none;
border: none;
}
</style>
组件MyList
这里实现的功能主要有,内容的遍历与显示,以及数据的传递。
注意:如果使用props只能逐层传递,App-----MyList----MyItem
<template>
<div class="listContent">
<MyItem v-for="todoObj in todos" :key="todoObj.id" :todo = "todoObj" :checkTodo="checkTodo" :deleteTodo="deleteTodo"></MyItem>
</div>
</template>
<script>
import MyItem from '../components/MyItem'
export default {
name: 'MyList',
components: {
MyItem
},
props: ['todos','checkTodo','deleteTodo'],
methods:{
}
}
</script>
<style scoped>
</style>
组件MyItem
这里实现的功能主要为每一项的勾选,以及每一项删除功能
这里用到了confirm()点击确定之后才会执行下面的内容,否则跳出
<template>
<div class="itemContent">
<div>
<input type="checkbox" :checked = "todo.done" @click="handleCheck(todo.id)" /> {{todo.title}}
</div>
<div class="delBtn" @click="handleDelete(todo.id)">删除</div>
</div>
</template>
<script>
export default {
name: 'MyItem',
// 声明接收todo对象
props:['todo','checkTodo','deleteTodo'],
methods:{
// 勾选活取消
handleCheck(id){
// 通知app调用勾选
this.checkTodo(id)
},
// 删除
handleDelete(id){
// confirm 确定为真,取消为假
if(confirm('确定删除吗??')){
// 通知app删除对应项
this.deleteTodo(id)
}
}
},
}
</script>
<style scoped>
.itemContent {
border: 1px solid #ddd;
margin-top: 5px;
width: 100%;
height: 40px;
padding: 5px 10px;
box-sizing: border-box;
display: flex;
align-items: center;
justify-content: space-between;
transition: .3s;
}
.delBtn{
padding: 5px 10px;
background: #f40;
color: #fff;
border-radius: 5px;
display: none;
transition: .3s;
cursor: pointer;
}
.itemContent:hover{
background-color: #ddd;
}
.itemContent:hover .delBtn{
display: block;
}
</style>
组件MyFooter
这里实现的主要是,事项的统计,以及删除已完成,实现全选或全不选功能
两个计算属性是可以同时使用的
这里还用到了reduce
<template>
<div class="footerContent" v-show="total">
<div>
<!-- <input type="checkbox" :checked="isAll" @change="checkAll"> -->
<input type="checkbox" v-model="isAll">
已完成{{doneTotal}} / 全部{{total}}
</div>
<div class="delAll" @click="clearAll">
清除已完成任务
</div>
</div>
</template>
<script>
export default {
name: 'MyFooter',
props:['todos','checkAllTodo','clearAllTodo'],
computed:{
total(){
return this.todos.length
},
doneTotal(){
// let i = 0
// this.todos.forEach((todo)=>{
// if(todo.done){
// i++
// }
// })
// return i
// es6 reduce专门做条件统计的 第一个参数为上一次返回值,第二个参数为每一个项
// 该函数数的最终返回值为pre的最后值
return this.todos.reduce((pre,todo)=>{
return pre + (todo.done ? 1 : 0)
},0)
},
isAll:{
get(){
return this.doneTotal === this.total && this.total > 0
},
set(value){
this.checkAllTodo(value)
}
}
},
methods:{
// checkAll(e){
// this.checkAllTodo(e.target.checked)
// }
clearAll(){
this.clearAllTodo()
}
}
}
</script>
<style scoped>
.footerContent {
width: 100%;
height: 40px;
margin-top: 5px;
display: flex;
align-items: center;
padding: 5px 10px;
box-sizing: border-box;
justify-content: space-between;
}
.delAll{
padding: 5px 10px;
background: #f40;
color: #fff;
border-radius: 5px;
cursor: pointer;
}
</style>
每天记录一个小案例!!!!