vue-todolist
功能列表
-
input输入文字按回车添加事项
-
双击事项显示编辑输入框,按回车键或失去焦点确认修改,按ESC键取消修改
-
点击x删除事项
-
点击勾选切换事项状态,未完成/已完成
-
全选/反选
-
切换all、active、completed显示对应的事项
-
保存数据到localstorage
结构样式
使用vue改写
-
引入vue.js
-
创建vue实例
let vm = new Vue({
el: '.todoapp',
data: {
list: [],
title: '',
editTodoIndex: -1,
beforeTitle: ''
},
methods: {
addTodo(){},
removeTodo(){},
editTodo(){},
editDone(){},
editCancel(){}
}
});
- 渲染数据
<ul class="todo-list" ref="todoList">
//事项有3种状态
//1.未完成没有class
//2.已完成有class completed
//3.编辑状态有class editing
<li
v-for="(item,index) in list"
:class="{completed: item.checked,editing: index === editTodoIndex}"
>
<div class="view">
<input
class="toggle"
type="checkbox"
v-model="item.checked"
/>
<label @dblclick="editTodo(index)">{{item.title}}</label>
<button
class="destroy"
@click="removeTodo(item)"
></button>
</div>
<input
class="edit"
v-model='item.title'
@blur="editDone(item)"
@keyup.13="editDone(item)"
@keyup.esc="editCancel(item)"
/>
</li>
</ul>
- 添加功能
<input class="new-todo" placeholder="请输入内容" @keyup.13 = 'addTodo' v-model='title' />
addTodo(){
if(this.title.trim() === '') return;
this.list.push({
title: this.title,
id: Date.now(),
checked: false
});
this.title = '';
}
- 删除功能
removeTodo(todo){
let index = this.list.findIndex(item => item === todo);
this.list.splice(index,1)
}
- 编辑功能
//添加ref
<input
ref='edit'
class="edit"
v-model='item.title'
@blur="editDone(item)"
@keyup.13="editDone(item)"
@keyup.esc="editCancel(item)"
/>
editTodo(index){
this.editTodoIndex = index;
//编辑的时候显示修改输入框,使用refs获取dom设置焦点
this.$nextTick( () => {
this.$refs.edit[index].focus()
})
// 编辑时候保存一下title
this.beforeTitle = this.list[index].title;
}
editDone(todo){
//当title为空删除
if(!todo.title.trim()){
this.removeTodo(todo);
}
this.editTodoIndex = -1;
this.beforeTitle = '';
}
editCancel(todo) {
this.editTodoIndex = -1;
todo.title = this.beforeTitle;
}
- 全选功能
<input class="toggle-all" v-model='isCheckedAll' type="checkbox" />
computed: {
isCheckedAll: {
get(){
return this.list.length ? this.list.every(item => item.checked) : false
},
set(newValue){
this.list.forEach(item => item.checked = newValue)
}
}
}
- 计算未选中多少条
<footer class="footer">
<span class="todo-count">
<strong>{{uncheckedLen}}</strong>
<span>条未选中</span>
</span>
...
</footer>
computed: {
...
uncheckedLen(){
return this.list.filter(item => !item.checked).length;
}
}
- 当todolist为空不显示footer
<footer class="footer" v-show="list.length">
...
</footer>
- todo类型切换
let filterActive = {
all(list){
return list
},
active(list){
return list.filter(item => !item.checked)
},
completed(list){
return list.filter(item => item.checked)
}
}
// 当hash值改变时触发的事件
window.onhashchange = function (){
let hashVal = window.location.hash;
if(hashVal){
hashVal = hashVal.slice(2);
hashVal = filterActive[hashVal] ? hashVal : 'all'
}else{
hashVal = 'all';
}
// 改变实例下的过滤条件
vm.hash = hashVal;
}
window.onhashchange();
//过滤todolist
computed: {
isCheckedAll: {
get(){
return this.filterList.length ? this.filterList.every(item => item.checked) : false
},
set(newValue){
this.filterList.forEach(item => item.checked = newValue)
}
},
uncheckedLen(){
return this.filterList.filter(item => !item.checked).length;
},
filterList(){
return filterActive[this.hash](this.list);
}
}
//修改渲染列表
<li
:class="{completed: item.checked,editing: index === editTodoIndex}"
v-for="(item,index) in filterList"
>
//添加hash
data: {
...
hash: 'all'
}
//判断hash值添加selected样式
<footer class="footer" v-show="list.length">
...
<ul class="filters">
<li><a href="#/all" :class="{selected:hash === 'all'}">All</a></li>
<li><a href="#/active" :class="{selected:hash === 'active'}">Active</a></li>
<li><a href="#/completed" :class="{selected:hash === 'completed'}">Completed</a></li>
</ul>
</footer>
- 把数据保存到localstorage
//添加修改删除修改数据都需要保存,所以使用watch深度监控list变化然后保存
watch: {
list:{
handler(){
localStorage.setItem('todoListData', JSON.stringify(this.list));
},
deep:true
}
}
//获取数据
let data = JSON.parse(localStorage.getItem('todoListData')) || [];
let vm = new Vue({
...
data: {
...
list: data
}
})
完整示例