Vue ToDoList案例源码

写在前面

最近在学习Vue,此处记录下第一个学习案例。

main.js

//引入vue
import Vue from 'vue'
//引入App组件
import App from './App.vue'

//关闭生产模式提示
Vue.config.productionTip = false

//创建vue实例
new Vue({
  el:'#app',
  render: h => h(App),
  beforeCreate(){
    Vue.prototype.$bus = this//全局事件总线,this为vm
  }
})

APP.vue

<template>
  <div id="app">
    <div class="todo-container">
      <div class="todo-wrap">
        <MyHeader @addtodo="addtodo"/>
        <MyList :todolist="todolist" @deltodo="deltodo"/>
        <MyFoot :todolist="todolist" @checkalltodo="checkalltodo" @delAll="delAll"/>
      </div>
    </div>
  </div>
</template>

<script>
  import MyHeader from "./components/MyHeader.vue";
  import MyList from "./components/MyList.vue";
  import MyFoot from "./components/MyFooter.vue";

  export default {
    name: 'App',
    components: {
      MyHeader,
      MyList,
      MyFoot
    },
    data() {
      return {
        //使用localStrage将数据存储到本地
        todolist:JSON.parse(localStorage.getItem('todolist')) || []
      }
    },
    methods:{
      addtodo(newtodo){
        this.todolist.unshift(newtodo)
      },
      checktodo(id){
        this.todolist.forEach((value) => {
          if(value.id === id) value.done = !value.done
        })
      },
      checkalltodo(status){
        this.todolist.forEach((value) => {
          value.done = status
        })
      },
      deltodo(id){
        this.todolist = this.todolist.filter((todo) => {
          return todo.id !== id
        })
      },
      delAll(){
        this.todolist = this.todolist.filter((todo) =>{
          return !todo.done
        })
      }
    },
    watch:{
      todolist:{
        deep:true,
        handler(val){
          localStorage.setItem('todolist',JSON.stringify(val))
        }
      }
    },
    mounted() {
      this.$bus.$on('deltodo',this.deltodo)//绑定自定义事件
    },
    beforeDestroy() {
      this.$bus.$off('deltodo')//取消一个绑定自定义事件
      // this.$bus.$off()//取消所有绑定自定义事件
    },
  }
</script>

<style>
  /*base*/
  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, 255, 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;
  }

  .todo-container {
    width: 600px;
    margin: 0 auto;
  }
  .todo-container .todo-wrap {
    padding: 10px;
    border: 1px solid #ddd;
    border-radius: 5px;
  }
</style>

MyHeader.vue

<template>
  <div class="todo-header">
    <input type="text" placeholder="请输入代办内容,按回车键确认" v-model="title" @keyup.enter="addToDo"/>
  </div>
</template>
 
<script>
    import {nanoid} from 'nanoid'
    export default {
        name: "MyHeader",
        data(){
            return {
                title:''
            }
        },
        methods:{
            addToDo(){
                this.title = this.title.trim()
                if(!this.title) return alert('请输入代办内容')
                const toDoObj = {id:nanoid(),title:this.title,done:false}
                this.$emit('addtodo',toDoObj)
                this.title=''
            }
        }
    }
</script>
 
<style scoped>
    /*header*/
    .todo-header input {
    width: 560px;
    height: 28px;
    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>

MyList.vue

<template>
  <ul class="todo-main">
    <MyItem v-for="todoObj in todolist" :key="todoObj.id" :todoObj="todoObj"/>
  </ul>
</template>

<script>
    import MyItem from "./MyItem.vue";
    export default {
        name: "MyList",
        props:['todolist'],
        components:{
            MyItem
        }
    }
</script>

<style scoped>
    /*main*/
    .todo-main {
    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>

MyItem.vue

<template>
  <ul class="todo-empty">
    <li>
      <label>
        <input type="checkbox" v-model="todoObj.done"/>
        <span>{{todoObj.title}}</span>
      </label>
      <button class="btn btn-danger" @click="del(todoObj.id)" >删除</button>
    </li>
  </ul>
</template>

<script>

  export default {
    name: "MyItem",
    props:['todoObj'],
    methods: {
      del(id){
        // this.deltodo(id);
        this.$bus.$emit('deltodo',id)
      },
    },
  }
</script>

<style scoped>
    .todo-empty {
    height: 40px;
    line-height: 40px;
    border: 1px solid #ddd;
    border-radius: 2px;
    padding-left: 5px;
    margin-top: 10px;
  }

  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;
    display: black;
    margin-top: 3px;
  }

  li:before {
    content: initial;
  }

  li:last-child {
    border-bottom: none;
  }
</style>

MyFooter.vue

<template>
  <div class="todo-footer">
    <label>
      <input type="checkbox" v-model="isAll" />
    </label>
    <span>
      <span>已完成{{doneTodo}}</span> / 全部{{todol}}
    </span>
    <button class="btn btn-danger" @click="delchecktodo">清除已完成代办</button>
  </div>
</template>

<script>
  export default {
    name: "MyFoot",
    props:['todolist'],
    computed:{
      todol(){
        return this.todolist.length
      },
      doneTodo(){
        //数组过滤方法reduce
        return this.todolist.reduce((pre,current)=> {
          return pre + (current.done ? 1 : 0)
        },0)
      },
      isAll:{
        set(value){
          this.$emit('checkalltodo',value)
        },
        get(){
          return this.todol === this.doneTodo && this.todol > 0
        }
      }
    },
    methods:{
      delchecktodo(){
        if(confirm("确认删除嘛")){
          this.$emit('delAll')
        }
      }
    }
  }
</script>

<style scoped>
  /*footer*/
  .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>

总结

以上为第一版代码,后续会进行优化。
参考文章.

  • 1
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值