vue组件——购物车案例

851 阅读3分钟

购物车案例

  1. 项目准备

    • 创建目录文件

      vue create shopping-cart-demo
      
  2. 相关代码

    • App.vue
    <template>
      <div id="app">
        <h1>{{title}}</h1>
        <hr>
        <div>
          <h2>添加课程</h2>  
          <div>
            <label for="">课程名称:</label>
            <input type="text" v-model="courseInfo.name">  
          </div> 
          <div>
            <label for="">课程价格:</label>
            <input type="text" v-model="courseInfo.price">
          </div>
          <div>
            <button @click="addCourseToList">添加课程</button>
          </div>
          <table>
            <tr>
              <th>课程名称</th>
              <th>课程价格</th>
              <th>操作</th>
            </tr>
            <tr v-for="(item,index) in courseList" :key="item.id"> 
              <td>{{item.name}}</td>
              <td>{{item.price}}</td>
              <td><button @click="addCourseToCart(index)">添加到购物车</button></td>
            </tr>
            
          </table>
        </div>
        <cart :courseItem="courseItem" @removeItem="remove"></cart>
      </div>
    </template>
    
    <script>
    import Cart from './components/Cart.vue'
    
    export default {
      name: 'App',
      components: {
        Cart,
      },
      data(){
        return{
          title:"购物车",
          courseItem:[],
          courseInfo:{
            name:'',
            price:''
          },
          courseList:[
            {
              name:"sin",
              price:123
            },
            {
              name:"txm",
              price:123
            }
          ]
        }
      },
      methods:{
        addCourseToList(){
          this.courseList.push(this.courseInfo)
        },
        addCourseToCart(index){
          let item = this.courseList[index];
          let isHasCourse = this.courseItem.find(x=>x.name == item.name)
          if(isHasCourse){
            isHasCourse.number +=1;
          }
          else{
             this.courseItem.push({
               ...item,
               number:1,
               isActive:true
             })
          }
        },
        remove(index){
          this.courseItem.splice(index,1)
        }
      }
    }
    </script>
    
    <style>
    #app {
      font-family: Avenir, Helvetica, Arial, sans-serif;
      -webkit-font-smoothing: antialiased;
      -moz-osx-font-smoothing: grayscale;
      text-align: center;
      color: #2c3e50;
      margin-top: 60px;
    }
    </style>
    
    
    • Cart.vue

      <template>
          <div>
              <h2>购物车</h2>
              <table>
                          <div>
                  <tr>
                      <th>勾选</th>
                      <th>课程</th>
                      <th>数量</th>
                      <th>价格</th>
                  </tr>
                  <tr v-for="(item,index) in courseItem " :key='index'>
                      <td>
                          <input type="checkbox" v-model="item.isActive">
                      </td>
                      <td>{{item.name}}</td>
                      <td>
                          <button @click="min(index)">-</button>
                          {{item.number}}
                          <button @click="add(index)">+</button>
                      </td>
                      <td>{{item.price*item.number}}</td>
                  </tr>
                  <tr>
                      <td></td>
                      <td colspan='2'>{{isActiveCourse}}/{{allCourseList}}</td>
                      <td colspan='2'>{{AllPrice}}</td>
                      //colspan 属性规定单元格可横跨的列数。
                  </tr>
              </div>
      
              </table>
          </div>
      </template>
      
      <script>
      export default {
          props:['courseItem'],
          methods:{
              min(index){
                  let number = this.courseItem[index].number
                  if(number>1){
                      this.courseItem[index].number -=1
                  }else{
                      if(window.confirm('确定要删除吗')){
                          this.$emit('removeItem',index)
                      }
                  }
              },
              add(index){
                  this.courseItem[index].number +=1
              }
          },
          computed:{
              isActiveCourse(){
                  return this.courseItem.filter(item=>item.isActive).length
              },
              allCourseList(){
                  return this.courseItem.length;
              },
              AllPrice(){
                  let number = 0 
                  this.courseItem.forEach(item => {
                      if(item.isActive){
                          number +=item.price*item.number
                      }
                  });
                  return number
              },
          }
      }
      </script>
      
      <style>
      
      </style>
      
  3. 思路

    • 添加课程

      使用v-model实现双向数据绑定,获取input内的内容。

      通过@click="addCourseToList"点击事件,实现:

      methods:{
      
       addCourseToList(){
      
        this.courseList.push(this.courseInfo)
      
       },
      
      }
      
      方法,讲数据添加到courseInfo中:
      
      data(){
      
       return{
      
        courseInfo:{
      
         name:'',
      
         price:''
      
        },
      
      }
      
      }
      
      • push()

        push() 方法可向数组的末尾添加一个或多个元素,并返回新的长度。要想数组的开头添加一个或多个元素,使用 unshift() 方法。

    • 添加购物车

      首先创建一个购物车组件Cart.vue。

      然后在App.vue中使用该组件:

      • 父向子传值— v-bind props:[]:

      在App.vue中使用v-on动态绑定courseItem,在子组件中使用:props:['courseItem'],来接收数据

      给按钮添加点击事件<button @click="addCourseToCart(index)">添加到购物车,将数组下标传入。然后通过方法将数据添加到数组中:

      addCourseToCart(index){
      
         let item = this.courseList[index];
      
         let isHasCourse = this.courseItem.find(x=>x.name == item.name)
         //判断传入的数据是否已经存在
      
         if(isHasCourse){
      
          isHasCourse.number +=1;
      
         }
      
         else{
      
           this.courseItem.push({
      
           ...item,
      
            number:1,
      
           isActive:true
      
          })
      
         }
      
        },
      
      将表格中的数据添加到courseItem数组中:
      
       data(){
      
        return{
      
         courseItem:[],
      
         }
      
      }
      
      • Array.find()

        数组实例的find方法,用于找出第一个符合条件的数组成员。它的参数是一个回调函数,所有数组成员依次执行该回调函数,直到找出第一个返回值为true的成员,然后返回该成员。如果没有符合条件的成员,则返回undefined。

        [1, 4, -5, 10].find((n) => n < 0)
        // -5
        

        find方法的回调函数可以接受三个参数,依次为当前的值、当前的位置和原数组。

        [1, 5, 10, 15].find(function(value, index, arr) {
          return value > 9;
        }) // 10
        
      • 扩展语法— ...

        function sum(x, y, z) {
          return x + y + z;
        }
        
        const numbers = [1, 2, 3];
        
        console.log(sum(...numbers));
        // expected output: 6
        
    • 购物车数据处理

      • 数量加减

        首先给数量两边加上加减按钮,并添加点击事件:

                        <td>
                            <button @click="min(index)">-</button>
                            {{item.number}}
                            <button @click="add(index)">+</button>
                        </td>
        

        然后通过方法控制数量:

        methods:{
                min(index){
                    let number = this.courseItem[index].number
                    if(number>1){
                        this.courseItem[index].number -=1
                    }else{
                        if(window.confirm('确定要删除吗')){
                            this.$emit('removeItem',index)
                            // removeItem事件名称,index传递的值
                        }
                    }
                },
                add(index){
                    this.courseItem[index].number +=1
                }
            },
        
        • window.confirm(message)

          confirm()方法用于显示一个带有指定消息和确认及取消按钮的对话框。

          如果访问者点击"确定",此方法返回true,否则返回false。

        • 子向父传值—v-on $emit

          在子组件上添加一个删除的点击事件,事件名称为$emit中定义的名称:

              <cart :courseItem="courseItem" @removeItem="remove"></cart>
          

          实现删除方法:

              remove(index){
                this.courseItem.splice(index,1)
              }
          
      • 勾选数量,价格汇总

        这里需要用到计算属性computed监听数据的变化:

                    <tr>
                        <td></td>
                        <td colspan='2'>{{isActiveCourse}}/{{allCourseList}}</td>
                        <td colspan='2'>{{AllPrice}}</td>
                    </tr>
        

        isActiveCourse为当前勾选的个数,allCourseList为总个数,AllPrice为总价格。

            computed:{
                isActiveCourse(){
                    return this.courseItem.filter(item=>item.isActive).length
                },
                allCourseList(){
                    return this.courseItem.length;
                },
                AllPrice(){
                    let number = 0 
                    this.courseItem.forEach(item => {
                        if(item.isActive){
                            number +=item.price*item.number
                        }
                    });
                    return number
                },
            }
        
        • filter

          filter() 方法创建一个新的数组,新数组中的元素是通过检查指定数组中符合条件的所有元素。 注意: filter() 不会对空数组进行检测。 注意: filter() 不会改变原始数组。

          • filter函数的参数:

            // filter函数的参数
            let arr = ['a', 'b', 'c'] 
            let array = arr.filter((item, index, self) => {
                console.log(item, index, self) 
                //依次返回   a 0 (3) ["a", "b", "c"]   b 1 (3) ["a", "b", "c"]         c 2 (3) ["a", "b", "c"]
                //即第一个参数是里面的元素,第二个参数为元素索引值,第三个参数为数组本身
             })