案例:todo

根据已有的文件让vue文件跑起来怎么做?
案例:todo
案例:todo

1.根据已有的 package.json 下依赖包 yarn install / yarn(可以省略 install)

完成
案例:todo

todo案例

完整效果演示

案例:todo

todo案例-创建工程和组件

目标: 新建工程, 准备好所需的一切

  • 需求1: 创建新工程
  • 需求2: 分组件创建 – 准备标签和样式

分析:

​ ①:初始化todo工程

​ ②:创建3个组件和里面代码

​ ③:把styles的样式文件准备好

​ ④: App.vue引入注册使用, 最外层容器类名todoapp

预先准备: 把styles的样式文件准备好(从预习资料复制), 在App.vue引入使用

// 1.0 样式引入
import "./styles/base.css"
import "./styles/index.css"

根据需求: 我们定义3个组件准备复用

案例:todo

components/TodoHeader.vue - 复制标签和类名


<template>
  <header class="header">
    <h1>todos</h1>
    <input id="toggle-all" class="toggle-all" type="checkbox" >
    <label for="toggle-all"></label>
    <input
      class="new-todo"
      placeholder="输入任务名称-回车确认"
      autofocus
    />
  </header>
</template>

<script>
export default {
 
}
</script>

components/TodoMain.vue - 复制标签和类名

<template>
  <ul class="todo-list">
    <!-- completed: 完成的类名 -->
    <li class="completed" >
      <div class="view">
        <input class="toggle" type="checkbox" />
        <label>任务名</label>
        <button class="destroy"></button>
      </div>
    </li>
  </ul>
  
</template>

<script>
export default {
}
</script>

components/TodoFooter.vue - 复制标签和类名

<template>
  <footer class="footer">
    <span class="todo-count">剩余<strong>数量值</strong></span>
    <ul class="filters">
      <li>
        <a class="selected" href="javascript:;" >全部</a>
      </li>
      <li>
        <a href="javascript:;">未完成</a>
      </li>
      <li>
        <a href="javascript:;" >已完成</a>
      </li>
    </ul>
    <button class="clear-completed" >清除已完成</button>
  </footer>
</template>

<script>
export default {

}
</script>

App.vue中引入和使用

<template>
  <section class="todoapp">
    <!-- 除了驼峰, 还可以使用-转换链接 -->
    <TodoHeader></TodoHeader>
    <TodoMain></TodoMain>
    <TodoFooter></TodoFooter>
  </section>
</template>

<script>
// 1.0 样式引入
import "./styles/base.css"
import "./styles/index.css"
    
import TodoHeader from "./components/TodoHeader";
import TodoMain from "./components/TodoMain";
import TodoFooter from "./components/TodoFooter";


export default {
  components: {
    TodoHeader,
    TodoMain,
    TodoFooter,
  },
};
</script>

todo案例-铺设待办任务

目的: 把待办任务, 展示到页面TodoMain.vue组件上

  • 需求1: 把待办任务, 展示到页面TodoMain.vue组件上
  • 需求2: 关联选中状态, 设置相关样式

分析:

​ ①: App.vue – 准备数组传入TodoMain.vue内

​ ②: v-for循环展示数据

​ ③: v-model绑定复选框选中状态

​ ④: 根据选中状态, 设置完成划线样式

App.vue

 <TodoMain :arr="showArr"></TodoMain>

export default {
  data() {
    return {
      list: [
        { id: 100, name: "吃饭", isDone: true },
        { id: 201, name: "睡觉", isDone: false },
        { id: 103, name: "打豆豆", isDone: true },
      ],
    };
  }
};

TodoMain.vue

<template>
  <ul class="todo-list">
    <!-- 2.2 循环任务-关联选中状态-铺设数据 -->
    <!-- completed: 完成的类名 -->
    <li :class="{completed: obj.isDone}" v-for="(obj, index) in arr" :key='obj.id'>
      <div class="view">
        <input class="toggle" type="checkbox" v-model="obj.isDone"/>
        <label>{{ obj.name }}</label>
        <!-- 4.0 注册点击事件 -->
        <button @click="delFn(index)" class="destroy"></button>
      </div>
    </li>
  </ul>
</template>

<script>
export default {
  props: ["list"]
};
</script>

