Advertisement

vue学习记录(七):todoList(任务计划列表)的实现

阅读量:

学了一段时间的vue了,做一个实战的小项目todoList。

大致功能如下:

1、在输入框中输入内容后按enter键,即可把内容添加到下面的列表中(如果内容为空则不能添加)

2、动态计算有几个未完成的任务

3、点击复选框,实现选中或不选中效果

4、鼠标移入列表,会出现一个删除按钮,点击删除按钮即可删除该列表

5、已经添加的列表任务,即便关闭浏览器或者电脑,下次打开任务还在列表中(用到了本地存储)

具体的项目目录如下:

下面各个文件的具体代码,都有注释哈

main.js

复制代码
 // The Vue build version to load with the `import` command

    
 // (runtime-only or standalone) has been set in webpack.base.conf with an alias.
    
 import Vue from 'vue'
    
 import App from './App'
    
 // import router from './router'
    
  
    
 import './base.css'
    
  
    
 Vue.config.productionTip = false
    
  
    
 /* eslint-disable no-new */
    
 new Vue({
    
   el: '#app',
    
   // router,
    
   components: { App },
    
   template: '<App/>'
    
 })

App.vue

复制代码
 <template>

    
   <div class="todo-container">
    
     <div class="todo-wrap">
    
       <TodoHeader :addTodo="addTodo" />
    
  
    
       <TodoList :todos="todos" :deleteTodo="deleteTodo" />
    
       <TodoFooter :todos="todos" :deleteCompeteTodos="deleteCompeteTodos" :selectAllTodos="selectAllTodos" />
    
     </div>
    
   </div>
    
 </template>
    
  
    
 <script>
    
   import TodoHeader from './components/TodoHeader'
    
   import TodoList from './components/TodoList'
    
   import TodoFooter from './components/TodoFooter'
    
   export default {
    
  
    
     data() {
    
       //localStorage将列表存入一个中间数组list,在每次开始前赋值给任务列表数组todos
    
       var list=JSON.parse(localStorage.getItem('key'));
    
       console.log(list,list.length);
    
       return {
    
     todos: list
    
       }
    
     },
    
     methods: {
    
       addTodo(todo) { //接收todo
    
     // this.todos.unshift(todo)     //添加到第一个todo上面,title[0]
    
     this.todos.push(todo);
    
     //同时更新数据到localStorage
    
     localStorage.setItem('key',JSON.stringify(this.todos));  
    
       },
    
       //删除
    
       deleteTodo(index) {
    
     this.todos.splice(index, 1)
    
     //同时更新数据到localStorage
    
     localStorage.setItem('key',JSON.stringify(this.todos));
    
       },
    
       //删除所有选中的todo
    
       deleteCompeteTodos() {
    
     //过滤出false的留下 todo.complete取反
    
     this.todos = this.todos.filter(todo => !todo.complete)
    
     //同时更新数据到localStorage
    
     localStorage.setItem('key',JSON.stringify(this.todos));
    
       },
    
       //全选/全不选
    
       selectAllTodos(check) { //状态变化所以传参数
    
     //遍历todos改变check状态
    
     this.todos.forEach(todo => todo.complete = check)
    
     //同时更新数据到localStorage
    
     localStorage.setItem('key',JSON.stringify(this.todos));
    
       }
    
     },
    
     components: {
    
       TodoHeader,
    
       TodoList,
    
       TodoFooter
    
     }
    
   }
    
  
    
 </script>
    
  
    
 <style scoped>
    
   .todo-container {
    
     width: 600px;
    
     margin: 0 auto;
    
   }
    
  
    
   .todo-container .todo-wrap {
    
     padding: 10px;
    
     border: 1px solid #ddd;
    
     border-radius: 5px;
    
   }
    
  
    
 </style>

base.css

复制代码
 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, 2155, 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;
    
 }

TodoHeader.vue

