天天看点

底部购物车(美团外卖效果)

1、实现列表数据和底部购物车数据共享:
	(1)vuex的state添加一个属性,用来存储购物车列表的数据,购物车列表的每一项都是商品对象
	
	(2)当点击增加按钮时,往当前商品对象中添加count数据:
	   利用vuex,当点击增加/减少按钮时,将当前物品对象中添加一个count属性,
	   因为是对象的引用关系,如果vuex之前的state状态中已经存在商品列表的数据,无需再添加额外的state状态,
	   当前对象改变属性(利用Vue.set,将对象的新属性交给vue来动态绑定数据),商品列表中的该元素也会相应改变
	   添加count属性后,即可根据加减来改变count属性的值,从而实现获得该商品的数量
	   
	(3)vuex创建对应的方法来进行第(2)步
		actions.js中创建方法来调用mutations.js内的方法来使得状态增/减,方法的传入当前对象
		 调用actions.js:this.$store.dispatch('xx',{对象:xx}); 
		 actions.js:  xx({commit},{对象})
					  {
					      commit(RECEIVE_XXX,{对象});
					  },
		 mutations.js:
			  [RECEIVE_XXX](state,{对象})
			  {
			    if(对象.count)
			    {
			      food.count++;
			    }else{
			       state.购物车列表.push(对象); //增加时往购物车列表添加该商品对象,相同商品只改变了count属性,所以每个商品只需要添加一次
			       Vue.set(对象,'count',1); //让对象新增的数据能够被vue数据绑定,动态更新
			    }
			}
			
	(4)通过state中的购物车列表的内容,实现底部购物车的数据展示
		因为需要实时更新,且建立在已有数据state上
		在getters.js中定义计算属性方法,通过遍历购物车列表的每一个对象中的count,来计算数量和总价
			  totalCount(state)
			  {
			    return state.购物车列表.reduce((preTotal,food)=>{return preTotal+food.count},0)
			  },
			  totalPrice(state)
			  {
			    return state.购物车列表.reduce((preTotal,food)=>{return preTotal+food.count*food.price},0)
			    
			  }
			  
	(5)将vuex状态引入到组件,实现对应功能
	
	(6)清除购物车
		遍历购物车列表,将所有对象的count属性值置为0,然后将整个列表置空(=[ ])

对象的引用:
		多个对象引用同一个对象,其中一个改变,都会改变
		多个对象引用同一个对象,其中一个指向另一个对象,其他对象不变
           

代码示例:

state.js:

export default{
  address:"", //地址
  categorys:[],//食品分类
  shops:[],//商家
  userInfo:{}, //用户信息
  goods:[], //商品列表
  ratings:[], //商家评价
  info:[] ,//商家信息
  cartFoods:[] //购物车中的商品列表
}

           

actions.js

import {
  RECEIVE_ADDRESS,
  RECEIVE_CATEGORYS,
  RECEIVE_SHOPS,
  RECEIVE_USERINFO,
  RECEIVE_RESET,
  RECEIVE_GOODS,
  RECEIVE_RATINGS,
  RECEIVE_INFO,
  RECEIVE_ADD,
  RECEIVE_DELETE
} from './mutation-types'

import axios from 'axios';

export default{

  //同步更新food中的count数量,增加
  updateFoodCount({commit},{food})
  {
      commit(RECEIVE_ADD,{food});
  },
  //同步更新food中的counte数量,减少
  updateFoodCountDelete({commit},{food})
  {
      commit(RECEIVE_DELETE,{food});
  }
}

           

mutations.js:

import {
  RECEIVE_ADDRESS,
  RECEIVE_CATEGORYS,
  RECEIVE_SHOPS,
  RECEIVE_USERINFO,
  RECEIVE_RESET,
  RECEIVE_GOODS,
  RECEIVE_RATINGS,
  RECEIVE_INFO,
  RECEIVE_ADD,
  RECEIVE_DELETE
} from './mutation-types'

import Vue from 'vue'

export default{

  [RECEIVE_ADD](state,{food})
  {
    if(food.count)
    {

      food.count++;

    }else{
       state.cartFoods.push(food); //增加时往购物车列表添加该商品对象,相同商品只改变了count属性,所以每个商品只需要添加一次
       Vue.set(food,'count',1); //让对象新增的数据能够被vue数据绑定,动态更新
    }
  },
  [RECEIVE_DELETE](state,{food})
  {
    if(food.count)
    {
      food.count--; //对象引用,使得属性值减1
      
      if(food.count==0)
      {
        state.cartFoods.splice(state.cartFoods.indexOf(food),1);
      }
      
    }
  }

}

           

getters.js:

export default{
  totalCount(state) //返回数量
  {
    return state.cartFoods.reduce((preTotal,food)=>{return preTotal+food.count},0)
  },
  totalPrice(state)//返回总价
  {
    return state.cartFoods.reduce((preTotal,food)=>{return preTotal+food.count*food.price},0)
    
  }
}

           

