基于Vue脚手架的web端简易待办

该文介绍了使用Vue1.0.0构建一个简单的待办事项应用,通过v-bind实现组件间的通信,包括添加、检查和删除待办任务的功能。应用由MyHeader、MyList、MyItem和MyFooter组件构成,每个组件都有明确的职责,事件处理在父组件App.vue中进行集中管理。
摘要由CSDN通过智能技术生成

注:本web端待办版本为1.0.0,实现原理仅有vue基础知识,利用v-bind实现组件间通信 

 展示效果如下:

待办 v1.0.0

项目架构

Vue的实例对象(vm)与VueComponent的实例对象(vc)之间的管理关系:

从内到外编写template代码,如下:

MyItem.vue的组件结构:

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

 MyList.vue:

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

MyHeader.vue:

<template>
  <div class="todo-header">
    <input type="text" placeholder="请输入你的任务名称,按回车确认" @keyup.enter="add" />
  </div>
</template>

MyFooter.vue:

<template>
  <div class="todo-footer" v-show="todos.length">
    <label>
      <!-- 对应methods配置中被注掉的写法 -->
      <!-- <input type="checkbox" :checked="isAll" @change="checkAll" /> -->
      <input type="checkbox" v-model="isAll" />
    </label>
    <span>
      <span>已完成{{ doneTotal }}</span> / 全部{{ todos.length }}
    </span>
    <button class="btn btn-danger" @click="clearAll">清除已完成任务</button>
  </div>
</template>

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>

要在父组件中使用子组件,首先要在父组件内引用子组件,语法如下:

// App.vue
import MyHeader from "./components/MyHeader.vue";
import MyList from "./components/MyList.vue";
import MyFooter from "./components/MyFooter.vue";

// MyList.vue
import MyItem from "../components/MyItem.vue";

此外,由于要存储加入的待办事件名和该事件是否已完成的信息,所以需要用数组+对象的形式存储信息。

又因为待办信息列表所有组件都要参与处理,所以把待办信息存放在App组件内更为合适。

App.vue的配置:

export default {
  components: { MyHeader, MyList, MyFooter },    //注册组件
  name: "App",
  data(){
    return{
        todos:[    //初始化待办列表
            {id:'001', title:'吃饭', done:true},
            {id:'002', title:'睡觉', done:false},
            {id:'003', title:'喝酒', done:true},
        ]
    }
  },
  methods:{
    // 添加一个todo
      addTodo(todoObj){
        // console.log('我是App组件,我收到了数据',x)
        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)=>{
          return todo.id !== id
        })
      },
      // 全选or全不选
      checkAllTodo(done){
        this.todos.forEach(todo => {
          todo.done = done
        });
      },
      // 清除所有已经完成的todo
      clearAllTodo(){
        this.todos = this.todos.filter((todo)=>{
          return !todo.done
        })
      }
    }
};

methods配置中写的函数都是拿来给其他组件传参时调用的,这些函数直接改变待办列表,其他组件中的函数都通过调用App.vue中的函数实现功能

因为用户要在MyHeader组件中的input框中向待办信息列表增加数据,所以要在MyHeader中写一个add()来调用App.vue中的addTodo(),此时App.vue中的addTodo()是要通过v-bind传递给MyHeader.vue的(详情见App.vue结构中MyHeader标签的v-bind)

所以,MyHeader.vue的配置如下:

export default {
  name: "MyHeader",
  methods:{
    add(e){
        // 校验数据(如果为空则提醒用户)
        if(e.target.value.trim() === '') return alert('输入不能为空')
        // 将用户的输入包装成一个todo对象(nonoid()是一个外部的随机uid生成库)
        const todoObj = {id:nanoid(), title:e.target.value, done:false}
        // console.log(todoObj)
        // 通知App组件添加一个todo对象(调用App组件的方法,将包装好的todoObj传递给App)
        this.addTodo(todoObj)
        // 清空输入(按照正常逻辑,回车以后输入框内的文字要清空)
        e.target.value = ''
    }
  },
  props:['addTodo']
};

同理,MyList.vue的配置如下:

export default {
  name: "MyList",
  components: { MyItem },
  props: ["todos", "checkTodo", "deleteTodo"]
};

MyItem.vue的配置如下:

export default {
  name: "MyItem",
  //   声明接收todo对象
  props: ["todo", "checkTodo", "deleteTodo"],
  methods: {
    handleCheck(id) {
      // console.log(id)
      // 通知App组件将todo对象的done值取反
      this.checkTodo(id);
    },
    handleDelete(id) {
      // 通知App组件将todo对象删除
      if (confirm("确定删除吗?")) {
        this.deleteTodo(id);
      }
    },
  },
};

MyFooter.vue的配置如下:

export default {
  name: "MyFooter",
  props: ["todos", "checkAllTodo", "clearAllTodo"],
  computed: {
    doneTotal() {
      // 下面一行非注释代码的完整写法
      // return this.todos.reduce((pre, current)=>{
      //   return pre + (current.done ? 1 : 0)
      // },0)
      return this.todos.reduce((pre, todo) => {
        return pre + (todo.done ? 1 : 0);
      }, 0);
    },
    isAll: {
      get() {
        return this.doneTotal === this.todos.length && this.doneTotal > 0;
      },
      set(value) {
        this.checkAllTodo(value);
      },
    },
  },
  methods: {
    // checkAll(e){
    //   this.checkAllTodo(e.target.checked)
    // },
    clearAll() {
      if (confirm("确定删除吗?")) {
        this.clearAllTodo();
      }
    },
  },
};

最后,再附上各个组件的样式代码:

App.vue

<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 o 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: #da4f49;
}

.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>

MyHeader.vue

<style lang="css" 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>

MyList.vue

<style scoped>
/* main */
.todo-main {
  margin-left: 0xp;
  border: 1px solid #ddd;
  border-radius: 2px;
  padding: 0px;
}

.todo-empty {
  height: 40px;
  border: 1px solid #ddd;
  border-radius: 2px;
  padding-left: 5px;
  margin-top: 10px;
}
</style>

MyItem.vue

<style lang="css" 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>

MyFooter.vue

<style lang="css" 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-top: 5px;
}

.todo-footer button {
  float: right;
  margin-top: 5px;
}
</style>

总结

这个待办项目其实没有很难,对于初学者(such as me)而言,需要做的就是理清项目结构页面结构、弄清楚组件间通信的方式与信息流,知道什么所有功能怎么实现的函数传了哪些参怎么传的,把这些弄清楚就够了。

至于页面结构和样式表这些,不在vue学习的重点内,大部分都是学vue之前都应该掌握的基础。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

superHappyman.

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值