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)
    }, 


相关文章
|
3天前
|
JavaScript
Vue中如何设置在执行删除等危险操作时给用户提示(二次确认后执行对应的操作)
这篇文章介绍了在Vue项目中如何实现执行删除等危险操作时的二次确认机制,使用Element UI的`el-popconfirm`组件来弹出确认框,确保用户在二次确认后才会执行删除操作。
Vue中如何设置在执行删除等危险操作时给用户提示(二次确认后执行对应的操作)
|
3天前
|
JavaScript
如何创建一个Vue项目(手把手教你)
这篇文章是一篇手把手教读者如何创建Vue项目的教程,包括使用管理员身份打开命令行窗口、找到存放项目的位置、通过vue-cli初始化项目、填写项目信息、进入项目目录、启动项目等步骤,并提供了一些常见第三方库的引入方法。
如何创建一个Vue项目(手把手教你)
|
3天前
|
前端开发
StringBoot+Vue实现游客或用户未登录系统前、可以浏览商品等信息、但是不能购买商品或者加入购物车等操作。登录系统显示用户的登录名(源码+讲解)
这篇文章介绍了使用StringBoot+Vue实现用户登录状态判断的方法,包括前端加载用户信息和后端设置session的源码示例。
|
3天前
|
JavaScript 编译器
成功解决:Module build failed: Error: Vue packages version mismatch
这篇文章记录了解决Vue项目中遇到的"Module build failed: Error: Vue packages version mismatch"错误的步骤,原因是项目中Vue依赖的版本不一致,解决方法是删除`node_modules`后重新安装指定版本的Vue和`vue-template-compiler`,确保版本匹配,最终成功运行项目。
成功解决:Module build failed: Error: Vue packages version mismatch
|
3天前
|
JavaScript
在Vue中使用Avue、配置过程以及实际应用
这篇文章介绍了作者在Vue项目中集成Avue组件库的完整过程,包括安装、配置和实际应用,展示了如何利用Avue实现动态表单和数据展示的功能。
在Vue中使用Avue、配置过程以及实际应用
|
4天前
|
JavaScript
Vue devDependencies 与 dependencies 能别
Vue devDependencies 与 dependencies 能别
10 3
|
3天前
|
JavaScript
如何在Vue页面中引入img下的图片作为背景图。../的使用
这篇文章介绍了在Vue页面中如何引入`img`目录下的图片作为背景图,提供了两种使用相对路径的方法。第一种是使用`../assets/img/`作为路径引入图片,第二种是使用`../../assets/img/`作为路径。文章还展示了使用这些方法的代码实现和效果展示,并鼓励读者学无止境。
如何在Vue页面中引入img下的图片作为背景图。../的使用
|
3天前
|
JavaScript
如何通过点击商品的信息(图片或者文字)跳转到更加详细的商品信息介绍(前后端分离之Vue实现)
该博客文章介绍了如何在Vue 2框架下实现前后端分离的商品信息展示和详情页跳转,包括排序筛选、详情展示、加入购物车和分享功能。
如何通过点击商品的信息(图片或者文字)跳转到更加详细的商品信息介绍(前后端分离之Vue实现)
|
3天前
|
JavaScript
在Vue中使用Swiper轮播图、同时解决点击轮播图左右切换按钮不生效的问题、同时将轮播图抽离出为一个公共组件
这篇文章介绍了在Vue中如何使用Swiper插件创建轮播图,解决Swiper左右切换按钮不生效的问题,并展示了如何将Swiper轮播图抽离成一个可复用的公共组件,同时提供了详细的安装、配置和优化建议。
在Vue中使用Swiper轮播图、同时解决点击轮播图左右切换按钮不生效的问题、同时将轮播图抽离出为一个公共组件
|
3天前
|
JavaScript
Vue学习之--------插槽【默认插槽、具名插槽、作用域插槽】
这篇文章详细介绍了Vue.js中的插槽概念,包括默认插槽、具名插槽和作用域插槽的使用方式和实际应用示例,通过代码演示了如何在组件中定义和使用插槽来实现内容的灵活替换和展示。
Vue学习之--------插槽【默认插槽、具名插槽、作用域插槽】