TODOS案例

TODOS案例

    • 结构样式
    • 渲染任务列表
    • 添加任务
    • 删除任务
    • 顶部统计
    • 数据切换
    • 清除已完成
    • 数据缓存
    • 全选

目标成品图:
TODOS案例_第1张图片
css样式从简

结构样式

1:创建3个组件和里面的代码还有样式

2:APP.vue中引入三个组件

渲染任务列表

需求:把任务列表展示到页面TodoMain.vue中
需求:选中状态 设置相关的样式

步骤:

  • App.vue 中定义数组传入TodoMain.vue组件中
  • v-for循环展示数据
  • v-model绑定复选框选中状态
  • 根据选中状态 设置样式
//app.vue
<todo-main :arr="list"></todo-main>
data() {
    return {
      list: [
        { id: 100, name: '吃饭', ischeck: false },
        { id: 101, name: '睡觉', ischeck: false },
        { id: 102, name: '打豆豆', ischeck: false },
      ],
    }
  },
//TodoMain.vue
<template>
  <div class="main">
    <ul>
      <li v-for="item in arr" :key="item.id">
        <div class="left">
          <input type="checkbox" v-model="item.ischeck" />
          <span :class="{ undeline: item.ischeck }">
            {{ item.name }}
          span>
        div>
        <button>删除button>
      li>
    ul>
  div>
template>

<script>
export default {
  props: ['arr'],
}
script>

添加任务

需求:输入任务回车,新增任务
  • TodoHead.vue 输入框 --键盘事件—回车

  • 子传父 把任务 —App.vue中 加入到数组中

  • 数组改变,所有用到数组的地方都会更新

  • 输入框为空,提示用户必须输入内容

    //TodoHead.vue
    <input type="text" v-model="name" @keyup.enter="addFn" />
    
      methods: {
        addFn() {
          // 非空判断
          if (this.name.trim().length === 0) {
            alert('任务内容不能为空')
            return
          }
          // 子传父
          this.$emit('create', this.name)
    
          this.name = ''
        },
      },
    
    //App.vue
    <todo-head @create="createFn"></todo-head>
    
    methods: {
        createFn(name) {
          var id =
            this.list.length === 0 ? 100 : this.list[this.list.length - 1].id + 1
          this.list.push({
            id: id,
            name: name,
            ischeck: false,
          })
        },
      },
    

删除任务

  • 删除按钮–点击事件 传id
  • 子传父-把id传给App.vue 删除数组list里对应的对象
  • 数组改变所有用到数组的地方都会更新
//App.vue
<todo-main :arr="list" @delEvent="deleteFn"></todo-main>

  methods: {
    deleteFn(id) {
      // 删除
      let index = this.list.findIndex((item) => item.id === id)
      this.list.splice(index, 1)
    },
  }
//TodoMain.vue
 <button @click="delFn(item.id)">删除</button>
 
   methods: {
    delFn(id) {
      // 子传父
      this.$emit('delEvent', id)
    },
  },

顶部统计

统计当前任务的条数

步骤:

  • App.vue中把数组list传给TodoFoot组件
  • 直接在标签上显示or使用计算属性显示
//TodoFoot.vue
<template>
  <div class="todo-foot">
    <div>剩余{{ farr.length }}---{{ allNum }}div>
    <ul>
      <li>全部li>
      <li class="active">已完成li>
      <li>未完成li>
    ul>
    <button>清除已完成button>
  div>
template>

<script>
export default {
  props: ['farr'],
  computed: {
    // 计算属性
    allNum() {
      return this.farr.length
    },
  },
}
script>

数据切换

点击底部切换,点谁谁有边框样式

对应切换不同的数据展示

步骤:

  • TodoFoot.vue中,定义isSel 值为all,yes,no中的一个,默认为all
  • 多个class分别判断谁应该有类名active
  • 点击修改isSel的值
  • 子传父,把isSel传到App.vue中
  • 定义计算属性,从list过滤数据 给TodoMain
//App.vue
<todo-main :arr="showArr" @delEvent="deleteFn">todo-main>
<todo-foot :farr="showArr" @changType="typeFn">todo-foot>

<script>
export default {
  data() {
    return {

      getSel: 'all', //默认显示全部
    }
  },
  computed: {
    showArr() {
      switch (this.getSel) {
        case 'all':
          return this.list
          break
        case 'yes':
          return this.list.filter((item) => item.ischeck === true)
          break
        case 'no':
          return this.list.filter((item) => item.ischeck === false)
          break
      }
    },
  },
  methods: {
    typeFn(sel) {
      //点击底部的类型
      this.getSel = sel
    },
  },
}
script>
//TodoFoot.vue
<ul @click="fn">
      <li @click="isSel = 'all'" :class="{ active: isSel === 'all' }">全部</li>
      <li @click="isSel = 'yes'" :class="{ active: isSel === 'yes' }">
        已完成
      </li>
      <li @click="isSel = 'no'" :class="{ active: isSel === 'no' }">未完成</li>
</ul>

export default {
  data() {
    return {
      // 1变量isSel
      isSel: 'all', //all 全部  yes已完成  no未完成
    }
  },
  methods: {
    fn() {
      // 点击全部,已完成,未完成
      // 2子--->父  把isSel的值传给App.vue
      this.$emit('changType', this.isSel)
    },
  },
}

清除已完成

点击清空按钮,把已完成的任务清空

步骤:

  • 给按钮添加点击事件
  • 子传父 --App.vue 调用清空方法
  • 过滤未完成的覆盖list数组
//TodoFoot.vue
<button @click="clearFn">清除已完成</button>

methods:{
 fn() {
      // 点击全部,已完成,未完成
      // 2子--->父  把isSel的值传给App.vue
      this.$emit('changType', this.isSel)
 },
}
//App.vue
 methods: {
    clearFun() {
      // 清空已完成
      this.list = this.list.filter((item) => item.ischeck == false)
    },
  }

数据缓存

页面刷新后数据还存在

步骤:

  • App.vue中监听list–存储到本地
  • data中list 从本地读取

data() {
    return {
      list: JSON.parse(localStorage.getItem('list') || '[]'),
      getSel: 'all', //默认显示全部
    }
  },
  watch: {
    list: {
      deep: true,
      handler(newval) {
        localStorage.setItem('list', JSON.stringify(newval))
      },
    },
  },

全选

点击全选 —小选框状态和全选状态一样

小选框都选中(手选)—全选会自动选中

步骤:

  • TodoHeader.vue 计算属性----isAll
  • App.vue 给TodoHeader.vue传入数组list 在isAll的set里影响小选框
  • isAll的get里统计小选框最后状态,影响isAll —影响全选状态
  • 考虑无数据情况—全选不应该勾选
//TodoHead.vue

<input type="checkbox" v-model="isAll" />
 
 computed: {
    isAll: {
      set(checked) {
        // 影响数组里每个小选框绑定的ischeck属性
        this.list.forEach((item) => {
          item.ischeck = checked
        })
      },
      get() {
        // 小选框统计状态--->全选框
        return (
          this.list.length !== 0 &&
          this.list.every((item) => item.ischeck === true)
        )
      },
    },
  },
//App.vue
<todo-head :list="list" @create="createFn">todo-head>

哪天需要了,可以去下载地址

你可能感兴趣的:(学习,vue.js,前端,javascript)