美文网首页react & vue & angular
用 vue2 写的一个 todolist 案例

用 vue2 写的一个 todolist 案例

作者: 小小前端搬运工 | 来源:发表于2022-03-14 22:28 被阅读0次

整体效果图

vue2-todolist.png

此案例涉及到的知识点

在子组件中需要向父组件传值处使用this.$emit("function",param); 其中function为父组件定义函数,param为需要传递参数


子组件操作.png
父组件监听.png
父组件操作.png

父组件 在引用子组件时,通过属性绑定(v-bind:)的形式,把需要传递给子组件的数据,传递到子组件内部,供子组件使用 例 :remain="remain"


父子传值-父组件.png

子组件通过props接收父组件传递过来的数据


父子传值-子组件.png

localStorage.getItem(key):获取指定key本地存储的值
localStorage.setItem(key,value):将value存储到key字段


setItem-getItem.png

v-model 数据的双向绑定


v-model.png

首先写页面的入口文件,也就是页面的主页面 App.vue

<template>
  <div id="app">
    <todos-header></todos-header>
    <todos-input @all_check_event="handleAllCheck" @save_todo_event="handleSaveTodo" :allChecked="allChecked"></todos-input>
    <todos-item 
     v-for="(todo,index) in filterTodos" :key="todo.id" 
     :index="index" :todo="todo" @item-click-event="handleItemClick"
     @delete-todo-event="handleItemDelete"></todos-item>
    <todos-bottom :activeTab="activeTab" @status_change_event="handleStatusChange" :remaining="remaining" @clear_complete_event="handleClearComplete"></todos-bottom>
  </div>
</template>
<script>
import TodosHeader from '@/components/todos-header';
import TodosInput from '@/components/todos-input';  // 引入组件
import TodosItem from '@/components/todos-item';
import TodosBottom from '@/components/todos-bottom';
export default {
  watch: {
    todos: {
      deep:true,
      handler(newTodos){
        localStorage.setItem("todos",JSON.stringify(newTodos))  
      } // 进行本地数据存储,复杂数据不能直接存,需要转换为json字符串
    },
  },
  computed: {
    filterTodos(){
      if (this.activeTab === "complete") { // 点击路由状态的改变
        return this.todos.filter(c=>c.checked) // 将状态为 "complete" 的数据过滤出来
      }
      if (this.activeTab === "active") {
        return this.todos.filter(c=>!c.checked) 
      } // 将状态非 "checked" 的数据过滤出来
      return this.todos
    },
    remaining(){
      return this.todos.filter(c=>!c.checked).length 
    },  // 返回非 "checked" 状态数据的长度
    allChecked() {
      return this.todos.every(t=>t.checked)   // 将返回状态为 “checked” 的数据
    },
  },
  methods: {
    handleStatusChange(activeTab){
      this.activeTab = activeTab
    },
    handleClearComplete(){
      let filterTodos = this.todos.filter((todo)=>{
        return !todo.checked  
      })  // 返回非 "checked" 状态的数据(删除"checked"状态的数据)
      this.todos = filterTodos
    },
    handleItemDelete(index){
      this.todos.splice(index,1) // 从当前索引位置开始,删除一个数据
    },
    handleItemClick:function({index,checked}){
      this.todos[index].checked = checked
    },
    handleAllCheck(checked){
      this.todos.map(t=>t.checked = checked)  // 将所有数据设置为 “checked”
    }, 
    handleSaveTodo:function(todo) {
      this.todos.push(todo) // 从数组尾部增加一条数据
    },
  },
  data() {
    return {
      activeTab: "all",
      todos:JSON.parse(localStorage.getItem("todos")||'[]')
    }; // 获取本地数据并解析,将之前存储的JSON字符串先转成JSON对象再进行操作
  },
  components:{
    "todos-header":TodosHeader,  // 
    "todos-input":TodosInput,
    "todos-item":TodosItem,
    "todos-bottom":TodosBottom
  }
}
</script>

