目标:用Vue.js制作一个todo任务列表

功能:
1.增删便签任务
2. 编辑便签任务
3. 深度监控数据
4. 利用hash过滤数据

知识点:
1. 常见指令的使用
2. 双向数据绑定(v-model)
3. 动态添加class
4. 循环(v-for)
5. 数据监控
5. 自定义属性
6. 过滤器
7. 计算属性
8. hash过滤数据
9. 事件修饰符

最终效果:
未添加任务前:
未添加任务前

添加3个任务
添加3个任务

标记一个已完成
一个已完成

筛选未完成任务
筛选未完成任务

筛选已完成任务
筛选已完成任务

html代码

<!DOCTYPE html>
<html>

<head>
    <meta charset="utf-8" />
    <title></title>
    <link rel="stylesheet" type="text/css" href="style.css" />
    <script src="https://unpkg.com/vue/dist/vue.js"></script>
</head>

<body>
    <section>
        <div class="page-top">
            <div class="page-content">
                <h2>任务计划列表</h2>
            </div>
        </div>
        <div class="main" id="main">
            <h3 class="title">添加任务</h3>
            <!--:keyup.enter事件修饰符的用法-->
            <input type="text" class="task-input" placeholder="输入任务,按下回车键即可添加新任务" v-model='todo' v-on:keyup.enter='addTodo' />
            <ul class="task-count" v-show='list.length'>
                <li><strong style="color: #EE0000;">{{noCheckedNum}}</strong>个任务未完成</li>
                <li class="action">
                    <a href="#all" :class="{active:visibility === 'all'}">所有任务</a>
                    <a href="#unfinished" :class="{active:visibility === 'unfinished'}">未完成</a>
                    <a href="#finished" :class="{active:visibility === 'finished'}">已完成</a>
                </li>
            </ul>
            <h3 class="title">任务列表</h3>
            <div class="tasks">
                <span class="no-task-tip" v-show='!list.length'>还没有添加任何任务</span>
                <ul class="todo-list">
                    <li class="todo" v-bind: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 for="" @dblclick="editTodo(item)">{{item.title}}</label>
                            <a class="destroy" v-on:click='deleteTodo(item)'></a>
                        </div>
                        <input type="text" class="edit" v-myfocus='editTodos === item' v-on:blur='edited(item)' v-on:keyup.13='edited(item)' v-on:keyup.esc='cancelEdit(item)' v-model='item.title' />
                    </li>
                </ul>
            </div>
        </div>
    </section>
    <script src="app.js" type="text/javascript" charset="utf-8"></script>
</body>

</html>

JavaScript代码:

//存取localStorage中的数据
var store = {
    save(key, value) { //存数据
        localStorage.setItem(key, JSON.stringify(value));
    },
    fetch(key) { //取数据
        return JSON.parse(localStorage.getItem(key)) || [];
    }
}

//所有的list
var list = store.fetch('vue-dotolist'); //fetch的key值可自定义

//过滤任务的三种状态
var filter = {
    all: function(list) {
        return list;
    },
    unfinished: function(list) {
        return list.filter(function(item) {
            return !item.isChecked;
        });
    },
    finished: function(list) {
        return list.filter(function(item) {
            return item.isChecked;
        });
    }
};
var vm = new Vue({
    el: '.main',
    data: {
        list: list,
        todo: '', //记录新增任务的数据
        editTodos: '', //记录正在编辑任务的数据
        beforTitle: '', //记录正在编辑的数据的原title
        visibility: 'all' //通过这个属性值的变化对数据筛选
    },
    watch: {
        //浅复制
        /*list: function(){ //监控list属性,当该值发生变化时调用函数
            store.save('vue-dotolist', this.list);
        }*/
        //深复制
        list: {
            handler: function() {
                store.save('vue-dotolist', this.list);
            },
            deep: true
        }
    },
    methods: {
        addTodo(e) { //添加任务
            //向list中添加一项任务
            //事件处理函数中的list指向的是当前根实例
            this.list.push({
                title: this.todo,
                isChecked: false //新增任务默认不勾选
            });
            this.todo = '';
        },
        deleteTodo(todo) { //删除任务
            var index = this.list.indexOf(todo); //找出在数组中的下标
            this.list.splice(index, 1);
        },
        editTodo(todo) { //编辑任务
            //   console.log(todo);
            //编辑任务时先记录这条任务的title,以便取消编辑时能保留之前的title
            this.beforTitle = todo.title;
            this.editTodos = todo;
        },
        edited(todo) { //编辑完成
            //按下enter键(keyCode为13)或者失去焦点完成编辑
            this.editTodos = '';
        },
        cancelEdit(todo) { //按下esc键取消编辑,保持原任务内容
            todo.title = this.beforTitle;
            this.beforTitle = '';
            //让div显示出来,input隐藏掉,也就是去掉li的class名editing
            this.editTodos = '';
        }
    },
    computed: { //计算属性
        noCheckedNum: function() { //计算未完成任务数
            return this.list.filter(function(item) { //筛选出isChecked为false的任务,即未完成
                return !item.isChecked
            }).length
        },
        filteredList: function() { //过滤任务状态
            //找到过滤函数就返回过滤后的数据,如果没有则返回所有函数
            return filter[this.visibility] ? filter[this.visibility](list) : list;
        }
    },
    directives: { //自定义事件,
        'myfocus': { //自定义事件名称
            update(el, binding) { //数据更新时
                //     console.log(binding);  //可查看到value属性值
                if (binding.value) { //如果value属性值为true执行获取焦点focus
                    el.focus();
                }
            }
        }
    }
});
// 监测hash改变,过滤任务
function watchHashChange() {
    var hash = window.location.hash.slice(1);
    vm.visibility = hash;
    console.log(hash)
}

//进入页面就执行函数
watchHashChange();
//hash改变时也执行函数
window.addEventListener('hashchange', watchHashChange);

CSS代码

ul, li {
    list-style: none;
}
section {
    display: block;
    width: 300px;
    margin: 30px auto;
    text-align: center;
}
.main input[class='task-input'] {
    line-height: 24px;
    width: 230px;
    height: 24px;
    padding: 3px;
    outline: none;
}
.title {
    color: #008b8b;
}
.task-count li {
    margin-top: 10px;
}
.action a {
    text-decoration: none;
}
.active{
    border: 1px solid #B22222;
    border-radius: 3px;
    padding: 3px;
}
.completed .view label {
    text-decoration: line-through;
    color: #aaa;
}
input.edit {
    display: none;
    height: 24px;
}
.todo-list li {
    text-align: left;
}
.editing {
    text-align: center;
}
.editing div.view {
    display: none;
}
.editing .edit {
    display: block;
    text-align: center;
}
.view input, .view label, .view a {
    line-height: 20px;
    height: 20px;
    vertical-align: middle;
}
a.destroy {
    display: inline-block;
    width: 20px;
    background: url(dele.png) no-repeat center center;
}
Logo

前往低代码交流专区

更多推荐