1、项目结构
index.html引入
<link rel="stylesheet" type="text/css" href="./css/bootstrap.min.css">
2、App.vue
<template>
<div class="container">
<div class="row">
<div class="col-xs-6 col-xs-push-3 header-site">
<!-- 引入子组件 -->
<Header ref="header" />
<List :todos="todos" @changeStatusByIndex="changeTodoStatus" :delTodo="delTodo" />
<Footer :todos="todos" :delDone="delDone" />
</div>
</div>
</div>
</template>
<script>
// 引入子组件
import Header from "./components/Header.vue";
import List from "./components/List.vue";
import Footer from "./components/Footer.vue";
export default {
components: {
//定义主件
Header,
List,
Footer
},
data() {
return {
// 代办事情从sessionStorage获取
todos: JSON.parse(sessionStorage.getItem("myTodos")) || []
};
},
mounted() {
//给子组件提供获取该方法的途径
this.$refs.header.$on("addTodo", this.addTodo);
},
watch: {
todos: {
deep: true,
handler(newVal) {
//添加代办的事情存到sessionStorage
sessionStorage.setItem("myTodos", JSON.stringify(newVal));
}
}
},
methods: {
//添加代办事件
addTodo(todo) {
this.todos.unshift(todo);
},
//删除已完成事件
delDone() {
this.todos = this.todos.filter(todo => !todo.complete);
},
//改变代办事件状态,是否已完成
changeTodoStatus(index, completed) {
this.todos[index].complete = completed;
},
//删除代办事件
delTodo(index) {
this.todos.splice(index, 1);
}
}
};
</script>
<style>
.header-site {
margin-top: 20px;
border: 1px solid #888888;
border-radius: 5px;
padding: 20px 10px;
}
</style>
3、Header.vue
<template>
<p>
<input class="form-control" @keyup.enter="addItem" v-model="title" placeholder="请输入待办事项" />
</p>
</template>
<script>
export default {
components: {},
data() {
return {
//绑定输入框的值
title: ""
};
},
methods: {
//添加代办事件
addItem() {
let title = this.title.trim();
if (!title) {
alert("代办事项不能为空");
return;
}
let todo = {title:title,complete:false}
//获取父组件的值
this.$emit("addTodo", todo);
this.title = "";
}
}
};
</script>
<style>
</style>
4、List.vue
<template>
<ul class="list-group">
<li v-for="(todo, index) in todos" @mouseenter="mouseEnterTodo(index)" @mouseleave="mouseLeaveTodo"
:key="index" :class="['list-group-item', {'todo-bg':index==currentIndex}]">
<input v-model="todo.complete" @change="changeTodoStatus(index, todo.complete)"
type="checkbox"> {{todo.title}}
<button @click="delItem(index)"
v-show="index==currentIndex" class="btn btn-danger btn-xs pull-right">删除</button>
</li>
</ul>
</template>
<script>
export default {
props:{
//获取父组件的属性和方法
todos:Array,
delTodo:Function
},
components: {
},
data(){
return {
//记录鼠标进入和离开状态,默认-1离开
currentIndex:-1
}
},
methods:{
//鼠标进入时
mouseEnterTodo(index){
this.currentIndex = index
},
//鼠标离开时
mouseLeaveTodo(){
this.currentIndex = -1
},
//改变代办事件状态
changeTodoStatus(index, completed){
//获取父组件的方法
this.$emit('changeStatusByIndex',index,completed)
},
//删除代办事件
delItem(index){
//获取父组件的方法
this.delTodo(index)
}
}
};
</script>
<style>
.todo-bg{
background-color: bisque;
}
</style>
5、Footer.vue
<template>
<div v-show="todos.length != 0">
<input type="checkbox" v-model="selectAllorNot" />
已完成 {{completedTodoNum}} / 总共 {{todos.length}}
<button
@click="delDoneItems"
class="btn btn-danger btn-xs pull-right"
>删除已完成</button>
</div>
</template>
<script>
export default {
props: {
//获取父主键的属性和方法
todos: Array,
delDone: Function
},
computed: {
//计算出已经完成的todo的数量
completedTodoNum: function() {
let todos = this.todos;
return todos.reduce(
(total, current) => total + (current.complete ? 1 : 0),
0
);
},
selectAllorNot: {
get() {
// todos只要有一个是没选中,那么最终状态是不选中。
let todos = this.todos;
let flag = true;
todos.forEach(element => {
if (!element.complete) {
flag = false;
return flag;
}
});
return flag;
},
//改变selectAllOrNot的状态,保证每个todo和它的状态一致就可以了。
set(value) {
let todos = this.todos;
todos.forEach(element => {
// value的值为true或者false
element.complete = value;
});
}
}
},
data() {
return {};
},
methods: {
//获取父组件的方法
delDoneItems() {
this.delDone();
}
},
components: {}
};
</script>
<style>
</style>