利用vue做一个简单的todolist

1.分模块

刚拿到一个项目时,要先想好一个项目可以分为几部分,每一部分怎么规划。这里我们按照最终想要的效果,主要分为四部分,分别为

2.理清楚层次关系

大的App组件中有myHeader,myList,myFooter组件,然后myList组件中包含myItem组件。

3.代码展示

src/App.vue

<template>
    <div id="root">
        <div class="todo-container">
            <div class="todo-wrap">
                <myHeader :addTodo="addTodo" />
                <myList :todos="todos" :checkTodo="checkTodo" :deleteTodo="deleteTodo" />
                <myFooter :todos="todos" :checkAllTodo="checkAllTodo" :clearAllTodo="clearAllTodo" />
            </div>
        </div>
    </div>

</template>
<script>
import myHeader from './components/myHeader.vue';
import myFooter from './components/myFooter.vue';
import myList from './components/myList.vue';



export default {
    name: 'App',
    components: {
        myHeader,
        myList,
        myFooter
    },
    data() {
        return {
            todos: [
                { id: '001', title: '学习python框架', done: false },
                { id: '002', title: 'vue知识点回顾', done: false },
                { id: '003', title: '英语PPT制作', done: false },
            ]
        }
    },
    methods: {
        //添加一个todo
        addTodo(todoObj) {
            this.todos.unshift(todoObj)
        },
        //勾选or取消勾选一个todo
        checkTodo(id) {
            this.todos.forEach((todo) => {
                if (todo.id === id) todo.done = !todo.done
            })
        },
        //删除一个todo
        deleteTodo(id) {
            this.todos = this.todos.filter(todo => todo.id !== id)
        },
        //全选or取消勾选
        checkAllTodo(done) {
            this.todos.forEach(todo => todo.done = done)
        },
        //删除已完成的todo
        clearAllTodo() {
            this.todos = this.todos.filter(todo => !todo.done)
        }
    }
}

</script>
<style>
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>

src/myHeader.vue

<template>
    <div class="todo-header">
        <input type="text" placeholder="请输入你的任务名称,按回车键确认" @keyup.enter="add" v-model="title" />
    </div>
</template>
<script>
import { nanoid } from 'nanoid'
export default {
    name: 'myHeader',
    props: ['addTodo'],
    data() {
        return {
            title: ''
        }
    },
    methods: {
        // trim去掉前后空格
        add() {
            if (!this.title.trim()) return
            const todoObj = { id: nanoid(), title: this.title, done: false }
            this.addTodo(todoObj)
            this.title = ''

        }
    }
}
</script>
<style scoped>
.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>

 src/myList.vue

<template>
    <ul class="todo-main">
        <myItem v-for="todo in todos" :key="todo.id" :todo="todo" :checkTodo="checkTodo" :deleteTodo="deleteTodo" />
    </ul>

</template>
<script>
import myItem from './myItem.vue';

export default {
    name: 'myList',
    components: {
        myItem
    },
    props: ['todos', 'checkTodo', 'deleteTodo']
}
</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>

 src/myItem.vue

<template>
    <li>
        <label>
            <input type="checkbox" :checked="todo.done" @click="handleCheck(todo.id)" />
            <span>{
   
   { todo.title }}</span>
        </label>
        <button class="btn btn-danger" @click="handleDelete(todo.id, todo.title)">删除</button>
    </li>

</template>
<script>
export default {
    name: 'myItem',
    props: ['todo', 'checkTodo', 'deleteTodo'],
    methods: {
        handleCheck(id) {
            this.checkTodo(id)
        },
        handleDelete(id, title) {
            if (confirm("确定删除任务:" + title + "吗?")) {
                this.deleteTodo(id)
            }
        }
    }

}
</script>
<style scoped>
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-color: #eee;
}

li:hover button {
    display: block;
}
</style>

 src/myFooter.vue

<template>
    <div class="todo-footer" v-show="total">
        <label>
            <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', 'checkAllTodo', 'clearAllTodo'],
    // 计算属性,将函数结果返回到模板语法中 reduce是es6知识点
    computed: {
        doneTotal() {
            return this.todos.reduce((pre, todo) => pre + (todo.done ? 1 : 0), 0)
            // let i=0
            // this.todos.forEach(todo => {
            //     if (todo.done) i++
            // })
            // return i
        },
        total() {
            return this.todos.length
        },
        isAll: {
            get() {
                return this.total === this.doneTotal && this.total > 0
            },
            set(value) {
                this.checkAllTodo(value)
            }
        }
    },
    methods: {
        clearAll() {
            this.clearAllTodo()
        }
    }

}
</script>
<style scoped>
.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>

 main.js

import Vue from 'vue'
import App from './App.vue'


Vue.config.productionTip = false



new Vue({
  render: h => h(App),
}).$mount('#app')

 4.运行结果展示

在vscode终端输入npm run serve,就得到了本地的一个网址

http://localhost:8080/

 上面的代码有些方法和函数不太熟的朋友可以查MDN网站哦,上面都有详细的教程和介绍,链接如下:

https://developer.mozilla.org/zh-CN/

5.总结

(1)采用组件嵌套,这个学起来较为简单,只要将要展示的内容,层次结构理清楚。

(2)学习props传数据,让组件接收外部传过来的数据

(3)

  • props适用于:

    1. 父组件 ==> 子组件 通信
    2. 子组件 ==> 父组件 通信(要求父组件先给子组件一个函数)

猜你喜欢

转载自blog.csdn.net/qq_50280292/article/details/127584064