vue2 src_Todolist消息订阅版本

main.js

php 复制代码
//引入Vue
import Vue from "vue";
//引入App
import App from './App';

//关闭Vue的生产提示
Vue.config.productionTip = false;


new Vue({
    el: '#app',
    render: h => h(App),
    beforeCreate() {
        //事件总线
       Vue.prototype.$bus = this;
    }
});

App.vue

php 复制代码
<template>
  <div id="root">
    <div class="todo-container">
      <div class="todo-wrap">
        <MyHeader @addTodo="addTodo"/>
        <List
            :todos="todos"
        />
        <MyFooter
            :todos="todos"
            @checkAllTodo="checkAllTodo"
            @clearAllDoneTodo="clearAllDoneTodo"
        />
      </div>
    </div>
  </div>
</template>

<script>
import MyHeader from "@/components/MyHeader";
import List from "@/components/List";
import MyFooter from '@/components/MyFooter';
import pubsub from "pubsub-js";
export default {
  name: "App",
  components:{
    List,
    MyFooter,
    MyHeader
  },
  data() {
    return {
      // todos: [
      //   {id: '001', title: '吃饭', done: false},
      //   {id: '002', title: "睡觉", done: true},
      //   {id: '003', title: '打代码', done: false}
      // ]
      todos:JSON.parse(localStorage.getItem('todos')) || []
    }
  },
  methods:{
    //添加的todo
    addTodo(todo){
      console.log('我是app组件,我收到了数据');
      this.todos.unshift(todo);
    },
    checkTodo(id){
      const todo = this.todos.find(todo => todo.id === id);
      todo.done = !todo.done;
    },
    deleteTodo(_, id){
      this.todos = this.todos.filter(todo => todo.id !== id);
    },
    checkAllTodo(done){
      this.todos.forEach(todo => todo.done = done);
    },
    clearAllDoneTodo(){
      this.todos = this.todos.filter(todo => !todo.done)
    }
  },
  watch:{
    //深度监视
    todos:{
      deep: true, //深度监视当我监视数组中的对象的某个属性的变化它也会产生反应
      handler(newValue) {
        //本地存储存的是key和value都是字符串
        //数据存放在本地存储中
        localStorage.setItem("todos", JSON.stringify(newValue))
      }
    },
  },
  //已挂在绑定事件总线
  mounted() {
    this.$bus.$on('checkTodo', this.checkTodo);
    this.pubId = pubsub.subscribe('deleteTodo', this.deleteTodo);
  },
  //被卸载注意解绑
  beforeMount() {
    this.$bus.$off('checkTodo');
    pubsub.unsubscribe(this.pubId); //取消订阅的方式与取消定时器的方式是类似的,记住
  }
}
</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-danger:hover {
  color: #fff;
  background-color: #bd362f;
}

.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>

MyHeader.vue

php 复制代码
<template>
  <div class="todo-header">
    <input type="text" placeholder="请输入你的任务名称,按回车键确认" v-model="title" @keyup.enter="add"/>
  </div>
</template>

