Code 可以在如下 git 中获取:
https://github.com/slhuang520/vue
基本的项目结构如下:
taskList.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>任务列表</title>
<link rel="stylesheet" href="css/taskList.css">
<script src="../lib/vue.min.js"></script>
</head>
<body>
<div class="page-top">
<div class="page-content">
<h2>任务计划列表</h2>
</div>
</div>
<div class="main">
<h3 class="big-title">添加任务</h3>
<input type="text"
placeholder="例如:吃饭睡觉打豆豆 提示:+回车就可以添加任务"
class="task-input"
@keyup.enter="addTodo()"
v-model="todo"
>
<ul class="task-count" v-show="list.length">
<li class="msg" v-if="idx==0">{{unCheckedCount}}个未完成任务</li>
<li class="msg" v-if="idx==1">{{checkedCount}}个完成任务</li>
<li class="msg" v-if="idx==2">{{list.length}}个所有任务</li>
<li class="action">
<a href="#" v-on:click="active(0)" class="active">未完成的任务</a>
<a href="#" v-on:click="active(1)">完成的任务</a>
<a href="#" v-on:click="active(2)">所有任务</a>
</li>
</ul>
<h3>任务列表</h3>
<span v-show="(idx==0 && !unCheckedCount)||(idx==1 && !checkedCount)||(idx==2 && !list.length)">目前还没有添加任何任务</span>
<div>
<ol class="list">
<li v-if="(idx==0 && !obj.isChecked)||(idx==1 && obj.isChecked)||(idx==2)" v-for="obj in list">
<div v-show="editItem!=obj">
<input type="checkbox" v-model="obj.isChecked"><h4 :class="{completed:obj.isChecked}" @dblclick="edit(obj)">{{obj.name}}</h4><i @click="del(obj)">×</i>
</div>
<input type="text"
class="edit"
v-show="editItem && editItem === obj"
v-model="editItem.name"
v-focus="obj === editItem"
@blur="blur(obj)"
@keyup.enter="blur(obj)"
@keyup.esc="rollback(obj)"
>
</li>
</ol>
</div>
</div>
<script src="js/taskList.js"></script>
</body>
</html>
taskList.js
//使用 localStorage 存储数据
var store = {
save: function (key, value) { //保存数据
localStorage.setItem(key, JSON.stringify(value)); // localStorage中只能保存 string
},
fetch: function (key) {
return JSON.parse(localStorage.getItem(key)) || [];
}
};
var list = store.fetch("task-list");
/*var list = [
{
title: "v-for",
name: " v-for指令需要以 site in sites 形式的特殊语",
isChecked: false
},
{
title: "Vue.js",
name: "Vue.js 使用了基于 HTML 的模版语法,允许开发者声明式地将 DOM 绑定至底层 Vue 实例的数据。",
isChecked: false
},
{
title: "第二部分",
name: " 使用原生JS实现jQuery的addClass, removeClass, hasClass函数功能",
isChecked: true
},
{
title: "获取 class 内容",
name: "判断获取到的 class 是否为空, 如果不为空在前面加个'空格'",
isChecked: true
}
];*/
new Vue({
el: ".main",
data: {
list: list,
todo: "", //添加的数据
idx: 0, //选中的 menu 下标
editItem: "", //当前编辑的任务
beforeName: "" //记录正在编辑的任务的原来的 name
},
watch: {
// list: function () { // 监听 list 的变化,但不可以监听其子对象的变化
// store.save("task-list", this.list);
// }
list: {
handler: function () {
store.save("task-list", this.list);
},
deep: true
}
},
computed: {
unCheckedCount: function () {
return this.list.filter(function (item) {
return !item.isChecked;
}).length;
},
checkedCount: function () {
return this.list.filter(function (item) {
return item.isChecked;
}).length;
}
},
methods: {
addTodo: function (e) { //添加任务
this.list.push({
title: this.todo,
name: this.todo,
isChecked: false
});
this.todo = "";
},
active: function (n) { //切换 menu
this.idx = n;
document.getElementsByClassName("active")[0].className = "";
window.event.target.className = "active";
},
del: function (obj) { //删除任务
var idx = list.indexOf(obj);
if (idx != -1) { //判断当前操作的是哪个 menu
list.splice(idx, 1);
}
},
edit: function (obj) { //编辑
this.editItem = obj;
this.beforeName = obj.name;
},
blur: function (obj) { //失去光标保存
this.editItem = "";
},
rollback: function (obj) {
obj.name = this.beforeName;
this.beforeName = "";
this.editItem = ""; //退出编辑模式
}
},
directives: {
focus: { //创建获取焦点的指令
update: function (el, binding) {
if (binding.value) {
console.log(el, binding);
el.focus();
}
}
}
}
});
taskList.css
@charset "utf-8";
html, body, h2 {
margin: 0;
padding: 0;
}
.page-top {
width: 100%;
text-align: center;
background-color: #1b6d85;
}
.page-content {
padding: 5px;
}
.main {
width: 80%;
margin: 0 auto;
}
.task-input {
width: 100%;
height: 30px;
line-height: 30px;
}
.task-count {
padding: 0;
border-bottom: 1px solid gray;
}
.task-count li{
list-style: none;
margin: 10px;
display: inline-block;
height: 22px;
}
.task-count .action {
float: right;
}
.task-count li a {
border: 1px solid grey;
padding: 5px;
margin: 0;
text-decoration: none;
float: left;
}
/*.task-count a::after{
content: "";
}*/
.task-count li a:not(:first-child) {
border-left: none;
}
.task-count .msg {
color: #8c2a1b;
}
.task-count .active {
background-color: lightgray;
border-bottom-color: lightgray;
}
.list {
cursor: pointer;
}
.list li {
border: 1px solid lightgray;
position: relative;
height: 40px;
}
.list li:nth-child(2n) {
background-color: #eee;
}
.list li:hover {
background-color: #ddd;
}
.list h4 {
margin: 0;
height: 40px;
line-height: 40px;
display: inline-block;
padding-left: 5px;
}
.list i {
float: right;
width: 15px;
height: 15px;
line-height: 15px;
border: 1px solid #666;
border-radius: 50% 50%;
font-family: monospace;
margin-top: 12px;
margin-right: 10px;
border-radius: 50% 50%;
}
.list i:hover {
background-color: mediumvioletred;
}
.list .edit {
width: 90%;
height: 34px;
line-height: 34px;
position: absolute;
top: 0;
left: 0;
}
.list .completed {
color: #aaa;
text-decoration-line: line-through;
}
.list input[type="checkbox"] {
width: 28px;
height: 28px;
margin-top: 7px;
float: left;
}
画面结果如下: