Copy the ToDoList to-do list:

1. Render data

arr:[
    {
        id:1.content:Content of "1".isShowBorder:true.isFinished:true
    },
    {
        id:2.content:"Content 2".isShowBorder:true.isFinished:true
    },
    {
        id:3.content:"Content 3".isShowBorder:true.isFinished:false}]Copy the code
<div class="tit"><h3>The ongoing</h3> <span>2</span></div>
<ul>
    <li v-for="item in arr" v-if=! "" item.isFinished">
        <label :for="item.id"><input type="checkbox" :id="item.id" :checked="item.ifCheck"></label>
        <div v-if="item.isShowDiv">{{item.content}}</div>
        <input v-else type="text" v-model="item.content">
        <span>-</span>
    </li>
</ul>
<div class="tit"><h3>Has been completed</h3> <span>1</span></div>
<ul>
    <li v-for="item in arr" v-if="item.isFinished">
        <label :for="item.id"><input type="checkbox" :id="item.id" :checked="item.ifCheck"></label>
        <div v-if="item.isShowDiv">{{item.content}}</div>
        <input v-else type="text" v-model="item.content">
        <span>-</span>
    </li>
</ul>
Copy the code

2. Separate the UL into components

<div class="wrap">
    <list-comp :arr="arr" title="In progress" type="doing" :counts="counts"></list-comp>
    <list-comp :arr="arr"  title="已经完成" type="finished" :counts="counts"></list-comp>
</div>.<template id="tmpl">
    <div class="con-list">
        <div class="hd">
            <h3>{{title}}</h3>
            <div class="num">{{counts(type)}}</div>
        </div>
        <ul class="bd">
            <li v-for="item in arr" v-show="type=='doing'? ! item.isFinished:item.isFinished">
                <div class="check-box"><input type="checkbox"></div>
                <input type="text" v-model="item.content">
                <div class="btn-box"><div class="del-btn">-</div></div>
            </li>
        </ul>
    </div>
</template>
Copy the code
let listComp = {
    template:"#tmpl".props: ["arr"."title"."type"."counts"]}Copy the code

3. Calculate attributes. Calculate the number of data items in the upper right corner

/ / the Vue instances
computed: {counts(){
        return type= >{
            let ret = this.arr.filter(item= >{
                return type=="doing"? ! item.isFinished:item.isFinished })return ret.length
        }

    }
}
Copy the code

4. Select the check box to move the ongoing tasks to the completed list

<! Parent component passes event function to child component -->
<list-comp :arr="arr" title="In progress" type="doing" :counts="counts" @fn="changeChecked"></list-comp>
<list-comp :arr="arr"  title="已经完成" type="finished" :counts="counts"  @fn="changeChecked"></list-comp>

<! -->
<div class="check-box"><input type="checkbox" @click="chchecked(key)"></div>
Copy the code
// 4. Call the method passed by the parent
methods: {chchecked(index){
        this.$emit("fn",index)
    }
}


// 1. Define methods in the parent component
// Click to change the selected state
methods: {changeChecked(index){
        // alert(111)
        this.arr[index].isFinished = !this.arr[index].isFinished
    }
},
Copy the code

5. Click Input to switch the label border color

<! -- Parent component -->
<list-comp 
       :arr="arr" 
       title="In progress" 
       type="doing" 
       :counts="counts"                
       @fn="changeChecked"
       @fn2="changeBorder"
></list-comp>
<list-comp :arr="arr"  title="已经完成" type="finished" :counts="counts"  @fn="changeChecked" @fn2="changeBorder"></list-comp>

<! -- -- -- > templates
<input type="text" v-model="item.content" @click="showBorder(key)" :class="item.isShowBorder? 'show-border':''">
Copy the code
/ / child component
showBorder(index){
    this.$emit("fn2",index)
}
/ / the parent component
changeBorder(index){
    this.arr[index].isShowBorder = !this.arr[index].isShowBorder
}
Copy the code

6. Set out of focus

<! Blur ="showBorder(key)"-->
<input type="text" v-model="item.content" @click="showBorder(key)" @blur="showBorder(key)" :class="item.isShowBorder? 'show-border':''">
Copy the code