<style>
</style>

todo案例-添加任务

目标: 在顶部输入框输入要完成的任务名, 敲击回车, 完成新增功能

  • 需求: 输入任务敲击回车, 新增待办任务

分析:

​ ①: TodoHeader.vue – 输入框 – 键盘事件 – 回车按键

​ ②: 子传父, 把待办任务 – App.vue中 – 加入数组list里

​ ③: 原数组改变, 所有用到的地方都会更新

​ ④: 输入框为空, 提示用户必须输入内容

子组件:TodoHeader.vue

<template>
  <header class="header">
    <h1>todos</h1>
    <input id="toggle-all" class="toggle-all" type="checkbox" />
    <label for="toggle-all"></label>
    <!-- 3.0 v-model       双向绑定获取输入框的值  -->
    <!-- 3.1 @keyup.enter  监听用户按下回车  -->
    <input
      class="new-todo"
      placeholder="输入任务名称-回车确认"
      autofocus
      v-model.trim="task"
      @keyup.enter="downFn"
    />
  </header>
</template>

<script>
export default {
  data() {
    return {
      task: "",
    };
  },
  methods: {
    downFn() {
      if (this.task === "") {
        return alert("请输入用户名称");
      }
      // console.log("用户按下了回车键", this.task);
      // 3.2(重要) - 当前任务名字要加到list数组里
      this.$emit("create", this.task);
      // 为了更好地用户体验,清空输入框
      this.task = "";
    },
  },
};
</script>

父组件:综合案例_Todo.vue

<template>
  <!-- 1.3 在根标签添加 todoapp 类名 -->
  <div class="todoapp">
    <!-- 3.2 注册自定义事件 让子组件触发 -->
    <TodoHender @create="createFn"></TodoHender>
    <TodoMain :arr="list"></TodoMain>
    <TodoFooter></TodoFooter>
  </div>
</template>

<script>
// 1.1 使用组件的四个步骤:  新建组件( vue 文件)  2.引入组件 import  3.注册组件 components  4.使用组件
import TodoFooter from "./components/TodoFooter.vue";
import TodoMain from "./components/TodoMain.vue";
import TodoHender from "./components/TodoHender.vue";
// 1.2 样式引入
import "./styles/base.css";
import "./styles/index.css";
export default {
  // components:定义组件的结构
  components: {
    TodoFooter,
    TodoMain,
    TodoHender,
  },
  // data:定义数据的结构
  data() {
    return {
      list: [
        { id: 100, name: "吃饭", isDone: true },
        { id: 101, name: "睡觉", isDone: false },
        { id: 103, name: "打豆豆", isDone: true },
      ],
    };
  },
  //methods:定义函数/方法的结构
  methods: {
    createFn(theTesk) {
      // 3.3 push 到数组里
      this.list.push({
        // id 准备
        id: this.list.length ? this.list[this.list.length - 1].id + 1 : 10,
        // 任务是子组件触发自定义事件的时候传过来的
        name: theTesk,
        // 新建的任务默认都是未完成
        isDone: false,
      });
    },
  },
};
</script>

<style>
</style>

todo案例-删除任务

目标: 实现点x, 删除任务功能

  • 需求: 点击任务后的x, 删除当前这条任务

分析:

​ ①: x标签 – 点击事件 – 传入id区分

​ ②: 子传父, 把id传回– App.vue中 – 删除数组list里某个对应的对象

​ ③: 原数组改变, 所有用到的地方都会更新

App.vue - 传入自定义事件等待接收要被删除的序号

父组件:综合案例_Todo.vue

<template>
  <!-- 1.3 在根标签添加 todoapp 类名 -->
  <div class="todoapp">
    <!-- 3.2 注册自定义事件 让子组件触发 -->
    <TodoHender @create="createFn"></TodoHender>
    <TodoMain @del="deleteFn" :arr="list"></TodoMain>
    <TodoFooter></TodoFooter>
  </div>
</template>

