效果图
代码
主要代码:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Document</title>
<link rel="stylesheet" href="./04.css" />
</head>
<body>
<div id="app">
<!-- todos 标题的开始 -->
<app-todo-header></app-todo-header>
<!-- todos 标题的结束 -->
<!-- 中间内容的开始 -->
<div class="app-todo-content">
<div class="todo-content-top">
<span
:class="[{'content-top-allCheck':true},{active:allChecked}]"
@click.stop="handleAllCheck"
>></span
>
<!--
1. input上面添加 键盘事件 => 按下 enter => 保存
2. v-model 值绑定
-->
<input
type="text"
v-focus
v-model="inputVal"
@keyup.enter="saveTodo"
/>
</div>
<div class="todo-content-bottom">
<!-- 代办清单列表 -->
<div class="content-bottom-list">
<div
class="bottom-list-item"
v-for="(item,index) in filterTodos"
:key="item.id"
@dblclick="editTodo(index)"
>
<input type="checkbox" name="" id="" v-model="item.checked" />
<div>
<span
:class="[{'title':true}, {hidden:selectIndex===index},{'complete':item.checked}]"
>{{item.text}}</span
>
<input
type="text"
v-model="item.text"
@blur="saveChangedTodo"
:class="[{hidden:selectIndex!=index}]"
v-todo-focus
/>
</div>
<span class="delete-item" @click="deleteTodo(index)">X</span>
</div>
</div>
</div>
</div>
<!-- 中间内容结束 -->
<!-- todos 底部工具栏开始 -->
<div class="todo-bottom">
<div class="todo-bottom-left">
{{remaining}} 项剩下
</div>
<div class="todo-bottom-mid">
<div
@click.stop="handleItemClick('all')"
:class="[{'bottom-mid-left':true},{active:activeItem==='all'}]"
>
全部
</div>
<div
@click.stop="handleItemClick('active')"
:class="[{'bottom-mid-mid':true},{active:activeItem==='active'}]"
>
激活
</div>
<div
@click.stop="handleItemClick('complete')"
:class="[{'bottom-mid-right':true},{active:activeItem==='complete'}]"
>
完成
</div>
</div>
<div class="todo-bottom-right" @click.stop="clearCompleted">
清除已完成
</div>
</div>
<!-- todos 底部工具栏结束 -->
</div>
</body>
<script src="./node_modules/vue/dist/vue.js"></script>
<script src="./storageUtil.js"></script>
<script>
/*
定义一个 v-focus 可以使得 input 自动聚焦
指令名字: focus
全局指令
*/
// Vue.directive("focus",{
// bind:function(el,binding,vnode,oldNode){
// console.log("指令已经绑定到元素上面");
// },
// // 指令所在的元素 已经插到 父节点中
// inserted:function(el,binding,vNonde,oldNode){
// console.log(el);
// el.focus();
// }
// })
let vm = new Vue({
el: "#app",
watch:{
todos:{
handler:function(value,oldValue){
storageUtil.save("todos",value);
},
deep:true
}
},
computed: {
// 过滤之后的 todos
filterTodos: function () {
// activeItem
// all
if (this.activeItem === "all") {
return this.todos;
} else if (this.activeItem === "active") {
// active => checked === false
return this.todos.filter((todo, index) => {
if (!todo.checked) {
return true;
}
});
} else {
// complete
return this.todos.filter((todo, index) => {
// complete => checked === true
if (todo.checked) {
return true;
}
});
}
},
// 剩下的todos
remaining: function () {
// todos => todo.checked => false => 挑选出来
let remainTodos = this.todos.filter((todo, index) => {
if (!todo.checked) {
return true;
}
});
return remainTodos.length;
},
allChecked: function () {
// 检查todos 如果todos 中有没有被选中的值 allChecked false
let checked = true;
// 如果 todos 为空的情况下 checked = false
// bug fix
if (!this.todos.length) {
checked = false;
}
this.todos.map((todo, index) => {
if (!todo.checked) {
checked = false;
}
});
return checked;
},
},
data: {
activeItem: "all", // all 全部的todos active 激活的todos complete 完成的todos
// allChecked: false,// 全选 默认情况下没有被选中
selectIndex: -1, // -1 没有编辑项 0 正在编辑第一项 1 正在编辑第二项
inputVal: "",
/*
{
id: Date.now(),
text:"ssss",
checked:false
}
*/
todos: storageUtil.fetch("todos"),
},
methods: {
// 点击 全部 激活 完成的点击事件
handleItemClick: function (activeItem) {
this.activeItem = activeItem;
},
// 清除已完成
clearCompleted: function () {
// 过滤掉 todos => todo=> todo.checked === true
// todo.checked === true 已完成
// todo.checked === false 未完成
let filterTodos = this.todos.filter((todo, index) => {
if (!todo.checked) {
return true;
}
});
this.todos = filterTodos; // 替换todos
},
handleAllCheck: function () {
let newTodos = JSON.parse(JSON.stringify(this.todos));
// newTodos.forEach(())
this.todos.map((todo, index) => {
todo.checked = true;
});
// 现在所有的todo 都被选中了
// this.allChecked = true;
},
// 保存todo
saveChangedTodo: function () {
this.selectIndex = -1; // -1 表示没有双击编辑任何的 todo
},
// 编辑todo
editTodo: function (index) {
this.selectIndex = index;
},
// 删除 todo
deleteTodo: function (index) {
// splice(index,1)
this.todos.splice(index, 1);
},
saveTodo: function () {
console.log("获取到input 值", this.inputVal);
if (!this.inputVal) {
alert("输入不能为空!");
return;
}
this.todos.push({
id: Date.now(),
text: this.inputVal,
checked: false,
});
},
},
components: {
"app-todo-header": {
template: `
<div class="app-todo-header">
<span>todos</span>
</div>
`,
},
},
// 局部指令
directives: {
// "todo-focus":{
// // 节点插入父节点
// inserted:function(el,binding){
// el.focus();
// },
// // 节点更新的时候 update
// update:function(el,binding){
// console.log(el);
// el.focus();
// }
// },
// 如果 inserted 和 update 的时候 执行的相同的行为 可以写成以下形式
"todo-focus": function (el, binding) {
el.focus();
},
focus: {
bind: function (el, binding, vNode, oldNode) {},
inserted: function (el, binding, vNode, oldNode) {
el.focus();
},
},
},
});
</script>
</html>
04.css代码
#app {
width: 700px;
margin: 0 auto;
border: 1px solid #ddd;
min-height: 300px;
}
#app .app-todo-header {
height: 50px;
text-align: center;
color: #ead7d7;
font-weight: 100;
font-size: 30px;
}
#app .app-todo-content .todo-content-top {
display: flex;
justify-content: center;
align-items: center;
}
#app .app-todo-content .todo-content-top .content-top-allCheck {
display: inline-block;
transform: rotate(90deg);
}
#app .app-todo-content .todo-content-top .active {
color: #e91f50;
}
#app .app-todo-content .todo-content-top input {
width: 90%;
height: 30px;
}
#app .app-todo-content .todo-content-bottom .content-bottom-list .bottom-list-item {
height: 30px;
display: flex;
justify-content: space-between;
padding: 5px;
border-bottom: 1px solid #ddd;
}
#app .app-todo-content .todo-content-bottom .content-bottom-list .bottom-list-item .hidden {
display: none;
}
#app .app-todo-content .todo-content-bottom .content-bottom-list .bottom-list-item .complete {
color: #d9d9d9;
text-decoration: line-through;
}
#app .app-todo-content .todo-content-bottom .content-bottom-list .bottom-list-item .delete-item {
visibility: hidden;
}
#app .app-todo-content .todo-content-bottom .content-bottom-list .bottom-list-item:hover .delete-item {
visibility: visible;
}
#app .todo-bottom {
height: 50px;
display: flex;
justify-content: space-between;
padding: 5px;
align-items: center;
}
#app .todo-bottom .todo-bottom-mid {
display: flex;
align-items: center;
}
#app .todo-bottom .todo-bottom-mid .active {
border: 1px solid #ddd;
color: red;
}
#app .todo-bottom .todo-bottom-mid .bottom-mid-left {
margin-right: 5px;
}
#app .todo-bottom .todo-bottom-mid .bottom-mid-mid {
margin-right: 5px;
}
#app .todo-bottom .todo-bottom-mid .bottom-mid-right {
margin-right: 5px;
}
storageUtil.js
/*
1. fetch(key) => 获取localStorage 中key 的记录 返回对象
2. save(key,obj) => 保存 key 为 key对象 => localStorage
*/
(function () {
let storageUtil = {};
storageUtil.fetch = function (key) {
let rawObj = localStorage.getItem(key) || "[]"; // 字符串
return JSON.parse(rawObj);
};
storageUtil.save = function (key, obj) {
localStorage.setItem(key, JSON.stringify(obj));
};
window.storageUtil = storageUtil;
})();
此处没有用脚手架 直接安装vue插件完成的
npm install --save vue
然后在HTML内根据安装的路径引用安装的vue.js