自学Vue Eight Day!!!

本文详细介绍了如何使用Vue.js实现一个TodoList应用,包括数据渲染、组件化、计算属性、事件处理、样式切换、数据持久化等功能。通过拆分组件、计算已完成任务数量、复选框切换任务状态、输入框聚焦与失焦效果、删除任务及回车添加任务等操作,展示了Vue.js的基本用法和实践技巧。
摘要由CSDN通过智能技术生成

模仿ToDoList代办事项列表:

目录

模仿ToDoList代办事项列表:

1、渲染数据

2、把ul 抽离成组件

3、计算属性计算右上角数据条数

4、勾选复选框可以将进行的任务转移至已经完成列表中

5、点击input切换标签边框颜色

6、设置失焦

7、删除某一项

8、输入后回车,可以往“正在进行”中添加一个元素

9、记录现有数据,使得下一次用这个浏览器打开还是可以看到现在的数据

10、完整代码:


1、渲染数据

 arr:[
     {
         id:1,
         content:"内容1",
         isShowBorder:true,
         isFinished:true
     },
     {
         id:2,
         content:"内容2",
         isShowBorder:true,
         isFinished:true
     },
     {
         id:3,
         content:"内容3",
         isShowBorder:true,
         isFinished:false
     }
 ​
 ]
 <div class="tit"><h3>正在进行</h3> <span>2</span></div>
 <ul>
     <li v-for="item in arr" v-if="!item.isFinished">
         <label :for="item.id"><input type="checkbox" :id="item.id" :checked="item.ifCheck"></label>
         <div v-if="item.isShowDiv">{{item.content}}</div>
         <input v-else type="text" v-model="item.content">
         <span>-</span>
     </li>
 </ul>
 <div class="tit"><h3>已经完成</h3> <span>1</span></div>
 <ul>
     <li v-for="item in arr" v-if="item.isFinished">
         <label :for="item.id"><input type="checkbox" :id="item.id" :checked="item.ifCheck"></label>
         <div v-if="item.isShowDiv">{{item.content}}</div>
         <input v-else type="text" v-model="item.content">
         <span>-</span>
     </li>
 </ul>

2、把ul 抽离成组件

 <div class="wrap">
     <list-comp :arr="arr" title="正在进行" type="doing" :counts="counts"></list-comp>
     <list-comp :arr="arr"  title="已经完成" type="finished" :counts="counts"></list-comp>
 </div>
 ​
 ...
 <template id="tmpl">
     <div class="con-list">
         <div class="hd">
             <h3>{{title}}</h3>
             <div class="num">{{counts(type)}}</div>
         </div>
         <ul class="bd">
             <li v-for="item in arr" v-show="type=='doing'?!item.isFinished:item.isFinished">
                 <div class="check-box"><input type="checkbox"></div>
                 <input type="text" v-model="item.content">
                 <div class="btn-box"><div class="del-btn">-</div></div>
             </li>
         </ul>
     </div>
 </template>
 let listComp = {
     template:"#tmpl",
     props:["arr","title", "type", "counts"]
 }

3、计算属性计算右上角数据条数

 //Vue实例中
 computed:{
     counts(){
         return type=>{
             let ret = this.arr.filter(item=>{
                 return type=="doing"?!item.isFinished:item.isFinished
             })
 ​
             return ret.length
         }
 ​
     }
 }

4、勾选复选框可以将进行的任务转移至已经完成列表中

 <!--2、父组件传递事件函数给子组件 -->
 <list-comp :arr="arr" title="正在进行" type="doing" :counts="counts" @fn="changeChecked"></list-comp>
 <list-comp :arr="arr"  title="已经完成" type="finished" :counts="counts"  @fn="changeChecked"></list-comp>
 ​
 <!-- 3、模板中绑定事件-->
 <div class="check-box"><input type="checkbox" @click="chchecked(key)"></div>
 // 4、 子组件中调用父组件传来的方法
 methods:{
     chchecked(index){
         this.$emit("fn",index)
     }
 }
 ​
 ​
 // 1、父组件中定义方法
 // 点击修改选中状态
 methods:{
     changeChecked(index){
         // alert(111)
         this.arr[index].isFinished = !this.arr[index].isFinished
     }
 },

 

5、点击input切换标签边框颜色

 <!--父组件-->
 <list-comp 
        :arr="arr" 
        title="正在进行" 
        type="doing" 
        :counts="counts"                
        @fn="changeChecked"
        @fn2="changeBorder"
 ></list-comp>
 <list-comp :arr="arr"  title="已经完成" type="finished" :counts="counts"  @fn="changeChecked" @fn2="changeBorder"></list-comp>
 ​
 <!--模板-->
 <input type="text" v-model="item.content" @click="showBorder(key)" :class="item.isShowBorder?'show-border':''">
 // 子组件
 showBorder(index){
     this.$emit("fn2",index)
 }
 // 父组件
 changeBorder(index){
     this.arr[index].isShowBorder = !this.arr[index].isShowBorder
 }

