首页 > 其他分享 >day77-todolist案例

day77-todolist案例

时间:2023-02-27 21:12:54浏览次数:38  
标签:todolist border 案例 day77 组件 todoList todo id pubsub

todolist案例

总和

设计一个添加删除框,添加代办的事项,外加一个勾选框

可以一键删除所有已完成的事项

初期设计

首先设计静态功能:

分为头部 中间 尾部三部分

头部

一个添加框,输入代办的事情,进行添加,判断是否输入为空

中间体

分为list总表和小元素item表

list总表根据设计的数组遍历给对象添加元素

item表设计页面布局与样式

尾部

设计全选按钮与勾选框,并统计共有多少事项完成,设计删除全部已完成事件

头部header

 <template>
 <div class="todo-header">
  <input type="text" placeholder="请输入你的任务名称,按回车键确认"
  v-model="title" @keyup.enter="add"/>
 </div>
 </template>
 ​
 <script>
 import {nanoid} from 'nanoid'
 export default {
   name: "HeaderCom",
   props:['addTodo'],//接受传入addTodo
   data(){
     return{
       title:''//收集用户输入的title
     }
   },
   methods:{
     add(){
       //校验数据
       if (!this.title.trim()) return alert("输入不能为空")
       //将输入包装成todo对象
       const todoObj={id:nanoid(),title:this.title,done:false}
       //通知app组件添加一个todo对象
       this.addTodo(todoObj)
       //清空输入
       this.title=''
     }
   },
 }
 </script>
 ​
 <style scoped>
 /* 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)}
 </style>

 

中间体-list

 
<template>
   <ul class="todo-main">
     <ItemCom
         v-for="todo in todoList"
           :key="todo.id"
           :todo="todo"
         :checkTodo="checkTodo"
     ></ItemCom>
   </ul>
 </template>
 ​
 <script>
 import ItemCom from "@/components/ItemCom";
 export default {
   name: "ListCom",
   components:{
     ItemCom
   },
   props:['todoList','checkTodo']
 }
 </script>
 ​
 <style scoped>
 /*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;
 }
 </style>

 

中间体item

 <template>
   <transition name="todo" appear>
     <li>
       <label>
         <input type="checkbox" :checked="todo.done" @change="handleCheck(todo.id)"/>
         <span v-show="!todo.isEdit">{{todo.title}}</span>
         <input type="text" :value="todo.title" v-show="todo.isEdit"
                @blur="handleBlur(todo, $event)" ref="inputTitle">
       </label>
       <button class="btn btn-danger" @click="handleDelete(todo.id)">删除</button>
       <button class="btn btn-edit" @click="handleEdit(todo)" v-show="!todo.isEdit">编辑</button>
     </li>
   </transition>
 </template>
 ​
 <script>
 import pubsub from 'pubsub-js'
 ​
 export default {
   name: 'ItemCom',
   props: ['todo','checkTodo'],
   methods: {
     //勾选或取消勾选
     handleCheck(id) {
       // 通知App组件将对应todo的done取反
       // pubsub.publish('checkTodo', id)
       this.checkTodo(id)
     },
     handleDelete(id) {
       if(confirm('确定删除吗?')){
         // 通知App组件删除对应的todo
         pubsub.publish('deleteTodo', id)
       }
     },
     handleEdit(todo) {
       // eslint-disable-next-line no-prototype-builtins
       if(todo.hasOwnProperty('isEidt')) {
         todo.isEdit = true
       }else {
         this.$set(todo, 'isEdit', true)
       }
       this.$nextTick(() => {
         this.$refs.inputTitle.focus()
       })
     },
     handleBlur(todo, e) {
       let id = todo.id
       let title = e.target.value
       todo.isEdit = false
       if(!e.target.value.trim()) return alert('输入不能为空')
       pubsub.publish('updateTodo', {id, title} )
     }
   },
 }
 </script>
 ​
 <style scoped>
 /*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;
 }
 ​
 li:hover{
   background-color: #ddd;
 }
 ​
 li:hover button{
   display: block;
 }
 ​
 .todo-enter-active{
   animation: todo 0.5s linear;
 }
 ​
 .todo-leave-active{
   animation: todo 0.5s linear reverse;
 }
 ​
 @keyframes todo {
   from{
     transform: translateX(100%);
   }
   to{
     transform: translateX(0px);
   }
 }
 </style>

 

尾部footer

 
<template>
   <div class="todo-footer" v-show="total">
     <label>
 <!--      <input type="checkbox" :checked="isAll" @change="checkAll"/>-->
       <input type="checkbox" :checked="isAll" @change="checkAll"/>
     </label>
     <span>
       <span>已完成{{doneTodo}}</span> / 全部{{total}}
     </span>
     <button class="btn btn-danger" @click="clearAll">清除已完成任务</button>
   </div>
 </template>
 ​
 <script>
 export default {
   name: "FooterCom",
   props: ['todoList','checkAllTodo','clearAllTodo'],
   computed:{
     total() {
       return this.todoList.length
     },
     doneTodo() {
       // return this.todoList.filter(todo => todo.done === true).length
       return this.todoList.reduce((pre,todo)=>pre+(todo.done ? 1 : 0),0)
     },
     isAll() {
       return this.doneTodo === this.total && this.total > 0
     }
   },
   methods: {
     checkAll(e){
       // this.$emit('checkAllTodo', e.target.checked)
       this.checkAllTodo(e.target.checked)
     },
 ​
     clearAll() {
       if(confirm("确定清除吗?")){
         // this.$emit('clearAllTodo')
         this.clearAllTodo()
       }
 ​
     }
   }
 }
 ​
 </script>
 ​
 <style scoped>
 /*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;
 }
 </style>

 

app总和

 
<template>
   <div id="app">
     <div class="todo-container">
       <div class="todo-wrap">
 ​
         <HeaderCom :addTodo="addTodo"></HeaderCom>
 ​
         <ListCom :todoList="todoList" :checkTodo="checkTodo"></ListCom>
 ​
         <FooterCom :todoList="todoList" :checkAllTodo="checkAllTodo" :clearAllTodo="clearAllTodo"></FooterCom>
       </div>
     </div>
   </div>
 </template>
 ​
 <script>
 import HeaderCom from "@/components/HeaderCom";
 import ListCom from "@/components/ListCom";
 import FooterCom from "@/components/FooterCom";
 import pubsub from "pubsub-js";
 export default {
   name: "App",
   components:{
     HeaderCom,
     ListCom,
     FooterCom
   },
   data() {
     return {
       todoList: JSON.parse(localStorage.getItem('todoList')) || []
     }
   },
   methods: {
     // 添加一个todo
     addTodo(todo){
       this.todoList.unshift(todo)
     },
     // 勾选或取消勾选一个todo
     checkTodo(id) {
       this.todoList.forEach(todo => {
         if(todo.id === id) {
           todo.done = !todo.done
         }
       })
     },
     // 更新一个todo
     updateTodo(_, data) {
       let { id, title } = data
       this.todoList.forEach(todo => {
         if(todo.id === id) {
           todo.title = title
         }
       })
     },
     // 删除一个todo
     deleteTodo(_, id) {
       this.todoList = this.todoList.filter(todo => todo.id !== id)
     },
     // 全选或取消全选
     checkAllTodo(done) {
       this.todoList.forEach(todo => {
         todo.done = done
       })
     },
     clearAllTodo() {
       this.todoList = this.todoList.filter(todo => todo.done === false)
     }
   },
   watch: {
     todoList: {
       handler(value) {
         localStorage.setItem('todoList', JSON.stringify(value))
       },
       deep: true
     }
   },
   mounted() {
     this.pubId_check = pubsub.subscribe('checkTodo', this.checkTodo)
     this.pubId_delete = pubsub.subscribe('deleteTodo', this.deleteTodo)
     this.pubId_update = pubsub.subscribe('updateTodo', this.updateTodo)
   },
   beforeDestroy() {
     pubsub.unsubsribe(this.pubId_check)
     pubsub.unsubsribe(this.pubId_delete)
     pubsub.unsubsribe(this.pubId_update)
   },
 ​
 }
 </script>
 ​
 <style>
 /*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-edit {
   color: #fff;
   background-color: lightgreen;
   border: 1px solid green;
   margin-right: 5px;
 }
 ​
 .btn-danger:hover {
   color: #fff;
   background-color: #bd362f;
 }
 ​
 .btn-edit:hover {
   color: #fff;
   background-color: green;
 }
 ​
 .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>

 

总结

 
/*
 总结:
   1.组件化编码流程:
     1.拆分静态组件:组件要按照功能点拆分,命名不要与HTML元素冲突
     2.实现动态组件时,要考虑好数据的存放位置,数据是一个组件再用还是一些组件在用:
       1.一个组件在用:放在组件自身即可
       2.一些组件在用:放在共同的父组件上(状态提升)
     3.实现交互:从绑定事件开始
   2.props适用于:
     1.父组件 ==> 子组件通信
     2.子组件 ==> 父组件通信(要求父组件先给子组件一个函数)
   3.使用v-model时切记:v-model绑定的值不能是props传过来的值,因为props是不可以修改的
   4.props传过来的若是对象类型的值,修改对象中的属性时vue不会报错,但不推荐这样使用
 */

 

