静态资源
这是一个思路
拿到功能点之后,一定要分析一下可以拆分为几个组件
1.实现静态组件
拿到功能点之后,一定要分析一下可以拆分为几个组件
<br />然后把上图的List 里面具体的每一个Todo,也都拆成一个组件

下面我们只考虑下面四个组件
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>
效果:
数据结构
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>
效果如下:

关于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>

父传子
我们要思考我们要显示todos里面的结构化数据,放在哪个组件里面的问题,比如我们放在UserList组件里面,我们就需要将数据传递给UserItem,这样才可以显示每一个Item的详情,这样还不如放在App.Vue中,这样任意组件都可以调用了
下面我们就把todos数据放在App.vue中,想想看怎么向下传递?
那么我们先说一下父传子的例子:
我们先说父传子:
App.Vue 想给 Mylist传一个参数是可以的,这叫父传子
在App里这样修改


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

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

但是这还不行,我们还需要把todos 的数据传递给UserItem
所以还有一层父传子,这次父变成了UserList,子变成了UserItem
这次是传的上面经过循环后的todoObj,还是双向绑定的形式
UserList.Vue

看UserItem.Vue

添加一个TODO(子传父)
我们在UserHeader组件里面通过input添加数据,并且应该提交到todos这个数组里面,但是现在todo这个数组在App.Vue里面,涉及到从Header组件向App.Vue传递数据的问题,这个叫做子 传 父
子传父是离不开父传子的
我们来看下面的代码
这是MyHeader.Vue的代码
需要在App.vue中有一个方法,然后把这个方法通过父传子props传递给子组件
子组件接收到props之后,就可以执行这个方法
但是执行方法的时候传递的参数是子组件的,方法其实是在父组件中执行的
(以上一定要看懂)


输入之后框里还有数据

第一个问题,我们输入值之后会产生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组件
在App.Vue中
在UserList组件中,传递给UserItem组件

勾选方法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.通过选中某行,某行的删除按钮就会显示

3.button click的功能

这是App.vue中的方法
底部:


全选或者全不选


我们可以得到如下的值

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


总结

