• Vue中组件化编码 完成任务的添加、删除、统计、勾选需求(实战练习三完结)


    上一个章节实现数据在组件之间的传递 。这一章主要是完成添加任务到任务栏、删除任务栏、统计任务完成情况。主要还是参数在各个组件之间的传递。
    上一章节的链接地址:https://blog.csdn.net/weixin_43304253/article/details/126238030

    1、实现的效果演示

    Vue组件实战应用完成任务功能演示

    2、需求实现的大致流程

    • 添加任务到任务栏:需要考虑TheHeader中的数据怎样传入到TheList组件中
    • 删除一个任务:需要拿到待删除的任务主键标识、然后在数组对象中删除(遍历的形式)
    • 勾选任务:根据对象中的勾状态、取反
    • 初次加载勾选已完成任务:在checkbox中 根据任务的状态展示完成情况(checked)
    • 统计任务完成情况:根据对象中的任务完成状态 遍历数组。累加计算

    3 、代码(这里只给出主要部分代码、详细代码请看第一章节)

    3.1 App.vue(省略了样式)

    <template>
      <div id="root">
        <div class="todo-container">
          <div class="todo-wrap">
            <TheHeader :addTodo="addTodo" />
            <TheList
              :todos="todos"
              :checkTodo="checkTodo"
              :deleteTodo="deleteTodo"
            />
            <TheFooter
              :todos="todos"
              :checkAllTodo="checkAllTodo"
              :clearAllTodo="clearAllTodo"
            />
          div>
        div>
      div>
    template>
    
    <script>
    import TheHeader from "./components/TheHeader";
    import TheList from "./components/TheList";
    import TheFooter from "./components/TheFooter.vue";
    
    export default {
      name: "App",
      components: { TheHeader, TheList, TheFooter },
      data() {
        return {
          //由于todos是MyHeader组件和MyFooter组件都在使用,所以放在App中(状态提升)
          todos: [
            { id: "001", title: "吃饭", done: true },
            { id: "002", title: "睡觉", done: false },
            { id: "003", title: "打豆豆", done: true },
          ],
        };
      },
      methods: {
        //添加一个todo
        addTodo(todoObj) {
          this.todos.unshift(todoObj);
        },
        //勾选or取消勾选一个todo
        checkTodo(id) {
          this.todos.forEach((todo) => {
            if (todo.id === id) todo.done = !todo.done;
          });
        },
    
        //删除一个todo
        deleteTodo(id) {
          this.todos = this.todos.filter((todo) => todo.id !== id);
        },
        //全选or取消全选
        checkAllTodo(done) {
          this.todos.forEach((todo) => {
            todo.done = done;
          });
        },
        //清除所有已经完成的todo
        clearAllTodo() {
          this.todos = this.todos.filter((todo) => {
            return !todo.done;
          });
        },
      },
    };
    script>
    
    
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72

    3.2 TheList.vue (省略了样式)

    <template>
      <ul class="todo-main">
        <TheItem
          v-for="todoObj in todos"
          :key="todoObj.id"
          :todo="todoObj"
          :checkTodo="checkTodo"
          :deleteTodo="deleteTodo"
        />
      ul>
    template>
    
    <script>
    import TheItem from "./TheItem";
    
    export default {
      name: "TheList",
      components: { TheItem },
      //声明接收App传递过来的数据,其中todos是自己用的
      props: ["todos", "checkTodo",'deleteTodo'],
    };
    script>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22

    3.3 TheItem.vue(样式改进:删除按钮只有停留在对应行才会显示出来)

    <template>
      <li>
        <label>
          <input
            type="checkbox"
            :checked="todo.done"
            @change="handleCheck(todo.id)"
          />
          <span>{{ todo.title }}span>
        label>
        <button class="btn btn-danger" @click="handleDelete(todo.id)">删除button>
      li>
    template>
    
    <script>
    export default {
      name: "MyItem",
      //声明接收todo、checkTodo、deleteTodo
      props: ["todo", "checkTodo",'deleteTodo'],
    
      methods: {
        //勾选or取消勾选
        handleCheck(id) {
          //通知App组件将对应的todo对象的done值取反
          this.checkTodo(id);
        },
        //删除
        handleDelete(id) {
          if (confirm("确定删除吗?")) {
            //通知App组件将对应的todo对象删除
            this.deleteTodo(id);
          }
        },
      },
    };
    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;
    	}
    style>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80

    3.4 TheHeader.vue (省略了样式)

    <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: "TheHeader",
    	//接收从App传递过来的addTodo
    		props:['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>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37

    3.5 TheFooter.vue (省略了样式)

    <template>
    	<div class="todo-footer" v-show="total">
    		<label>
    			
    			<input type="checkbox" v-model="isAll"/>
    		label>
    		<span>
    			<span>已完成{{doneTotal}}span> / 全部{{total}}
    		span>
    		<button class="btn btn-danger" @click="clearAll">清除已完成任务button>
    	div>
    template>
    
    <script>
    	export default {
    		name:'TheFooter',
    		props:['todos','checkAllTodo','clearAllTodo'],
    		computed: {
    			//总数
    			total(){
    				return this.todos.length
    			},
    			//已完成数
    			doneTotal(){
    				//此处使用reduce方法做条件统计
    				/* const x = this.todos.reduce((pre,current)=>{
    					console.log('@',pre,current)
    					return pre + (current.done ? 1 : 0)
    				},0) */
    				//简写
    				return this.todos.reduce((pre,todo)=> pre + (todo.done ? 1 : 0) ,0)
    			},
    			//控制全选框
    			isAll:{
    				//全选框是否勾选
    				get(){
    					return this.doneTotal === this.total && this.total > 0
    				},
    				//isAll被修改时set被调用
    				set(value){
    					this.checkAllTodo(value)
    				}
    			}
    		},
    		methods: {
    			/* checkAll(e){
    				this.checkAllTodo(e.target.checked)
    			} */
    			//清空所有已完成
    			clearAll(){
    				this.clearAllTodo()
    			}
    		},
    	}
    script>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55

    4、实现的效果

    在这里插入图片描述


    整理不易、欢迎白嫖党一键三连 !!! 哈哈哈哈哈哈哈哈哈。曾经我也是白嫖党

  • 相关阅读:
    轻松连接远程服务器SecureCRT for Mac/Windows
    【小黑送书—第八期】>>别再吐槽大学教材了,来看看这些网友强推的数学神作!
    PAT A1013 Battle Over Cities分数 25(连通块数量,割点是针对连通块的,不是整个图)
    03使用Spring基于XML的方式注册第一个组件
    Golang并发-Go优雅的退出程序(同步等待组 sync.WaitGroup)
    网络安全和隐私保护技术
    嵌入式图形开发框架Qt——让牙科手术开始迈入机器人时代
    HEVC的Profile和Level介绍
    基础:html5、html、htm、xhtml、dhtml、shtml、shtm、xml、xsl、xslt
    React Hooks之useState
  • 原文地址:https://blog.csdn.net/weixin_43304253/article/details/126256518