<script>
// 1.1 使用组件的四个步骤:  新建组件( vue 文件)  2.引入组件 import  3.注册组件 components  4.使用组件
import TodoFooter from "./components/TodoFooter.vue";
import TodoMain from "./components/TodoMain.vue";
import TodoHender from "./components/TodoHender.vue";
// 1.2 样式引入
import "./styles/base.css";
import "./styles/index.css";
export default {
  // components:定义组件的结构
  components: {
    TodoFooter,
    TodoMain,
    TodoHender,
  },
  // data:定义数据的结构
  data() {
    return {
      list: [
        { id: 100, name: "吃饭", isDone: true },
        { id: 101, name: "睡觉", isDone: false },
        { id: 103, name: "打豆豆", isDone: true },
      ],
    };
  },
  //methods:定义函数/方法的结构
  methods: {
    //   创建任务
    createFn(theTesk) {
      // 3.3 push 到数组里
      this.list.push({
        // id 准备
        id: this.list.length ? this.list[this.list.length - 1].id + 1 : 10,
        // 任务是子组件触发自定义事件的时候传过来的
        name: theTesk,
        // 新建的任务默认都是未完成
        isDone: false,
      });
    },
    // 删除任务
    deleteFn(theId) {
      // 注意:filter 不改变原数组,需要重新赋值
      this.list = this.list.filter((item) => item.id !== theId);
    },
  },
};
</script>

<style>
</style>

子组件:TodoMain.vue

<template>
  <ul class="todo-list">
    <!-- completed: 完成的类名 -->
    <li v-for="item in arr" :key="item.id" :class="{ completed: item.isDone }">
      <div class="view">
        <input v-model="item.isDone" class="toggle" type="checkbox" />
        <label>{{ item.name }}</label>
        <button class="destroy" @click="destroyBtn(item.id)"></button>
      </div>
    </li>
  </ul>
</template>

<script>
export default {
  // 接收父组件的数据
  props: ["arr"],
  methods: {
    destroyBtn(id) {
      // console.log("点击了删除按钮", id);
      this.$emit("del", id);
    },
  },
};
</script>

todo案例-底部统计

目的: 显示现在任务的总数

  • 需求: 统计当前任务的条数

分析:

​ ①: App.vue中 – 数组list – 传给TodoFooter.vue

​ ②: 直接在标签上显示 / 定义计算属性用于显示都可以

​ ③: 原数组只要改变, 所有用到此数组的地方都会更新

TodoFooter.vue - 接收list统计直接显示
子组件:TodoFooter.vue

<template>
  <footer class="footer">
    <span class="todo-count"
      >剩余<strong>{{ count }}</strong></span
    >
    <ul class="filters">
      <li>
        <a class="selected" href="javascript:;">全部</a>
      </li>
      <li>
        <a href="javascript:;">未完成</a>
      </li>
      <li>
        <a href="javascript:;">已完成</a>
      </li>
    </ul>
    <button class="clear-completed">清除已完成</button>
  </footer>
</template>

<script>
export default {
  props: ["farr"],
  // 通过 computed 计算结构
  computed: {
    count() {
      return this.farr.length;
    },
  },
};
</script>

父组件:综合案例_Todo.vue

<template>
  <!-- 1.3 在根标签添加 todoapp 类名 -->
  <div class="todoapp">
    <!-- 3.2 注册自定义事件 让子组件触发 -->
    <TodoHender @create="createFn"></TodoHender>
    <TodoMain @del="deleteFn" :arr="list"></TodoMain>
    <TodoFooter :farr="list"></TodoFooter>
  </div>
</template>

<script>
// 1.1 使用组件的四个步骤:  新建组件( vue 文件)  2.引入组件 import  3.注册组件 components  4.使用组件
import TodoFooter from "./components/TodoFooter.vue";
import TodoMain from "./components/TodoMain.vue";
import TodoHender from "./components/TodoHender.vue";
// 1.2 样式引入
import "./styles/base.css";
import "./styles/index.css";
export default {
  // components:定义组件的结构
  components: {
    TodoFooter,
    TodoMain,
    TodoHender,
  },
  // data:定义数据的结构
  data() {
    return {
      list: [
        { id: 100, name: "吃饭", isDone: true },
        { id: 101, name: "睡觉", isDone: false },
        { id: 103, name: "打豆豆", isDone: true },
      ],
    };
  },
  //methods:定义函数/方法的结构
  methods: {
    //   创建任务
    createFn(theTesk) {
      // 3.3 push 到数组里
      this.list.push({
        // id 准备
        id: this.list.length ? this.list[this.list.length - 1].id + 1 : 10,
        // 任务是子组件触发自定义事件的时候传过来的
        name: theTesk,
        // 新建的任务默认都是未完成
        isDone: false,
      });
    },
    // 删除任务
    deleteFn(theId) {
      // 注意:filter 不改变原数组,需要重新赋值
      this.list = this.list.filter((item) => item.id !== theId);
    },
  },
};
</script>

