效果浏览
![效果浏览](https://img-blog.csdnimg.cn/879f8c9a23d44d03bc870027e957c02c.gif#pic_center)
案例结构
App.vue{
components{
Top.vue,
List.vue:{Item.vue},
Bottom.vue
}
}
Item.vue中间组件里的单条数据
<template>
<!-- List里的单个Item -->
<div class="item all_border">
<div class="left left_">
<!-- 拿todo.done给checked赋值, -->
<!-- input框值发生改变时调用handleCheck -->
<!-- vue只会监视浅层次的值修改,所以当勾选和反选的时候todo.done可以被修改 -->
<!-- 但是如果这样类似todo = {x:1,y:2}这样浅层次的修改就会被vue阻止 -->
<input
type="checkbox"
class="check_item"
:checked="todo.done"
@change="handleCheck(todo.id)"
/>
<span class="tag">{{ todo.title }}</span>
</div>
<div class="right right_">
<button class="button_bg button_hide" @click="deleteItem(todo.id)">删除</button>
</div>
</div>
</template>
<script>
export default {
name: "MyItem",
props: ["todo", "checkTodo","deleteTodo"],
methods: {
handleCheck(id) {
this.checkTodo(id);
},
deleteItem(id){
if(confirm('确认删除吗'))
this.deleteTodo(id);
}
},
};
</script>
<style scoped>
.item {
width: 100%;
height: 30px;
display: flex;
flex-direction: row;
}
.item:hover{
background-color: #ddd;
}
.item:hover .button_hide{
display: block;
}
.left_ {
width: 89%;
}
.right_ {
width: 10%;
}
.button_hide{
display: none;
}
</style>
List.vue中间组件
<template>
<div class="list">
<!-- v-for使用App传入的todos数组循环渲染 -->
<!-- :key设置键值 -->
<!-- :todo传给个子组件单一条数据 -->
<!-- :checkTodo从App那里拿来的方法再传给子组件Item.vue -->
<!-- :deleteTodo从App那里拿来的方法再传给子组件Item.vue -->
<item
v-for="todo in todos"
:key="todo.id"
:todo="todo"
:checkTodo="checkTodo"
:deleteTodo="deleteTodo"
/>
</div>
</template>
<script>
import Item from "./Item.vue";
export default {
name: "MyList",
components: {
Item,
},
props: ["todos","checkTodo","deleteTodo"],
};
</script>
<style>
.list {
width: 90%;
height: auto;
margin-top: 15px;
}
</style>
Top.vue头部组件
<template>
<div class="top all_border">
<!-- @keyup.enter回车时调用add方法 -->
<input
type="text"
class="all_border"
placeholder="请输入你的任务名称,按回车键确认"
@keyup.enter="add"
v-model="title"
/>
</div>
</template>
<script>
import { nanoid } from "nanoid";
export default {
name: "MyTop",
props: ["addTodo"],
data() {
return {
title: "",
};
},
methods: {
add(e) {
console.log("插入'" + e.target.value + "'成功!");
if (!this.title.trim()) return alert("输入不能为空");
const todo = { id: nanoid(), title: this.title, done: false };
this.addTodo(todo);
this.title = "";
},
},
};
</script>
<style scoped>
.top {
width: 90%;
height: 30px;
display: flex;
align-items: center;
justify-content: center;
margin-top: 10px;
}
input {
width: 100%;
height: 90%;
}
</style>
Bottom.vue底部组件
<template>
<!-- 底部组件 -->
<div class="bottom" v-show="total">
<div class="left left_">
<input type="checkbox" v-model="isAll"/>
<!-- <input type="checkbox" class="check_item" :checked="isAll" @change="checkAll"/> -->
<span>已完成 {{doneTotal}}/ 全部 {{total}}</span>
</div>
<div class="right right_">
<button class="button_bg" @click="clearAll">清除已完成任务</button>
</div>
</div>
</template>
<script>
export default {
name: "MyBottom",
props:['todos',"checkAllTodo","clearAll"],
computed:{
total(){
return this.todos.length;
},
doneTotal(){
return this.todos.reduce((pre,current)=>pre+(current.done?1:0),0);
},
isAll:{
get(){
return this.doneTotal === this.total&&this.total>0;
},
set(value){
this.checkAllTodo(value);
}
}
}
};
</script>
<style scoped>
.bottom {
display: flex;
width: 90%;
height: 30px;
margin-top: 10px;
}
.left {
width: 69%;
}
.right {
width: 30%;
}
</style>
App.vue调用组件Top,List,Bottom
<template>
<div class="app all_border">
<!-- 显示三个子组件 -->
<top :addTodo="addTodo"></top>
<!-- 传给List.vue一个todos数组,一个checkTodo方法,一个deleteTodo方法 -->
<list :todos="todos" :checkTodo="checkTodo" :deleteTodo="deleteTodo"></list>
<bottom :todos="todos" :checkAllTodo="checkAllTodo" :clearAll="clearAll"></bottom>
</div>
</template>
<script>
import Top from "./components/Top.vue";
import List from "./components/List.vue";
import Bottom from "./components/Bottom.vue";
export default {
name: "App",
components: { Top, List, Bottom },
data() {
return {
todos: [
{ id: "0001", title: "吃饭", done: true },
{ id: "0002", title: "睡觉", done: false },
{ id: "0003", title: "打游戏", done: true },
{ id: "0004", title: "上班", done: true },
],
};
},
methods: {
addTodo(todo) {
this.todos.unshift(todo);
},
checkTodo(id) {
this.todos.forEach((todo) => {
if (todo.id === id) todo.done = !todo.done;
});
},
deleteTodo(id){
this.todos = this.todos.filter(todo=> todo.id !== id);
},
checkAllTodo(done){
this.todos.forEach((todo) => {
todo.done = done;
});
},
clearAll(){
if(confirm('确认删除选中任务吗'))
this.todos = this.todos.filter((todo)=>{
return !todo.done;
});
}
},
};
</script>
<style>
.app {
width: 500px;
height: auto;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
}
.button_bg {
background-color: #e46852;
border: 0.5px solid #eb9282ce;
border-radius: 3px;
color: aliceblue;
width: auto;
height: 25px;
}
.check_item,
.text {
width: auto;
}
.all_border {
border: 1px solid rgba(168, 163, 163, 0.548);
border-radius: 3px;
}
.right {
display: flex;
flex-direction: row;
justify-content: flex-end;
align-items: center;
}
.left {
display: flex;
justify-content: start;
align-items: center;
}
</style>