代码
MyFooter.vue
- <template>
- <div class="todo-footer" v-show="total">
- <label>
- <!-- <input type="checkbox" :checked="isAll" @change="checkAll"/> -->
- <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'],
- computed: {
- //总数
- total(){
- return this.todos.length
- },
- //已完成数
- doneTotal(){
- //此处使用reduce方法做条件统计
- /* const x = this.todos.reduce((pre,current)=>{
- console.log('@',pre,current)
- return pre + (current.done ? 1 : 0)
- },0) */
- //简写
- return this.todos.reduce((pre,todo)=> pre + (todo.done ? 1 : 0) ,0)
- },
- //控制全选框
- isAll:{
- //全选框是否勾选
- get(){
- return this.doneTotal === this.total && this.total > 0
- },
- //isAll被修改时set被调用
- set(value){
- // this.checkAllTodo(value)
- this.$emit('checkAllTodo',value)
- }
- }
- },
- methods: {
- /* checkAll(e){
- this.checkAllTodo(e.target.checked)
- } */
- //清空所有已完成
- clearAll(){
- // this.clearAllTodo()
- this.$emit('clearAllTodo')
- }
- },
- }
- </script>
- <style scoped>
- /*footer*/
- .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>
复制代码 MyHeader.vue
- <template>
- <div class="todo-header">
- <input type="text" placeholder="请输入你的任务名称,按回车键确认" v-model="title" @keyup.enter="add"/>
- </div>
- </template>
- <script>
- import {nanoid} from 'nanoid'
- export default {
- name:'MyHeader',
- data() {
- return {
- //收集用户输入的title
- title:''
- }
- },
- methods: {
- add(){
- //校验数据
- if(!this.title.trim()) return alert('输入不能为空')
- //将用户的输入包装成一个todo对象
- const todoObj = {id:nanoid(),title:this.title,done:false}
- //通知App组件去添加一个todo对象
- this.$emit('addTodo',todoObj,1,2,3)
- //清空输入
- this.title = ''
- }
- },
- }
- </script>
- <style scoped>
- /*header*/
- .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>
复制代码 MyItem.vue
- <template>
- <li>
- <label>
- <input type="checkbox" :checked="todo.done" @change="handleCheck(todo.id)"/>
- <!-- 如下代码也能实现功能,但是不太推荐,因为有点违反原则,因为修改了props -->
- <!-- <input type="checkbox" v-model="todo.done"/> -->
- <span>{{todo.title}}</span>
- </label>
- <button class="btn btn-danger" @click="handleDelete(todo.id)">删除</button>
- </li>
- </template>
- <script>
- export default {
- name:'MyItem',
- //声明接收todo、checkTodo、deleteTodo
- props:['todo','checkTodo','deleteTodo'],
- methods: {
- //勾选or取消勾选
- handleCheck(id){
- //通知App组件将对应的todo对象的done值取反
- this.checkTodo(id)
- },
- //删除
- handleDelete(id){
- if(confirm('确定删除吗?')){
- //通知App组件将对应的todo对象删除
- this.deleteTodo(id)
- }
- }
- },
- }
- </script>
- <style scoped>
- /*item*/
- 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: #ddd;
- }
-
- li:hover button{
- display: block;
- }
- </style>
复制代码 MyList.vue
- <template>
- <ul class="todo-main">
- <MyItem
- v-for="todoObj in todos"
- :key="todoObj.id"
- :todo="todoObj"
- :checkTodo="checkTodo"
- :deleteTodo="deleteTodo"
- />
- </ul>
- </template>
- <script>
- import MyItem from './MyItem'
- export default {
- name:'MyList',
- components:{MyItem},
- //声明接收App传递过来的数据,其中todos是自己用的,checkTodo和deleteTodo是给子组件MyItem用的
- props:['todos','checkTodo','deleteTodo']
- }
- </script>
- <style scoped>
- /*main*/
- .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>
复制代码 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'
- import MyList from './components/MyList'
- import MyFooter from './components/MyFooter.vue'
- export default {
- name:'App',
- components:{MyHeader,MyList,MyFooter},
- data() {
- return {
- //由于todos是MyHeader组件和MyFooter组件都在使用,所以放在App中(状态提升)
- todos:JSON.parse(localStorage.getItem('todos')) || []
- }
- },
- 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)=>{
- return !todo.done
- })
- }
- },
- watch: {
- todos:{
- deep:true,
- handler(value){
- localStorage.setItem('todos',JSON.stringify(value))
- }
- }
- },
- }
- </script>
- <style>
- /*base*/
- 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>
复制代码
运行
免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。 |