<style lang="less">
#app {
  width: 800px;
  margin: 0 auto;
  min-height: 100px;
  border: 1px solid #ddd;
}
*{
  padding: 0;
  margin: 0;
  box-sizing: border-box;
}
#nav {
  padding: 30px;
  a {
    font-weight: bold;
    color: #2c3e50;

    &.router-link-exact-active {
      color: #42b983;
    }
  }
}
</style>

todolist 头部组件 todos-header.vue

<template>
    <div class="todos-header">
        <h1>todos</h1>
    </div>
</template>

<style lang="less" scoped>
    .todos-header{             
        text-align: center;
        font-size: 30px;
        color: #ead7d7;
        h1{
        }
    }
</style>

输入框的设置 组件todos-input.vue

<template>
    <div>
        <div class="todos-input">
            <div class="todos-input-left" @click.stop="handleAllCheck">
                <i :class="[{'iconfont':true}, {'icon-jiantouxia':true},{'active':allChecked}]"></i>
            </div>
            <div class="todos-input-right">
                <input type="text" v-focus v-model="inputVal" @keyup.enter="saveTodo"> // 自动聚焦 按下回车键向数组尾部增加记录
            </div>
        </div>
        <div class="error_msg">
            <span style="color: red;">{{error_msg}}</span>
        </div>
    </div>
</template>
<script>
export default {
    name: 'MytodosTodosInput',
    props:['allChecked'], // 从父组件中接受数据
    data() {
        return {
            inputVal:"",
            "error_msg":''
        };
    },
    directives:{
        "focus":{
            bind:function (el, binding, vnode, oldVnode) {
                console.log(el,binding,vnode,oldVnode);
            }, // 自定义 focus 组件
            inserted:function(el){
                el.focus()
            }
        }
    },
    methods: {
        handleAllCheck(){
            this.$emit('all_checked_event', !this.allChecked);
        },   // 向父组件中传递数据,all_checked_event 为父组件定义函数,!this.allChecked 为传递的数据
        saveTodo(){
            if (!this.inputVal) {
                this.error_msg = "todo 不能为空!"
                return false
            }
            this.error_msg = ""
            this.$emit('save_todo_event', {
                id:Date.now(),
                text:this.inputVal,
                checked:false
            });
            this.inputVal = '' // 向数组中添加数据后,清空输入框
        }
    },
};
</script>
<style lang="less" scoped>
     .error_msg{
        text-align: center;
    }
.todos-input{
    height: 50px;
    display: flex;
    align-items: center;
    .todos-input-left{
        flex: 1;
        text-align: center;
        .active{
            color: red;
        }
    }
    .todos-input-right{
        flex: 7;
        input{
            height: 40px;
            width: 90%;
        }
    }
}
</style>

组件 todos-item.vue

<template>
    <div class="todos-item">
        <div class="todos-item-left" @click.stop="handleItemClick">
            <i :class="[{'iconfont':true}, {'icon-xuanzhong':todo.checked},{'icon-weixuanzhong':!todo.checked}]"></i>
        </div>
        <div @dblclick.stop="handleEditTodo" :class="[{'todos-item-mid':true}, {'deleteItem':todo.checked}]">
            <span :class="[{'todo-item-text':true},{'hidden':editing}]">{{todo.text}}</span>
            <input type="text" @blur="handleSave" v-model="todo.text" :class="[{'todo-item-input':true},{'hidden':!editing}]">
        </div>
        <div class="todos-item-right" @click="deleteTodo">
            <i class="iconfont icon-cross"></i>
        </div>
    </div>
</template>
<script>
export default {
    props:{
        todo:{
            type:Object,
            default:function(){
                return {}
            }
        },
        index:{
            type:Number,
            default:-1
        }
    },
    data() {
        return {
            editing:false
        };
    },

    methods: {
        handleSave(){
            this.editing = false
        },
        handleEditTodo(){
            this.editing = true
        },
        deleteTodo:function(){
            this.$emit('delete-todo-event',this.index)
        },
        handleItemClick:function(){
            this.$emit('item-click-event', {index:this.index,checked:!this.todo.checked});
        }
    },
};
</script>
<style lang="less" scoped>
    .todos-item{
        height: 50px;
        display: flex;
        align-items: center;
        justify-content: space-between;
        padding: 10px;
        border-bottom: 1px solid #ddd;
        .todos-item-left{
            .iconfont{

            }
        }
        .todos-item-mid{
            .hidden{
                display: none;
            }
            .todo-item-input{
                /* display: none; */
            }
            .todos-item-text{

            }
        }
        .deleteItem{
            color: #ddd;
            text-decoration: line-through;
        }
        &:hover{
            .todos-item-right{
                visibility: visible;
            }
        }
        .todos-item-right{
            color: #cc9a9a;
            visibility: hidden;
        }
    }