<script>
import { nanoid } from 'nanoid';
export default {
  //注意不管是你写的data也还还是methods也好,甚至是computed计算属性也好都会出现在组件事例对象vc身上
  //属性值不能重名
  name: "MyHeader",
  data(){
    return {
      title: ''
    }
  },
  methods:{
    add(){
      //将用户的输入包装成一个todo对象
      console.log(this.title)
      if(!this.title.trim()) {
        alert('代办事项不能为空')
        return; //输入的代办事项为空则不走下面流程
      }
      const todoObj = {
        id: nanoid(),
        title: this.title,
        done:false
      }
      // console.log(todoObj);
      // this.addTodo(todoObj);
      //采用自定义事件来修改
      this.$emit('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>

MyFooter.vue

php 复制代码
<template>
  <!--隐式类型转换-->
  <div class="todo-footer" v-show="total">
    <label>
      <!--这里也可用v-model来替代,此时不需要计算属性了-->
<!--      <input type="checkbox" :checked="isAll" @change="checkAll"/>-->
      <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: "MyFooter",
  props: ['todos'],
  computed:{
    total(){
      return this.todos.length;
    },
    doneTotal(){
      return this.todos.reduce((todoTotal, todo) => {
        //隐士类型转换
        return todoTotal + todo.done;
      }, 0);
      // return this.todos.filter(todo => todo.done).length;
    },
    isAll:{
      get(){
        return this.total === this.doneTotal && this.doneTotal > 0; //计算属性可以通过其他的计算属性接着进行计算得到结果
      },
      set(value){
        //value注意要么为true,要么为false,因为你是把它应用在了checkbox上
        //this.checkAllTodo(value);
        //采用自定义事件来修改
        this.$emit('checkAllTodo', value);
      }
    }
  },
  methods:{
    // checkAll(e){
    //   // console.log(e.target.checked); //判断这个checkbox到底是不是全选 true全选 false全不选
    //   this.checkAllTodo(e.target.checked);
    // }
    clearAll(){
       // this.clearAllDoneTodo();
      //修改为自定义事件
      this.$emit('clearAllDoneTodo');
    }
  }
}
</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>

List.vue

php 复制代码
<template>
  <ul class="todo-main">
    <Item
        v-for="todoObj in todos"
        :key="todoObj.id"
        :todo="todoObj"
    />
  </ul>
</template>

<script>
import Item from "@/components/Item";

export default {
  name: "List",
  components: {
    Item,
  },
  props:['todos']
}
</script>

<style scoped>
/*main*/
.todo-main {
  margin-left: 0;
  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.vue

php 复制代码
<template>
  <li>
    <label>
      <!--这里勾选和取消勾选可以使用change和click作为事件处理-->
      <input type="checkbox" :checked="todo.done" @change="handleCheck(todo.id)"/>
      <!--v-model数据的双向绑定,checkbox使用v-model来双向绑定其是否被勾选,也可以实现效果但不推荐(因为其实修改了props中的数据)-->
      <!--这里修改了从List修改过来的props,这里的不允许改是浅层次,就是如果props是一个对象则这个修改这个对象的某一个属性vue是放行的-->
      <!-- <input type="checkbox" v-model="todo.done"/>-->
      <span>{{  todo.title }}</span>
    </label>
    <button class="btn btn-danger" @click="handleDelete(todo.id)">删除</button>
  </li>
</template>

<script>
import pubsub from "pubsub-js";
export default {
  name: "Item",
  //声明接收todo
  props: ['todo'],
  methods:{
    handleCheck(id){
      //事件总线
      this.$bus.$emit('checkTodo',id);
    },
    handleDelete(id){
      if(confirm(`确定删除编号为${id}的todo吗`)){
        // console.log(id);
        //事件总线
        // this.$bus.$emit('deleteTodo',id);
        //消息订阅改写
        pubsub.publish('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: #ddd;
}

li:hover button{
  display: block;
}
</style>
相关推荐
百度地图开放平台2 分钟前
LBS 开发微课堂|智能调度API升级:解决循环取货场景下的调度难题
前端·javascript
Nymph_Zhu14 分钟前
vue3+antV G6节点与文本框实现
vue.js·element-plus·g6
lh_125417 分钟前
VUE2脚手架的下载与安装
vue.js
cypking19 分钟前
vue实现一个pdf在线预览,pdf选择文本并提取复制文字触发弹窗效果
前端·vue.js·pdf
飘逸飘逸22 分钟前
若依前后端分离版使用Electron打包前端Vue为Exe文件
前端·vue.js·electron·vue·ruoyi
入门级前端开发23 分钟前
npm install 报错ERESOLVE
前端·npm·node.js
anyup1 小时前
最终!我还是抛弃了 VSCode 这个开发工具
前端·aigc·visual studio code
木亦Sam1 小时前
前端安全之 CSRF 攻击的防御策略
前端
光影少年1 小时前
es6+新增特性有哪些
前端·javascript·es6
木亦Sam1 小时前
前端代码优化之函数节流与防抖技巧
前端