<style>
</style>

todo案例-数据切换

目的: 点击底部切换数据

  • 需求1: 点击底部切换 – 点谁谁有边框
  • 需求2: 对应切换不同数据显示

分析:

​ ①: TodoFooter.vue – 定义isSel – 值为all, yes, no其中一种

​ ②: 多个class分别判断谁应该有类名selected

​ ③: 点击修改isSel的值

​ ④: 子传父, 把类型isSel传到 综合案例_Todo.vue

​ ⑤: 定义计算属性showArr, 决定从list里显示哪些数据给TodoMain.vue和TodoFooter.vue

父组件:综合案例_Todo.vue

<template>
  <!-- 1.3 在根标签添加 todoapp 类名 -->
  <div class="todoapp">
    <!-- 3.2 注册自定义事件 让子组件触发 -->
    <TodoHender @create="createFn"></TodoHender>
    <TodoMain @del="deleteFn" :arr="showArr"></TodoMain>
    <TodoFooter @changeType="typeFn" :farr="showArr"></TodoFooter>
  </div>
</template>

<script>
// 1.1 使用组件的四个步骤:  新建组件( vue 文件)  2.引入组件 import  3.注册组件 components  4.使用组件
import TodoFooter from "./components/TodoFooter.vue";
import TodoMain from "./components/TodoMain.vue";
import TodoHender from "./components/TodoHender.vue";
// 1.2 样式引入
import "./styles/base.css";
import "./styles/index.css";
export default {
  // components:定义组件的结构
  components: {
    TodoFooter,
    TodoMain,
    TodoHender,
  },
  // data:定义数据的结构
  data() {
    return {
      list: [
        { id: 100, name: "吃饭", isDone: true },
        { id: 101, name: "睡觉", isDone: false },
        { id: 103, name: "打豆豆", isDone: true },
      ],
      getSel:'all',
    };
  },
  //methods:定义函数/方法的结构
  methods: {
    //   创建任务
    createFn(theTesk) {
      // 3.3 push 到数组里
      this.list.push({
        // id 准备
        id: this.list.length ? this.list[this.list.length - 1].id + 1 : 10,
        // 任务是子组件触发自定义事件的时候传过来的
        name: theTesk,
        // 新建的任务默认都是未完成
        isDone: false,
      });
    },
    // 删除任务
    deleteFn(theId) {
      // 注意:filter 不改变原数组,需要重新赋值
      this.list = this.list.filter((item) => item.id !== theId);
    },
    // 切换任务
    typeFn(theSel){
      this.getSel = theSel
    }
  },
  // computed 写计算属性的结构(算出来新的变量)
  computed: {
    showArr(){
      if( this.getSel === 'yes'){
        return this.list.filter(item => item.isDone === true)
      }else if(this.getSel === 'no'){
        return this.list.filter(item => item.isDone === false)
      }else{
        return this.list
      }
    }
  }
};
</script>

<style>
</style>

子组件:TodoFooter.vue

<template>
  <footer class="footer">
    <span class="todo-count"
      >剩余<strong>{{ count }}</strong></span
    >
    <ul class="filters">
      <li>
        <a :class="{selected :  isSel === 'all'}" href="javascript:;" @click="changeSel('all')" >全部</a>
        
      </li>
      <li>
        <a :class="{selected :  isSel === 'no'}" href="javascript:;" @click="changeSel('no')" >未完成</a>
      </li>
      <li>
        <a :class="{selected :  isSel === 'yes'}" href="javascript:;" @click="changeSel('yes')">已完成</a>
      </li>
    </ul>
    <button class="clear-completed">清除已完成</button>
  </footer>
</template>