</style>

组件 todos-bottom.vue

<template>
    <div class="todos-bottom">
        <div class="todos-bottom-left">
            <span v-if="remain>1">{{remaining}} items left</span> 
            <span v-else>{{remaining}} items left </span>
        </div>
        <div class="todos-bottom-mid">
            <div :class="[{'bottom-mid-item':true},{active:activeTab==='all'}]" @click.stop="hanldeGoTo('/')">
                all
            </div>
            <div :class="[{'bottom-mid-item':true},{active:activeTab==='active'}]" @click.stop="hanldeGoTo('active')">
                active
            </div>
            <div :class="[{'bottom-mid-item':true},{active:activeTab==='complete'}]" @click.stop="hanldeGoTo('complete')">
                completed
            </div>
        </div>
        <div class="todos-bottom-right" @click.stop="handleClearComplete">
            clear completed
        </div>
    </div>
</template>

<script>
export default {
    name: 'MytodosTodosBottom',
    props:['remaining','activeTab'],
    data() {
        return {
            
        };
    },
    methods: {
        handleClearComplete(){
            this.$emit('clear_complete_event', '');
        },
        hanldeGoTo(path){
            this.$router.push(path)
            let activeTab = path
            if (path==="/") {
                activeTab = "all"
            }
            this.$emit("status_change_event",activeTab)
        }
    },
};
</script>

<style lang="less" scoped>
    .todos-bottom{
        height: 50px;
        display: flex;
        justify-content: space-between;
        align-items: center;
        padding: 10px;
        .todos-bottom-left{

        }
        .todos-bottom-mid{
            display: flex;
            .active{
                border: 1px solid #e3d1d1;
            }
            .bottom-mid-item{
                height: 30px;
                // width: 50px;
                padding: 0px 10px;
                border-radius: 5px;
                margin-right: 5px;
                text-align: center;
                line-height: 30px;
            }
        }
    }
</style>

相关文章

  • 用 vue2 写的一个 todolist 案例

    整体效果图 此案例涉及到的知识点 在子组件中需要向父组件传值处使用this.$emit("function",pa...

  • 用 vue3 写的简洁的 todolist 案例

    整体效果 父子组件传值父组件 子组件 mutations 处理操作 Home.vue 接收来自 store 中的数...

  • mobx-react简明教程

    mobx是很好,很容易理解的状态管理工具 先讲一下这个案例是做TODOlist的案例,利用antd样式,用mobx...

  • toDoList案例

    1.组件化编码流程(1)拆分静态组件:组件要按照功能点拆分,命名不要与 html 元素冲突(2)实现动态组件:考虑...

  • todolist 再思考

    有一天,老师布置了一个任务,用 React、 Redux 、 Mocha 写一个 todolist。 这个任务老师...

  • Vuex案例ToDoList

    Vuex学习 使用UI:ant-design-vue 修改main.js 修改App.vue组件,完善功能 创建 ...

  • react案例todoList

    项目结构 效果图 app.js app.css Foot.js Head.js Item.js List.js

  • toDoList 案例分析

    1.8.1 案例:案例介绍 鼠标按下keydown ;鼠标弹起 keyup ; keyCode 记录键盘按下事件...

  • vue todoList 案例

    添加,修改,删除 Footer组件 Head组件 List 组件 MyItem 组件 App 主组件 main.js

  • 我的Vue.js之旅

    初入Vue.js (Vue2) 项目后台用PHP接口形式写,前台学习着用Vue调自己接口,给公司写一个运营支撑平台...

网友评论

    本文标题:用 vue2 写的一个 todolist 案例

    本文链接:https://www.haomeiwen.com/subject/untedrtx.html