用vue完成一个便签的小程序
基本样式如下:
1.添加任务
- v-model
- v-for
- v-on:click.enter
- v-show
- methods
输入框代码,v-model
记录输入信息,同时将值赋值到todo
里,当回车时,用到v-on:click.enter
事件.enter
事件修饰符,触发addTodo
方法,此方法记录在methods
对象里,这时通过addTodo
方法将记录的todo
值传到列表里,在经过v-for
渲染,ok完成
<input
type="text"
v-model="todo"
v-on:keyup.enter="addTodo"
placeholder="提示:回车会添加任务"
>
v-for
将vm
实例中list
对象数组渲染到列表里
<li v-for="(item,index) in list">
<div class="view">
<span class="icon-check"><label><input type="checkbox"></label></span>
<span class="text">{{item.title}}</span>
<span class="f-fr close"></span>
</div>
<input
type="text"
class="edit"
/>
</li>
js里
var vm = new Vue({
el:".m-body",
data:{
list:[],
todo:"",//要添加任务
total:"0"//共有任务数
},
methods:{
addTodo(){
var obj = {
title :this.todo
}
this.list.push(obj);
this.todo="";//输入完,清空
this.total=list.length;
}
}
})
注意一些小细节,当输入完,input
要清空内容,这是让todo=""
当有任务出现时,
这个要去掉,用到了v-show:""
其值真假可控制元素是否出现
<span class="msg" v-show="!list.length">未添加任务</span>
2.完成任务,加删除符号,双击编辑
在checkbox上v-model="item.isChecked"
用isChecked值记录控制有没有选中,在li
通过v-bind:class{completed:item.isChecked}
来控制样式改变
<li
v-for="(item,index) in list"
:class={completed:item.isChecked}"
>
<div class="view">
<span class="icon-check">
<label
:class="{checked:item.isChecked}"
>
<input
type="checkbox"
v-model="item.isChecked"
>
</label>
</span>
<span class="text">{{item.title}}</span>
<span class="f-fr close" v-on:click="deleteTodo(index)"></span>
</div>
<input //编辑框
type="text"
class="edit"
/>
</li>
在view元素上,添加双击事件@dblclick="edtorTodo(item)
换出编辑框
<input
type="text"
class="edit"
v-model="item.title"
v-focus="edtorTodos===item"
@blur="edtorTodoed(item)"
@keyup.13="edtorTodoed(item)"
@keyup.esc="cancelTodo(item)"
/>
v-focus
自定义指令,当edtorTodos===item
当前编辑时自动获得焦点,添加失去焦点事件,回车事件.创建一个方法cancelTodo(item)
当编辑中按esc
键自动返回原来值
directives:{
'focus':{
update(el,binding){ //钩子函数update绑定元素更新时调用
if(binding.value){//binding.value是指令的绑定值
el.focus()
}
}
}
}
methods:{
addTodo(){ //添加任务
this.list.push({
title :this.todo,
isChecked:false
});
this.todo="";//输入完,清空
console.log(this.list[0].title)
this.total=this.list.length;
},
deleteTodo(index){ //删除任务
this.list.splice(index,1);
this.total=this.list.length;
},
edtorTodo(todo){ //编辑任务
this.beforeTitle=todo.title;
this.edtorTodos=todo;
},
edtorTodoed(todo){//编辑完成
this.edtorTodos = "";
},
cancelTodo(todo){//当按esc时返回原先编辑数据
todo.title = this.beforeTitle;
this.edtorTodos = "";
this.beforeTitle="";
}
}
3.过滤任务
通过hash来过滤,添加visibility
记录当前hash
function watchHashChange(){
var hash = window.location.hash.slice(1);
vm.visibility = hash;
}
window.addEventListener("hashchange", watchHashChange)
在vm
实例上增加计算,过滤filteredList
,将view
上list
替换
computed:{
filteredList:function(){
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;
})
}
}
return filter[this.visibility]?filter[this.visibility](list):list
}
},
4.本地储存localStorage
//本地存取localStorage中数据
var store = {
save(key,value){
localStorage.setItem(key,JSON.stringify(value));
},
fetch(key){
return JSON.parse(localStorage.getItem(key))||[]
}
}
在vm实例中添加watch
用来监控list
,当list
中值发生变化时,触发对应函数,即改变本地存储
watch:{ //深度监控
list:{
handler:function(){
store.save("miaov-class",this.list)
},
deep:true
}
}
最后定义var list = store.fetch('miaov-class');当刷新时就能从本地实时拿值了.
最后附上完整代码
HTML
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Document</title>
<link rel="stylesheet" href="./css.css">
<script src="./vue/vue.js"></script>
</head>
<body>
<!-- 标题 -->
<div class="m-nav">
<div class="g-main">
<h2>任务计划列表</h2>
</div>
</div>
<div class="m-body">
<div class="g-main">
<!-- 添加任务区 -->
<div class="u-add">
<h3>添加任务:</h3>
<input
type="text"
v-model="todo"
v-on:keyup.enter="addTodo"
placeholder="提示:回车会添加任务"
>
<!-- 按钮区 -->
<div class="message">
<span class="msg f-fl">共有{{noCheckedList}}个任务</span>
<a class="btn" href="#all">所有任务</a>
<a class="btn" href="#finished">已完成任务</a>
<a class="btn" href="#unfinished">未成任务</a>
</div>
</div>
<!-- 任务列表 -->
<div class="u-list">
<h3>任务列表:</h3>
<span class="msg" v-show="!list.length">未添加任务</span>
<ul>
<li v-for="(item,index) in filteredList" :class="{completed:item.isChecked,editing:edtorTodos===item}"><!-- 在li上统一管理class -->
<div class="view" @dblclick="edtorTodo(item)">
<span class="icon-check"><label :class="{checked:item.isChecked}"><input type="checkbox" v-model="item.isChecked"></label></span>
<span class="text">{{item.title}}</span>
<span class="f-fr close" v-on:click="deleteTodo(index)"></span>
</div>
<input
type="text"
class="edit"
v-model="item.title"
v-focus="edtorTodos===item"
@blur="edtorTodoed(item)"
@keyup.13="edtorTodoed(item)"
@keyup.esc="cancelTodo(item)"
/>
</li>
</ul>
</div>
</div>
</div>
<script src="./app1.js"></script>
</body>
</html>
JS
//本地存取localStorage中数据
var store = {
save(key,value){
localStorage.setItem(key,JSON.stringify(value));
},
fetch(key){
return JSON.parse(localStorage.getItem(key))||[]
}
}
var list = store.fetch('miaov-class');
var vm = new Vue({
el:".m-body",
data:{
list:list,
todo:"",//要添加任务
edtorTodos:"",//记录正在编辑数据
beforeTitle:"",//编辑前数据
visibility:"all"//通过属性值变化对数据筛选
},
methods:{
addTodo(){ //添加任务
this.list.push({
title :this.todo,
isChecked:false
});
this.todo="";//输入完,清空
console.log(this.list[0].title)
this.total=this.list.length;
},
deleteTodo(index){ //删除任务
this.list.splice(index,1);
this.total=this.list.length;
},
edtorTodo(todo){ //编辑任务
this.beforeTitle=todo.title;
this.edtorTodos=todo;
},
edtorTodoed(todo){//编辑完成
this.edtorTodos = "";
},
cancelTodo(todo){//当按esc时返回原先编辑数据
todo.title = this.beforeTitle;
this.edtorTodos = "";
this.beforeTitle="";
}
},
directives:{
'focus':{
update(el,binding){ //钩子函数update绑定元素更新时调用
if(binding.value){//binding.value是指令的绑定值
el.focus()
}
}
}
},
computed:{
noCheckedList:function(){
return list.filter(function(item){
return !item.isChecked
}).length
},
filteredList:function(){
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;
})
}
}
return filter[this.visibility]?filter[this.visibility](list):list
}
},
watch:{
list:{
handler:function(){
store.save("miaov-class",this.list)
},
deep:true
}
}
})
function watchHashChange(){
var hash = window.location.hash.slice(1);
vm.visibility = hash;
}
window.addEventListener("hashchange", watchHashChange)
css
html,body,h2,h3,ul{margin: 0;border: 0;padding: 0;}
h2,h3{line-height: 2em}
li{list-style: none;font-family: "Helvetica Neue",Helvetica,"PingFang SC","Hiragino Sans GB","Microsoft YaHei","微软雅黑",Arial,sans-serif;}
a{text-decoration: none;color: #666;}
.f-fl{float:left;}
.f-fr{float: right;}
.g-main{
width: 500px;
margin: 0 auto;
}
body{
background-color: #efe;
}
.m-nav{
background-color: #E8583C;
color: #fff;
}
.u-add input{
width: 100%;
margin-bottom: 10px;
}
.u-add .message{
text-align: right;
margin: 0 10px;
font:14px '微软雅黑';
color: #666;
}
.u-add .message .btn{
display: inline-block;
border: 1px solid #efe;
padding: 0 10px;
}
.u-add .message .btn:hover{
border-color: #999;
background-color: #fff;
cursor: pointer;
}
.u-add .message .msg{
color: red;
}
.u-list .completed{
text-decoration: line-through;
color:#aaa;
}
.u-list .msg{
font :14px '微软雅黑';
color: #666;
margin-left: 10px;
}
.u-list li{
background-color: #fff;
margin-bottom: 1px;
padding: 10px 0;
position: relative;
}
.u-list li .edit{
display: none;
}
.u-list li.editing .edit{
display: block;
padding: 0;
width: 100%;
height: 100%;
box-sizing: border-box;
}
.u-list li.editing .view{
display: none;
}
.u-list .icon-check{
position: relative;
display: inline-block;
height: 20px;
width: 20px;
margin-left: 10px;
}
.u-list .icon-check input{
height: 20px;
width: 20px;
visibility: hidden;
margin: 0;
padding:0;
}
.u-list .icon-check label{
position: absolute;
top:4px;
display: inline-block;
height: 18px;
width: 18px;
border: 1px solid #666;
border-radius: 50% 50%;
}
.u-list .icon-check label.checked{
background:url(./icon.png) 2px 2px/90% 90%;
}
.u-list li:hover{
background-color: #efe;
}
.u-list li:hover .close{
display: block;
}
.u-list .close{
display: none;
position: absolute;
width: 19px;
height: 19px;
top: 12px;
right: 10px;
color: #cc9a9a;
}
.u-list .close:after{
content: "X"
}
.u-list .close:hover{
cursor: pointer;
}