Vue基础12之TodoList案例第二篇

删除

跟勾选框那个类似

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)">
      &nbsp;{{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">
  &nbsp;已完成 <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="">
  &nbsp;已完成 <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">
  &nbsp;已完成 <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">
  &nbsp;已完成 <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">
  &nbsp;已完成 <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. 组件化编码流程
    (1)拆分静态组件:组件要按照功能点拆分,命名不要与html元素冲突
    (2)实现动态组件:考虑好数据的存放位置,数据是一个组件在用,还是一些组件在用
    ①一个组件在用:放在组件自身即可
    ②一些组件在用:放在他们共同的父组件上(状态提升)
    (3)实现交互:从绑定事件开始
  2. props适用于:
    (1)父组件 ===> 子组件 通信
    (2)子组件 ===> 父组件 通信(要求父先给子一个函数)
  3. 使用v-model时要切记:v-model绑定的值补不能是props传过来的值,因为props是不可以修改的!
  4. props传过来的若是对象类型的值,修改对象中的属性时Vue不会报错,但不推荐这样做
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值