【IMWeb训练营作业】基于Vue的简易todolist

完成效果

这里写图片描述

源码

  • DOM结构 - todos.html

    <!DOCTYPE html>
    <html lang="en">
    <head>
      <meta charset="UTF-8">
      <title>todo-list</title>
      <link type="text/css" href="css/style.css" rel="stylesheet">
      <script src="./js/vue.js"></script>
    </head>
    <body>
    
    <div class="navbar">
      <div class="content">
          <h2>任务计划列表</h2>
      </div>
    
    </div>
    <div id="todos">
      <h3>添加任务</h3>
      <input type="text" placeholder="例如:跑步5公里" class="task-input"
             v-model="todo"
             @keyup.13="addTodo">
      <ul class="task-info">
          <li class="task-count">{{noCheckeLength}}个任务未完成</li>
          <li class="task-sort">
             <a @click="isSelected(index)" :class="{active:index === myIndex}" :href="val.hash" v-for="(val,index) in sort">{{val.title}}</a>
          </li>
      </ul>
      <h3 class="task-list">任务列表</h3>
      <div class="tasks">
          <span class="no-task-tip" v-show="!list.length">还没有添加任何任务</span>
          <ul class="todo-list">
              <li class="todo" :class="{completed:item.isChecked,editing: item === editTodos}" v-for="item in filteredList">
                  <div class="view">
                      <input type="checkbox" class="toggle" v-model="item.isChecked">
                      <label @dblclick="editTodo(item)">{{ item.title }}</label>
                      <button class="destroy" @click="deleteTodo(item)"></button>
                  </div>
                  <input type="text" class="edit"
                         v-foucs="editTodos === item"
                         class="edit"
                         type="text"
                         v-model = "item.title"
                         @blur="editTodoed(item)"
                         @keyup.13="editTodoed(item)"
                         @keyup.esc="cancelTodo(item)"
                          />
              </li>
          </ul>
      </div>
    </div>
    <script src="js/app.js"></script>
    </body>
    </html>
  • 样式 - style.css

    * {
      margin: 0;
      padding: 0;
    }
    button{
      background-color: #ffffff;
    }
    
    .navbar {
      width: 100%;
      height: 50px;
      background-color: #F35F3F;
      color: #DBD9CA;
      margin-bottom: 15px;
    }
    
    .content {
      width: 50%;
      margin: 0 auto;
    }
    
    .navbar h2 {
      line-height: 50px;
    }
    
    
    #todos {
    
      width: 50%;
      margin: 0 auto;
    }
    
    .task-input {
      margin-top: 15px;
      line-height: 40px;
      width: 100%;
      font-size: 18px;
      padding-left: 10px;
    }
    
    .task-info {
      margin: 15px 0;
      display: flex;
      list-style: none;
    }
    .task-info li{
      padding-left: 10px;
      flex: 1;
    }
    .task-count {
      color: #FE414D;
    }
    
    .task-sort {
      text-align: center;
      display: flex;
    }
    
    .task-sort a {
      padding: 5px 0;
      /*cursor: pointer;*/
      margin: 0 10px;
      flex: 1;
      text-decoration: none;
      color: #333;
    }
    
    .active {
      text-align: center;
      background-color: #FB9C51;
      border-radius: 2px;
      color: #ffffff !important;
    }
    
    .task-list {
      margin: 15px 0;
    }
    
    .tasks {
      background-color: #fff;
    }
    .no-task-tip {
      padding:10px 0 10px 10px;
      display: block;
      border-bottom: 1px solid #ededed;
      color:#777;
    }
    
    .todo-list {
      margin: 0;
      padding: 0;
      list-style: none;
    }
    
    .todo-list li {
      position: relative;
      font-size: 16px;
      border-bottom: 1px solid #ededed;
    }
    
    .todo-list li:hover {
      background-color: #fafafa;
    }
    
    
    .todo-list li.editing {
      border-bottom: none;
      padding: 0;
    }
    
    .todo-list li.editing .edit {
      display: block;
      width: 506px;
      padding: 13px 17px 12px 17px;
      margin: 0 0 0 43px;
    }
    
    .todo-list li.editing .view {
      display: none;
    }
    
    
    .toggle {
      text-align: center;
      width: 40px;
      height: auto;
      position: absolute;
      top: 5px;
      bottom: 0;
      margin: auto 0;
      border: none; 
      -webkit-appearance: none;
      appearance: none;
    }
    
    .toggle:after {
      content: url('data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" width="40" height="40" viewBox="-10 -18 100 135"><circle cx="50" cy="50" r="40" fill="none" stroke="#ededed" stroke-width="3"/></svg>');
    }
    
    .toggle:checked:after {
      content: url('data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" width="40" height="40" viewBox="-10 -18 100 135"><circle cx="50" cy="50" r="40" fill="none" stroke="#bddad5" stroke-width="3"/><path fill="#5dc2af" d="M72 25L42 71 27 56l-4 4 20 20 34-52z"/></svg>');
    }
    
    .todo-list li label {
      white-space: pre-line;
      word-break: break-all;
      padding: 15px 60px 15px 15px;
      margin-left: 45px;
      display: block;
      line-height: 1.2;
      transition: color 0.4s;
    }
    
    .todo-list li.completed label {
      color: #d9d9d9;
      text-decoration: line-through;
    }
    
    .todo-list li .destroy {
      display: none;
      position: absolute;
      top: 0;
      right: 10px;
      bottom: 0;
      width: 40px;
      height: 40px;
      margin: auto 0;
      font-size: 30px;
      color: #cc9a9a;
      transition: color 0.2s ease-out;
      background-color: initial;
      border: none;
    }
    
    .todo-list li .destroy:hover {
      color: #af5b5e;
    }
    
    .todo-list li .destroy:after {
      content: '×';
    }
    
    .todo-list li:hover .destroy {
      display: block;
    }
    
    .todo-list li .edit {
      display: none;
    }
    
    .todo-list li.editing:last-child {
      margin-bottom: -1px;
    }
  • 数据渲染和绑定 - app.js

    
    //存取localStorage中的数据
    
    var store = {
      save(key, value)
    {
      localStorage.setItem(key, JSON.stringify(value));
    }
    ,
    fetch(key)
    {
      return JSON.parse(localStorage.getItem(key)) || [];
    }
    }
    
    
    //取出所有的值
    var list = store.fetch("vue-todos");
    
    //过滤的时候有三种情况 all finished unfinished
    
    var filter = {
      all: function (list) {
          return list;
      },
      finished: function (list) {
          return list.filter(function (item) {
              return item.isChecked;
          })
      },
      unfinished: function () {
          return list.filter(function (item) {
              return !item.isChecked;
          })
      }
    };
    //分类情况
    var sort = [{
      title: "全部任务",
      hash:"#all"
    },
      {
          title: "已完成任务",
          hash:"#finished"
      },
      {
          title: "未完成任务",
          hash:"#unfinished"
      }];
    
    var vm = new Vue({
      el: "#todos",
      data: {
          list: list,
          todo: "",
          editTodos: '',  //记录正在编辑的数据
          beforeTitle: '', //记录正在编辑的数据的title
          visibility: "all", //通过这个属性值的变化对数据进行筛选
          sort:sort,
          myIndex: 0  //分类初始选中的是“全部任务”
      },
      watch: {
          list: {
              handler: function () {
                  store.save("miaov-new-class", this.list);
              },
              deep: true
          }
      },
      computed: {
          noCheckeLength: function () {
              return this.list.filter(function (item) {
                  return !item.isChecked
              }).length
          },
          filteredList: function () {
              //返回过滤后的数据;如果没有返回所有数据
    
              return filter[this.visibility] ? filter[this.visibility](list) : list;
    
          }
      },
      methods: {
          addTodo: function () {  //添加任务
              this.list.push({
                  title: this.todo,
                  isChecked: false
              });
              this.todo = '';
    
          }
          ,
          deleteTodo: function (todo) { //删除任务
              var index = this.list.indexOf(todo);
              this.list.splice(index, 1);
    
          }
          ,
          editTodo: function (todo) {  //编辑任务
              //编辑任务的时候,记录一下编辑这条任务的title,方便在取消编辑的时候重新给之前的title
              this.beforeTitle = todo.title;
    
              this.editTodos = todo;
    
    
          }
          ,
          editTodoed: function (todo) { //编辑任务成功
              this.editTodos = '';
          }
          ,
          cancelTodo: function (todo) {  //取消编辑任务
    
              todo.title = this.beforeTitle;
    
              this.beforeTitle = '';
    
              //让div显示出来,input隐藏
              this.editTodos = '';
          }
          ,
          //分类查看
          isSelected: function (index) {
              this.myIndex = index;
          }
      },
      directives: {
          "foucs": {
              update: function (el, binding) {
                  if (binding.value) {
                      el.focus();
                  }
              }
          }
      }
    });
    
    function watchHashChange() {
      var hash = window.location.hash.slice(1);
    
      vm.visibility = hash;
    
    }
    
    watchHashChange();
    
    window.addEventListener("hashchange", watchHashChange);

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值