112.【Vue-细刷-03】(五)

简介: 112.【Vue-细刷-03】

(二十三)、todoList案列

1.案列_静态

(1).未拆分的静态资源

下面的数据直接复制:就行。博主没有进行修改或添加什么,和张天禹来时的静态资源一摸一样的。

App.vue

<template>
  <div>
   <div class="todo-container">
    <div class="todo-wrap">
      <!-- 1.头部 -->
      <div class="todo-header">
        <input type="text" placeholder="请输入你的任务名称,按回车键确认"/>
      </div>
      <!-- 2.列表 -->
      <ul class="todo-main">
        <li>
          <label>
            <input type="checkbox"/>
            <span>xxxxx</span>
          </label>
          <button class="btn btn-danger" style="display:none">删除</button>
        </li>
        <li>
          <label>
            <input type="checkbox"/>
            <span>yyyy</span>
          </label>
          <button class="btn btn-danger" style="display:none">删除</button>
        </li>
      </ul>
      <!-- 3.底部导航 -->
      <div class="todo-footer">
        <label>
          <input type="checkbox"/>
        </label>
        <span>
          <span>已完成0</span> / 全部2
        </span>
        <button class="btn btn-danger">清除已完成任务</button>
      </div>
    </div>
  </div>
  </div>
</template>
<script>
export default {
  name:'App' // 目的是在浏览器VUE插件中的名字都是App不会被改变。
}
</script>
<style>
  /*base*/
  body {
    background: #fff;
  }
  .btn {
    display: inline-block;
    padding: 4px 12px;
    margin-bottom: 0;
    font-size: 14px;
    line-height: 20px;
    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;
  }
  /*header*/
  .todo-header input {
    width: 560px;
    height: 28px;
    font-size: 14px;
    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);
  }
  /*list*/
  .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;
  }
  /*item*/
  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;
  }
  /*footer*/
  .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>

(2).拆分之后的静态资源

Header.vue

<template>
  <div>
          <!-- 1.头部 -->
      <div class="todo-header">
        <input type="text" placeholder="请输入你的任务名称,按回车键确认"/>
      </div>
  </div>
