Vue基础12
删除
跟勾选框那个类似
App.vue
<template>
<div class="bg">
<div class="todoList">
<h2 class="title">待办事项</h2>
<Header :addTodo="addTodo"/>
<div class="listItem">
<List :todos="todos" :checkTodo="checkTodo" :deleteTodo="deleteTodo"/>
<Footer />
</div>
</div>
</div>
</template>
<script>
import Header from "@/components/Header";
import List from "@/components/List";
import Footer from "@/components/Footer";
export default {
name: "App",
components:{Footer, Header, List},
data(){
return{
todos:[
{id:'001',title:'吃饭',done:false},
{id:'002',title:'唱歌',done:false},
{id:'003',title:'看电影',done:false},
]
}
},
methods:{
//添加一个todo
addTodo(addObj){
this.todos.unshift(addObj)
},
//勾选or取消勾选一个todo
checkTodo(id){
this.todos.forEach((todo)=>{
if(todo.id==id) {
todo.done=!todo.done
}
})
},
//删除一个todo
deleteTodo(id){
//第一种方法删除
// this.todos.splice(this.todos.forEach(todo=>todo.id===id),1)
//第二种方法删除
this.todos=this.todos.filter(todo=>todo.id!==id)
}
}
}
</script>
<style lang="less">
*{
padding: 0;
margin: 0;
}
.bg{
background-color: #333;
height: 937px;
padding-top: 100px;
box-sizing: border-box;
.todoList{
background-color: #fff;
width: 50%;
height: 90%;
margin: 0 auto;
//box-shadow: 5px 5px 10px 3px rgba(147, 221, 255, 0.5),-5px -5px 10px 3px rgba(147, 221, 255, 0.5); 蓝色阴影
box-shadow: 5px 5px 10px 3px rgba(0, 0, 0, 0.5),-5px -5px 10px 3px rgba(0, 0, 0, 0.5);
padding-top: 20px;
box-sizing: border-box;
.title{
text-align: center;
font-size: 30px;
font-weight: 300;
color: #00a4ff;
}
.listItem{
width: 90%;
//height: 200px;
margin: auto;
/*background-color: pink;*/
list-style: none;
border-radius: 0 0 5px 5px;
box-shadow: 1px 1px 5px 1px rgba(0,0,0,0.1),-1px -1px 5px 1px rgba(0,0,0,0.1);
padding: 20px 0;
box-sizing: border-box;
}
}
}
</style>
List.vue
<template>
<div>
<ul>
<div class="con">
<Item v-for="todoObj in todos" :key="todoObj.id" :todo="todoObj" :checkTodo="checkTodo" :deleteTodo="deleteTodo"/>
</div>
</ul>
</div>
</template>
<script>
import Item from "@/components/Item";
export default {
name:'List',
components:{Item},
props:['todos','checkTodo','deleteTodo']
}
</script>
<style scoped lang="less">
ul{
.con{
//width: 95%;
//margin: auto;
border-bottom: 1px solid rgba(87, 87, 87, 0.3);
border-left: 1px solid rgba(87, 87, 87, 0.3);
border-right: 1px solid rgba(87, 87, 87, 0.3);
margin: 0px 8px;
//background-color: pink;
}
}
</style>
Item.vue
<template>
<div>
<li>
<input type="checkbox" name="matter" id="" :checked="todo.done" @change="handleCheck(todo.id)">
{{todo.title}}
<button class="delete" @click="handleDelete(todo.id)">删除</button>
</li>
</div>
</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('确定删除吗?')){
console.log(id)
this.deleteTodo(id)
}
}
}
}
</script>
<style scoped lang="less">
li{
//height: 35%;
//width: 96%;
display: block;
//background-color: pink;
margin: auto;
padding: 12px;
border-top: 1px solid rgba(87, 87, 87, 0.3);
//border-left: 1px solid rgba(87, 87, 87, 0.3);
//border-right: 1px solid rgba(87, 87, 87, 0.3);
//box-sizing: border-box;
border-collapse: collapse;
button{
background-color: #d9534f;
float: right;
padding: 3px 10px;
color: white;
border: 1px solid #d43f3a;
border-radius: 5px;
cursor: pointer;
&:hover{
background-color: #c9302c;
border: 1px solid #ac2925;
}
}
&:hover{
background-color: rgba(0,0,0,0.1);
}
}
</style>
底部统计
reduce的用法
reduce专门做条件统计的
可以筛选出数组中想要的,例如,学生中年龄大于18的,订单中金额大于1000的,一堆人中男士有几个人等等
基本使用—计数
局部代码:
doneTotal(){
const x = this.todos.reduce((pre,current)=>{
console.log("@pre:"+pre+" @current:",current)
return pre+1
},0)
console.log("###",x)
}
整体代码:
<template>
<div>
<input type="checkbox" name="matter" id="" @change="handleCbx">
已完成 <span>{{doneTotal}}</span> / 全部 <span>{{todos.length}}</span>
<button>清除已完成任务</button>
</div>
</template>
<script>
export default {
name: "Footer",
props:['todos','handleCbx','handleDTodo'],
methods:{
},
computed:{
doneTotal(){
const x = this.todos.reduce((pre,current)=>{
console.log("@pre:"+pre+" @current:",current)
return pre+1
},0)
console.log("###",x)
}
}
}
</script>
<style scoped lang="less">
div{
width: 95%;
margin: auto;
margin-top: 10px;
//border: 1px solid rgba(87, 87, 87, 0.3);
button{
background-color: #d9534f;
float: right;
padding: 3px 10px;
color: white;
border: 1px solid #d43f3a;
border-radius: 5px;
cursor: pointer;
&:hover{
background-color: #c9302c;
border: 1px solid #ac2925;
}
}
}
</style>
结果展示:
操作分析:
条件统计使用
比如有两项的todo都已完成,则todo.done有两项是true,使用reduce并对每次的todo.done进行判断,则最后的x值为2
doneTotal(){
const x = this.todos.reduce((pre,current)=>{
console.log("@pre:"+pre+" @current:",current)
return pre+(current.done?1:0)
},0)
console.log("###",x)
}
项目中使用
App.vue
<template>
<div class="bg">
<div class="todoList">
<h2 class="title">待办事项</h2>
<Header :addTodo="addTodo"/>
<div class="listItem">
<List :todos="todos" :checkTodo="checkTodo" :deleteTodo="deleteTodo"/>
<Footer :todos="todos"/>
</div>
</div>
</div>
</template>
<script>
import Header from "@/components/Header";
import List from "@/components/List";
import Footer from "@/components/Footer";
export default {
name: "App",
components:{Footer, Header, List},
data(){
return{
todos:[
{id:'001',title:'吃饭',done:false},
{id:'002',title:'唱歌',done:false},
{id:'003',title:'看电影',done:false},
]
}
},
methods:{
//添加一个todo
addTodo(addObj){
this.todos.unshift(addObj)
},
//勾选or取消勾选一个todo
checkTodo(id){
this.todos.forEach((todo)=>{
if(todo.id==id) {
todo.done=!todo.done
if(todo.done){
this.todoed++
}else{
this.todoed--
}
}
})
},
//删除一个todo
deleteTodo(id){
//第一种方法删除
// this.todos.splice(this.todos.forEach(todo=>todo.id===id),1)
//第二种方法删除
this.todos=this.todos.filter(todo=>todo.id!==id)
},
}
}
</script>
<style lang="less">
*{
padding: 0;
margin: 0;
}
.bg{
background-color: #333;
height: 937px;
padding-top: 100px;
box-sizing: border-box;
.todoList{
background-color: #fff;
width: 50%;
height: 90%;
margin: 0 auto;
//box-shadow: 5px 5px 10px 3px rgba(147, 221, 255, 0.5),-5px -5px 10px 3px rgba(147, 221, 255, 0.5); 蓝色阴影
box-shadow: 5px 5px 10px 3px rgba(0, 0, 0, 0.5),-5px -5px 10px 3px rgba(0, 0, 0, 0.5);
padding-top: 20px;
box-sizing: border-box;
.title{
text-align: center;
font-size: 30px;
font-weight: 300;
color: #00a4ff;
}
.listItem{
width: 90%;
//height: 200px;
margin: auto;
/*background-color: pink;*/
list-style: none;
border-radius: 0 0 5px 5px;
box-shadow: 1px 1px 5px 1px rgba(0,0,0,0.1),-1px -1px 5px 1px rgba(0,0,0,0.1);
padding: 20px 0;
box-sizing: border-box;
}
}
}
</style>
Footer.vue
<template>
<div>
<input type="checkbox" name="matter" id="">
已完成 <span>{{doneTotal}}</span> / 全部 <span>{{total}}</span>
<button>清除已完成任务</button>
</div>
</template>
<script>
export default {
name: "Footer",
props:['todos'],
methods:{
},
computed:{
total(){
return this.todos.length
},
doneTotal(){
/* const x = this.todos.reduce((pre,current)=>{
console.log("@pre:"+pre+" @current:",current)
return pre+(current.done?1:0)
},0)
console.log("###",x)*/
return this.todos.reduce((pre,todo)=>pre+(todo.done?1:0),0)
}
}
}
</script>
<style scoped lang="less">
div{
width: 95%;
margin: auto;
margin-top: 10px;
//border: 1px solid rgba(87, 87, 87, 0.3);
button{
background-color: #d9534f;
float: right;
padding: 3px 10px;
color: white;
border: 1px solid #d43f3a;
border-radius: 5px;
cursor: pointer;
&:hover{
background-color: #c9302c;
border: 1px solid #ac2925;
}
}
}
</style>
底部交互
全选or取消全选
第一种方法
App.vue
<template>
<div class="bg">
<div class="todoList">
<h2 class="title">待办事项</h2>
<Header :addTodo="addTodo"/>
<div class="listItem">
<List :todos="todos" :checkTodo="checkTodo" :deleteTodo="deleteTodo"/>
<Footer :todos="todos" :checkAllTodo="checkAllTodo"/>
</div>
</div>
</div>
</template>
<script>
import Header from "@/components/Header";
import List from "@/components/List";
import Footer from "@/components/Footer";
export default {
name: "App",
components:{Footer, Header, List},
data(){
return{
todos:[
{id:'001',title:'吃饭',done:false},
{id:'002',title:'唱歌',done:false},
{id:'003',title:'看电影',done:false},
]
}
},
methods:{
//添加一个todo
addTodo(addObj){
this.todos.unshift(addObj)
},
//勾选or取消勾选一个todo
checkTodo(id){
this.todos.forEach((todo)=>{
if(todo.id==id) {
todo.done=!todo.done
if(todo.done){
this.todoed++
}else{
this.todoed--
}
}
})
},
//删除一个todo
deleteTodo(id){
//第一种方法删除
// this.todos.splice(this.todos.forEach(todo=>todo.id===id),1)
//第二种方法删除
this.todos=this.todos.filter(todo=>todo.id!==id)
},
//全选or取消全选
checkAllTodo(done){
this.todos.forEach(todo=>todo.done=done)
}
}
}
</script>
<style lang="less">
*{
padding: 0;
margin: 0;
}
.bg{
background-color: #333;
height: 937px;
padding-top: 100px;
box-sizing: border-box;
.todoList{
background-color: #fff;
width: 50%;
height: 90%;
margin: 0 auto;
//box-shadow: 5px 5px 10px 3px rgba(147, 221, 255, 0.5),-5px -5px 10px 3px rgba(147, 221, 255, 0.5); 蓝色阴影
box-shadow: 5px 5px 10px 3px rgba(0, 0, 0, 0.5),-5px -5px 10px 3px rgba(0, 0, 0, 0.5);
padding-top: 20px;
box-sizing: border-box;
.title{
text-align: center;
font-size: 30px;
font-weight: 300;
color: #00a4ff;
}
.listItem{
width: 90%;
//height: 200px;
margin: auto;
/*background-color: pink;*/
list-style: none;
border-radius: 0 0 5px 5px;
box-shadow: 1px 1px 5px 1px rgba(0,0,0,0.1),-1px -1px 5px 1px rgba(0,0,0,0.1);
padding: 20px 0;
box-sizing: border-box;
}
}
}
</style>
Footer.vue
<template>
<div v-show="total">
<input type="checkbox" name="matter" id="" :checked="isAll" @change="checkAll">
已完成 <span>{{doneTotal}}</span> / 全部 <span>{{total}}</span>
<button>清除已完成任务</button>
</div>
</template>
<script>
export default {
name: "Footer",
props:['todos','checkAllTodo'],
methods:{
checkAll(e){
console.log(e.target.checked)
this.checkAllTodo(e.target.checked)
}
},
computed:{
total(){
return this.todos.length
},
doneTotal(){
/* const x = this.todos.reduce((pre,current)=>{
console.log("@pre:"+pre+" @current:",current)
return pre+(current.done?1:0)
},0)
console.log("###",x)*/
return this.todos.reduce((pre,todo)=>pre+(todo.done?1:0),0)
},
isAll(){
return this.total === this.doneTotal && this.total>0
}
}
}
</script>
<style scoped lang="less">
div{
width: 95%;
margin: auto;
margin-top: 10px;
//border: 1px solid rgba(87, 87, 87, 0.3);
button{
background-color: #d9534f;
float: right;
padding: 3px 10px;
color: white;
border: 1px solid #d43f3a;
border-radius: 5px;
cursor: pointer;
&:hover{
background-color: #c9302c;
border: 1px solid #ac2925;
}
}
}
</style>
第二种方法
使用v-model与计算属性的get和set方法
Footer.vue
<template>
<div v-show="total">
<!-- <input type="checkbox" name="matter" id="" :checked="isAll" @change="checkAll">-->
<input type="checkbox" name="matter" id="" v-model="isAll">
已完成 <span>{{doneTotal}}</span> / 全部 <span>{{total}}</span>
<button>清除已完成任务</button>
</div>
</template>
<script>
export default {
name: "Footer",
props:['todos','checkAllTodo'],
methods:{
/*checkAll(e){
console.log(e.target.checked)
this.checkAllTodo(e.target.checked)
}*/
},
computed:{
total(){
return this.todos.length
},
doneTotal(){
/* const x = this.todos.reduce((pre,current)=>{
console.log("@pre:"+pre+" @current:",current)
return pre+(current.done?1:0)
},0)
console.log("###",x)*/
return this.todos.reduce((pre,todo)=>pre+(todo.done?1:0),0)
},
isAll:{
get(){
return this.total === this.doneTotal&&this.total>0
},
set(value){
this.checkAllTodo(value)
}
}
}
}
</script>
<style scoped lang="less">
div{
width: 95%;
margin: auto;
margin-top: 10px;
//border: 1px solid rgba(87, 87, 87, 0.3);
button{
background-color: #d9534f;
float: right;
padding: 3px 10px;
color: white;
border: 1px solid #d43f3a;
border-radius: 5px;
cursor: pointer;
&:hover{
background-color: #c9302c;
border: 1px solid #ac2925;
}
}
}
</style>
清除已完成任务
App.vue
<template>
<div class="bg">
<div class="todoList">
<h2 class="title">待办事项</h2>
<Header :addTodo="addTodo"/>
<div class="listItem">
<List :todos="todos" :checkTodo="checkTodo" :deleteTodo="deleteTodo"/>
<Footer :todos="todos" :checkAllTodo="checkAllTodo" :clearAllTodo="clearAllTodo"/>
</div>
</div>
</div>
</template>
<script>
import Header from "@/components/Header";
import List from "@/components/List";
import Footer from "@/components/Footer";
export default {
name: "App",
components:{Footer, Header, List},
data(){
return{
todos:[
{id:'001',title:'吃饭',done:false},
{id:'002',title:'唱歌',done:false},
{id:'003',title:'看电影',done:false},
]
}
},
methods:{
//添加一个todo
addTodo(addObj){
this.todos.unshift(addObj)
},
//勾选or取消勾选一个todo
checkTodo(id){
this.todos.forEach((todo)=>{
if(todo.id==id) {
todo.done=!todo.done
if(todo.done){
this.todoed++
}else{
this.todoed--
}
}
})
},
//删除一个todo
deleteTodo(id){
//第一种方法删除
// this.todos.splice(this.todos.forEach(todo=>todo.id===id),1)
//第二种方法删除
this.todos=this.todos.filter(todo=>todo.id!==id)
},
//全选or取消全选
checkAllTodo(done){
this.todos.forEach(todo=>todo.done=done)
},
clearAllTodo(){
this.todos=this.todos.filter(todo=>{
return !todo.done
})
}
}
}
</script>
<style lang="less">
*{
padding: 0;
margin: 0;
}
.bg{
background-color: #333;
height: 937px;
padding-top: 100px;
box-sizing: border-box;
.todoList{
background-color: #fff;
width: 50%;
height: 90%;
margin: 0 auto;
//box-shadow: 5px 5px 10px 3px rgba(147, 221, 255, 0.5),-5px -5px 10px 3px rgba(147, 221, 255, 0.5); 蓝色阴影
box-shadow: 5px 5px 10px 3px rgba(0, 0, 0, 0.5),-5px -5px 10px 3px rgba(0, 0, 0, 0.5);
padding-top: 20px;
box-sizing: border-box;
.title{
text-align: center;
font-size: 30px;
font-weight: 300;
color: #00a4ff;
}
.listItem{
width: 90%;
//height: 200px;
margin: auto;
/*background-color: pink;*/
list-style: none;
border-radius: 0 0 5px 5px;
box-shadow: 1px 1px 5px 1px rgba(0,0,0,0.1),-1px -1px 5px 1px rgba(0,0,0,0.1);
padding: 20px 0;
box-sizing: border-box;
}
}
}
</style>
Footer.vue
<template>
<div v-show="total">
<!-- <input type="checkbox" name="matter" id="" :checked="isAll" @change="checkAll">-->
<input type="checkbox" name="matter" id="" v-model="isAll">
已完成 <span>{{doneTotal}}</span> / 全部 <span>{{total}}</span>
<button @click="clearAll">清除已完成任务</button>
</div>
</template>
<script>
export default {
name: "Footer",
props:['todos','checkAllTodo','clearAllTodo'],
methods:{
/*checkAll(e){
console.log(e.target.checked)
this.checkAllTodo(e.target.checked)
}*/
clearAll(){
if(confirm('确认清除已完成任务吗?')){
this.clearAllTodo()
}
}
},
computed:{
total(){
return this.todos.length
},
doneTotal(){
/* const x = this.todos.reduce((pre,current)=>{
console.log("@pre:"+pre+" @current:",current)
return pre+(current.done?1:0)
},0)
console.log("###",x)*/
return this.todos.reduce((pre,todo)=>pre+(todo.done?1:0),0)
},
isAll:{
get(){
return this.total === this.doneTotal&&this.total>0
},
set(value){
this.checkAllTodo(value)
}
}
}
}
</script>
<style scoped lang="less">
div{
width: 95%;
margin: auto;
margin-top: 10px;
//border: 1px solid rgba(87, 87, 87, 0.3);
button{
background-color: #d9534f;
float: right;
padding: 3px 10px;
color: white;
border: 1px solid #d43f3a;
border-radius: 5px;
cursor: pointer;
&:hover{
background-color: #c9302c;
border: 1px solid #ac2925;
}
}
}
</style>
总结TodoList案例
- 组件化编码流程
(1)拆分静态组件:组件要按照功能点拆分,命名不要与html元素冲突
(2)实现动态组件:考虑好数据的存放位置,数据是一个组件在用,还是一些组件在用
①一个组件在用:放在组件自身即可
②一些组件在用:放在他们共同的父组件上(状态提升)
(3)实现交互:从绑定事件开始 - props适用于:
(1)父组件 ===> 子组件 通信
(2)子组件 ===> 父组件 通信(要求父先给子一个函数) - 使用v-model时要切记:v-model绑定的值补不能是props传过来的值,因为props是不可以修改的!
- props传过来的若是对象类型的值,修改对象中的属性时Vue不会报错,但不推荐这样做