目录
070.TodoList案例_静态
首先分析拆分组件,新建下面四个组件
<template>
<div id="root">
<div class="todo-container">
<div class="todo-wrap">
<Header></Header>
<List></List>
<Footer></Footer>
</div>
</div>
</div>
</template>
<script>
import Header from "./components/Header.vue";
import Footer from "./components/Footer.vue";
import List from "./components/List.vue";
export default {
name:'App',
components:{
Header,
Footer,
List
},
}
</script>
<style>
body{
background: #fff;
}
.btn{
display: inline-block;
padding: 4px 12px;
margin-bottom: 0;
line-height: 20px;
font-size: 14px;
text-align: center;
vertical-align: middle;
cursor: pointer;
box-shadow: inset 0 1px 0 rgba(255,255,255,0.2) 0 1px 2px rgba(0, 0, 0, 0.05);
border-radius: 4px;
}
.btn-danger{
color: #fff;
background-color: #da4f49;
border: 1px solid #bd362f;
}
.btn-danger:hover{
color: #fff;
background-color: #bd362f;
}
.btn:focus{
outline: none;
}
.todo-container{
width: 600px;
margin: 0 auto;
}
.todo-container .todo-wrap{
padding: 10px;
border: 1px solid #ddd;
border-radius: 5px;
}
</style>
<template>
<div class="todo-header">
<input type="text" placeholder="请输入您的任务名称,按回车键确认"/>
</div>
</template>
<script>
export default {
name:'Header',
}
</script>
<style scoped>
.todo-header input{
width: 560px;
height: 28px;
font-size: 28px;
border: 1px solid #ccc;
border-radius: 4px;
padding: 4px 7px;
}
.todo-header input:focus{
outline: none;
border-color: rgba(82, 168, 236, 0.8);
box-shadow: inset 0 1px 1px rgba(0, 0 ,0, 0.075) 0 0 8px rgba(82, 168, 236, 0.6);
}
</style>
<template>
<ul class="todo-main">
<Item></Item>
<Item></Item>
<Item></Item>
<Item></Item>
</ul>
</template>
<script>
import Item from "./Item.vue";
export default {
name:'List',
components:{Item}
}
</script>
<style scoped>
.todo-main{
margin-left: 0px;
border: 1px solid #ddd;
border-radius: 2px;
padding: 0px;
}
.todo-empty{
height: 40px;
line-height: 40px;
border: 1px solid #ddd;
border-radius: 2px;
padding-left: 5px;
margin-top: 10px;
}
</style>
<template>
<li>
<label>
<input type="checkbox"/>
<span>xxxxx</span>
</label>
<button class="btn btn-danger" style="display:none">删除</button>
</li>
</template>
<script>
export default {
name:'Item',
}
</script>
<style scoped>
li{
list-style: none;
height: 36px;
line-height: 36px;
padding: 0 5px;
border-bottom: 1px solid #ddd;
}
li label{
float: left;
cursor: pointer;
}
li label li input{
vertical-align: middle;
margin-right: 6px;
position: relative;
top: -1px;
}
li button{
float: right;
display: none;
margin-top: 3px;
}
li:before{
content: initial;
}
li:last-child{
border-bottom: none;
}
</style>
<template>
<div class="todo-footer">
<label>
<input type="checkbox"/>
</label>
<span>
<span>已完成0</span> / 全部2
</span>
<button class="btn btn-danger">清除已完成任务</button>
</div>
</template>
<script>
export default {
name:'Footer',
}
</script>
<style scoped>
.todo-footer{
height: 40px;
line-height: 40px;
padding-left: 6px;
margin-top: 5px;
}
.todo-footer label{
display: inline-block;
margin-right: 20px;
cursor: pointer;
}
.todo-footer label input{
position: relative;
top: -1px;
vertical-align: middle;
margin-right: 5px;
}
.todo-footer button{
float: right;
margin-top: 5px;
}
</style>
071.TodoList案例_初始化列表
首先在List中定义数据并用v-for循环,然后用v-bind(简写直接 ‘:’)绑定动态的传递数据给item
<template>
<ul class="todo-main">
<Item v-for="todoObj in todos" :key="todoObj.id" :todo="todoObj"></Item>
</ul>
</template>
<script>
import Item from "./Item.vue";
export default {
name:'List',
components:{Item},
data() {
return {
todos:[
{id:'001',title:'唱跳',done:true},
{id:'002',title:'rep',done:false},
{id:'003',title:'篮球',done:true},
]
}
},
}
</script>
在item中声明接收数据,用插值语法渲染;input的checked属性默认勾选,动态决定加v-bind
<template>
<li>
<label>
<input type="checkbox" :checked="todo.done"/>
<span>{{todo.title}}</span>
</label>
<button class="btn btn-danger" style="display:none">删除</button>
</li>
</template>
<script>
export default {
name:'Item',
// 声明接收todo对象
props:['todo']
}
</script>
072.TodoList案例_添加
首先绑定一个键盘事件add并使用数据的双向绑定v-model让todos获取输入的数据(也可以不用双向绑定,使用直接操作DOM的方式获取数据 add(e){title:e.target.value),然后对id的处理可以安装nanoid库并引入nanoid,安装方法: npm i nanoid
<template>
<div class="todo-header">
<input type="text" placeholder="请输入您的任务名称,按回车键确认" v-model="title" @keyup.enter="add"/>
</div>
</template>
<script>
import { nanoid } from "nanoid";
export default {
name:'Header',
props:['addTodo'],
data() {
return {
title:''
}
},
methods: {
// 绑定键盘事件
add(){
// 校验数据
if(!this.title.trim()) return alert('输入不能为空')
// 将用户的输入包装成一个todo对象
const todoObj = {id:nanoid(),title:this.title,done:false}
// console.log(todoObj);
// 通知App组件去添加一个todo对象
this.addTodo(todoObj)
// 清空输入
this.title=''
}
},
}
</script>
在父组件App里定义函数,并给子组件Header使用
备注:上一节把todos放在List里面,由于Header返回的todoObj要传到todos里,但是Header和List是同级关系,以当前的知识量同级之间传递不了数据,所以可以把todos放到App里面,由App传给List(父向子传递数据),Header返回的todoObj传给App里的todos(子向父传递数据,父组件定义一个函数并给子组件使用,子组件调用这个函数,父组件就可以收到参数)
<template>
<div id="root">
<div class="todo-container">
<div class="todo-wrap">
<Header :addTodo="addTodo"></Header>
<List :todos="todos"></List>
<Footer></Footer>
</div>
</div>
</div>
</template>
<script>
import Header from "./components/Header.vue";
import Footer from "./components/Footer.vue";
import List from "./components/List.vue";
export default {
name:'App',
components:{
Header,
Footer,
List
},
data() {
return {
todos:[
{id:'001',title:'唱跳',done:true},
{id:'002',title:'rep',done:false},
{id:'003',title:'篮球',done:true},
]
}
},
methods: {
addTodo(todoObj){
// 在组数第一位加元素unshift
this.todos.unshift(todoObj)
}
},
}
</script
073.TodoList案例_勾选
首先在App中定义一个勾选修改函数,并交给子组件使用
<template>
<div id="root">
<div class="todo-container">
<div class="todo-wrap">
<Header :addTodo="addTodo"></Header>
<List :todos="todos" :checkTodo="checkTodo"></List>
<Footer></Footer>
</div>
</div>
</div>
</template>
<script>
import Header from "./components/Header.vue";
import Footer from "./components/Footer.vue";
import List from "./components/List.vue";
export default {
name:'App',
components:{
Header,
Footer,
List
},
data() {
return {
todos:[
{id:'001',title:'唱跳',done:true},
{id:'002',title:'rep',done:false},
{id:'003',title:'篮球',done:true},
]
}
},
methods: {
// 添加一个todo
addTodo(todoObj){
// 在组数第一位加元素unshift
this.todos.unshift(todoObj)
},
// 勾选or取消勾选一个todo
checkTodo(id){
this.todos.forEach(todo=>{
if(todo.id === id) todo.done = !todo.done
})
}
},
}
</script>
因为App向item传递要经过list,所以list要中转接受传过来的函数
<template>
<ul class="todo-main">
<Item v-for="todoObj in todos" :key="todoObj.id" :todo="todoObj" :checkTodo="checkTodo"></Item>
</ul>
</template>
<script>
import Item from "./Item.vue";
export default {
name:'List',
components:{Item},
props:['todos','checkTodo']
}
</script>
在item中绑定一个change事件,并通知App组件将对应的todo对象的done值取反
<template>
<li>
<label>
<input type="checkbox" :checked="todo.done" @change="handleCheck(todo.id)"/>
<!-- 如下代码也能实现功能,但是不太推荐,因为有点违反原则,因为修改了props -->
<!-- <input type="checkbox" v-model="todo.done"> -->
<span>{{todo.title}}</span>
</label>
<button class="btn btn-danger" style="display:none">删除</button>
</li>
</template>
<script>
export default {
name:'Item',
// 声明接收todo对象
props:['todo','checkTodo'],
methods: {
handleCheck(id){
// 通知App组件将对应的todo对象的done值取反
this.checkTodo(id)
}
},
}
</script>
074.TodoList案例_删除
在item中绑定删除事件,设置悬浮显示删除按钮样式,调用函数
<template>
<li>
<label>
<input type="checkbox" :checked="todo.done" @change="handleCheck(todo.id)"/>
<!-- 如下代码也能实现功能,但是不太推荐,因为有点违反原则,因为修改了props -->
<!-- <input type="checkbox" v-model="todo.done"> -->
<span>{{todo.title}}</span>
</label>
<button class="btn btn-danger" @click="handleDelete(todo.id)">删除</button>
</li>
</template>
<script>
export default {
name:'Item',
// 声明接收todo对象
props:['todo','checkTodo','deleteTodo'],
methods: {
// 勾选or取消勾选
handleCheck(id){
// 通知App组件将对应的todo对象的done值取反
this.checkTodo(id)
},
// 删除
handleDelete(id){
if(confirm('确定删除吗?')){
this.deleteTodo(id)
}
}
},
}
</script>
<style scoped>
}
/* 鼠标悬浮 */
li:hover{
background-color: #ddd;
}
/* 悬浮时删除按钮显现 */
li:hover button{
display: block;
}
</style>
中转
<template>
<ul class="todo-main">
<Item v-for="todoObj in todos" :key="todoObj.id" :todo="todoObj" :checkTodo="checkTodo" :deleteTodo="deleteTodo"></Item>
</ul>
</template>
<script>
import Item from "./Item.vue";
export default {
name:'List',
components:{Item},
props:['todos','checkTodo','deleteTodo']
}
</script>
把除了要删除的过滤出来
<template>
<div id="root">
<div class="todo-container">
<div class="todo-wrap">
<Header :addTodo="addTodo"></Header>
<List :todos="todos" :checkTodo="checkTodo" :deleteTodo="deleteTodo"></List>
<Footer></Footer>
</div>
</div>
</div>
</template>
<script>
import Header from "./components/Header.vue";
import Footer from "./components/Footer.vue";
import List from "./components/List.vue";
export default {
name:'App',
components:{
Header,
Footer,
List
},
data() {
return {
todos:[
{id:'001',title:'唱跳',done:true},
{id:'002',title:'rep',done:false},
{id:'003',title:'篮球',done:true},
]
}
},
methods: {
// 添加一个todo
addTodo(todoObj){
// 在组数第一位加元素unshift
this.todos.unshift(todoObj)
},
// 勾选or取消勾选一个todo
checkTodo(id){
this.todos.forEach(todo=>{
if(todo.id === id) todo.done = !todo.done
})
},
// 删除一个todo
deleteTodo(id){
this.todos = this.todos.filter(todo=>{
return todo.id !== id
})
}
},
}
</script>
075.TodoList案例_底部设计
在App中给Footer传入toods
<template>
<div id="root">
<div class="todo-container">
<div class="todo-wrap">
<Header :addTodo="addTodo"></Header>
<List :todos="todos" :checkTodo="checkTodo" :deleteTodo="deleteTodo"></List>
<Footer :todos="todos"></Footer>
</div>
</div>
</div>
</template>
在Footer中利用计算属性
<template>
<div class="todo-footer">
<label>
<input type="checkbox"/>
</label>
<span>
<span>已完成{{doneTotal}}</span> / 全部{{todos.length}}
</span>
<button class="btn btn-danger">清除已完成任务</button>
</div>
</template>
<script>
export default {
name:'Footer',
props:['todos'],
computed:{
doneTotal(){
/* 条件统计reduce((pre,current)=>{},0),统计初始值为0,数组长度是多少,函数调用几次,
pre上一次的返回值,current当前的值,最后一次调用函数的返回值是reduce的返回值 */
/* this.todos.reduce((pre,current)=>{
console.log('@',pre,current);
return pre + (current.done? 1 : 0)
},0) */
return this.todos.reduce((pre,current)=> pre + (current.done ? 1 : 0),0)
}
}
}
</script>
076.TodoList案例_底部交互
<template>
<!-- 当内容删完时,list隐藏 -->
<div class="todo-footer" v-show="total">
<label>
<!-- 方法一 -->
<!-- <input type="checkbox" :checked="isAll" @change="checkAll"/> -->
<!-- 方法二 -->
<input type="checkbox" v-model="isAll"/>
</label>
<span>
<span>已完成{{doneTotal}}</span> / 全部{{total}}
</span>
<button class="btn btn-danger" @click="clearAll">清除已完成任务</button>
</div>
</template>
<script>
export default {
name:'Footer',
props:['todos','checkAllTodo','clearAllTodo'],
computed:{
total(){
return this.todos.length
},
doneTotal(){
/* 条件统计reduce((pre,current)=>{},0),统计初始值为0,数组长度是多少,函数调用几次,
pre上一次的返回值,current当前的值,最后一次调用函数的返回值是reduce的返回值 */
/* this.todos.reduce((pre,current)=>{
console.log('@',pre,current);
return pre + (current.done? 1 : 0)
},0) */
return this.todos.reduce((pre,todo)=> pre + (todo.done ? 1 : 0),0)
},
isAll:{
get(){
return this.doneTotal === this.total && this.total>0
},
set(checked){
this.checkAllTodo(checked)
}
}
/* isAll(){
return this.doneTotal === this.total && this.total>0
} */
},
methods: {
/* checkAll(e){
this.checkAllTodo(e.target.checked)
} */
clearAll(){
this.clearAllTodo()
}
},
}
</script>
template>
<div id="root">
<div class="todo-container">
<div class="todo-wrap">
<Header :addTodo="addTodo"></Header>
<List :todos="todos" :checkTodo="checkTodo" :deleteTodo="deleteTodo"></List>
<Footer :todos="todos" :checkAllTodo="checkAllTodo" :clearAllTodo="clearAllTodo"></Footer>
</div>
</div>
</div>
</template>
<script>
import Header from "./components/Header.vue";
import Footer from "./components/Footer.vue";
import List from "./components/List.vue";
export default {
name:'App',
components:{
Header,
Footer,
List
},
data() {
return {
todos:[
{id:'001',title:'唱跳',done:true},
{id:'002',title:'rep',done:false},
{id:'003',title:'篮球',done:true},
]
}
},
methods: {
// 添加一个todo
addTodo(todoObj){
// 在组数第一位加元素unshift
this.todos.unshift(todoObj)
},
// 勾选or取消勾选一个todo
checkTodo(id){
this.todos.forEach(todo=>{
if(todo.id === id) todo.done = !todo.done
})
},
// 删除一个todo
deleteTodo(id){
this.todos = this.todos.filter(todo=>{
return todo.id !== id
})
},
// 全选or取消全选
checkAllTodo(done){
this.todos.forEach(todo=>{
todo.done = done
})
},
// 清除所有已经完成的todo
clearAllTodo(){
this.todos= this.todos.filter(todo=>{
return !todo.done
})
}
},
}
</script>
077.TodoList案例_总结
## 总结TodoList案例
1.组件化编码流程:
1 拆分静态组件:组件要按照功能点拆分,命名不用与HTML元素冲突
2 实现动态组件:考虑好数据存放位置,数据是一个组件在用,还是一些组件在用
一个组件在用放在组件自身即可
一些组件在用放在他们共同的父组件上(状态提升)
3 实现交互:从绑定事件开始
2.props适用于:
1 父组件==>子组件 通信
2 子组件==>父组件 通信(要求父给子一个函数)
3.使用v-model时要切记v-model绑定的值不能是props传过来的值,因为props是不可以修改的
4.props传过来的若是对象类型的值,修改对象中的属性时vue不会报错,但不推荐这样做
078.浏览器本地存储
## webStorage
1.存储内容大小一般支持5MB左右(不同浏览器可能还不一样)
2.浏览器端通过Widow.sessionStorage和Widow.localStorage属性来本地存储机制
3.相关API:
1 xxxStorage.setItem('key','value')
该方法接受一个键和值作为参数,会把键和值添加到存储中,如果键名存在则更新对应的值
2 xxxStorage.getItem('person')
该方法接受一个键名作为参数,返回键名对应的值
3 xxxStorage.removeItem('key')
该方法接受一个键名作为参数,并把该键名从存储中删除
4 xxxStorage.clear()
该方法会清空存储中所有的数据
4.备注:
1 sessionStorage存储的内容会随着浏览器窗口关闭而消失
2 localStorage存储的内容需要手动清除才会消失
3 xxxStorage.getItem(xxx)如果xxx对应的value获取不到,那么getItem的返回值是null
4 JSON.parse(null)的结果依然是null
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>localStorage</title>
</head>
<body>
<h2>localStorage</h2>
<button onclick="saveData()">点我保存一个数据</button>
<button onclick="readData()">点我读取一个数据</button>
<button onclick="deleteData()">点我删除一个数据</button>
<button onclick="deleteAllData()">点我清空数据</button>
<script>
let p ={name:'张三',age:'19'}
function saveData() {
localStorage.setItem('msg','hello!')
localStorage.setItem('msg1',666)
localStorage.setItem('person',JSON.stringify(p))
}
function readData() {
console.log(localStorage.getItem('msg'));
console.log(localStorage.getItem('msg1'));
const res =localStorage.getItem('person')
console.log(JSON.parse(res));
console.log(localStorage.getItem('msg2'));
}
function deleteData() {
localStorage.removeItem('msg')
}
function deleteAllData() {
localStorage.clear()
}
</script>
</body>
</html>
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>sessionStorage</title>
</head>
<body>
<h2>sessionStorage</h2>
<button onclick="saveData()">点我保存一个数据</button>
<button onclick="readData()">点我读取一个数据</button>
<button onclick="deleteData()">点我删除一个数据</button>
<button onclick="deleteAllData()">点我清空数据</button>
<script>
let p ={name:'张三',age:'19'}
function saveData() {
sessionStorage.setItem('msg','hello!')
sessionStorage.setItem('msg1',666)
sessionStorage.setItem('person',JSON.stringify(p))
}
function readData() {
console.log(sessionStorage.getItem('msg'));
console.log(sessionStorage.getItem('msg1'));
const res =sessionStorage.getItem('person')
console.log(JSON.parse(res));
console.log(sessionStorage.getItem('msg2'));
}
function deleteData() {
sessionStorage.removeItem('msg')
}
function deleteAllData() {
sessionStorage.clear()
}
</script>
</body>
</html>
079.TodoList_本地存储
在App利用深度监视属性,实现本地存储
<script>
import Header from "./components/Header.vue";
import Footer from "./components/Footer.vue";
import List from "./components/List.vue";
export default {
name:'App',
components:{
Header,
Footer,
List
},
data() {
return {
todos:JSON.parse(localStorage.getItem('todos')) || []
}
},
watch:{
todos:{
deep:true,
handler(value){
localStorage.setItem('todos',JSON.stringify(value))
}
}
}
}
</script>
最后完成代码
<template>
<div id="root">
<div class="todo-container">
<div class="todo-wrap">
<Header :addTodo="addTodo"></Header>
<List :todos="todos" :checkTodo="checkTodo" :deleteTodo="deleteTodo"></List>
<Footer :todos="todos" :checkAllTodo="checkAllTodo" :clearAllTodo="clearAllTodo"></Footer>
</div>
</div>
</div>
</template>
<script>
import Header from "./components/Header.vue";
import Footer from "./components/Footer.vue";
import List from "./components/List.vue";
export default {
name:'App',
components:{
Header,
Footer,
List
},
data() {
return {
todos:JSON.parse(localStorage.getItem('todos')) || []
}
},
methods: {
// 添加一个todo
addTodo(todoObj){
// 在组数第一位加元素unshift
this.todos.unshift(todoObj)
},
// 勾选or取消勾选一个todo
checkTodo(id){
this.todos.forEach(todo=>{
if(todo.id === id) todo.done = !todo.done
})
},
// 删除一个todo
deleteTodo(id){
this.todos = this.todos.filter(todo=>{
return todo.id !== id
})
},
// 全选or取消全选
checkAllTodo(done){
this.todos.forEach(todo=>{
todo.done = done
})
},
// 清除所有已经完成的todo
clearAllTodo(){
this.todos= this.todos.filter(todo=>{
return !todo.done
})
}
},
watch:{
todos:{
deep:true,
handler(value){
localStorage.setItem('todos',JSON.stringify(value))
}
}
}
}
</script>
<style>
<template>
<div class="todo-header">
<input type="text" placeholder="请输入您的任务名称,按回车键确认" v-model="title" @keyup.enter="add"/>
</div>
</template>
<script>
import { nanoid } from "nanoid";
export default {
name:'Header',
props:['addTodo'],
data() {
return {
title:''
}
},
methods: {
// 绑定键盘事件
add(){
// 校验数据
if(!this.title.trim()) return alert('输入不能为空')
// 将用户的输入包装成一个todo对象
const todoObj = {id:nanoid(),title:this.title,done:false}
// console.log(todoObj);
// 通知App组件去添加一个todo对象
this.addTodo(todoObj)
// 清空输入
this.title=''
}
},
}
</script>
<style scoped>
.todo-header input{
width: 560px;
height: 28px;
font-size: 28px;
border: 1px solid #ccc;
border-radius: 4px;
padding: 4px 7px;
}
.todo-header input:focus{
outline: none;
border-color: rgba(82, 168, 236, 0.8);
box-shadow: inset 0 1px 1px rgba(0, 0 ,0, 0.075) 0 0 8px rgba(82, 168, 236, 0.6);
}
</style>
<template>
<ul class="todo-main">
<Item v-for="todoObj in todos" :key="todoObj.id" :todo="todoObj" :checkTodo="checkTodo" :deleteTodo="deleteTodo"></Item>
</ul>
</template>
<script>
import Item from "./Item.vue";
export default {
name:'List',
components:{Item},
props:['todos','checkTodo','deleteTodo']
}
</script>
<style scoped>
.todo-main{
margin-left: 0px;
border: 1px solid #ddd;
border-radius: 2px;
padding: 0px;
}
.todo-empty{
height: 40px;
line-height: 40px;
border: 1px solid #ddd;
border-radius: 2px;
padding-left: 5px;
margin-top: 10px;
}
</style>
<template>
<li>
<label>
<input type="checkbox" :checked="todo.done" @change="handleCheck(todo.id)"/>
<!-- 如下代码也能实现功能,但是不太推荐,因为有点违反原则,因为修改了props -->
<!-- <input type="checkbox" v-model="todo.done"> -->
<span>{{todo.title}}</span>
</label>
<button class="btn btn-danger" @click="handleDelete(todo.id)">删除</button>
</li>
</template>
<script>
export default {
name:'Item',
// 声明接收todo对象
props:['todo','checkTodo','deleteTodo'],
methods: {
// 勾选or取消勾选
handleCheck(id){
// 通知App组件将对应的todo对象的done值取反
this.checkTodo(id)
},
// 删除
handleDelete(id){
if(confirm('确定删除吗?')){
this.deleteTodo(id)
}
}
},
}
</script>
<style scoped>
li{
list-style: none;
height: 36px;
line-height: 36px;
padding: 0 5px;
border-bottom: 1px solid #ddd;
}
li label{
float: left;
cursor: pointer;
}
li label li input{
vertical-align: middle;
margin-right: 6px;
position: relative;
top: -1px;
}
li button{
float: right;
display: none;
margin-top: 3px;
}
li:before{
content: initial;
}
li:last-child{
border-bottom: none;
}
/* 鼠标悬浮 */
li:hover{
background-color: #ddd;
}
/* 悬浮时删除按钮显现 */
li:hover button{
display: block;
}
</style>
<template>
<!-- 当内容删完时,list隐藏 -->
<div class="todo-footer" v-show="total">
<label>
<!-- 方法一 -->
<!-- <input type="checkbox" :checked="isAll" @change="checkAll"/> -->
<!-- 方法二 -->
<input type="checkbox" v-model="isAll"/>
</label>
<span>
<span>已完成{{doneTotal}}</span> / 全部{{total}}
</span>
<button class="btn btn-danger" @click="clearAll">清除已完成任务</button>
</div>
</template>
<script>
export default {
name:'Footer',
props:['todos','checkAllTodo','clearAllTodo'],
computed:{
total(){
return this.todos.length
},
doneTotal(){
/* 条件统计reduce((pre,current)=>{},0),统计初始值为0,数组长度是多少,函数调用几次,
pre上一次的返回值,current当前的值,最后一次调用函数的返回值是reduce的返回值 */
/* this.todos.reduce((pre,current)=>{
console.log('@',pre,current);
return pre + (current.done? 1 : 0)
},0) */
return this.todos.reduce((pre,todo)=> pre + (todo.done ? 1 : 0),0)
},
isAll:{
get(){
return this.doneTotal === this.total && this.total>0
},
set(checked){
this.checkAllTodo(checked)
}
}
/* isAll(){
return this.doneTotal === this.total && this.total>0
} */
},
methods: {
/* checkAll(e){
this.checkAllTodo(e.target.checked)
} */
clearAll(){
this.clearAllTodo()
}
},
}
</script>
<style scoped>
.todo-footer{
height: 40px;
line-height: 40px;
padding-left: 6px;
margin-top: 5px;
}
.todo-footer label{
display: inline-block;
margin-right: 20px;
cursor: pointer;
}
.todo-footer label input{
position: relative;
top: -1px;
vertical-align: middle;
margin-right: 5px;
}
.todo-footer button{
float: right;
margin-top: 5px;
}
</style>