Vue 入门demo todos案例

703 阅读1分钟

最终效果 image.png

源码:

App.vue组件

<template>
<div class="todo-container">
<div class="todo-wrap">
  <Header @addTodo="addTodo"></Header>
  <List :todos="todos">
  </List>
  <Footer :todos="todos" @SelectAll="SelectAll" @deleteComplete="deleteComplete"></Footer>
</div>
</div>
</template>

<script>
import Header from "./components/Header";
import Footer from "./components/Footer";
import List from "./components/List";
// 导入PubSub
import Pubsub from 'pubsub-js'
export default {
  name: "App",
  components: {Footer, List, Header},
  comments:{
    Header,
    Footer,
    List
  },
  mounted(){
    //发布消息
    Pubsub.subscribe("deleteTodo",(msg,index)=>{
      console.log(index)
      this.deleteTodo(index)
    })
    // 这里也可以给某个元素绑定事件,
    //譬如 挂载时直接给hander标签绑定事件, 这样会直接传递给子组件
   // this.$refs.hander.$on('addTodo',this.addTodo)
  },
  data(){
    return {
      todos:[
        {name:'吃饭',complete:true},
        {name:'睡觉',complete:false},
        {name:'codding',complete:false}
      ]
    }
  },
  methods:{
    addTodo(todo){
      this.todos.unshift(todo)
    },
    deleteTodo(index){
      this.todos.splice(index,1)
    },
    // 全选/ 全不选
    SelectAll(flag){
      this.todos.forEach(item=>{item.complete=flag})
    },
    // 删除已完成任务
    deleteComplete(){
      // filter 过滤已经完成的,取反,重新给数组赋值
      this.todos=this.todos.filter((item)=>!item.complete)
    }
  },

}
</script>

<style scoped>
.todo-container {
  width: 600px;
  margin: 0 auto;
}
.todo-container .todo-wrap {
  margin-top: 50px;
  padding: 10px;
  border: 1px solid #ddd;
  border-radius: 5px;
}
</style>

Header.vue 组件

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

<script>
export default {
name: "Header",
  data(){
  return{
    title:'',
    complete:false
    }
  },
  methods:{
    addTodo(){
      if(this.title==='' || this.title===null){
        alert('标题不能为空')
        return
      }
     const  todos={name:this.title,complete:this.complete}
      // 调用父组件传入自定义事件,事件名要加 ''
      this.$emit('addTodo',todos)
      this.title=''
    }
  }
}
</script>

<style scoped>
.todo-header input {
  width: 100%;
  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>

List.Vue 组件

<template>
<div>
  <ul class="todo-main">
    <Item  v-for="(todo,index) in todos" :key="index" :todo="todo" :index="index"></Item>
  </ul>

</div>
</template>

<script>
import Item from "./Item";
export default {
  name: "List",
  components: {Item},
  props:{
    todos:{
      type:Array,
      required:false
    }
  }
}
</script>

<style scoped>
.todo-main {
  margin-left: 0px;
  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组件\

<template>
<div>
  <li @mouseenter="handleEnter(true)" @mouseleave="handleEnter(false)" :style="{backgroundColor:color}">
    <label>
      <input type="checkbox" v-model="todo.complete">
      <span>{{todo.name}}</span>
    </label>
    <button class="btn btn-danger" v-show="isshow" @click="deleteTodo(index)">删除</button>
  </li>
  </div>
</template>

<script>
export default {
  name: "Item",
  props:{
    todo:{
      type:Object,
      required:false
    },
    index:{
      type:Number,
      required: true
    }
  },
  data(){
    return {
      color:'white',
      isshow:false
    }
  },
  methods:{
    handleEnter(flag){
      if(flag){
        this.color="gainsboro"
        this.isshow=true
      }else{
        this.color="white"
        this.isshow=false
      }
    },
    deleteTodo(index){
      const { todo } =this
      // 第一个参数message订阅的事件名
      if(window.confirm(`确定删除${todo.name}吗?`)){
        PubSub.publish('deleteTodo',index)
      }

    }
  }
}
</script>

<style scoped>
li {

  list-style: none;
  height: 36px;
  line-height: 36px;
  padding: 0 5px;
  border-bottom: 1px solid #ddd;

}

li label {
  cursor: pointer;
}

li label li input {
  vertical-align: middle;
  margin-right: 6px;
  position: relative;
  top: -1px;
}

li button {
  float: right;
  margin-top: 3px;
}

li:before {
  content: initial;
}
</style>