over

标签:todolist,border,案例,day77,组件,todoList,todo,id,pubsub
From: https://www.cnblogs.com/GUGUZIZI/p/17161912.html

相关文章

  • 5_1表单验证案例
     验证要求:用户名不能为空用户名长度大于等于6用户名中不能有数字密码不少于5位两次密码必须一致邮箱格式正确必须有@和. 例如[email protected]实现效果:   <!DOCTY......
  • 5_1表单验证案例
    ​ 验证要求:用户名不能为空用户名长度大于等于6用户名中不能有数字密码不少于5位两次密码必须一致邮箱格式正确必须有@和. 例如[email protected]实现效果: ......
  • 5_1表单验证案例
    ​ 验证要求:用户名不能为空用户名长度大于等于6用户名中不能有数字密码不少于5位两次密码必须一致邮箱格式正确必须有@和. 例如[email protected]实现效果: ......
  • Caffeine - 实际案例:为什么要引入Caffeine本地缓存
    问题背景情景分析服务,老版本里会每次查询/翻页,均会重新请求一次。每次请求都会涉及到重新查询permission的工作。permission信息,是scenarioService通过grpc调用faneDataS......
  • # yyds干货盘点 # 盘点一个ddddocr实现登录的实战案例
    大家好,我是皮皮。一、前言前几天在Python白银交流群【空翼】问了一个​​Pyhton​​网络爬虫的问题,这里拿出来给大家分享下。二、实现过程一开始看上去并不能登录,找不到原因......
  • 虹科案例|Redis企业版数据库:金融行业客户案例解读
    传统银行无法提供无缝的全渠道客户体验、无法实时检测欺诈、无法获得业务洞察力、用户体验感较差、品牌声誉受损和业务损失?虹科提供的Redis企业版数据库具有低延迟、高吞......
  • 成功案例展示 | 1.54寸屏应用于艾美特空气净化器
    伴随着国民经济的飞速发展,空气质量问题对健康的影响受到了越来越广泛的关注,由于各种空气污染现象的发生以及消费水平升级的驱动,更多的消费者开始使用空气净化器。空气净化......
  • 跑通darknet官网的案例
    ------------------------------------------到底是哪里的问题--------------------------------------  一、下载数据集二、划分数据集1.划分哪里的数据集,又......
  • scrapy框架图片爬取案例——以堆糖网为例
    本节主要分享的是scrapy框架中关于图片类的简单爬取方法,在这里只需要用到三个文件:1.setting.py进行scrapy抓取图片所用到的基础。2.duitang_spider.py实现获取多出url进......
  • 02_08_Java语音进阶||day08_File类、递归、综合案例(文件过滤器)
    第一章File类1.1概述(API)java.io.File类是文件和目录路径名的抽象表示,主要用于文件和目录(表示文件夹)的创建,查找和删除等操作。目录就是文件夹的意思java把电脑中的文件和......