7. Delete an item

<! -- Parent template -->
<list-comp 
                :arr="arr" 
                title="In progress" 
                type="doing" 
                :counts="counts"                
                @fn="changeChecked"
                @fn2="changeBorder"
                @fn3="del"
></list-comp>
<list-comp :arr="arr"  title="已经完成" type="finished" :counts="counts"  @fn="changeChecked" @fn2="changeBorder" @fn3="del"></list-comp>
<! -- Subtemplate -->
<div class="btn-box"><div class="del-btn" @click="del(key)">-</div></div>
Copy the code
// In the child component
del(index){
    this.$emit("fn3",index)
}

// Parent component:
del(index){
    this.arr.splice(index,1)}Copy the code

8. Enter and press Enter to add an element to “In Progress”

<input class="input-text" type="text" placeholder="Please enter the task"  @keyup.enter="add" v-model="txtVal">
Copy the code
// The Vue instance defines num as the id of each data item, and also as a basis for whether the site has been saved locally
methods: {add(){
        this.arr.push({
            id:this.num,
            isFinished:false.content:this.txtVal,
            isShowBorder:false
        });
        this.num++; }}Copy the code

9, record the existing data, so that the next time you open the browser you can still see the current data

// Define methods in the parent component
setToLocalStorage(){
    localStorage.setItem('num'.this.num);
    localStorage.setItem('arr'.JSON.stringify(this.arr));
}
// Save localStorage for each change
this.setToLocalStorage();

// After each component creation, the two localstorages can be retrieved
created(){
    this.num = localStorage.getItem("num")?localStorage.getItem("num") :0;
    this.arr=localStorage.getItem("arr")?JSON.parse(localStorage.getItem('arr')) : []; },Copy the code

10. Complete code:

<! DOCTYPEhtml>
<html>

<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="Width = device - width, initial - scale = 1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title></title>
    <script src="https://cdn.jsdelivr.net/npm/vue"></script>
    <link rel="stylesheet" href="style/index.css">
</head>

