vue todolist

父组件todo.vue

<template>
    <section class="real-app">
        <headers></headers>
        <input 
            type="text" 
            class="add-input"
            autofocus="autofocus"
            placeholder="接下来做什么"
            @keyup.enter="addTodo"
        >
        <Item  
            v-for="todo in filterTodos"
            :todo="todo"
            :key="todo.id"
            @del="deleteTodo"
        />
        <Tabs 
            :filter="filter" 
            :todos="todos"
            @toggle="toggleFilter"
            @clearAll="clearAllCompletedTodo"
        />
    </section>
</template>

<script>
import Item from './item.vue';
import Tabs from './tabs.vue';
import headers from './header.vue';
// import { constants } from 'crypto';

let id = 0;

export default {
    data() {
        return {
            todos: [],  //list
            filter: '全部'
        }
    },
    components: {
        Item,
        Tabs,
        headers
    },
    computed: {
        filterTodos() {
            if(this.filter === '全部') {
                return this.todos;
            }
            const filterCompleted = this.filter === '已完成';
            return this.todos.filter(todo=> todo.completed===filterCompleted);
        }
    },
    methods: {
        //input 按下enter事件 给todo中添加条目
        addTodo(e) {
            this.todos.unshift({
                id: id++,
                content: e.target.value,
                completed: false
            });

            e.target.value = '';
        },
        //点击当前todo删除
        deleteTodo(id) {
            this.todos.splice(this.todos.findIndex(todo => id === todo.id), 1);
        },
        //点击状态按钮
        toggleFilter(state) {
            console.log('我是state',state);
            this.filter = state;
        },
        //清除所有已完成 todo.completed设为false
        clearAllCompletedTodo() {
            this.todos = this.todos.filter(todo=> todo.completed===false);
        }
    }
}
</script>

<style  lang='less' scoped>
.real-app{
    width:600px;
    margin:0 auto;
    box-shadow:0 0 5px #666;
}
.add-input{
    position:relative;
    margin:0;
    width:100%;
    font-size:24px;
    font-family:inherit;
    font-weight:inherit ;
    line-height:1.4em;
    border:none;
    outline:none;
    color:inherit; 
    box-sizing:border-box;
    // font-smoothing:antialiased;
    padding:16px 16px 16px 36px;
    border:none;
    box-shadow:inset 0 -2px 1px rgba(0, 0, 0, 0.03);
} 
</style>


子组件header.vue

<template>
    <header class="main-header">
        <h1>Todo</h1>
    </header>
</template>

<style lang="less" scoped>
.main-header{
    text-align:center;
    h1{
        font-size:100px;
        color:rgba(175,47,47,0.4);
        font-weight:400;
        margin:20px;                                                  
    }
}  
</style>

子组件item.vue

<template>
    <div :class="['todo-item', todo.completed? 'completed': '']">
        <!-- 多选框 -->
        <input 
            type="checkbox"
            class="toggle" 
            v-model="todo.completed"
        >
        <!-- item -->
        <label for="">{{todo.content}}</label>
        <!-- 删除键 -->
        <button class="destory" @click="deleteTodo"></button>
    </div>
</template>

<script>
export default {
    props: {
        todo: {
            type: Object,
            required: true
        }
    },
    methods: {
        //把要删除的id传递给父元素
        deleteTodo() {
            this.$emit('del', this.todo.id);
        }
    }
}
</script>

<style  lang='less' scoped>
.todo-item{
    position:relative;
    background-color :#fff;
    font-size :24px;
    border-bottom :1px solid rgba(0,0,0,0.06);
    &:hover{
        .destory:after{
            content:'x';
        }
    }  
    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;
         &.completed{
             label{
                color:#d9d9d9;
                text-decoration:line-through;
             }              
         }     
    }                
}
.toggle{
        text-align:center;
        width:50px;
        height:30px;
        position:absolute;
        top:0;
        bottom:0;
        margin:auto 0;
        border:none;
        appearance:none;
        outline:none;
        &:after{
            content:url('../assets/img/unChecked.svg');
        }
        &:checked:after{
            content:url('../assets/img/checked.svg');
        }          
}                  
 .destory{
        position:absolute;
        top:0;
        right:10px;
        bottom:0;
        width:40px;
        height:40px;
        margin:auto 0;
        font-size:30px;
        color:#cc9a9a;
        margin-bottom:11px;
        transition:color 0.2s ease-out;
        background-color:transparent;
        appearance:none;
        border-width :0;
        cursor:pointer;
        outline:none;  
}     
</style>


子组件tabs.vue

<template>
    <div class="helper">
        <span class="left">{{this.unfinishedTodo.length}} items left</span>
        <span class="tabs">
            <span v-for="state in states" :key="state" :class="[state, filter === state ? 'actived': '']" @click="toggleFilter(state)">
                {{state}}
            </span>
        </span>
        <span class="clear" @click="clearAllCompleted">清除已完成</span>
    </div>
</template>

<script>
export default {
    props: {
        filter: {
            type: String,
            required: true  //必传
        },
        todos: {
            type: Array,
            required: true
        }
    },
    data() {
        return {
            states: ['全部', '未做', '已完成']
        }
    },
    computed: {
        //页面初始过滤未完成数据 监听展示
        unfinishedTodo: function() {
            return this.todos.filter(todo => todo.completed===false);
        }
    },
    methods: {
        //清除所有已完成
        clearAllCompleted() {
            this.$emit('clearAll');  //未传值 只是通知父组件发生了点击 逻辑在父组件里面判断
        },
        //点击状态按钮
        toggleFilter(state) {
            this.$emit('toggle', state);  //传值
        }
    },
    mounted(){
        
    }
}
</script>

<style lang='less' scoped>
.helper{
    font-weight:100;
    display:flex;
    justify-content:space-between;
    padding: 5px 0;
    line-height:30px;
    background-color :#ffffff;
    font-size:14px;
    // font-smoothing:antialiased;
}
    
.left, .clear, .tabs{
    padding:0 10px;
}  
.left .clear{
     width:150px;
} 
.left{
    text-align:center;
}
.clear{
    text-align:right;
    cursor:pointer;
}
.tabs{
    width:200px;
    display:flex;
    justify-content:space-between;
    *{
        display:inline-block;
        padding:0 10px;
        cursor:pointer;
        border:1px solid rgba(175,47,47,0);
        &.actived{
            border-color:rgba(175,47,47,0.4);
            border-radius:5px;
        }
    }
}          
</style>

 

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值