复制代码
 <template>

    
   <div class="todo-todo">
    
     <h2>任务计划列表</h2> 
    
   <div class="todo-header">
    
     <input
    
       type="text"
    
       placeholder="请输入你的任务名称,按回车键确认"
    
       v-model="title"
    
       @keyup.enter="addItem"
    
     />
    
   </div>
    
   </div>
    
 </template>
    
  
    
 <script>
    
 export default {
    
   props: {
    
     addTodo: Function
    
   },
    
   data() {
    
     return {
    
       title: ""
    
     };
    
   },
    
   methods: {
    
     addItem() {
    
       //1.检查输入的合法性
    
       const title = this.title.trim();
    
       if (!title) {
    
     alert("必须输入");
    
     return;
    
       }
    
       //2.根据输入生成一个todo对象
    
       const todo = {
    
     title,
    
     complete: false
    
       };
    
       //3.添加到todos
    
       this.addTodo(todo);
    
       //4.清除输入
    
       this.title = "";
    
     }
    
   }
    
 };
    
 </script>
    
  
    
 <style scoped>
    
 h2 {
    
   margin: 0;
    
   font-size: 18px;
    
   color: rgb(255, 255, 255);
    
 }
    
 .todo-todo {
    
   width: 100%;
    
   height: 37px;
    
   border: 10px;
    
   border-radius: 4px;
    
   background-color: rgb(197, 70, 70);
    
 }
    
 .todo-header input {
    
   width: 560px;
    
   height: 28px;
    
   margin-top: 25px;
    
   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>

TodoFooter.vue

复制代码
 <template>

    
   <div class="todo-footer">
    
     <label>
    
       <input type="checkbox" v-model="isAllCheck" />
    
     </label>
    
     <span>
    
       <span>已完成{{ completeSize }}</span> / 全部{{ todos.length }}
    
     </span>
    
     <button class="btn btn-danger" v-show="completeSize" @click="deleteCompeteTodos">清除已完成任务</button>
    
   </div>
    
 </template>
    
  
    
 <script>
    
   export default {
    
     props: {
    
       todos: Array,
    
       deleteCompeteTodos: Function,
    
       selectAllTodos: Function
    
     },
    
     computed: {
    
       completeSize() { //统计已完成
    
     return this.todos.reduce((preTotal, todo) => preTotal + (todo.complete ? 1 : 0), 0)
    
       },
    
       isAllCheck: {
    
     get() { //确定当前是true还是false
    
       return this.completeSize === this.todos.length && this.completeSize > 0
    
     },
    
     set(value) { //value是当前checkbox最新的值,set监视变化
    
       this.selectAllTodos(value)
    
     }
    
       }
    
     }
    
   }
    
  
    
 </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>

TodoItem.vue

复制代码
 <template>

    
  
    
  
    
   <li @mouseenter="handleEnter(true)" @mouseleave="handleEnter(false)" :style="{background: bgColor}">
    
     <label>
    
       <input type="checkbox" v-model="todo.complete" />
    
       <span>{{ todo.title }}</span>
    
     </label>
    
     <button class="btn btn-danger" v-show="isShow" @click="deleteItem">删除</button>
    
   </li>
    
 </template>
    
  
    
 <script>
    
   export default {
    
     props: {
    
       todo: Object,
    
       index: Number,
    
       deleteTodo: Function
    
     },
    
     data() {
    
       return {
    
     bgColor: 'white', //默认的背景颜色
    
     isShow: false //按钮默认是否显示
    
       }
    
     },
    
     methods: {
    
       handleEnter(isEnter) {
    
     if (isEnter) {
    
       this.bgColor = '#aaaaaa'
    
       this.isShow = true
    
     } else {
    
       this.bgColor = 'white'
    
       this.isShow = false
    
     }
    
       },
    
       deleteItem() {
    
     const {
    
       todo,
    
       index,
    
       deleteTodo
    
     } = this
    
     if (window.confirm(`确认删除${todo.title}吗?`)) {
    
       deleteTodo(index) //调用函数更新
    
     }
    
       }
    
     }
    
   }
    
  
    
 </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;
    
     margin-top: 3px;
    
   }
    
  
    
   li:before {
    
     content: initial;
    
   }
    
  
    
   li:last-child {
    
     border-bottom: none;
    
   }
    
  
    
 </style>

TodoList.vue

复制代码
 <template>

    
   <ul class="todo-main">
    
     <TodoItem v-for="(todo,index) in todos" :key="index" :todo="todo" :index="index" :deleteTodo="deleteTodo" />
    
   </ul>
    
 </template>
    
  
    
 <script>
    
   import TodoItem from './TodoItem'
    
   export default {
    
     props: {
    
       todos: Array,
    
       deleteTodo: Function
    
     },
    
     components: {
    
       TodoItem,
    
     }
    
   }
    
  
    
 </script>
    
  
    
 <style scoped>
    
   .todo-main {
    
     margin-top: 55px;
    
     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>

运行截图:

因为采用了本地存储技术,并且为了更好地掌握JavaScript中的localStorage功能的具体操作方法的缘故,特意参考了这篇博客以获取更多信息。

完整代码:GitHub

全部评论 (0)

还没有任何评论哟~