效果图
一、运用element写html
划分区域,头部的添加以及下面的完成和未完成板块,可以运用<el-card>来获得卡片
1.头部
input输入框组件
<!-- 头部板块,el-header顶栏容器 --> <el-header style="text-align: left"> <div class="title"> <span style="font-size:20px">TodoList</span> <!-- input输入框组件 --> <el-input v-model="input" placeholder="Please input" clearable //v-on指令,.enter指回车确认修饰符 @keyup.enter="submit" /> </div> </el-header>
2.主体板块
<!-- 主体板块,主要区域容器 --> <el-main style="text-align: left;margin-top:15px;"> <!-- space间距组件,direction="vertical"定义子元素垂直排序,也可以用wrap实现不换行 --> <el-space direction="vertical"> <!-- card用来将已完成和未完成添加到不同的是card中,使得其以两个卡片的形式出现 --> <!--未完成板块--> <el-card style="width: 450px" class="todoList"> <p style="font-size:18px">todo</p> <!--任务列表--> <div class="todo"> <span>{{ input }}</span> <span>{{ times }}</span> <el-checkbox v-model="checked" @change="handleChange()" /> <el-button text size="small" @click="deleteMessage()"> <el-icon style="vertical-align: middle" size="15"> <Delete /> </el-icon> </el-button> </div> </el-card> <!--已完成板块--> <el-card style="width: 450px" class="finishList"> <sp style="font-size:18px">finish</p> <!--任务列表--> <div class="finish"> <span>{{ input }}</span> <span>{{ times }}</span> <el-checkbox v-model="checked" @change="handleChange()" /> <el-button text size="small" @click="deleteMessage()"> <el-icon style="vertical-align: middle" size="15"> <Delete /> </el-icon> </el-button> </div> </el-card> </el-space> </el-main>
知识点:
<div class="todo" >
<span>{{ item.input }}</span>
<span>{{ item.times }}</span>
<el-checkbox v-model="item.checked" @change="handleChange()" />
<el-button text size="small" @click="deleteMessage()">
<el-icon style="vertical-align: middle" size="15">
<Delete />
</el-icon>
</el-button>
</div>1.icon图标:
(1) 使用el-icon为SVG图标提供属性
<el-icon>
<Delete/>
</el-icon>
(2)Delete组件需要先引入注册才能使用
引入:import { Delete } from "@element-plus/icons-vue";
注册:components: { Delete },
二、运用less写style
<style lang="less"> p{ margin-bottom: 10px; } .todo { border: 1px solid var(--el-border-color); padding: 0px 5px; margin-bottom: 5px; // margin-top: 5px; border-radius: 5px; display: flex; justify-content: space-between; align-items: center; } .finish { .todo(); color: gray; text-decoration: line-through; } </style>
知识点:混合,将一组属性从一组规则集包含(或混入)另一个规则集
.todo{}
.finish{.todo()}
三、运用vue写js部分
1.思路
1.在data()里面初始化所需变量以及属性
data(){ input:"", times:"", checked:"", list:[] },
2.在method中撰写方法
(1)回车获取input数据并添加到list中
submit(){ //判空 if(!this.input.trim()){ alert("请输入数据"); return; } //push添加数据 this.list.push({ input:this.input, times:this.times, checked:false }); }
(2)获取当前时间
getNowTime() { var date = new Date(); var year = date.getFullYear(); var month = date.getMonth() + 1; var dates = date.getDate(); var hours = date.getHours()<10?"0"+date.getHours():date.getHours(); var minute = date.getMinutes()<10?"0"+date.getMinutes():date.getMinutes(); var second = date.getSeconds()<10?"0"+date.getSeconds():date.getSeconds(); return ( year + "-" + month + "-" + dates + " " + hours + ":" + minute + ":" + second ); },
将push中的this.times替换成this.getNowTime(),并删除掉data()里面对times的初始化
(3)获取checkbox状态并存储到本地,实现点击删除该列
//获取checkbox状态并存储到本地 handleChange(item){ item.checked=!item.checked; localStorage.serItem("todolist",JSON.stringify(this.list) } //点击实现删除该列 deleteMessage(index){ this.list.splice(index,1); }
(4)将push后的新数组存储到本地,并清空input的内容
submit(){ this.input = ""; localStorage.setItem("todolist", JSON.stringify(this.list)); }
(5)将list的内容渲染到页面
<div class="todo" v-for="(item,index) in list"> <span>{{item.input}}</span> <span>{{item.times}}</span> <el-checkbox v-model="item.checked" @change="handleChange(item)" /> <el-button text size="small" @click="deleteMessage(index)"> <el-icon style="vertical-align:middle" size="15> <Delete /> </el-icol> </el-button> </div>
2.会发现上面的代码繁琐又存在很多问题,于是可以进行优化
1.对data的优化:
可以直接通过将属性都放在一个对象里面实现优化
data() { return { input: "", list: [ { id: 1, input: "ss", times: "2022-11-22 16:6:4", checked: true }, { id: 2, input: "ss", times: "2022-11-22 16:6:4", checked: true }, { id: 3, input: "ss", times: "2022-11-22 16:6:4", checked: false }, ], }; },
2.对submit的优化
submit() { if (!this.input.trim()) { alert("请输入数据"); } else { this.list.push({ input: this.input, times: this.getNowTime(), checked: false, }) this.input = "" } },
2.对checkbox的优化。
因为v-model可以绑定property并侦听相关事件,所以当我点击checkbox时,checked会自动发生变化,所以可以直接删除对应的点击事件
<el-checkbox v-model="item.checked"/> <el-button text size="small" @click="deleteMessage(index)> <el-icon style="vertical-align: middle" size="15"> <Delete /> </el-icon> </el-button>
3.区分已完成和未完成
通过计算属性来区分完成和未完成
computed:{ //已完成 checkedList(){ return this.list.filter(item =>item.checked); } //未完成 uncheckedList(){ return this.list.filter(item =>!item.checked); } }
//未完成 <div class="todo" v-for="(item,index) in uncheckedList"> <span>{{item.input}}</span> <span>{{item.times}}</span> <el-checkbox v-model="item.checked" /> <el-button text size="small" @click="deleteMessage(index)"> <el-icon style="vertical-align:middle" size="15> <Delete /> </el-icol> </el-button> </div> //已完成 <div class="todo" v-for="(item,index) in checkedList"> <span>{{item.input}}</span> <span>{{item.times}}</span> <el-checkbox v-model="item.checked" /> <el-button text size="small" @click="deleteMessage(index)"> <el-icon style="vertical-align:middle" size="15> <Delete /> </el-icol> </el-button> </div>
知识点:
this.list.filter(item => item.checked)
① filter过滤元素,item.checked为true的情况下完成过滤
② 箭头函数的简写:
(item)=>{return true}
1.当参数只有一个的时候可以省略()
(item)=>{return true} item=>return true
(item,index)=>{return true} //不能简写
2.当{}里面可以直接返回函数体的时候可以省略{}
item=>true
3.通过三元运算符简化if..else
item=>{ if(Math.random()=>0.5{
return true
}else{
return false
}
}
//item=>Math.random()=>0.5?true:false
4.存储到本地的优化
运用watch监听器来监听list的改变并存储到本地
watch:{ list:{ deep:true, handler(val){ localStorage.setItem("todolist",JSON.stringify(this.list)) //localStorage.setItem("todolist",JSON.stringify(val)) } }, },
知识点:
watch:{
input(val,oldVal){
consloe.log('this.input 发生了变化","改变后的值为:",val,"改变前的值为:",oldVal)
}
}
因为list为引用数据类型,不能直接判等,所以无法监听判断数据是否发生变化,但是用deep深度监听可以实现
5.将本地数据渲染到页面上优化
直接将本地的列表赋给list,然后通过渲染list渲染到页面上
data(){ return { input:'', list:localStorage.getItem("todlist")?JSON.parse(localStorage.getItem("todolist")):[], }; },
6.对删除操作的优化
因为checkedList和uncheckedList只读,所以需要给list中的数据添加唯一性(id)
data() { return { input: "", list: [ { id: 1, input: "ss", times: "2022-11-22 16:6:4", checked: true }, { id: 2, input: "ss", times: "2022-11-22 16:6:4", checked: true }, { id: 3, input: "ss", times: "2022-11-22 16:6:4", checked: false }, ], }; }, //点击实现删除该列 deleteMessage(id){ consol.log("删除列的id":id) }
<el-button text size="small" @click="deleteMessage(id)"> <el-icon style="vertical-align: middle" size="15"> <Delete /> </el-icon> </el-button>
但由于已完成和未完成和id没有关系,所以直接选择删除点击的该条数据
<el-button size="small" text @click="deleteList(item)"> <el-icon size="18"> <Delete /> </el-icon> </el-button>
deleteList(item){ this.list.splice(this.list.indexOf(item),1) }
四、整体代码
<template> <el-container style="width: 500px"> <el-header style="text-align: left"> <div class="title"> <p style="font-size:20px">TodoList</p> <el-input v-model="input" placeholder="Please input" clearable @keyup.enter="submit" /> </div> </el-header> <!-- 主体板块,主要区域容器 --> <el-main style="text-align: left;margin-top: 15px;"> <el-space direction="vertical"> <el-card style="width: 450px" class="todoList"> <p style="font-size:18px">todo</p> <div class="todo" v-for="(item, index) in uncheckedList" > <span>{{ item.input }}</span> <span>{{ item.times }}</span> <el-checkbox v-model="item.checked" /> <el-button text size="small" @click="deleteMessage(item)"> <el-icon style="vertical-align: middle" size="15"> <Delete /> </el-icon> </el-button> </div> </el-card> <el-card style="width: 450px" class="finishList"> <p style="font-size:18px">finish</p> <div class="finish" v-for="(item, index) in checkedList"> <span>{{ item.input }}</span> <span>{{ item.times }}</span> <el-checkbox v-model="item.checked" /> <el-button text size="small" @click="deleteMessage(item)"> <el-icon style="vertical-align: middle" size="15"> <Delete /> </el-icon> </el-button> </div> </el-card> </el-space> </el-main> </el-container> </template> <script> import { Delete } from "@element-plus/icons-vue"; export default { components: { Delete, }, data() { return { input: "", list: localStorage.getItem("todolist") ? JSON.parse(localStorage.getItem("todolist")) : [], }; }, computed:{ checkedList(){ return this.list.filter(item=>item.checked); }, uncheckedList(){ return this.list.filter(item=>!item.checked); } }, watch: { list: { deep: true, handler(val) { console.log('this.list 发生了改变'); localStorage.setItem("todolist", JSON.stringify(val)) } } }, methods: { submit() { if (!this.input.trim()) { alert("请输入数据"); } else { this.list.push({ input: this.input, times: this.getNowTime(), checked: false, }) this.input = "" } }, getNowTime() { var date = new Date(); var year = date.getFullYear(); var month = date.getMonth() + 1; var dates = date.getDate(); var hours = date.getHours()<10?"0"+date.getHours():date.getHours(); var minute = date.getMinutes()<10?"0"+date.getMinutes():date.getMinutes(); var second = date.getSeconds()<10?"0"+date.getSeconds():date.getSeconds(); return ( year + "-" + month + "-" + dates + " " + hours + ":" + minute + ":" + second ); }, deleteMessage(item) { this.list.splice(this.list.indexOf(item),1); }, }, }; </script> <style lang="less"> p{ margin-bottom: 10px; } .todo { border: 1px solid var(--el-border-color); padding: 0px 5px; margin-bottom: 5px; // margin-top: 5px; border-radius: 5px; display: flex; justify-content: space-between; align-items: center; } .finish { .todo(); color: gray; text-decoration: line-through; } </style>