<script>
export default {
  props: ["farr"],
  data () {
          return {
            // all 全部, no  未完成, yes  已完成
          isSel:"all"  
    }
  },
  methods: {
    changeSel(sel){
      // 点击时按钮添加边框
      this.isSel = sel
      // 触发父子间上的自定义事件,把 选中的类型 传到父组件中
      this.$emit('changeType', sel)
      
    }
  },
  // 通过 computed 计算结构
  computed: {
    count() {
      return this.farr.length;
    },
  },
};
</script>

todo案例-清空已完成

目的: 点击右下角按钮- 把已经完成的任务清空了

  • 需求: 点击右下角链接标签, 清除已完成任务

分析:

​ ①: 清空标签 – 点击事件

​ ②: 子传父 – App.vue – 一个清空方法

​ ③: 过滤未完成的覆盖list数组 (不考虑恢复)

综合案例_Todo.vue - 先传入一个自定义事件-因为得接收TodoFooter.vue里的点击事件

父组件:综合案例_Todo.vue

<template>
  <!-- 1.3 在根标签添加 todoapp 类名 -->
  <div class="todoapp">
    <!-- 3.2 注册自定义事件 让子组件触发 -->
    <TodoHender @create="createFn"></TodoHender>
                            <!--  换成计算出来的 showArr 数组  -->
    <TodoMain @del="deleteFn" :arr="showArr"></TodoMain>
    <TodoFooter @changeType="typeFn" @clear="clearFn" :farr="showArr"></TodoFooter>
  </div>
</template>

<script>
// 1.1 使用组件的四个步骤:  新建组件( vue 文件)  2.引入组件 import  3.注册组件 components  4.使用组件
import TodoFooter from "./components/TodoFooter.vue";
import TodoMain from "./components/TodoMain.vue";
import TodoHender from "./components/TodoHender.vue";
// 1.2 样式引入
import "./styles/base.css";
import "./styles/index.css";
export default {
  // components:定义组件的结构
  components: {
    TodoFooter,
    TodoMain,
    TodoHender,
  },
  // data:定义数据的结构
  data() {
    return {
      list: [
        { id: 100, name: "吃饭", isDone: true },
        { id: 101, name: "睡觉", isDone: false },
        { id: 103, name: "打豆豆", isDone: true },
      ],
      getSel:'all',
    };
  },
  //methods:定义函数/方法的结构
  methods: {
    //   创建任务
    createFn(theTesk) {
      // 3.3 push 到数组里
      this.list.push({
        // id 准备
        id: this.list.length ? this.list[this.list.length - 1].id + 1 : 10,
        // 任务是子组件触发自定义事件的时候传过来的
        name: theTesk,
        // 新建的任务默认都是未完成
        isDone: false,
      });
    },
    // 删除任务
    deleteFn(theId) {
      // 注意:filter 不改变原数组,需要重新赋值
      this.list = this.list.filter((item) => item.id !== theId);
    },
    // 切换任务
    typeFn(theSel){
      this.getSel = theSel
    },
    // 清除已完成
    clearFn(){
      // 真是修改了 list
      this.list = this.list.filter(item => item.isDone !== true)
    }
  },
  // computed 写计算属性的结构(算出来新的变量)
  computed: {
    showArr(){
      if( this.getSel === 'yes'){
        return this.list.filter(item => item.isDone === true)
      }else if(this.getSel === 'no'){
        return this.list.filter(item => item.isDone === false)
      }else{
        return this.list
      }
    }
  }
};
</script>

<style>
</style>

子组件:TodoFooter.vue

<template>
  <footer class="footer">
    <span class="todo-count"
      >剩余<strong>{{ count }}</strong></span
    >
    <ul class="filters">
      <li>
        <a :class="{selected :  isSel === 'all'}" href="javascript:;" @click="changeSel('all')" >全部</a>
        
      </li>
      <li>
        <a :class="{selected :  isSel === 'no'}" href="javascript:;" @click="changeSel('no')" >未完成</a>
      </li>
      <li>
        <a :class="{selected :  isSel === 'yes'}" href="javascript:;" @click="changeSel('yes')">已完成</a>
      </li>
    </ul>
    <button class="clear-completed" @click="clearBtn">清除已完成</button>
  </footer>
</template>

