【Vue】第十部分 任务清单小案例

【Vue】第十部分 任务清单小案例



10. 任务清单案例

案例展示:
在这里插入图片描述



点击这里可以到我的github上获取源码

main.js

import Vue from "vue"
import App from "./App.vue"
Vue.config.productionTip = false   //阻止vue在生成时自动产生提示
new Vue({
    el: '#app',
    data: {
    },
    beforeCreate(){
        Vue.prototype.$bus = this  //绑定事件总线
    },
    render: h => h(App)
})

App.vue

<template>
    <div class="wrapper">
      <MyTop @addTodo="addTodo"/>
      <MyContent :plans="plans" />
      <MyBottom :plans="plans" @selectAll="selectAll" @clearTodo="clearTodo"/>
  </div>
</template>

<script>
    import MyTop from "./components/MyTop.vue"
    import MyBottom from "./components/MyBottom.vue"
    import MyContent from "./components/MyContent.vue"
    export default {
        name:"App",
        components:{MyTop,MyBottom,MyContent},
        data(){
            return{
                // []保证在没有值的时候可以读到length的值为0
                plans:JSON.parse(localStorage.getItem("plan")) || []
            }
        },
        watch:{
            plans:{
                handler(val){
                    localStorage.setItem("plan",JSON.stringify(val))
                },
                // 开启深层次监视,不然改对象里的属性watch检测不到
                deep:true
            }
        },
        methods:{
            // 添加任务
            addTodo(val){
                this.plans.unshift(val)
            },
            // 获取计划是否执行
            select(id){
                this.plans.filter((ele)=>{
                    if(ele.id === id)
                    return ele.complete = !ele.complete
                })
            },
            // 删除指定计划
            delTodo(id){
                /* 
                    filter返回的是一个全新的数组但是不会破坏原数组
                    所以想要改变就的重新赋值
                */
                this.plans = this.plans.filter((ele)=>{
                    return ele.id !== id
                })
            },
            //全选和全不选
            selectAll(flag){
                this.plans.forEach((ele)=>{
                    ele.complete = flag
                })
            },
            // 清除已经完成的任务
            clearTodo(){
                this.plans = this.plans.filter((ele)=>{
                    return !ele.complete
                })
            },
            //编辑内容
            editData(id,value){
               this.plans.forEach((ele)=>{
                   if(ele.id == id)
                   {
                       ele.name = value
                   }
               })
            }

        },
        mounted(){
            this.$bus.$on("select",this.select)
            this.$bus.$on("delTodo",this.delTodo)
            this.$bus.$on("edit",this.editData)
        }
    }
</script>

<style>
    *{
        padding: 0;
        margin: 0;
    }
    .wrapper
    {
        width: 600px;
        outline: 1px solid rgb(133, 133, 133) ;
        border-radius: 10px;
        margin: 100px auto;
        padding-top: 20px;
        box-sizing:border-box;
    }
</style>

其他的子组件我放在components文件夹中,我将其拆分成四个部分

components

—MyTop.vue //顶部

—MyItem.vue //每一条list

—MyContent.vue //包裹list

—MyBottom.vue //底部的组件

MyTop.vue

<template>
      <div class="top-wrapper">
        <input type="text" placeholder="请输入需要添加的任务" @keyup.enter="addPlan" v-model="val">
    </div> 
</template>

<script>
    // 导入nanoid,这是个随机生成不重复值的包
    import { nanoid } from "nanoid"
    export default {
        name:"Top",
        data(){
            return{
                val:""
            }
        },
        methods:{
            addPlan(){
                if(this.val.trim())
                {   
                    const wrapper = {id:nanoid(),name:this.val,complete:false}
                    this.$emit("addTodo",wrapper)
                    this.val = ""
                }
            }
        },
    }
</script>

/* 
    scoped表示该样式为该组件的样式,避免了类名冲突问题
    - 但是发现父子组件是没有办法避免的,因为子组件会得到
    父组件的类名所以没有办法解决类名冲突问题.
    - 该属性如果给App使用那就变成只能App所有的样式给他自己使用
    造成样式出现问题,所以并不适合使用
 */
<style scoped>
    .top-wrapper{
        width: 550px;
        height: 50px;
        margin: 0 auto;
    }
    .top-wrapper input{
        width: 550px;
        height: 30px;
        font-size: 18px;
        outline: none;
        border: 1px solid black;
        padding-left: 8px;
        box-sizing: border-box;
    }
    .top-wrapper input:focus{
        border: 1px solid rgb(75, 75, 75);
        box-shadow: 0 0 4px rgb(75,75,75);
    }
</style>

MyItem.vue

<template>
    <li class="it-wrapper" >
      <input type="checkbox" :checked = planobj.complete @click="negation">
      <span v-show="!planobj.isEdit">{{planobj.name}}</span>

      <input type="text" 
      v-show="planobj.isEdit" 
      :value="planobj.name"
       @blur="cancel(planobj,$event)"
        ref="inp">

      <button class="btn" @click="del">删除</button>
      <button class="btn1" @click="edit(planobj)">编辑</button>
  </li>
</template>

<script>
export default {
    name:"MyItem",
    props:["planobj"],
    methods:{
      negation(){
        this.$bus.$emit("select",this.planobj.id)
      },
      del(){
        if(confirm("确定要删除 "+this.planobj.name +" 任务"))
        {
          this.$bus.$emit("delTodo",this.planobj.id)
        }
      },
      edit(planobj){
        this.$set(planobj,"isEdit",true)
        this.$nextTick(function(){
           this.$refs.inp.focus()
        })
       
      },
      cancel(planobj,e){
        planobj.isEdit = false
        this.$bus.$emit("edit",planobj.id,e.target.value)
      }
    }
}
</script>

<style scoped>
     .it-wrapper{
      list-style: none;
      height: 40px;
      width: 550px;
      line-height: 40px;
      padding-left: 12px;
      border: 1px solid #bbb;
      border-bottom: none;
      box-sizing: border-box;
      position: relative;
    }
    .it-wrapper:hover
    {
      background-color:rgba(187, 187, 187, 0.301) ;
    }
    .it-wrapper:hover .btn,.it-wrapper:hover .btn1
    {
      display: block;
    }  
    .it-wrapper span{
      padding-left: 8px;
      font-size: 16px;
    }
    .btn,.btn1{
      border: none;
      width: 60px;
      height: 30px;
      color: aliceblue;
      border-radius: 5px;
      position: absolute;
      outline: none;
      background-color: #f04b4b;
      top: 50%;
      right:3px;
      transform: translateY(-50%);
      display: none;
    }
    .btn:hover{
      background-color: #cc4040;  
    }
    .btn1:hover{
       background-color: rgb(43, 173, 224);
    }
    .btn1{
      background-color: rgb(41, 194, 255);
      right: 75px;
    }

</style>

MyContent.vue

<template>
    <div class="con-wrapper">
    <MyItem 
      v-for="planobj in plans" 
      :key="planobj.id" 
      :planobj="planobj"  
      /> 
  </div>
</template>

<script>
import MyItem from "./MyItem.vue"
export default {
    name:"MyContent",
    components:{MyItem},
    props:["plans"]
}
</script>

<style scoped>
    .con-wrapper{
    width: 550px;
    margin: 0 auto;
  }
</style>

总结

以上就是今天要讲的内容,大家也可以亲自动手写一写!

  • 2
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值