6、设置失焦

 <!--添加失去焦点的事件   @blur="showBorder(key)"-->
 <input type="text" v-model="item.content" @click="showBorder(key)" @blur="showBorder(key)" :class="item.isShowBorder?'show-border':''">

7、删除某一项

 <!--父模板中-->
 <list-comp 
                 :arr="arr" 
                 title="正在进行" 
                 type="doing" 
                 :counts="counts"                
                 @fn="changeChecked"
                 @fn2="changeBorder"
                 @fn3="del"
 ></list-comp>
 <list-comp :arr="arr"  title="已经完成" type="finished" :counts="counts"  @fn="changeChecked" @fn2="changeBorder" @fn3="del"></list-comp>
 <!--子模板中-->
 <div class="btn-box"><div class="del-btn" @click="del(key)">-</div></div>
 //子组件中
 del(index){
     this.$emit("fn3",index)
 }
 ​
 //父组件中:
 del(index){
     this.arr.splice(index,1)
 }

 

8、输入后回车,可以往“正在进行”中添加一个元素

 <input class="input-text" type="text" placeholder="请输入任务"  @keyup.enter="add" v-model="txtVal">
 //Vue实例中   定义数据num   用来记录每条数据的id,  同时也作为是否来过这个网站的依据想来保存在本地
 methods:{
     add(){
         this.arr.push({
             id:this.num,
             isFinished:false,
             content:this.txtVal,
             isShowBorder:false
         });
         this.num++;
     }
 }

9、记录现有数据,使得下一次用这个浏览器打开还是可以看到现在的数据

 //父组件中定义方法
 setToLocalStorage(){
     localStorage.setItem('num', this.num);
     localStorage.setItem('arr', JSON.stringify(this.arr));
 }
 // 每次修改都要保存localStorage
 this.setToLocalStorage();
 ​
 // 组件每次创建完毕之后,可以获取这两个localStorage
 created(){
     this.num = localStorage.getItem("num")?localStorage.getItem("num"):0;
     this.arr=localStorage.getItem("arr")?JSON.parse(localStorage.getItem('arr')):[];
 },

 

