• 纯前端Vue实现Todo_list备忘录及导航案例


    Vue实现Todo_list备忘录及导航

    1、前言

    去年在学习Vue的时候做了一个todoList案例,这里我简单的对其进行了一些改造,并加了些导航,如果做得更完善一些的话可以做成个人浏览器主页。

    image-20220630181453016

    2、主要用到的技术及依赖

    • Vue
    • core-js
    • nanoid

    3、关于项目

    https://github.com/GitHub-Ninghai/Todo_list_vue

    🌟引入:npm init

    🚀运行: npm run serve

    daka1文件夹为打包后的文件夹

    4、代码实现

    首先将页面拆分为五个小组件,为了方便理解我绘制了一份容器图。

    image-20220630213322514

    由于主要以Vue为主,下面的示例就不写style标签了,详情请看github仓库。

    1、HelloWorld组件

    先说一下最简单的HelloWord组件,是的连Vue初始化的名字都没有改的组件,而且在组件内部也保持了高度一致(非常尊重Vue的模板好吧),主要是将一些导航加了上去,实现了跳转各常用链接的功能。具体代码如下

    <template>
      <div class="hello">
        <h1>{{ msg }}</h1>
        <h2>
          这是一个供您记录任务并打卡的小小备忘录网站(*^▽^*)<br>
          <!-- Vue官方文档
          <a href="https://cli.vuejs.org" target="_blank" rel="noopener">vue-cli documentation</a>. -->
        </h2>
        <h3>常用网站链接</h3>
        <ul>
          <li><a href="https://home.code-nav.cn/" target="_blank" rel="noopener">百度</a></li>
          <li><a href="https://www.bilibili.com/" target="_blank" rel="noopener">B站</a></li>
          <li><a href="https://www.code-nav.cn/" target="_blank" rel="noopener">编程导航</a></li>
          <li><a href="https://www.csdn.net/" target="_blank" rel="noopener">CSDN</a></li>
          <li><a href="https://github.com/" target="_blank" rel="noopener">github</a></li>
        </ul>
         <h3>学校相关链接</h3>
        <ul>
          <li><a href="https://www.lnkjzm.cn/" target="_blank" rel="noopener">筑梦辽科</a></li>
          <li><a href="http://cxcyxy.lnist.edu.cn/" target="_blank" rel="noopener">创新创业学院</a></li>
          <li><a href="https://lkyjw.lnist.edu.cn/" target="_blank" rel="noopener">教务在线</a></li>
          <li><a href="http://210.30.224.150/cxxf/" target="_blank" rel="noopener">双创学分系统</a></li>
        </ul>
      </div>
    </template>
    
    <script>
    export default {
      name: 'HelloWorld',
      props: {
        msg: String
      }
    }
    </script>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34

    2、myHeader组件

    myHeader组件主要设计了输入框,并利用 @keyup.enter的方法实现回车后检验数据、包装数据之后将数据清空。

    其中nanoid是唯一的 JavaScript 字符串ID生成器。用于生成对象的id。

    <template>
        <div class="todo-header">
            <input type="text" placeholder="请输入你的任务名称,按回车键确认"  v-model="title" @keyup.enter="add"/>
        </div>
    </template>
    
    <script>
      import {nanoid} from 'nanoid'
      export default {
        name:'myHeader',
        props:['addTodo'],
        data() {
          return {
            title:''
          }
        },
        methods: {
          add(){
            // 检验数据
            if (!this.title.trim()) {
              return alert('输入不能为空')
            }
            // 将用户的输入包装成一个todo对象
            const todoNew = {id:nanoid(),title:this.title,done:false}
            // 通知Ap组件去添加一个todo对象
            // this.addTodo(todoNew)
            this.$emit('addTodo',todoNew)
            // 清空输入
            this.title = ''
          }
        },
      }
    </script>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33

    3、myFooter组件

    主要实现完成任务的功能包括通过定义totaldoneTotal计算属性来计算待办总数。isAll方法则计算是否全选,并定义clearAll方法来清除全选内容。

    <template>
      <div class="todo-footer" v-show="total">
            <label>
                <!-- <input type="checkbox" :checked="isAll" @change="checkAll"/> -->
                 <input type="checkbox" v-model="isAll"/>
            </label>
            <span>
                <span>已完成{{doneTotal}}</span> / 全部{{total}}
            </span>
            <button class="btn btn-danger" @click="clearAll">清除已完成任务</button>
        </div>
    </template>
    <script>
    export default {
        name:'myFooter',
        props:['todos'],
        computed:{
          total(){
            return this.todos.length
          },
          doneTotal(){
            return this.todos.reduce((pre,todo) => pre + (todo.done ? 1 : 0),0)
          },
          isAll:{
            get(){
              return this.doneTotal === this.total && this.total > 0
            },
            set(value){
              this.$emit('checkAllTodo',value)
            }
          }
        },
        methods: {
          clearAll(){
            this.$emit('clearAllTodo')
          }
        },
    }
    </script>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39

    4、myList组件

    此组件内主要实现了动画效果以及遍历列表(待办任务)的功能

    动画

    <template>
      <ul class="todo-main">
        <transition-group appear name="todo">
          <myItem v-for="todoObj in todos" :key="todoObj.id"  :todo="todoObj" />
        </transition-group>
      </ul>
    </template>
    <script>
    import myItem from './myItem.vue'
    export default {
        name:'myList',
        components:{
            myItem
        },
        props:['todos']
    }
    </script>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17

    5、myItem组件

    此组件主要实现了每个列表里的编辑和删除功能。使用全局事件总线进行父子组件通信,将编辑修改后的数据传给myList组件。

    动画2

    <template>
          <li>
              <label>
                  <input type="checkbox" :checked="todo.done" @click="handleCheck(todo.id)"  />
                  <span v-show="!todo.isEdit">{{todo.title}}</span>
                  <input v-show="todo.isEdit" type="text" :value="todo.title" @blur="handleBlur(todo,$event)" ref="inputTitle">
              </label>
              <button class="btn btn-danger" @click="handleDelete(todo.id)">删除</button>
              <button v-show="!todo.isEdit" class="btn btn-edit" @click="handleEdit(todo)">编辑</button>
          </li>
    </template>
    <script>
    export default {
        name:'myItem',
        // 声明接收todo对象
        // props:['todo','checkTodo','deleteTodo'],
        props:['todo'],
        methods: {
          handleCheck(id){
            // 通知App组件将对应的todo
            // this.checkTodo(id)
            this.$bus.$emit('checkTodo',id)
          },
          handleDelete(id){
            if (confirm('确定删除吗?')) {
              // this.deleteTodo(id)
              this.$bus.$emit('deleteTodo',id)
              // pubsub.publish('deleteTodo',id)
            }
          },
          handleEdit(todo){
            if(todo.hasOwnProperty.call('isEdit')){
              todo.isEdit = true
            }
            else{
              this.$set(todo,'isEdit',true)
            }
            this.$nextTick(function(){
              this.$refs.inputTitle.focus()
            })
    
          },
          //失去焦点
          handleBlur(todo,e){
            todo.isEdit = false
            this.$bus.$emit('updateTodo',todo.id,e.target.value)
          }
        },
    }
    </script>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50

    6、App组件

    • 在App组件中实现各组件的引入注册和使用。
    • 在data中通过本地存储来将数据存储到本地磁盘。
    • 在methods中定义增删改勾的方法。
    • watch监听数据发生的变化从而保证实时同步到localstorage中。
    <template>
        <div id="root">
          <div class="todo-container">
          <img id="logo" alt="logo" src="./assets/logo.png">
            <div class="todo-wrap">
              <HelloWorld />
              <myHeader @addTodo="addTodo"/>
              <!-- <myList :todos="todos" :checkTodo="checkTodo" :deleteTodo="deleteTodo"/> -->
              <myList :todos="todos" />
              <myFooter :todos="todos" @checkAllTodo="checkAllTodo" @clearAllTodo="clearAllTodo"/>
            </div>
                  <img id="logo" alt="logo" src="./assets/chong.jpg">
          </div>
      </div>
    </template>
    
    <script>
    // import pubsub from 'pubsub-js'
    import myHeader from './components/myHeader.vue'
    import myList from './components/myList.vue'
    import myFooter from './components/myFooter.vue'
    import HelloWorld from './components/HelloWorld.vue'
    
    export default {
      name: 'App',
      components: {
        myHeader,myList,myFooter,HelloWorld
      },
      data() {
        return {
          todos:JSON.parse(localStorage.getItem('todos')) || []
        }
      },
      methods: {
        // 添加一个todo
        addTodo(todoObj){
          this.todos.unshift(todoObj)
        },
        // 勾选or取消勾选一个todo
        checkTodo(id){
          this.todos.forEach((todo)=>{
            if(todo.id === id) todo.done = !todo.done
          })
        },
        //改
        updateTodo(id,title){
          this.todos.forEach((todo)=>{
            if(todo.id === id) todo.title = title
          })
        },
        // 删除一个todo
        deleteTodo(id){
          this.todos=this.todos.filter(todo => todo.id !== id)
        },
        // 全选or全不选
        checkAllTodo(done){
          this.todos.forEach((todo)=>{
            todo.done = done
          })
        },
        //清除所有已经完成的todo
       clearAllTodo(){
          this.todos =  this.todos.filter((todo)=>{
            return !todo.done
          })
       }
      },
      watch:{
        todos:{
          deep:true,
          handler(value){
          localStorage.setItem('todos',JSON.stringify(value))
          }
        }
      },
      mounted(){
        this.$bus.$on('checkTodo',this.checkTodo)
        this.$bus.$on('updateTodo',this.updateTodo)
        this.$bus.$on('deleteTodo',this.deleteTodo)
      },
      beforeDestroy(){
        this.$bus.$off(['checkTodo','deleteTodo','updateTodo'])
      }
    }
    </script>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85

    以上就是todolist案例的详细解读啦!

    打卡在线访问地址http://daka.lnkjzm.cn/

  • 相关阅读:
    Xshell连接显示“服务器发送了一个意外的数据包。received:3,expected:20“问题
    c语言通信之串口通信
    Linux高级I/O:非阻塞IO fcntl | 多路转接之select | poll
    【高阶数据结构】跳表
    【PHP设计模式07】桥接模式(桥梁模式)
    【JavaWeb】-- Servlet优化(dispatcherServlet)
    miniob源码 架构概览
    MySQL索引事务
    【Redis】散列表(Hash)和列表(List)的运用和理解以及Hash和List应用场景对比详解
    MaskRcnn训练自己的数据集
  • 原文地址:https://blog.csdn.net/tianhai12/article/details/125550377