页面:

<template>
  <div class='bf'>
    <transition name='wave'>

      <div class='blist' v-show='show'> //展开列表
        <div class='b1'>
          <span>购物车</span>
          <span>清空</span>
        </div>
			
		//展开列表,根据购物车列表的状态中是否有数据来渲染
        <ul class='b2' v-for='(item,index) in cartFoods' :key='index' >

          <li class='b3'>
            <span>{{item.name}}</span>

            <div class='b4'>
              <span>¥{{item.price}}</span>
              <div class='lz'>
                <div class='cir' @click='del(item)'>
                  -
                </div>
                <span class='cp'>{{item.count}}</span>
                <div class='cir' @click='add(item)'>
                  +
                </div>
              </div>
            </div>
          </li>

        </ul>
      </div>
    </transition>

    <div class='bs'>
    
      <div class='bl'>
        <div class='bimg' @click='change'>
          <img src="./imgs/1.png" alt="">
        </div>
        <div class='binfo'>
          <p>¥{{totalPrice}}元</p> //根据vuex的计算属性返回总价
          <p>另需配送费¥{{info.deliveryPrice}}元</p>
        </div>
      </div>
      
      <div class='br' :class='{on:totalPrice>=info.minPrice}'>//根据vuex的计算属性计算价钱
        {{totalPrice?totalPrice>=info.minPrice?'去结算':'还差'+(info.minPrice-totalPrice)+'元起送':`¥${info.minPrice}元起送`}}
      </div>
      
      <!-- 购物车图标上的数量提示,根据vuex的购物车列表是否有值显示 -->
      <div class='balert' v-show='totalCount'>
        {{totalCount}}
      </div>
    </div>

  </div>
</template>

<script>

  import {mapState,mapGetters} from 'vuex';
  export default {
      data()
      {
        return{
          show:false
        }
      },
      methods: {
        change() {
          this.show=!this.show;
        },
        del(food) //减少count
        {
          this.$store.dispatch('updateFoodCountDelete',{food})
        },
        add(food) //增加count
        {
          this.$store.dispatch('updateFoodCount',{food})
        }
      },
      computed: {
        ...mapState(['cartFoods','info']),
        ...mapGetters(['totalCount','totalPrice'])
      }
  }

</script>

<style lang='less'>
  .on{
    background-color: #44BB00!important;
  }
  .bf{
    width: 100%;
    position: fixed;
    bottom: 0px;
    .blist{
      background-color:#F3F5F7;
      .b1{
        display: flex;
        justify-content: space-between;
        padding: 0 20px;
        height: 40px;
        line-height: 40px;
        border: solid 1px #ccc;
        span:last-child{
          padding: 0 10px;
          border-left: solid 1px #ccc;
          border-right: solid 1px #ccc;
        }
      }
      .b2{
        padding-bottom: 10px;
        background-color: white;
        .b3{
          height: 30px;
          line-height: 30px;
          border-bottom:solid 1px #ccc;
          padding:0 20px ;
          display: flex;
          justify-content: space-between;

          .b4{
            display: flex;
            >span{
             color:red
            }
            .lz{
              display: flex;
              width: 70px;
              justify-content: space-evenly;
              align-items: center;
              .cir{
                text-align: center;
                line-height: 20px;
                width: 20px;
                height: 20px;
                color: white;
                background-color: #008000;
                border-radius: 100px;

              }
              .cp{
                font-size: 10px;
              }
            }
          }
        }
      }

    }

    .bs{
      color: white;
      background-color: #131D29;
      height: 50px;
      width: 100%;
      display: flex;
      justify-content: space-between;
      position: relative;
      .bl{
        display: flex;
        .bimg{
          height: 45px;
          width: 45px;
          border-radius: 100px;
          margin-top: -10px;
          background-color: #131D29;
          text-align: center;
          font-size: 0px;
          line-height: 45px;
          margin-left: 20px;
          img{
            height: 40px;
            width: 40px;
            vertical-align: middle;
          }
        }
        .binfo{
          display: flex;
          flex-direction: column;
          justify-content: space-around;
          margin-left: 10px;
          p:last-child{
            color:#ccc;
            font-size: 12px;
          }
        }
      }
      .br{
        height: 50px;
        line-height: 50px;
        background-color: #2B333D;
        padding:0 10px;
        color: white;
      }

      .balert{
        background-color: red;
        height: 20px;
        width: 20px;
        line-height: 20px;
        border-radius: 100px;
        text-align: center;
        position: absolute;
        left: 45px;
        top:-10px;

      }
    }
  }

  .wave-enter-to,.wave-leave{
     transform:translateY(0)
  }
  .wave-enter,.wave-leave-to{
  		transform:translateY(100%)
  	}
  .wave-enter-active,.wave-leave-active{

    transition: transform 1s;
  }

</style>

           

效果图:

底部购物车(美团外卖效果)