赞
踩
如下图所示,制作一个这样的记事本,可以使用这个记事本进行数据的存储以及管理,样式是天禹老师写好的我们直接使用就好了,主要在这个小案例中体会一下在Vue中如何维护页面展示的数据。在读懂这篇博客之前,需要有一定的前置知识前置知识传送门。
最初版本:
各项完善之后
这个小小案例主要页面主要分为三大部分头、身体、脚部。这三个部分的组件为兄弟组件均归App组件进行管理
通过bootstrap.css渲染实现上面GIF的效果。
在写这个小案例时,可以有以下几个版本的更新迭代,建议从最基础的功能实现做起,由于篇幅有限这里只放基础功能源码及其他版本的核心代码,如果有什么疑问的话欢迎评论区留言。
在进行写代码的时候遵循以下步骤:
TodoList案例各个版本应实现的功能
基础功能版本页面的整体布局及代码如下:
main.js 存放基础的配置
import App from "./App.vue"
import Vue from "vue"
new Vue({
el:"#App",
render:h=>h(App)
})
App.vue 管理其余组件,提供页面的布局
<template> <div id="root"> <div class="todo-container"> <div class="todo-wrap"> <MyHead :lss="lss" :addobj="addtodo"></MyHead> <MyList :lss="lss" :alertDone="alertDone" :deltodo="deltodo"></MyList> <MyFoot :lss="lss" :alertall="alertall" :deld="deldoneall"></MyFoot> </div> </div> </div> </template> <script> import MyHead from "./components/MyHead.vue" import MyFoot from "./components/MyFoot.vue" import MyList from "./components/MyList.vue" import {nanoid} from "nanoid" export default { name:"App", data(){ return { lss:[ {id:1,title:"吃饭",done:false}, {id:2,title:"喝水",done:true}, {id:3,title:"睡觉",done:false} ] } }, components:{ MyFoot, MyHead, MyList }, methods:{ alertDone(id){ this.lss.forEach((todo)=>{ // alert("done") if (todo.id===id){todo.done=!todo.done} }) }, deltodo(id){ this.lss=this.lss.filter((todo)=>{ return todo.id!==id }) }, addtodo(titname){ if (titname===""){ return false; } var index = this.lss.findIndex(item => item.title === titname) console.log(index) if (index!==-1){ return false } console.log(index) this.lss.unshift({"id":nanoid(),"title":titname,"done":false}) return true }, alertall(e){ this.lss.forEach((todo)=>{ todo.done=e }) }, deldoneall(){ this.lss=this.lss.filter((todo)=>{ return !todo.done }) } } } </script> <style> body { background: #fff; } .btn { display: inline-block; padding: 4px 12px; margin-bottom: 0; font-size: 14px; line-height: 20px; text-align: center; vertical-align: middle; cursor: pointer; box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.2), 0 1px 2px rgba(0, 0, 0, 0.05); border-radius: 4px; } .btn-danger { color: #fff; background-color: #da4f49; border: 1px solid #bd362f; } .btn-danger:hover { color: #fff; background-color: #bd362f; } .btn:focus { outline: none; } .todo-container { width: 600px; margin: 0 auto; } .todo-container .todo-wrap { padding: 10px; border: 1px solid #ddd; border-radius: 5px; } </style>
MyList.vue 作为MyItem的父组件,也就是页面的中间部分
<template> <ul class="todo-main"> <!-- eslint-disable-next-line vue/valid-v-for --> <MyItem v-for="todo in lss" :todo="todo" :aleDone="alertDone" :deltodo="deltodo"></MyItem> </ul> </template> <script> import MyItem from "./MyItem.vue" export default { name:"MyList", components:{ MyItem }, props:["lss","alertDone","deltodo"] } </script> <style scoped> .todo-main { margin-left: 0px; border: 1px solid #ddd; border-radius: 2px; padding: 0px; } .todo-empty { height: 40px; line-height: 40px; border: 1px solid #ddd; border-radius: 2px; padding-left: 5px; margin-top: 10px; } </style>
MyItem.vue 展示一条代办事项信息
<template> <li> <label> <!-- 这里如果使用双向绑定的话。会修改掉props传过来的数据,虽然没有报错,但是一般不可以这么做。 --> <input type="checkbox" :checked="todo.done" @change="alertdone(todo.id)"/> <span>{{todo.title}}</span> </label> <button class="btn btn-danger" @click="delthistodo(todo.id)">删除</button> </li> </template> <script> export default { name:"MyItem", props:["todo","aleDone","deltodo"], computed:{ }, methods:{ alertdone(id){ if (id){ // alert(id) this.aleDone(id) } }, delthistodo(id){ this.deltodo(id) } } } </script> <style scoped> li { list-style: none; height: 36px; line-height: 36px; padding: 0 5px; border-bottom: 1px solid #ddd; } li label { /* float: left; */ cursor: pointer; } li label li input { vertical-align: middle; margin-right: 6px; position: relative; top: -1px; } li button { float: right; display: none; margin-top: 3px; } li:before { content: initial; } li:last-child { border-bottom: none; } li:hover{ background-color: #ddd; } li:hover button{ display:block; } </style>
MyHead.vue 页面的头部
<template> <div class="todo-header"> <input v-model="temp" placeholder="请输入你的任务名称,按回车键确认" @keydown.enter="adddata"/> </div> </template> <script> export default { name:"MyHead", props:["lss","addobj"], data(){ return { temp:"" } }, methods:{ adddata(){ if (this.addobj(this.temp)){ alert("添加成功!") }else{ alert("添加失败!") } this.temp="" } } } </script> <style scoped> .todo-header input { width: 560px; height: 28px; font-size: 14px; border: 1px solid #ccc; border-radius: 4px; padding: 4px 7px; } .todo-header input:focus { outline: none; border-color: rgba(82, 168, 236, 0.8); box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075), 0 0 8px rgba(82, 168, 236, 0.6); } </style>
MyFoot.vue 页面的脚部
<template> <div class="todo-footer" v-if="lss.length"> <label> <input type="checkbox" v-model="alerall"/> </label> <span> <span>已完成{{donenum}}</span> / 全部{{lss.length}} </span> <button class="btn btn-danger" @click="deldoneall">清除已完成任务</button> </div> </template> <script> export default { name:"MyFoot", props:["lss","alertall","deld"], computed:{ donenum(){ // 进行计数统计常用的方法。函数调用过程为第一个参数为回调函数,第二参数为初始值 // 回调函数中第一个参数是函数调用过程中的返回值,第二个为对象 return this.lss.reduce((pre,todo)=>pre+(todo.done?1:0),0) }, alerall:{ get(){ return (this.lss.length===this.donenum)&&this.lss.length!==0 }, set(e){ // 这里如果是勾选的话就传进来true,否则传进来false // console.log(e) this.alertall(e) } } }, methods:{ deldoneall(){ this.deld() } } } </script> <style scoped> .todo-footer { height: 40px; line-height: 40px; padding-left: 6px; margin-top: 5px; } .todo-footer label { display: inline-block; margin-right: 20px; cursor: pointer; } .todo-footer label input { position: relative; top: -1px; vertical-align: middle; margin-right: 5px; } .todo-footer button { float: right; margin-top: 5px; } </style>
可以先尝试将以下代码拆分为最初的v1版本,然后逐步向高版本迭代。
/*base*/ body { background: #fff; } .btn { display: inline-block; padding: 4px 12px; margin-bottom: 0; font-size: 14px; line-height: 20px; text-align: center; vertical-align: middle; cursor: pointer; box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.2), 0 1px 2px rgba(0, 0, 0, 0.05); border-radius: 4px; } .btn-danger { color: #fff; background-color: #da4f49; border: 1px solid #bd362f; } .btn-danger:hover { color: #fff; background-color: #bd362f; } .btn:focus { outline: none; } .todo-container { width: 600px; margin: 0 auto; } .todo-container .todo-wrap { padding: 10px; border: 1px solid #ddd; border-radius: 5px; } /*header*/ .todo-header input { width: 560px; height: 28px; font-size: 14px; border: 1px solid #ccc; border-radius: 4px; padding: 4px 7px; } .todo-header input:focus { outline: none; border-color: rgba(82, 168, 236, 0.8); box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075), 0 0 8px rgba(82, 168, 236, 0.6); } /*main*/ .todo-main { margin-left: 0px; border: 1px solid #ddd; border-radius: 2px; padding: 0px; } .todo-empty { height: 40px; line-height: 40px; border: 1px solid #ddd; border-radius: 2px; padding-left: 5px; margin-top: 10px; } /*item*/ li { list-style: none; height: 36px; line-height: 36px; padding: 0 5px; border-bottom: 1px solid #ddd; } li label { float: left; cursor: pointer; } li label li input { vertical-align: middle; margin-right: 6px; position: relative; top: -1px; } li button { float: right; display: none; margin-top: 3px; } li:before { content: initial; } li:last-child { border-bottom: none; } /*footer*/ .todo-footer { height: 40px; line-height: 40px; padding-left: 6px; margin-top: 5px; } .todo-footer label { display: inline-block; margin-right: 20px; cursor: pointer; } .todo-footer label input { position: relative; top: -1px; vertical-align: middle; margin-right: 5px; } .todo-footer button { float: right; margin-top: 5px; }
<!doctype html> <html lang="en"> <head> <meta charset="utf-8"> <title>React App</title> <link rel="stylesheet" href="index.css"> </head> <body> <div id="root"> <div class="todo-container"> <div class="todo-wrap"> <div class="todo-header"> <input type="text" placeholder="请输入你的任务名称,按回车键确认"/> </div> <ul class="todo-main"> <li> <label> <input type="checkbox"/> <span>xxxxx</span> </label> <button class="btn btn-danger" style="display:none">删除</button> </li> <li> <label> <input type="checkbox"/> <span>yyyy</span> </label> <button class="btn btn-danger" style="display:none">删除</button> </li> </ul> <div class="todo-footer"> <label> <input type="checkbox"/> </label> <span> <span>已完成0</span> / 全部2 </span> <button class="btn btn-danger">清除已完成任务</button> </div> </div> </div> </div> </body> </html>
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。