<body>
    <div id='app'>
        <header>
            <div class="wrap">
                <h1>ToDoList</h1>
                <input type="text" placeholder="Please enter..." v-model='inpTxt' @keyup.enter='add'>
            </div>
        </header>
        <con-comp title='In progress' :arr='arr' type='doing' @fn="changeType" @fn2='borderFa' @fn3='delFa'></con-comp>
        <con-comp title='Done' :arr='arr' type='finished' @fn="changeType" @fn2='borderFa' @fn3='delFa'></con-comp>

        <div class="footer">
            Copyright 2021 todolist.cn clear
        </div>
    </div>
    <template id="tmp">
        <div class="wrap">
            <div class="hd">
                <h3>{{title}}</h3>
                <div class="count">{{counts}}</div>
            </div>
            <ul class="bd">
                <! -- Li display or not display is item. IsFinished is for unfinished tasks! Item. isFinished displays unfinished tasks. For completed tasks, item.isFinished displays completed tasks. Item. isFinished or item.isFinished -->
                <li v-for='item,key in arr' v-show="type=='doing'? ! item.isFinished:item.isFinished">
                    <div class="l">
                        <input type="checkbox" @click="clickCheckbox(key)" :checked='item.isFinished'>
                    </div>
                    <input type="text" v-model='item.content' :class="item.isShowBorder? 'showBorder':''"
                        @focus='borderChil(key)' @blur='borderChil(key)'>
                    <div class="r">
                        <div @click='del(key)'>-</div>
                    </div>
                </li>
            </ul>
        </div>
    </template>
    <script>
        let conComp = {
            template: '#tmp'.props: ['title'.'arr'.'type'].computed: {
                // doingCounts() {
                       // Count the number of incomplete entries
                // // filters out elements with isFinished as false in the ARR array
                // let newArr = this.arr.filter(item => {
                // return item.isFinished == false
                / /})
                // return newArr.length
                // },
                // finishedCounts(){
                        // Count the number of completed entries
                // // filters out elements in the ARR array with isFinished as true
                // let newArr = this.arr.filter(item => {
                // return item.isFinished == true
                / /})
                // return newArr.length
                // }
                counts() {
                    let newArr = this.arr.filter(item= > {
                        return this.type == 'doing'? ! item.isFinished : item.isFinished })return newArr.length
                }
            },
            methods: {
                clickCheckbox(key) {
                    // Modify isFinished for the data
                    this.$emit('fn', key)
                },
                borderChil(key) {
                    this.$emit('fn2', key)
                },
                del(key) {
                    this.$emit('fn3', key)
                }
            },
        }
        new Vue({
            el: '#app'.data: {
                inpTxt: ' '.num: 3.arr: [
                    / / {
                // id: 1,
                // content: "1",
                // isFinished: true
                // isShowBorder: false
                / /},
                / / {
                // id: 2,
                // content: "2",
                // isFinished: true
                // isShowBorder: false
                / /},
                / / {
                // id: 3,
                // content: "3",
                // isFinished: false, // this is used to tell whether it isFinished
                // isShowBorder: false
                / /}]},components: {
                conComp
            },
            created() {
                // The lifecycle function that executes the code when the instance/component is first created
                // Get num and arr in localstorage
                // Assign values to this.num and this.arr
                this.num = localStorage.getItem('num')?parseInt(localStorage.getItem('num')) : 0
                this.arr =
                    localStorage.getItem("arr")?JSON.parse(localStorage.getItem("arr":)) []},methods: {
                changeType(index) {
                    this.arr[index].isFinished = !this.arr[index].isFinished
                    this.setLocalstorage()
                },
                borderFa(index) {
                    this.arr[index].isShowBorder = !this.arr[index].isShowBorder
                    this.setLocalstorage()
                },
                delFa(index) {
                    this.arr.splice(index, 1)
                    this.setLocalstorage()
                },
                add() {
                    if (!this.inpTxt) {
                        return
                    }
                    this.arr.push({
                        id: + +this.num,
                        content: this.inpTxt,
                        isFinished: false.// It is used to tell whether or not it is done
                        isShowBorder: false // To distinguish whether there is a border
                    })
                    this.inpTxt = ' '
                    this.setLocalstorage()
                },
                setLocalstorage() {
                    // Save data num and arr
                    localStorage.setItem("num".this.num)
                    localStorage.setItem("arr".JSON.stringify(this.arr))
                }
            },
        })
    </script>
</body>

</html>
Copy the code

CSS file code:

* {margin: 0;
    padding: 0;
    list-style: none;
    outline: none;
}
body{
    background-color: #ccc;
}
header{
    height: 50px;
    line-height: 50px;
    background-color: # 333;
}
h1{
    color: #fff;
}
.wrap{
    width: 600px;
    margin: 0 auto;
}
header .wrap{
    display: flex;
    justify-content: space-between;
}
header input{
    height: 40px;
    margin-top: 3px;
    width: 300px;
    text-indent: 10px;
}
.hd{
    display: flex;
    justify-content: space-between;
    margin-top: 10px;
}
.hd .count{
    width: 30px;
    height: 30px;
    color: #fff;
    background-color: # 666;
    border-radius: 50%;
    text-align: center;
    line-height: 30px;
}
.bd{
    margin-bottom: 20px;
}
.bd li{
    display: flex;
    background-color: #fff;
    height: 40px;
    margin-top: 8px;
}
.l..r{
    width: 40px;
    height: 40px;
    display: flex;
    align-items: center;
    justify-content: center;
}
.bd input[type=text]{
    margin-top: 3px;
    flex: 1;
    height: 30px;
    border-color: #fff;
    border-width: 0px;
}
.bd input[type=text].showBorder{
    border: 1px solid # 000;
}
.l>input..r>div{
    width: 30px;
    height: 30px;
}
.r>div{
    background-color: pink;
    border-radius: 50%;
    text-align: center;
    line-height: 30px;
}
.footer{
    text-align: center;
    margin-top: 20px;
}
.bd input[type=checkbox]..r>div{
    cursor: pointer;
}
Copy the code