静态资源

这是一个思路
image.png

拿到功能点之后,一定要分析一下可以拆分为几个组件

1.实现静态组件
拿到功能点之后,一定要分析一下可以拆分为几个组件
image.png

  1. <br />然后把上图的List 里面具体的每一个Todo,也都拆成一个组件

image.png

下面我们只考虑下面四个组件

App.vue

<template>
  <div id="root">
      <div class="todo-container">
        <div class="todo-wrap">
          <UserHeader></UserHeader>
          <UserList></UserList>
          <UserFooter></UserFooter>
        </div>
      </div>
  </div>
</template>

<script>
import UserHeader from "@/components/UserHeader";
import UserFooter from "@/components/UserFooter";
import UserList from "@/components/UserList";
export default {
  name: 'App',
  components : {UserHeader,UserFooter,UserList},
}
</script>

<style>

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 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;
  border-radius: 5px;
}
</style>

UserHeader.Vue

<template>
  <div  class="todo-header">
    <input type="text" placeholder="请输入你的任务名称,按回车键确认">
  </div>
</template>

<script>
export default {
  name: "UserHeader"
}
</script>

<style scoped>

    .todo-header input {
      width: 560px;
      height: 28px;
      font-size: 14px;
      border:1px solid;
      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>

UserFooter.vue

<template>
  <div class="todo-footer">
    <label>
      <input type="checkbox"/>
    </label>
    <span>
      <span>已完成0</span> / 全部2
    </span>
    <button class="btn btn-danger"> 清除已经完成的任务 </button>
  </div>
</template>

<script>
export default {
  name: "UserFooter"
}
</script>

<style scoped>
  .todo-footer {
    height: 40px;
    line-height: 40px;
    padding-left: 6px;
    margin-top: 5px;
  }

  .todo-footer label {
    display: inline-block;
    margin-right: 20px;
    cursor: pointer;

  }

  .todo-footer label input {
    position: relative;
    top: -1px;
    vertical-align: middle;
    margin-right: 5px;
  }
  .todo-footer button {
    float: right;
    margin-top: 5px;
  }
</style>

UserList.vue

<template>
  <ul class="todo-main">
      <UserItem></UserItem>
  </ul>
</template>

<script>
import UserItem from "@/components/UserItem";
export default {
  name: "UserList",
  components:{UserItem},
}
</script>

<style scoped>
  .todo-main {
    margin-left: 0px;
    border: 1px solid #ddd;
    border-radius: 2px;
    padding: 0px;
  }
  .todo-empty {
    height: 40px;
    line-height: 40px;
    border: 1px solid #ddd;
    border-radius: 2px;
    padding-left: 5px;
    margin-top:  10px;
  }
</style>

UserItem.vue

<template>
  <li>
    <label >
      <input type="checkbox"/>
      <span>xxxxx</span>
    </label>
    <button  class="btn btn-danger" style="display:none">删除</button>
  </li>
</template>

<script>
export default {
  name: "UserItem"
}
</script>

<style scoped>

li {
  list-style: none;
  height: 36px;
  line-height: 36px;
  padding: 0 5px;
  border-bottom: 1px solid #ddd;
}

li label{
  float: left;
  cursor: pointer;
}

li label li input {
  vertical-align: middle;
  margin-right: 6px;
  position: relative;
  top: -1px;
}

li button{
  float: right;
  display: none;
  margin-top: 3px;
}

li:before {
  content: initial;
}
li:last-child{
  border-bottom: none;
}

</style>

效果:
image.png

数据结构

todos:[
{id:’0001’,title:’吃饭’,done:true},
{id:’0002’,title:’睡觉’,done:true},
{id:’0003’,title:’打豆豆’,done:false},
]

UserList如下修改:

<template>
  <ul class="todo-main">
  <!--     循环+传值给其他组件的逻辑 props-->
      <UserItem v-for="todoObj in todos" :key="todoObj.id" :todo="todoObj" >

      </UserItem>
  </ul>
</template>

<script>
import UserItem from "@/components/UserItem";
export default {
  name: "UserList",
  components:{UserItem},
  data(){
    return {
      todos:[
        {id:'0001',title:'吃饭',done:true},
        {id:'0002',title:'睡觉',done:true},
        {id:'0003',title:'打豆豆',done:false},
      ]
    }
  }
}
</script>

UserItem 如下修改:

<template>
  <li>
    <label >
      <input type="checkbox"/>
      <span>{{ todo.title }}</span>
    </label>
    <button  class="btn btn-danger" style="display:none">删除</button>
  </li>
</template>

<script>
export default {
  name: "UserItem",
  //声名接收todo对象
  props: ['todo'],
}
</script>

效果如下:

image.png

关于checked,勾选的问题

:checked=”todo.done”

<template>
  <li>
    <label >
      <input type="checkbox" :checked="todo.done"/>
      <span>{{ todo.title }}</span>
    </label>
    <button  class="btn btn-danger" style="display:none">删除</button>
  </li>
</template>

<script>
export default {
  name: "UserItem",
  //声名接收todo对象
  props: ['todo'],
}
</script>

image.png

父传子

我们要思考我们要显示todos里面的结构化数据,放在哪个组件里面的问题,比如我们放在UserList组件里面,我们就需要将数据传递给UserItem,这样才可以显示每一个Item的详情,这样还不如放在App.Vue中,这样任意组件都可以调用了

下面我们就把todos数据放在App.vue中,想想看怎么向下传递?

那么我们先说一下父传子的例子:

我们先说父传子:

App.Vue 想给 Mylist传一个参数是可以的,这叫父传子
在App里这样修改

image.png

image.png

父传子的高级形式

上面a的例子 a 是静态参数,值是写死的,那么我们想把App.Vue data 里面todos这个列表传递过去怎么办呢?
看例子

image.png

我们怎么在UserList.Vue 里面接收呢?
还是使用props 呀

image.png

但是这还不行,我们还需要把todos 的数据传递给UserItem

所以还有一层父传子,这次父变成了UserList,子变成了UserItem
这次是传的上面经过循环后的todoObj,还是双向绑定的形式

UserList.Vue

image.png

看UserItem.Vue

image.png

添加一个TODO(子传父)

我们在UserHeader组件里面通过input添加数据,并且应该提交到todos这个数组里面,但是现在todo这个数组在App.Vue里面,涉及到从Header组件向App.Vue传递数据的问题,这个叫做子 传 父

子传父是离不开父传子的

我们来看下面的代码

这是MyHeader.Vue的代码
image.png

需要在App.vue中有一个方法,然后把这个方法通过父传子props传递给子组件
子组件接收到props之后,就可以执行这个方法
但是执行方法的时候传递的参数是子组件的,方法其实是在父组件中执行的
(以上一定要看懂)

image.png

image.png

输入之后框里还有数据

image.png

第一个问题,我们输入值之后会产生todo了,但是输入框内还会保持刚才输入的值
所以UserHeader在获取数据之后,要把input的输入框的值清空,所以还需要绑定一个值

第二个问题,当我们输入空值的时候,我们要return结束

<template>
  <div  class="todo-header">
    <input type="text" placeholder="请输入你的任务名称,按回车键确认" v-model="title"   @keyup.enter="adds" >
  </div>
</template>

<script>
import { nanoid } from 'nanoid'
export default {
  name: "UserHeader",
  props:['todos','receivesomething'],
  data(){
    return {
      title:'',
    }
  },
  methods:{
    adds(event){
      //校验数据
      if (!this.title) {
        alert("输入不能为空")
        return
      }
      const todoObj = {
        id:nanoid(),
        title: event.target.value,
        done: false,
      }
      //这里要传递到todo
      this.receivesomething(todoObj)
      //清空dom
      this.title =''
    },
  }
}
</script>

<style scoped>

    .todo-header input {
      width: 560px;
      height: 28px;
      font-size: 14px;
      border:1px solid;
      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>

勾选之后,todos状态的done值变动

应该把勾选这个动作绑定一个onclick事件,获取到id的值,然后修改todos 这个数据里面的done值

勾选这个动作在UserItem组件里,我们获取到todoObj.id

修改todos,这个又是子传父,而且是孙子传爷爷。。。所以又要去App.vue中操作�,因为中间还有一个UserList组件
image.png

在App.Vue中
image.png

在UserList组件中,传递给UserItem组件

image.png

勾选方法2

应该把勾选这个动作绑定一个onclick事件,获取到id的值,然后修改todos 这个数据里面的done值
但是有一个特殊情况

在input框中,如果是checkbox类型的,可以使用v-model来双向绑定一个bool值,这个bool值就决定了是否被选择

就不用子传父或者父传子了

<template>
  <li>
    <label >
      <input type="checkbox" v-model="todoObj.done" />
      <span>{{ todoObj.title }}</span>
    </label>
    <button  class="btn btn-danger" style="display:none">删除</button>
  </li>
</template>

<script>
export default {
  name: "UserItem",
  //声名接收todo对象
  props: ['todoObj']
}
</script>


删除

删除其实跟添加的逻辑是一样的呢,也是在UserItem里面的button写一个方法,然后调用App.Vue中的方法
那么在UserItem里如何调用App.Vue中的方法,就需要父传子了

1.首先我们通过:hover来实现选种效果
2.通过选中某行,某行的删除按钮就会显示

image.png

3.button click的功能

image.png

这是App.vue中的方法
image.png

底部:

image.png
image.png

全选或者全不选

image.png

image.png

我们可以得到如下的值

image.png

首先我们通过e.target.checked 获取了勾选状态
下一步我们把拿到的状态传递到App.Vue的一个方法中
Userfooter
image.png

App.Vue
image.png

清除已经勾选的选项

image.png

image.png

总结

image.png