10、完整代码:

 <!DOCTYPE html>
 <html>
 ​
 <head>
     <meta charset="UTF-8">
     <meta name="viewport" content="width=device-width, initial-scale=1.0">
     <meta http-equiv="X-UA-Compatible" content="ie=edge">
     <title></title>
     <script src="https://cdn.jsdelivr.net/npm/vue"></script>
     <link rel="stylesheet" href="style/index.css">
 </head>
 ​
 <body>
     <div id='app'>
         <header>
             <div class="wrap">
                 <h1>ToDoList</h1>
                 <input type="text" placeholder="请输入。。。" v-model='inpTxt' @keyup.enter='add'>
             </div>
         </header>
         <con-comp title='正在进行' :arr='arr' type='doing' @fn="changeType" @fn2='borderFa' @fn3='delFa'></con-comp>
         <con-comp title='已完成' :arr='arr' type='finished' @fn="changeType" @fn2='borderFa' @fn3='delFa'></con-comp>
 ​
         <div class="footer">
             Copyright 2021 todolist.cn clear
         </div>
     </div>
     <template id="tmp">
         <div class="wrap">
             <div class="hd">
                 <h3>{{title}}</h3>
                 <div class="count">{{counts}}</div>
             </div>
             <ul class="bd">
                 <!-- li展示或者不展示看的是item.isFinished  
                     对于未完成的任务  就是要   !item.isFinished   才能展示未完成的任务
                     对于已经完成的任务  就是要   item.isFinished   才能展示已经完成的任务
                     v-show 的值就要么  !item.isFinished   要么 item.isFinished
                 -->
                 <li v-for='item,key in arr' v-show="type=='doing'?!item.isFinished:item.isFinished">
                     <div class="l">
                         <input type="checkbox" @click="clickCheckbox(key)" :checked='item.isFinished'>
                     </div>
                     <input type="text" v-model='item.content' :class="item.isShowBorder?'showBorder':''"
                         @focus='borderChil(key)' @blur='borderChil(key)'>
                     <div class="r">
                         <div @click='del(key)'>-</div>
                     </div>
                 </li>
             </ul>
         </div>
     </template>
     <script>
         let conComp = {
             template: '#tmp',
             props: ['title', 'arr', 'type'],
             computed: {
                 // doingCounts() {
                        // 计算的是未完成的条数
                 //     // 在arr数组中过滤出   isFinished为false的元素
                 //     let newArr = this.arr.filter(item => {
                 //         return item.isFinished == false
                 //     })
                 //     return newArr.length
                 // },
                 // finishedCounts(){
                         // 计算的是已经完成的条数
                 //     // 在arr数组中过滤出   isFinished为true的元素
                 //     let newArr = this.arr.filter(item => {
                 //         return item.isFinished == true
                 //     })
                 //     return newArr.length
                 // }
                 counts() {
                     let newArr = this.arr.filter(item => {
                         return this.type == 'doing' ? !item.isFinished : item.isFinished
                     })
                     return newArr.length
                 }
             },
             methods: {
                 clickCheckbox(key) {
                     // 修改该数据的isFinished ,取反
                     this.$emit('fn', key)
                 },
                 borderChil(key) {
                     this.$emit('fn2', key)
                 },
                 del(key) {
                     this.$emit('fn3', key)
                 }
             },
         }
         new Vue({
             el: '#app',
             data: {
                 inpTxt: '',
                 num: 3,
                  arr: [
                     // {
                 //         id: 1,
                 //         content: "内容1",
                 //         isFinished: true, // 用来区分是否完成
                 //         isShowBorder: false // 用来区分是否有边框
                 //     },
                 //     {
                 //         id: 2,
                 //         content: "内容2",
                 //         isFinished: true, // 用来区分是否完成
                 //         isShowBorder: false // 用来区分是否有边框
                 //     },
                 //     {
                 //         id: 3,
                 //         content: "内容3",
                 //         isFinished: false, // 用来区分是否完成
                 //         isShowBorder: false // 用来区分是否有边框
                 //     }
                 ]
             },
             components: {
                 conComp
             },
             created() {
                 // 生命周期函数,表示实例/组件刚创建的时候执行这里的代码
                 // 获取localstorage里面的 num 和 arr
                 // 并赋值给this.num 和 this.arr
                 this.num = localStorage.getItem('num') ? parseInt(localStorage.getItem('num')) : 0
                 this.arr =
                     localStorage.getItem("arr") ? JSON.parse(localStorage.getItem("arr")) : []
             },
             methods: {
                 changeType(index) {
                     this.arr[index].isFinished = !this.arr[index].isFinished
                     this.setLocalstorage()
                 },
                 borderFa(index) {
                     this.arr[index].isShowBorder = !this.arr[index].isShowBorder
                     this.setLocalstorage()
                 },
                 delFa(index) {
                     this.arr.splice(index, 1)
                     this.setLocalstorage()
                 },
                 add() {
                     if (!this.inpTxt) {
                         return
                     }
                     this.arr.push({
                         id: ++this.num,
                         content: this.inpTxt,
                         isFinished: false, // 用来区分是否完成
                         isShowBorder: false // 用来区分是否有边框
                     })
                     this.inpTxt = ''
                     this.setLocalstorage()
                 },
                 setLocalstorage() {
                     // 保存数据 num 和 arr
                     localStorage.setItem("num", this.num)
                     localStorage.setItem("arr", JSON.stringify(this.arr))
                 }
             },
         })
     </script>
 </body>
 ​
 </html>

CSS文件代码:

 *{
     margin: 0;
     padding: 0;
     list-style: none;
     outline: none;
 }
 body{
     background-color: #ccc;
 }
 header{
     height: 50px;
     line-height: 50px;
     background-color: #333;
 }
 h1{
     color: #fff;
 }
 .wrap{
     width: 600px;
     margin: 0 auto;
 }
 header .wrap{
     display: flex;
     justify-content: space-between;
 }
 header input{
     height: 40px;
     margin-top: 3px;
     width: 300px;
     text-indent: 10px;
 }
 .hd{
     display: flex;
     justify-content: space-between;
     margin-top: 10px;
 }
 .hd .count{
     width: 30px;
     height: 30px;
     color: #fff;
     background-color: #666;
     border-radius: 50%;
     text-align: center;
     line-height: 30px;
 }
 .bd{
     margin-bottom: 20px;
 }
 .bd li{
     display: flex;
     background-color: #fff;
     height: 40px;
     margin-top: 8px;
 }
 .l,.r{
     width: 40px;
     height: 40px;
     display: flex;
     align-items: center;
     justify-content: center;
 }
 .bd input[type=text]{
     margin-top: 3px;
     flex: 1;
     height: 30px;
     border-color: #fff;
     border-width: 0px;
 }
 .bd input[type=text].showBorder{
     border: 1px solid #000;
 }
 .l>input,.r>div{
     width: 30px;
     height: 30px;
 }
 .r>div{
     background-color: pink;
     border-radius: 50%;
     text-align: center;
     line-height: 30px;
 }
 .footer{
     text-align: center;
     margin-top: 20px;
 }
 .bd input[type=checkbox],.r>div{
     cursor: pointer;
 }
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Jason_HeSL

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值