</template>
<script>
export default {
    name:'Header'
}
</script>
<style scoped>
  /*header*/
  .todo-header input {
    width: 560px;
    height: 28px;
    font-size: 14px;
    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>

List.vue

<template>
  <div>
    <ul class="todo-main">
        <Item/>
      </ul>
  </div>
</template>
<script>
    import Item from './Item.vue'
export default {
    name:'List',
    components:{
        Item,
    }
}
</script>
<style scoped>
  /*List*/
  .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>

Item.vue

<template>
  <div>
        <li>
          <label>
            <input type="checkbox"/>
            <span>xxxxx</span>
          </label>
          <button class="btn btn-danger" style="display:none">删除</button>
        </li>
        <li>
          <label>
            <input type="checkbox"/>
            <span>yyyy</span>
          </label>
          <button class="btn btn-danger" style="display:none">删除</button>
        </li>
  </div>
</template>
<script>
export default {
name:'Item'
}
</script>
<style>
 /*item*/
  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>

Footer.vue

<template>
  <div>
    <div class="todo-footer">
        <label>
          <input type="checkbox"/>
        </label>
        <span>
          <span>已完成0</span> / 全部2
        </span>
        <button class="btn btn-danger">清除已完成任务</button>
      </div>
  </div>
</template>
<script>
export default {
name:'Footer'
}
</script>
<style scoped>
 /*footer*/
  .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>

App.vue

<template>
  <div>
   <div class="todo-container">
    <div class="todo-wrap">
      <!-- 1.头部 -->
      <Header/>
      <!-- 2.列表 -->
      <list/>
      <!-- 3.底部导航 -->
      <Footer/>
    </div>
  </div>
  </div>
</template>
<script>
  // 1.引入
  import Header from './components/Header.vue'
  import List from './components/List.vue'
  import Footer from './components/Footer.vue'
export default {
  name:'App', // 目的是在浏览器VUE插件中的名字都是App不会被改变。
  // 2.注册
  components:{
    Header,
    List,
    Footer
  }
}
</script>
<style>
  /*base*/
  body {
    background: #fff;
  }
  .btn {
    display: inline-block;
    padding: 4px 12px;
    margin-bottom: 0;
    font-size: 14px;
    line-height: 20px;
    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>

2.案列_初始化数据 (父App.vue传递数组)

(1).利用props接受父App.vue传过来的数据

引用父类传递给子类的数据的时候,一定要添加上this.(父类传递的数据)

切记一句话,数据源在哪,就在哪里做增删改查,子类需要通知父App.vue.然后父App.vue进行真实的增删改查

1.App.vue

<template>
  <div>
   <div class="todo-container">
    <div class="todo-wrap">
      <!-- 1.头部 -->
      <Header/>
      <!-- 2.列表 -->
      <list :todosA="todos"/>
      <!-- 3.底部导航 -->
      <Footer/>
    </div>
  </div>
  </div>
</template>
<script>
  // 1.引入组件
  import Header from './components/Header.vue'
  import List from './components/List.vue'
  import Footer from './components/Footer.vue'
export default {
  name:'App', // 目的是在浏览器VUE插件中的名字都是App不会被改变。
  // 2.注册组件
  components:{
    Header,
    List,
    Footer
  },
  data() {
    return {
      todos:[
        {id:'001',name:'抽烟',done:true},
        {id:'002',name:'喝酒',done:false},
        {id:'003',name:'烫头',done:true}
      ]
    }
  },
}
</script>
<style>
  /*base*/
  body {
    background: #fff;
  }
  .btn {
    display: inline-block;
    padding: 4px 12px;
    margin-bottom: 0;
    font-size: 14px;
    line-height: 20px;
    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>

2.List.vue: 接受父App.vue传递的参数并遍历我们传入的数据

1.一定要存在 :key否则会报错。
<Item v-for="t in this.todosA" :key="t.id"/>
2.将数据传递给Item.vue子组件
<Item v-for="t in this.todosA" :key="t.id" :itemA="t"/>
<template>
  <div>
    <ul class="todo-main">
        <!-- 这里我们必须需要添加key。 并向Item.vue传递遍历的对象t-->
        <<Item v-for="t in this.todosA" :key="t.id" :itemA="t"/>
      </ul>
  </div>
</template>
<script>
    import Item from './Item.vue'
export default {
    name:'List',
    components:{
        Item,
    },
    props:['todosA'],  // 负责声明接受且不做限制...
}
</script>
<style scoped>
  /*List*/
  .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>

3.Item.vue: 接受父List.vue的传递的参数并赋值

<template>
  <div>
        <li>
          <label>
            <input type="checkbox" :checked="itemA.done"/>
            <span>{{itemA.name}}</span>
          </label>
          <button class="btn btn-danger" style="display:none">删除</button>
        </li>
  </div>
</template>
<script>
export default {
name:'Item',
props:['itemA']
}
</script>
<style>
 /*item*/
  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>

3.案列_添加todo (父APP.vue传递方法)

当我们在输入框输入文字之后,然后按下回车键。就在列表中添加数据。子类调用父类的时候一定要使用this.父类传递来的方法

Header.vue

<template>
  <div>
          <!-- 1.头部 -->
      <div class="todo-header">
        <input type="text" placeholder="请输入你的任务名称,按回车键确认" v-model="textA" @keyup.enter="add"/>
      </div>
  </div>
</template>
<script>
export default {
    name:'Header',
    data() {
      return {
        textA:''
      }
    },
    methods: {
      add(){
        if(this.textA!==null){
           // 根据用户的输入生成一个todo对象
        const todo={id:Date.now(),name:this.textA,done:false}
        // 通知父App.vue添加这个数据
        this.addFatherA(todo)
        }
        this.textA=''
      }
    },
    // 接受父APP.vue传递过来的方法
    props:['addFatherA']
}
</script>
<style scoped>
  /*header*/
  .todo-header input {
    width: 560px;
    height: 28px;
    font-size: 14px;
    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>

App.vue

<template>
  <div>
   <div class="todo-container">
    <div class="todo-wrap">
      <!-- 1.头部 将父APP.VUE的addFather方法传递给子组件-->
      <Header :addFatherA="addFather"/>
      <!-- 2.列表 : 将父APP.VUE的todos数组传递给子组件-->
      <list :todosA="todos"/>
      <!-- 3.底部导航 -->
      <Footer/>
    </div>
  </div>
  </div>
</template>
<script>
  // 1.引入组件
  import Header from './components/Header.vue'
  import List from './components/List.vue'
  import Footer from './components/Footer.vue'
export default {
  name:'App', // 目的是在浏览器VUE插件中的名字都是App不会被改变。
  // 2.注册组件
  components:{
    Header,
    List,
    Footer
  },
  data() {
    return {
      todos:[
        {id:'001',name:'抽烟',done:true},
        {id:'002',name:'喝酒',done:false},
        {id:'003',name:'烫头',done:true}
      ]
    }
  },
  methods: {
    addFather(todoObj){
      // 这个方法是对todos这个数组的尾部追加对象todoObj
      this.todos.unshift(todoObj)
    }
  },
}
</script>
<style>
  /*base*/
  body {
    background: #fff;
  }
  .btn {
    display: inline-block;
    padding: 4px 12px;
    margin-bottom: 0;
    font-size: 14px;
    line-height: 20px;
    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>

4.案列_Item高亮

(1).鼠标到达的地方出现残影
1. 利用三元运算符进行残影的确定。
<li @mouseenter="isEnter=true" @mouseleave="isEnter=false" :class="isEnter ? 'high_light' : ''">
2.知道样式却不知道用不用
<li @mouseenter="isEnter=true" @mouseleave="isEnter=false" :class="{'high_light':isEnter}">
<template>
  <div>
        <!-- 鼠标进入与鼠标移除的值是: boolean值。 利用三元运算符进行判定是否使用样式 -->
        <!-- <li @mouseenter="isEnter=true" @mouseleave="isEnter=false" :class="isEnter ? 'high_light' : ''"> -->
        <li @mouseenter="isEnter=true" @mouseleave="isEnter=false" :class="{'high_light':isEnter}">
          <label>
            <input type="checkbox" :checked="this.itemA.done"/>
            <span>{{this.itemA.name}}</span>
          </label>
          <button class="btn btn-danger" style="display:none">删除</button>
        </li>
  </div>
</template>
<script>
export default {
name:'Item',
props:['itemA'],
data() {
  return {
    isEnter:false // 标识鼠标是否移入
  }
},
}
</script>
<style>
 /*item*/
  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;
  }
  .high_light {
    background-color: bisque;
  }
</style>

(2).鼠标达到的地方出现残影+按钮展现

如果样式里面是: 用的是对象那么有下划线的地方需要使用单引号。否则可以省略,但建议都加上单引号

<!-- 想要动态的处理一定要进行 : 然后利用三元运算符进行判断是否要展示按钮block展示,none不展示-->
 <button class="btn btn-danger" :style="{'display':isEnter ? 'block' : 'none'}">删除</button>
<template>
  <div>
        <!-- 鼠标进入与鼠标移除的值是: boolean值。 利用三元运算符进行判定是否使用样式 -->
        <!-- <li @mouseenter="isEnter=true" @mouseleave="isEnter=false" :class="isEnter ? 'high_light' : ''"> -->
        <li @mouseenter="isEnter=true" @mouseleave="isEnter=false" :class="{'high_light':isEnter}">
          <label>
            <input type="checkbox" :checked="this.itemA.done"/>
            <span>{{this.itemA.name}}</span>
          </label>
          <!-- 想要动态的处理一定要进行 : 然后利用三元运算符进行判断是否要展示按钮block展示,none不展示-->
          <button class="btn btn-danger" :style="{'display':isEnter ? 'block' : 'none'}">删除</button>
        </li>
  </div>
</template>
<script>
export default {
name:'Item',
props:['itemA'],
data() {
  return {
    isEnter:false // 标识鼠标是否移入
  }
},
}
</script>
<style>
 /*item*/
  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;
  }
  .high_light {
    background-color: bisque;
  }
</style>

5.案列_勾选todo

(1).利用id去更新一个数组

Item.vue

主要负责通知App.vue和接受App.vue传递过来的方法

updateFather(id,doneA){
      // 利用map遍历数组todos,获取到的分别是数组对象和对象坐标
      this.todos=this.todos.map((value,index)=>{ // 这里的this是window,匿名
          if(id===value.id){  // 假如说子类传递过来的值和map遍历的值一样,那么就返回 所有的属性+修改后的值
              return {...value,done:doneA}
          }else{
            return value
          }
      })
    }
<template>
  <div>
        <!-- 鼠标进入与鼠标移除的值是: boolean值。 利用三元运算符进行判定是否使用样式 -->
        <!-- <li @mouseenter="isEnter=true" @mouseleave="isEnter=false" :class="isEnter ? 'high_light' : ''"> -->
        <li @mouseenter="isEnter=true" @mouseleave="isEnter=false" :class="{'high_light':isEnter}">
          <label>
            <input type="checkbox" :checked="this.itemA.done" @click="update(itemA.id,$event)"/>
            <span>{{this.itemA.name}}</span>
          </label>
          <!-- 想要动态的处理一定要进行 : 然后利用三元运算符进行判断是否要展示按钮block展示,none不展示-->
          <button class="btn btn-danger" :style="{'display':isEnter ? 'block' : 'none'}">删除</button>
        </li>
  </div>
</template>
<script>
export default {
name:'Item',
props:['itemA','updateFatherB'],
data() {
  return {
    isEnter:false // 标识鼠标是否移入
  }
},
methods: {
  update(id,event){
    // 获取勾选的id 和勾选的值。这里因为属性是checked所以我们获取到的应该是checked而不是value。
    console.log(id,event.target.checked);
    // 2.通知父App.vue
    this.updateFatherB(id,event.target.checked)
  }
},
}
</script>
<style>
 /*item*/
  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;
  }
  .high_light {
    background-color: bisque;
  }
</style>

App.vue

主要负责给子类List.vue传递勾选的方法

<template>
  <div>
   <div class="todo-container">
    <div class="todo-wrap">
      <!-- 1.头部 将父APP.VUE的addFather方法传递给子组件-->
      <Header :addFatherA="addFather"/>
      <!-- 2.列表 : 将父APP.VUE的todos数组传递给子组件-->
      <list :todosA="todos" :updateFatherA="updateFather"/>
      <!-- 3.底部导航 -->
      <Footer/>
    </div>
  </div>
  </div>
</template>
<script>
  // 1.引入组件
  import Header from './components/Header.vue'
  import List from './components/List.vue'
  import Footer from './components/Footer.vue'
export default {
  name:'App', // 目的是在浏览器VUE插件中的名字都是App不会被改变。
  // 2.注册组件
  components:{
    Header,
    List,
    Footer
  },
  data() {
    return {
      todos:[
        {id:'001',name:'抽烟',done:true},
        {id:'002',name:'喝酒',done:false},
        {id:'003',name:'烫头',done:true}
      ]
    }
  },
  methods: {
    addFather(todoObj){
      // 这个方法是对todos这个数组的尾部追加对象todoObj
      this.todos.unshift(todoObj)
    },
    updateFather(id,doneA){
      // 利用map遍历数组todos,获取到的分别是数组对象和对象坐标
      this.todos=this.todos.map((value,index)=>{
          if(id===value.id){  // 假如说子类传递过来的值和map遍历的值一样,那么就返回 所有的属性+修改后的值
              return {...value,done:doneA}
          }else{
            return value
          }
      })
    }
  },
}
</script>
<style>
  /*base*/
  body {
    background: #fff;
  }
  .btn {
    display: inline-block;
    padding: 4px 12px;
    margin-bottom: 0;
    font-size: 14px;
    line-height: 20px;
    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>

List.vue

主要负责当桥梁因为父App.vue不能直接传递给Item.vue。需要借助List.vue传递给Item.vue

<template>
  <div>
    <ul class="todo-main">
        <!-- 这里我们必须需要添加key -->
        <Item v-for="t in this.todosA" :key="t.id" :itemA="t" :updateFatherB="updateFatherA"/>
      </ul>
  </div>
</template>
<script>
    import Item from './Item.vue'
export default {
    name:'List',
    components:{
        Item,
    },
    // 接受更新的函数
    props:['todosA','updateFatherA'],  // 负责声明接受且不做限制...
}
</script>
<style scoped>
  /*List*/
  .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>

(2).利用index去更新一个数组

App.vue

<template>
  <div>
   <div class="todo-container">
    <div class="todo-wrap">
      <!-- 1.头部 将父APP.VUE的addFather方法传递给子组件-->
      <Header :addFatherA="addFather"/>
      <!-- 2.列表 : 将父APP.VUE的todos数组传递给子组件-->
      <list :todosA="todos" :updateFatherA="updateFather"/>
      <!-- 3.底部导航 -->
      <Footer/>
    </div>
  </div>
  </div>
</template>
<script>
  // 1.引入组件
  import Header from './components/Header.vue'
  import List from './components/List.vue'
  import Footer from './components/Footer.vue'
export default {
  name:'App', // 目的是在浏览器VUE插件中的名字都是App不会被改变。
  // 2.注册组件
  components:{
    Header,
    List,
    Footer
  },
  data() {
    return {
      todos:[
        {id:'001',name:'抽烟',done:true},
        {id:'002',name:'喝酒',done:false},
        {id:'003',name:'烫头',done:true}
      ]
    }
  },
  methods: {
    addFather(todoObj){
      // 这个方法是对todos这个数组的尾部追加对象todoObj
      this.todos.unshift(todoObj)
    },
/*    利用id
    updateFather(id,doneA){
      // 利用map遍历数组todos,获取到的分别是数组对象和对象坐标
      this.todos=this.todos.map((value,index)=>{
          if(id===value.id){  // 假如说子类传递过来的值和map遍历的值一样,那么就返回 所有的属性+修改后的值
              return {...value,done:doneA}
          }else{
            return value
          }
      })
    }
*/
    updateFather(index,doneA){
      this.todos[index].done=doneA
    }
  },
}
</script>
<style>
  /*base*/
  body {
    background: #fff;
  }
  .btn {
    display: inline-block;
    padding: 4px 12px;
    margin-bottom: 0;
    font-size: 14px;
    line-height: 20px;
    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>

List.vue

在遍历的时候: 我们添加一个index.并且将这个index传递给Item.vue
• 1
<template>
  <div>
    <ul class="todo-main">
        <!-- 这里我们必须需要添加key -->
        <Item v-for="(t,index) in this.todosA" :key="t.id" 
        :itemA="t" 
        :updateFatherB="updateFatherA"
        :indexB="index"  
        />
      </ul>
  </div>
</template>
<script>
    import Item from './Item.vue'
export default {
    name:'List',
    components:{
        Item,
    },
    // 接受更新的函数
    props:['todosA','updateFatherA'],  // 负责声明接受且不做限制...
}
</script>
<style scoped>
  /*List*/
  .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>

Item.vue

接受传递过来的index,并通知父App.vue

<template>
  <div>
        <!-- 鼠标进入与鼠标移除的值是: boolean值。 利用三元运算符进行判定是否使用样式 -->
        <!-- <li @mouseenter="isEnter=true" @mouseleave="isEnter=false" :class="isEnter ? 'high_light' : ''"> -->
        <li @mouseenter="isEnter=true" @mouseleave="isEnter=false" :class="{'high_light':isEnter}">
          <label>
            <input type="checkbox" :checked="this.itemA.done" @click="update(indexB,$event)"/>
            <span>{{this.itemA.name}}</span>
          </label>
          <!-- 想要动态的处理一定要进行 : 然后利用三元运算符进行判断是否要展示按钮block展示,none不展示-->
          <button class="btn btn-danger" :style="{'display':isEnter ? 'block' : 'none'}">删除</button>
        </li>
  </div>
</template>
<script>
export default {
name:'Item',
props:['itemA','updateFatherB','indexB'],
data() {
  return {
    isEnter:false // 标识鼠标是否移入
  }
},
methods: {
  /*  利用id
  update(id,event){
    // 获取勾选的id 和勾选的值。这里因为属性是checked所以我们获取到的应该是checked而不是value。
    console.log(id,event.target.checked);
    // 2.通知父App.vue
    this.updateFatherB(id,event.target.checked)
  }*/
    // 利用index
    update(index,event){
    // 获取勾选的id 和勾选的值。这里因为属性是checked所以我们获取到的应该是checked而不是value。
    console.log(index,event.target.checked);
    // 2.通知父App.vue
    this.updateFatherB(index,event.target.checked)
  }
},
}
</script>
<style>
 /*item*/
  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;
  }
  .high_light {
    background-color: bisque;
  }
</style>

(3).利用v-model更新数组
  1. 发现控制台输出的数据和实质不一样 ⭐
// 利用index
    update(index,event){
    // 获取勾选的id 和勾选的值。这里因为属性是checked所以我们获取到的应该是checked而不是value。
    console.log(index,this.isChecked);
    // 2.通知父App.vue
    this.updateFatherB(index,event.target.checked)
  }
<template>
  <div>
        <!-- 鼠标进入与鼠标移除的值是: boolean值。 利用三元运算符进行判定是否使用样式 -->
        <!-- <li @mouseenter="isEnter=true" @mouseleave="isEnter=false" :class="isEnter ? 'high_light' : ''"> -->
        <li @mouseenter="isEnter=true" @mouseleave="isEnter=false" :class="{'high_light':isEnter}">
          <label>
            <input type="checkbox" v-model="isChecked" @click="update(indexB,$event)"/>
            <span>{{this.itemA.name}}</span>
          </label>
          <!-- 想要动态的处理一定要进行 : 然后利用三元运算符进行判断是否要展示按钮block展示,none不展示-->
          <button class="btn btn-danger" :style="{'display':isEnter ? 'block' : 'none'}">删除</button>
        </li>
  </div>
</template>
<script>
export default {
name:'Item',
props:['itemA','updateFatherB','indexB'],
data() {
  return {
    isEnter:false, // 标识鼠标是否移入
    isChecked:this.itemA.done
  }
},
methods: {
  /*  利用id
  update(id,event){
    // 获取勾选的id 和勾选的值。这里因为属性是checked所以我们获取到的应该是checked而不是value。
    console.log(id,event.target.checked);
    // 2.通知父App.vue
    this.updateFatherB(id,event.target.checked)
  }*/
    // 利用index
    update(index,event){
    // 获取勾选的id 和勾选的值。这里因为属性是checked所以我们获取到的应该是checked而不是value。
    console.log(index,this.isChecked);
    // 2.通知父App.vue
    this.updateFatherB(index,event.target.checked)
  }
},
}
</script>
<style>
 /*item*/
  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;
  }
  .high_light {
    background-color: bisque;
  }
</style>

  1. 解决控制台输出的数据和实质不一样 ⭐

原因是vue事件优先,所以点击事件先被调用之后,才是model

直接打印出事件的值而不是data区域的数据
    // 利用index
    update(index,event){
    // 获取勾选的id 和勾选的值。这里因为属性是checked所以我们获取到的应该是checked而不是value。
    console.log(index,event.target.checked);
    // 2.通知父App.vue
    this.updateFatherB(index,event.target.checked)
  }
<template>
  <div>
        <!-- 鼠标进入与鼠标移除的值是: boolean值。 利用三元运算符进行判定是否使用样式 -->
        <!-- <li @mouseenter="isEnter=true" @mouseleave="isEnter=false" :class="isEnter ? 'high_light' : ''"> -->
        <li @mouseenter="isEnter=true" @mouseleave="isEnter=false" :class="{'high_light':isEnter}">
          <label>
            <input type="checkbox" v-model="isChecked" @click="update(indexB,$event)"/>
            <span>{{this.itemA.name}}</span>
          </label>
          <!-- 想要动态的处理一定要进行 : 然后利用三元运算符进行判断是否要展示按钮block展示,none不展示-->
          <button class="btn btn-danger" :style="{'display':isEnter ? 'block' : 'none'}">删除</button>
        </li>
  </div>
</template>
<script>
export default {
name:'Item',
props:['itemA','updateFatherB','indexB'],
data() {
  return {
    isEnter:false, // 标识鼠标是否移入
    isChecked:this.itemA.done
  }
},
methods: {
  /*  利用id
  update(id,event){
    // 获取勾选的id 和勾选的值。这里因为属性是checked所以我们获取到的应该是checked而不是value。
    console.log(id,event.target.checked);
    // 2.通知父App.vue
    this.updateFatherB(id,event.target.checked)
  }*/
    // 利用index
    update(index,event){
    // 获取勾选的id 和勾选的值。这里因为属性是checked所以我们获取到的应该是checked而不是value。
    console.log(index,event.target.checked);
    // 2.通知父App.vue
    this.updateFatherB(index,event.target.checked)
  }
},
}
</script>
<style>
 /*item*/
  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;
  }
  .high_light {
    background-color: bisque;
  }
</style>

6.案列_删除

App.vue: 传递给List.vue删除的方法"

deleteFather(index){
    // 根据坐标删除数据
    this.todos.splice(index,1)
  }
<template>
  <div>
   <div class="todo-container">
    <div class="todo-wrap">
      <!-- 1.头部 将父APP.VUE的addFather方法传递给子组件-->
      <Header :addFatherA="addFather"/>
      <!-- 2.列表 : 将父APP.VUE的todos数组传递给子组件-->
      <list 
      :todosA="todos" 
      :updateFatherA="updateFather"
      :deleteFatherA="deleteFather"
      />
      <!-- 3.底部导航 -->
      <Footer/>
    </div>
  </div>
  </div>
</template>
<script>
  // 1.引入组件
  import Header from './components/Header.vue'
  import List from './components/List.vue'
  import Footer from './components/Footer.vue'
export default {
  name:'App', // 目的是在浏览器VUE插件中的名字都是App不会被改变。
  // 2.注册组件
  components:{
    Header,
    List,
    Footer
  },
  data() {
    return {
      todos:[
        {id:'001',name:'抽烟',done:true},
        {id:'002',name:'喝酒',done:false},
        {id:'003',name:'烫头',done:true}
      ]
    }
  },
  methods: {
    addFather(todoObj){
      // 这个方法是对todos这个数组的尾部追加对象todoObj
      this.todos.unshift(todoObj)
    },
/*    利用id
    updateFather(id,doneA){
      // 利用map遍历数组todos,获取到的分别是数组对象和对象坐标
      this.todos=this.todos.map((value,index)=>{
          if(id===value.id){  // 假如说子类传递过来的值和map遍历的值一样,那么就返回 所有的属性+修改后的值
              return {...value,done:doneA}
          }else{
            return value
          }
      })
    }
*/
    updateFather(index,doneA){
      this.todos[index].done=doneA
    },
    deleteFather(index){
    // 根据坐标删除数据
    this.todos.splice(index,1)
  }
  },
}
</script>
<style>
  /*base*/
  body {
    background: #fff;
  }
  .btn {
    display: inline-block;
    padding: 4px 12px;
    margin-bottom: 0;
    font-size: 14px;
    line-height: 20px;
    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>

List.vue: 传递给Item.vue的删除方法

<template>
  <div>
    <ul class="todo-main">
        <!-- 这里我们必须需要添加key -->
        <Item v-for="(t,index) in this.todosA" :key="t.id" 
        :itemA="t" 
        :updateFatherB="updateFatherA"
        :indexB="index"
        :deleteFatherB="deleteFatherA"  
        />
      </ul>
  </div>
</template>
<script>
    import Item from './Item.vue'
export default {
    name:'List',
    components:{
        Item,
    },
    // 接受更新的函数
    props:['todosA','updateFatherA','deleteFatherA'],  // 负责声明接受且不做限制...
}
</script>
<style scoped>
  /*List*/
  .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>

Item.vue: 通知父App.vue传进来一个删除的方法

<template>
  <div>
    <ul class="todo-main">
        <!-- 这里我们必须需要添加key -->
        <Item v-for="(t,index) in this.todosA" :key="t.id" 
        :itemA="t" 
        :updateFatherB="updateFatherA"
        :indexB="index"
        :deleteFatherB="deleteFatherA"  
        />
      </ul>
  </div>
</template>
<script>
    import Item from './Item.vue'
export default {
    name:'List',
    components:{
        Item,
    },
    // 接受更新的函数
    props:['todosA','updateFatherA','deleteFatherA'],  // 负责声明接受且不做限制...
}
</script>
<style scoped>
  /*List*/
  .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>

7.案列_全选

(1).案列总数与已选总数

App.vue传递Footer.vue数组

<!-- 3.底部导航 -->
      <Footer 
      :FooterTodos="todos"/>
<template>
  <div>
   <div class="todo-container">
    <div class="todo-wrap">
      <!-- 1.头部 将父APP.VUE的addFather方法传递给子组件-->
      <Header :addFatherA="addFather"/>
      <!-- 2.列表 : 将父APP.VUE的todos数组传递给子组件-->
      <list 
      :todosA="todos" 
      :updateFatherA="updateFather"
      :deleteFatherA="deleteFather"
      />
      <!-- 3.底部导航 -->
      <Footer 
      :FooterTodos="todos"/>
    </div>
  </div>
  </div>
</template>
<script>
  // 1.引入组件
  import Header from './components/Header.vue'
  import List from './components/List.vue'
  import Footer from './components/Footer.vue'
export default {
  name:'App', // 目的是在浏览器VUE插件中的名字都是App不会被改变。
  // 2.注册组件
  components:{
    Header,
    List,
    Footer
  },
  data() {
    return {
      todos:[
        {id:'001',name:'抽烟',done:true},
        {id:'002',name:'喝酒',done:false},
        {id:'003',name:'烫头',done:true}
      ]
    }
  },
  methods: {
    addFather(todoObj){
      // 这个方法是对todos这个数组的尾部追加对象todoObj
      this.todos.unshift(todoObj)
    },
/*    利用id
    updateFather(id,doneA){
      // 利用map遍历数组todos,获取到的分别是数组对象和对象坐标
      this.todos=this.todos.map((value,index)=>{
          if(id===value.id){  // 假如说子类传递过来的值和map遍历的值一样,那么就返回 所有的属性+修改后的值
              return {...value,done:doneA}
          }else{
            return value
          }
      })
    }
*/
    updateFather(index,doneA){
      this.todos[index].done=doneA
    },
    deleteFather(index){
    // 根据坐标删除数据
    this.todos.splice(index,1)
  }
  },
}
</script>
<style>
  /*base*/
  body {
    background: #fff;
  }
  .btn {
    display: inline-block;
    padding: 4px 12px;
    margin-bottom: 0;
    font-size: 14px;
    line-height: 20px;
    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>

Footer.vue

利用计算属性: 获取总数和
computed:{
    doneCount(){
      // 数组的所有方法以及定时函数的所有方法都是归window管理的
      // 数组.reduce(累加数据,初始值) js自带的累加器
        return this.FooterTodos.reduce((pre,current)=>pre+=current.done ? 1: 0,0) // 匿名方法的参数:(上次调用函数的返回值,当前元素)
    },
    total(){
        return this.FooterTodos.length;
    }
}
<template>
  <div>
    <div class="todo-footer">
        <label>
          <input type="checkbox"/>
        </label>
        <span>
          <span>已完成{{doneCount}}</span> / 全部{{total}}
        </span>
        <button class="btn btn-danger">清除已完成任务</button>
      </div>
  </div>
</template>
<script>
export default {
name:'Footer',
// 声明
props:['FooterTodos'],
// 1.利用计算属性进行获取已完成
computed:{
    doneCount(){
      // 数组的所有方法以及定时函数的所有方法都是归window管理的
      // 数组.reduce(累加数据,初始值) js自带的累加器
        return this.FooterTodos.reduce((pre,current)=>pre+=current.done ? 1: 0,0) // 匿名方法的参数:(上次调用函数的返回值,当前元素)
    },
    total(){
        return this.FooterTodos.length;
    }
}
}
</script>
<style scoped>
 /*footer*/
  .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>

(2).案列全选

App.vue: 传递一个方法

<template>
  <div>
   <div class="todo-container">
    <div class="todo-wrap">
      <!-- 1.头部 将父APP.VUE的addFather方法传递给子组件-->
      <Header :addFatherA="addFather"/>
      <!-- 2.列表 : 将父APP.VUE的todos数组传递给子组件-->
      <list 
      :todosA="todos" 
      :updateFatherA="updateFather"
      :deleteFatherA="deleteFather"
      />
      <!-- 3.底部导航 -->
      <Footer 
      :FooterTodos="todos"
      :updateAllFatherA="updateAllFather"
      />
    </div>
  </div>
  </div>
</template>
<script>
  // 1.引入组件
  import Header from './components/Header.vue'
  import List from './components/List.vue'
  import Footer from './components/Footer.vue'
export default {
  name:'App', // 目的是在浏览器VUE插件中的名字都是App不会被改变。
  // 2.注册组件
  components:{
    Header,
    List,
    Footer
  },
  data() {
    return {
      todos:[
        {id:'001',name:'抽烟',done:true},
        {id:'002',name:'喝酒',done:false},
        {id:'003',name:'烫头',done:true}
      ]
    }
  },
  methods: {
    addFather(todoObj){
      // 这个方法是对todos这个数组的尾部追加对象todoObj
      this.todos.unshift(todoObj)
    },
/*    利用id
    updateFather(id,doneA){
      // 利用map遍历数组todos,获取到的分别是数组对象和对象坐标
      this.todos=this.todos.map((value,index)=>{
          if(id===value.id){  // 假如说子类传递过来的值和map遍历的值一样,那么就返回 所有的属性+修改后的值
              return {...value,done:doneA}
          }else{
            return value
          }
      })
    }
*/
  // 更新
    updateFather(index,doneA){
      this.todos[index].done=doneA
    },
    // 删除
    deleteFather(index){
    // 根据坐标删除数据
    this.todos.splice(index,1)
  },
  // 全选或者不选
  updateAllFather(doneOption){
    // map():创建一个新数组,这个新数组由原数组中的每个元素都调用一次提供的函数后的返回值组成。
      this.todos=this.todos.map((value)=>{  // 这个参数是遍历的单个对象
        return {...value,done:doneOption} // 返回将done属性改编为doneOption的完整对象
      })
  }
  },
}
</script>
<style>
  /*base*/
  body {
    background: #fff;
  }
  .btn {
    display: inline-block;
    padding: 4px 12px;
    margin-bottom: 0;
    font-size: 14px;
    line-height: 20px;
    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>

Footer.vue: 接受一个方法

// 计算属性是顺序的,也就是可以套娃
    isAll:{
      // 全选的效果
      set(flag){ //获取到的是一个布尔值
      console.log(flag)
            // 通知父App.vue传递方法
            return this.updateAllFatherA(flag)
      },
      // 
      get(){
          return this.doneCount===this.total && this.total>0
        }
<template>
  <div>
    <div class="todo-footer">
        <label>
          <!-- 类型为checkbox 的值是布尔值 -->
          <input type="checkbox" v-model="isAll"/>
        </label>
        <span>
          <span>已完成{{doneCount}}</span> / 全部{{total}}
        </span>
        <button class="btn btn-danger">清除已完成任务</button>
      </div>
  </div>
</template>
<script>
export default {
name:'Footer',
// 声明
props:['FooterTodos','updateAllFatherA'],
// 1.利用计算属性进行获取已完成
computed:{
    // 被选择的
    doneCount(){
      // 数组的所有方法以及定时函数的所有方法都是归window管理的
      // 数组.reduce(累加数据,初始值) js自带的累加器
        return this.FooterTodos.reduce((pre,current)=>pre+=current.done ? 1: 0,0) // 匿名方法的参数:(上次调用函数的返回值,当前元素)
    },
    // 总数
    total(){
        return this.FooterTodos.length;
    },
    // 计算属性是顺序的,也就是可以套娃
    isAll:{
      // 全选的效果
      set(flag){ //获取到的是一个布尔值
      console.log(flag)
            // 通知父App.vue传递方法
            return this.updateAllFatherA(flag)
      },
      // 
      get(){
          return this.doneCount===this.total && this.total>0
        }
   }
}
}
</script>
<style scoped>
 /*footer*/
  .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>

8.案列_清除所有已完成的

App.vue

// 清除已经已经勾选的
   clearAllDoneFather(){
    this.todos=this.todos.filter((value)=>{
      // 假如说done值为false就不用过滤-保留,否则就需要过滤-不保留
      return value.done===false
    })
   }
<template>
  <div>
   <div class="todo-container">
    <div class="todo-wrap">
      <!-- 1.头部 将父APP.VUE的addFather方法传递给子组件-->
      <Header :addFatherA="addFather"/>
      <!-- 2.列表 : 将父APP.VUE的todos数组传递给子组件-->
      <list 
      :todosA="todos" 
      :updateFatherA="updateFather"
      :deleteFatherA="deleteFather"
      />
      <!-- 3.底部导航 -->
      <Footer 
      :FooterTodos="todos"
      :updateAllFatherA="updateAllFather"
      :clearAllDoneFatherA="clearAllDoneFather"
      />
    </div>
  </div>
  </div>
</template>
<script>
  // 1.引入组件
  import Header from './components/Header.vue'
  import List from './components/List.vue'
  import Footer from './components/Footer.vue'
export default {
  name:'App', // 目的是在浏览器VUE插件中的名字都是App不会被改变。
  // 2.注册组件
  components:{
    Header,
    List,
    Footer
  },
  data() {
    return {
      todos:[
        {id:'001',name:'抽烟',done:true},
        {id:'002',name:'喝酒',done:false},
        {id:'003',name:'烫头',done:true}
      ]
    }
  },
  methods: {
    addFather(todoObj){
      // 这个方法是对todos这个数组的尾部追加对象todoObj
      this.todos.unshift(todoObj)
    },
/*    利用id
    updateFather(id,doneA){
      // 利用map遍历数组todos,获取到的分别是数组对象和对象坐标
      this.todos=this.todos.map((value,index)=>{
          if(id===value.id){  // 假如说子类传递过来的值和map遍历的值一样,那么就返回 所有的属性+修改后的值
              return {...value,done:doneA}
          }else{
            return value
          }
      })
    }
*/
  // 更新
    updateFather(index,doneA){
      this.todos[index].done=doneA
    },
    // 删除
    deleteFather(index){
    // 根据坐标删除数据
    this.todos.splice(index,1)
  },
  // 全选或者不选
  updateAllFather(doneOption){
    // map():创建一个新数组,这个新数组由原数组中的每个元素都调用一次提供的函数后的返回值组成。
      this.todos=this.todos.map((value)=>{  // 这个参数是遍历的单个对象
        return {...value,done:doneOption} // 返回将done属性改编为doneOption的完整对象
      })
  },
   // 清除已经已经勾选的
   clearAllDoneFather(){
    this.todos=this.todos.filter((value)=>{
      // 假如说done值为false就不用过滤-保留,否则就需要过滤-不保留
      return value.done===false
    })
   }
  },
}
</script>
<style>
  /*base*/
  body {
    background: #fff;
  }
  .btn {
    display: inline-block;
    padding: 4px 12px;
    margin-bottom: 0;
    font-size: 14px;
    line-height: 20px;
    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>

Footer.vue

<template>
  <div>
    <div class="todo-footer">
        <label>
          <!-- 类型为checkbox 的值是布尔值 -->
          <input type="checkbox" v-model="isAll"/>
        </label>
        <span>
          <span>已完成{{doneCount}}</span> / 全部{{total}}
        </span>
        <button class="btn btn-danger" @click="clearAllDone">清除已完成任务</button>
      </div>
  </div>
</template>
<script>
export default {
name:'Footer',
// 声明
props:['FooterTodos','updateAllFatherA','clearAllDoneFatherA'],
// 1.利用计算属性进行获取已完成
computed:{
    // 被选择的
    doneCount(){
      // 数组的所有方法以及定时函数的所有方法都是归window管理的
      // 数组.reduce(累加数据,初始值) js自带的累加器
        return this.FooterTodos.reduce((pre,current)=>pre+=current.done ? 1: 0,0) // 匿名方法的参数:(上次调用函数的返回值,当前元素)
    },
    // 总数
    total(){
        return this.FooterTodos.length;
    },
    // 计算属性是顺序的,也就是可以套娃
    isAll:{
      // 全选的效果
      set(flag){ //获取到的是一个布尔值
      console.log(flag)
            // 通知父App.vue传递方法
            return this.updateAllFatherA(flag)
      },
      // 
      get(){
          return this.doneCount===this.total && this.total>0
        }  
   }
},
methods: {
  clearAllDone(){
    if(confirm('确定删除已经完成的任务么?')){
      // 通知父组件安排方法
        this.clearAllDoneFatherA();
    }
  }
},
}
</script>
<style scoped>
 /*footer*/
  .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>

9.todoList 小结⭐⭐⭐⭐

(1).filter过滤数组的总结
array.filter(function(currentValue,index,arr), thisValue);

3、参数

返回值

4、用法

filter() 方法用于把Array中的某些元素过滤掉,然后返回剩下的未被过滤掉的元素。

5、注意事项

(1)filter() 不会对空数组进行检测;

(2)filter() 不会改变原始数组。

6、如果返回值为true就不过滤掉-保留、否则就过滤-不保留

  1. 简单实列

1.返回数组nums中所有大于5的元素

let nums = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10];
let res = nums.filter((num) => {
  return num > 5;
});
console.log(res);  // [6, 7, 8, 9, 10]
  1. 过滤数组对象中done值不为false的值、
arrA=arrB.filter((value)=>{
      // 假如说done值为false就不用过滤-保留,否则就需要过滤-不保留
      return value.done===false
    })
  1. 模糊查询

name.indexOf(keywords) 该方法将从头到尾地检索数组,看它是否含有对应的元素。如果在数组中没找到指定元素则返回 -1

filter的参数为一个的时候,return 和{}可以同时省略

fmtPersons(){
     const {keywords} = this;    //这里相当于 const keywords=this.keywords;
     return this.persons.filter(p=>p.name.indexOf(keywords)!==-1);
    }
fmtPersons(){
     const {keywords} = this;    //这里相当于 const keywords=this.keywords;
     return this.persons.filter((p)=>{ return p.name.indexOf(keywords)!==-1});
                }
(2).map过滤数组的总结
  1. 遍历数组对象

里面的参数第一个遍历的对象与所在的坐标

返回值: {...value,done:doneOption} 就是除了将原有对象属性done改为doneOption之外其他都不变的对象

updateAllFather(doneOption){
    // map():创建一个新数组,这个新数组由原数组中的每个元素都调用一次提供的函数后的返回值组成。
      this.todos=this.todos.map((value)=>{  // 这个参数是遍历的单个对象
        return {...value,done:doneOption} // 返回将done属性改编为doneOption的完整对象
      })
  },
(3).增、删、改
1. 根据坐标更改某一个对象的值
  // 更新
    updateFather(index,doneA){
      arr[index].done=doneA
    },
2.根据坐标删除某一个对象
    // 删除
    deleteFather(index){
    // 根据坐标删除数据
    arr.splice(index,1)
  },  
3.向某个数组的头部添加
    addFather(todoObj){
      // 这个方法是对todos这个数组的尾部追加对象todoObj
      arr.unshift(todoObj)
    }, 


相关文章
|
11天前
|
JavaScript
vue使用iconfont图标
vue使用iconfont图标
77 1
|
22天前
|
JavaScript 关系型数据库 MySQL
基于VUE的校园二手交易平台系统设计与实现毕业设计论文模板
基于Vue的校园二手交易平台是一款专为校园用户设计的在线交易系统,提供简洁高效、安全可靠的二手商品买卖环境。平台利用Vue框架的响应式数据绑定和组件化特性,实现用户友好的界面,方便商品浏览、发布与管理。该系统采用Node.js、MySQL及B/S架构,确保稳定性和多功能模块设计,涵盖管理员和用户功能模块,促进物品循环使用,降低开销,提升环保意识,助力绿色校园文化建设。
|
2月前
|
JavaScript 前端开发 开发者
vue学习第一章
欢迎来到我的博客!我是瑞雨溪,一名热爱前端的大一学生,专注于JavaScript与Vue,正向全栈进发。博客分享Vue学习心得、命令式与声明式编程对比、列表展示及计数器案例等。关注我,持续更新中!🎉🎉🎉
52 1
vue学习第一章
|
2月前
|
JavaScript 前端开发 索引
vue学习第三章
欢迎来到瑞雨溪的博客,一名热爱JavaScript与Vue的大一学生。本文介绍了Vue中的v-bind指令,包括基本使用、动态绑定class及style等,希望能为你的前端学习之路提供帮助。持续关注,更多精彩内容即将呈现!🎉🎉🎉
49 1
|
2月前
|
缓存 JavaScript 前端开发
vue学习第四章
欢迎来到我的博客!我是瑞雨溪,一名热爱JavaScript与Vue的大一学生。本文介绍了Vue中计算属性的基本与复杂使用、setter/getter、与methods的对比及与侦听器的总结。如果你觉得有用,请关注我,将持续更新更多优质内容!🎉🎉🎉
41 1
vue学习第四章
|
2月前
|
JavaScript 前端开发 算法
vue学习第7章(循环)
欢迎来到瑞雨溪的博客,一名热爱JavaScript和Vue的大一学生。本文介绍了Vue中的v-for指令,包括遍历数组和对象、使用key以及数组的响应式方法等内容,并附有综合练习实例。关注我,将持续更新更多优质文章!🎉🎉🎉
35 1
vue学习第7章(循环)
|
2月前
|
JavaScript 前端开发
vue学习第九章(v-model)
欢迎来到我的博客,我是瑞雨溪,一名热爱JavaScript与Vue的大一学生,自学前端2年半,正向全栈进发。此篇介绍v-model在不同表单元素中的应用及修饰符的使用,希望能对你有所帮助。关注我,持续更新中!🎉🎉🎉
40 1
vue学习第九章(v-model)
|
2月前
|
JavaScript 前端开发 开发者
vue学习第十章(组件开发)
欢迎来到瑞雨溪的博客,一名热爱JavaScript与Vue的大一学生。本文深入讲解Vue组件的基本使用、全局与局部组件、父子组件通信及数据传递等内容,适合前端开发者学习参考。持续更新中,期待您的关注!🎉🎉🎉
53 1
vue学习第十章(组件开发)
|
2月前
|
JavaScript 前端开发
vue学习第十一章(组件开发2)
欢迎来到我的博客,我是瑞雨溪,一名自学前端两年半的大一学生,专注于JavaScript与Vue。本文介绍Vue中的插槽(slot)使用方法,包括基本插槽、具名插槽及作用域插槽,帮助你在组件开发中实现内容的灵活定制。如果你觉得有帮助,请关注我,持续更新中!🎉🎉🎉
25 1
vue学习第十一章(组件开发2)
|
2月前
|
监控 JavaScript 前端开发
vue学习第十二章(生命周期)
欢迎来到我的博客,我是瑞雨溪,一名热爱JavaScript和Vue的大一学生。本文深入探讨了Vue实例的生命周期,从初始化到销毁各阶段的关键钩子函数及其应用场景,帮助你更好地理解Vue的工作原理。如果你觉得有帮助,欢迎关注我,将持续分享更多优质内容!🎉🎉🎉
39 1
vue学习第十二章(生命周期)