todolist案例源码(vue2脚手架实现)

实现效果

todolist

代码

1. main.js入口文件

//引入Vue
import Vue from 'vue'
//引入App
import App from './App.vue'
//关闭Vue的生产提示
Vue.config.productionTip = false;

// 创建vm
new Vue({
    
    
    el: '#app',
    render: h => h(App),
    beforeCreate() {
    
    
        Vue.prototype.$bus = this
    }
})

2. App.vue

<template>
  <div id="root">
    <div class="todo-container">
        <div class="todo-wrap">
            <HeaderInput @ReceiveAddTodo="ReceiveAddTodo" ></HeaderInput>
            <TodoList :todos="todos" >
            </TodoList>
            <FooterTotal :todos="todos" @selectAllTodo="selectAllTodo" @clearAlll="clearAlll"></FooterTotal>
        </div>
    </div>
</div>
</template>
<script>
    import HeaderInput from './components/HeaderInput.vue';
    import TodoList from './components/TodoList.vue';
    import FooterTotal from './components/FooterTotal.vue';

    import pubsub from 'pubsub-js'

    export default {
    
    
        name:"App",
        components:{
    
     HeaderInput, TodoList, FooterTotal},
        data(){
    
    
            return{
    
    
                todos:JSON.parse(localStorage.getItem("todos"))||[],
            }
        },
        methods:{
    
    
            
            //添加一个todo
            ReceiveAddTodo(todo){
    
    
                this.todos.unshift(todo)
            },

            //勾选一个todo
            checkTodo(id){
    
    
                this.todos.forEach((todo)=>{
    
    
                    if(todo.id===id) todo.done=!todo.done
                })
            },

            //删除一个todo
            deleteTodo(_,id){
    
    
                if(confirm("确认删除吗?")){
    
    
                    this.todos = this.todos.filter((todo)=>{
    
    
                    return todo.id !== id
                })
                }
            },


            //全选
            selectAllTodo(checkValue){
    
    

                if(checkValue){
    
    
                    this.todos.forEach(todo=>todo.done=true)
                }else{
    
    
                    this.todos.forEach(todo=>todo.done=false)
                }
            },

            //清除已选
            clearAlll(){
    
    
                if(confirm("确认要清除已完成的任务吗?")){
    
    
                    this.todos=this.todos.filter(todo=>todo.done!==true)
                }
            },
            

            editTitle(id,value){
    
    
                this.todos.forEach(todo=>{
    
    
                    if(todo.id===id) todo.title=value
                })
            }
        },
        watch:{
    
    
            todos:{
    
    
                deep:true,
                handler(value){
    
    
                    localStorage.setItem("todos",JSON.stringify(value))
                }
            }
        },
        mounted(){
    
    
            this.$bus.$on("checkTodo",this.checkTodo)
            // this.$bus.$on("deleteTodo",this.deleteTodo)
            this.pubId = pubsub.subscribe("delId",this.deleteTodo)
            this.$bus.$on("editTitle",this.editTitle)
        },
        beforeDestroy(){
    
    
            this.$bus.$off("checkTodo")
            // this.$bus.$off("checkTodo","deleteTodo")
            pubsub.unsubscrube(this.pubId)

        }
    }
    
</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-edit {
    
    
    color: #fff;
    background-color: skyblue;
    border: 1px solid rgb(117, 173, 196);
    }
    

    .btn-edit:hover {
    
    
    color: #fff;
    background-color: rgb(89, 147, 171);
    }

    .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>

3. 头部组件 HeaderInput.vue

<template>
    <div class="todo-header">
        <input type="text" placeholder="请输入你的任务名称,按回车键确认" @keyup.enter="addTodo" v-model.trim="title"/>
    </div>