<script>
export default {
  props: ["farr"],
  data () {
          return {
            // all 全部, no  未完成, yes  已完成
          isSel:"all"  
    }
  },
  methods: {
    changeSel(sel){
      // 点击时按钮添加边框
      this.isSel = sel
      // 触发父子间上的自定义事件,把 选中的类型 传到父组件中
      this.$emit('changeType', sel)
    },
    clearBtn(){
      this.$emit('clear')
    }
  },
  // 通过 computed 计算结构
  computed: {
    count() {
      return this.farr.length;
    },
  },
};
</script>

todo案例-数据缓存

目的: 新增/修改状态/删除 后, 马上把数据同步到浏览器本地存储

  • 需求: 无论如何变化 – 都保证刷新后数据还在

分析:

​ ①:~Todo.vue – 侦听list数组改变 – 深度

​ ②: 覆盖式存入到本地 – 注意本地只能存入JSON字符串

​ ③: 刷新页面 – list应该默认从本地取值 – 要考虑

父组件:Todo.vue

<template>
  <!-- 1.3 在根标签添加 todoapp 类名 -->
  <div class="todoapp">
    <!-- 3.2 注册自定义事件 让子组件触发 -->
    <TodoHender @create="createFn"></TodoHender>
                            <!--  换成计算出来的 showArr 数组  -->
    <TodoMain @del="deleteFn" :arr="showArr"></TodoMain>
    <TodoFooter @changeType="typeFn" @clear="clearFn" :farr="showArr"></TodoFooter>
  </div>
</template>

<script>
// 1.1 使用组件的四个步骤:  新建组件( vue 文件)  2.引入组件 import  3.注册组件 components  4.使用组件
import TodoFooter from "./components/TodoFooter.vue";
import TodoMain from "./components/TodoMain.vue";
import TodoHender from "./components/TodoHender.vue";
// 1.2 样式引入
import "./styles/base.css";
import "./styles/index.css";
export default {
  // components:定义组件的结构
  components: {
    TodoFooter,
    TodoMain,
    TodoHender,
  },
  // data:定义数据的结构
  data() {
    return {
      list:JSON.parse(localStorage.getItem('todoList')) || [
        { id: 100, name: "吃饭", isDone: true },
        { id: 101, name: "睡觉", isDone: false },
        { id: 103, name: "打豆豆", isDone: true },
      ],
      getSel:'all',
    };
  },
  //methods:定义函数/方法的结构
  methods: {
    //   创建任务
    createFn(theTesk) {
      // 3.3 push 到数组里
      this.list.push({
        // id 准备
        id: this.list.length ? this.list[this.list.length - 1].id + 1 : 10,
        // 任务是子组件触发自定义事件的时候传过来的
        name: theTesk,
        // 新建的任务默认都是未完成
        isDone: false,
      });
    },
    // 删除任务
    deleteFn(theId) {
      // 注意:filter 不改变原数组,需要重新赋值
      this.list = this.list.filter((item) => item.id !== theId);
    },
    // 切换任务
    typeFn(theSel){
      this.getSel = theSel
    },
    // 清除已完成
    clearFn(){
      // 真是修改了 list
      this.list = this.list.filter(item => item.isDone !== true)
    }
  },
  // computed 写计算属性的结构(算出来新的变量)
  computed: {
    showArr(){
      if( this.getSel === 'yes'){
        return this.list.filter(item => item.isDone === true)
      }else if(this.getSel === 'no'){
        return this.list.filter(item => item.isDone === false)
      }else{
        return this.list
      }
    }
  },
  watch: {
    list:{
      deep:true,
      handler(){
        localStorage.setItem('todoList',JSON.stringify(this.list))
      }
      
    }
  }
};
</script>

<style>
</style>

todo案例-全选功能

目标: 点击左上角v号, 可以设置一键完成, 再点一次取消全选

  • 需求1: 点击全选 – 小选框受到影响
  • 需求2: 小选框都选中(手选) – 全选自动选中状态

分析:

​ ①: TodoHeader.vue – 计算属性 - isAll

​ ②: App.vue – 传入数组list – 在isAll的set里影响小选框

​ ③: isAll的get里统计小选框最后状态, 影响isAll – 影响全选状态