</template>
<script>
    import {
    
    nanoid} from "nanoid"
    export default {
    
    
        name:"HeaderInput",
        data(){
    
    
            return{
    
    
                title:""
            }
        },
        methods:{
    
    
            addTodo(){
    
    
                if(this.title==="") return alert("不能为空")

                const todoObj = {
    
    id:nanoid(),title:this.title,done:false};
                this.$emit("ReceiveAddTodo",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>

4. 列表组件TodoList.vue

<template>
        <ul class="todo-main">
        <TodoItem 
            v-for="todo in todos" 
            :key="todo.id" 
            :todo="todo" >
        </TodoItem>

      </ul>
</template>
<script>
    import TodoItem from './TodoItem.vue'

    export default {
    
    
        name:"TodoList",
        components:{
    
     TodoItem},
        props:["todos"]
    }
</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>

5. 列表item组件TodoItem.vue

<template>
    <li>
        <label>
            <input type="checkbox" :checked="todo.done" @click="check(todo.id)"/>
            <span v-show="!todo.isEdit">{
   
   {todo.title}}</span>
            <input type="text" 
                :value="todo.title" 
                v-show="todo.isEdit" 
                @blur="cancelBlur(todo,$event) "
                ref='edit'
            >
        </label>
        <button class="btn btn-danger" @click="delT(todo.id)">删除</button>
        <button class="btn btn-edit" @click="editTodo(todo)" v-show="!todo.isEdit">编辑</button>
    </li>
</template>
<script>
    import pubsub from 'pubsub-js'
    export default {
    
    
        name:"TodoItem",
        props:["todo"],
        
        methods:{
    
    
            check(id){
    
    
                this.$bus.$emit("checkTodo",id)
                
            },
            delT(id){
    
    
                // this.$bus.$emit("deleteTodo",id)
                 pubsub.publish("delId",id)
            },

            //编辑
            editTodo(todo){
    
    
                if(Object.prototype.hasOwnProperty.call(todo,"isEdit")){
    
    
                    todo.isEdit=true
                }else{
    
    
                    this.$set(todo,"isEdit",true)
                }

                this.$nextTick(function(){
    
    
                    this.$refs.edit.select()
                })
            },

            //失去焦点
            cancelBlur(todo,e){
    
    
                if(e.target.value.trim()===""){
    
    
                    alert("输入不能为空")
                }else{
    
    
                    todo.isEdit=false
                this.$bus.$emit("editTitle",todo.id,e.target.value.trim())
                }

            }
        }

    }
</script>
<style scoped>
        /*item*/
    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: none;
    margin-top: 3px;
    }

    li:before {
    
    
    content: initial;
    }

    li:last-child {
    
    
    border-bottom: none;
    }

    li:hover{
    
    
        background-color: #ddd;
    }

    li:hover button{
    
    
        display: block;
    }
</style>

6. 底部组件FooterTotal.vue

<template>
        <div class="todo-footer" v-show="todos.length">
            <label>
                <!-- <input type="checkbox" @change="selectAll"  :checked="isAll"/> -->
                <input type="checkbox" v-model="isAll"/>
            </label>
            <span>
                <span>已完成{
   
   {todoDone}}</span> / 全部{
   
   {todos.length}}
            </span>
            <button class="btn btn-danger" @click="clearAll">清除已完成任务</button>
      </div>
</template>
<script>
    export default {
    
    
        name:"FooterTotal",

        props:["todos"],
        computed:{
    
    
            todoDone(){
    
    
                // let quantity=0
                // this.todos.forEach(todo => {
    
    
                //     if(todo.done){
    
    
                //         quantity++
                //     }
                // });
                // return quantity
                return this.todos.reduce((pre,todo)=>pre+(todo.done==true?1:0),0)
            },
            isAll:{
    
    
                get(){
    
    
                   return this.todoDone==this.todos.length&&this.todos.length>0
                },
                set(value){
    
    
                    this.$emit("selectAllTodo",value)
                }
            }
           
        },
        data(){
    
    
            return {
    
    
                selectA:""
            }
        },
        // methods: {
    
    
        //         selectAll(e){
    
    
        //             this.selectAllTodo(e.target.checked)
        //         }
        //     }
        methods:{
    
    
            clearAll(){
    
    
                this.$emit("clearAlll")
            }
        }
    }
</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>

猜你喜欢

转载自blog.csdn.net/weixin_46466212/article/details/126755427
今日推荐