​ ④: 考虑无数据情况空数组 – 全选不应该勾选

提示: 就是遍历所有的对象, 修改他们的完成状态属性的值

子组件:TodoHender.vue

<template>
  <header class="header">
    <h1>todos</h1>
    <input id="toggle-all" class="toggle-all" type="checkbox" v-model="isAll" />
    <label for="toggle-all"></label>
    <!-- 3.0 v-model       双向绑定获取输入框的值  -->
    <!-- 3.1 @keyup.enter  监听用户按下回车  -->
    <input
      class="new-todo"
      placeholder="输入任务名称-回车确认"
      autofocus
      v-model.trim="task"
      @keyup.enter="downFn"
    />
  </header>
</template>

<script>
export default {
  props:['arr'],
  data() {
    return {
      task: "",
    };
  },
  methods: {
    downFn() {
      if (this.task === "") {
        return alert("请输入用户名称");
      }
      // console.log("用户按下了回车键", this.task);
      // 3.2(重要) - 当前任务名字要加到list数组里
      this.$emit("create", this.task);
      // 为了更好地用户体验,清空输入框
      this.task = "";
    },
  },
  computed: {
    isAll:{
      get(){
        return this.arr.every(item => item.isDone === true)
      },
      set(val){
        this.arr.forEach(item => item.isDone = val)
      }
    }
  }
};
</script>

父组件:综合案例_Todo.vue

<template>
  <!-- 1.3 在根标签添加 todoapp 类名 -->
  <div class="todoapp">
    <!-- 3.2 注册自定义事件 让子组件触发 -->
    <TodoHender :arr="list" @create="createFn"></TodoHender>
                            <!--  换成计算出来的 showArr 数组  -->
    <TodoMain @del="deleteFn" :arr="showArr"></TodoMain>
    <TodoFooter @changeType="typeFn" @clear="clearFn" :farr="showArr"></TodoFooter>
  </div>
</template>

<script>
// 1.1 使用组件的四个步骤:  新建组件( vue 文件)  2.引入组件 import  3.注册组件 components  4.使用组件
import TodoFooter from "./components/TodoFooter.vue";
import TodoMain from "./components/TodoMain.vue";
import TodoHender from "./components/TodoHender.vue";
// 1.2 样式引入
import "./styles/base.css";
import "./styles/index.css";
export default {
  // components:定义组件的结构
  components: {
    TodoFooter,
    TodoMain,
    TodoHender,
  },
  // data:定义数据的结构
  data() {
    return {
      list:JSON.parse(localStorage.getItem('todoList')) || [
        { id: 100, name: "吃饭", isDone: true },
        { id: 101, name: "睡觉", isDone: false },
        { id: 103, name: "打豆豆", isDone: true },
      ],
      getSel:'all',
    };
  },
  //methods:定义函数/方法的结构
  methods: {
    //   创建任务
    createFn(theTesk) {
      // 3.3 push 到数组里
      this.list.push({
        // id 准备
        id: this.list.length ? this.list[this.list.length - 1].id + 1 : 10,
        // 任务是子组件触发自定义事件的时候传过来的
        name: theTesk,
        // 新建的任务默认都是未完成
        isDone: false,
      });
    },
    // 删除任务
    deleteFn(theId) {
      // 注意:filter 不改变原数组,需要重新赋值
      this.list = this.list.filter((item) => item.id !== theId);
    },
    // 切换任务
    typeFn(theSel){
      this.getSel = theSel
    },
    // 清除已完成
    clearFn(){
      // 真是修改了 list
      this.list = this.list.filter(item => item.isDone !== true)
    }
  },
  // computed 写计算属性的结构(算出来新的变量)
  computed: {
    showArr(){
      if( this.getSel === 'yes'){
        return this.list.filter(item => item.isDone === true)
      }else if(this.getSel === 'no'){
        return this.list.filter(item => item.isDone === false)
      }else{
        return this.list
      }
    }
  },
  watch: {
    list:{
      deep:true,
      handler(){
        localStorage.setItem('todoList',JSON.stringify(this.list))
      }
      
    }
  }
};
</script>

<style>
</style>
上一篇:栈:删除最外层的括号


下一篇:vue/cli实现简单待办事项页面