vue外卖十九:商家详情-底部购物车组件,购物车相关vuex状态设计、相关计算、清空购物车+滚动购物车

45 篇文章 2 订阅

一、购物车基础

1)购物车状态设计cartFoods+mutation

在这里插入图片描述

store/state.js

// 所有要管理的状态数据:从页面需求分析出来,最好和api/index.js里的命名相同
export default{
  latitude: 40.10038, // 纬度
  longitude: 116.36867, // 经度
  address: {}, //地址相关信息对象
  categorys: [], // 食品分类数组
  shops: [], // 商家数组
  
  userInfo: {}, // 用户信息
  
  goods: [], // 商品列表
  ratings: [], // 商家评价列表
  info: {}, // 商家信息
  cartFoods: [], // 【1】购物车中食物的列表
  
}

1.用于存储加入到了购物车中的food对象,即food.count加了此属性的food,放入cartFoods中
2.重点:【1-2】处

src/store/mutations.js

// 加购物车数量
  [INCREMENT_FOOD_COUNT](state,{food}){
    if(!food.count){//如果不存在数量属性则加一个
      // food.count=1//此操作虽能在food中加一个count属性,但视图无法更新
      /*可更新视图的设置新属性:Vue.set()参数:
      对象
      属性名
      属性值
       */
      Vue.set(food,'count',1)
      
      // 【1】把加入了数量的食物,都放入cartFoods的状态中,用于购物车结算
      state.cartFoods.push(food)
    }else{//如果存在则直接加1
      food.count++
    }
  },
  // 减购物车数量
  [DECREMENT_FOOD_COUNT](state,{food}){
    if(food.count){//如果购物车数量>0才进行减操作,防止数量为负
      food.count--
      // 如果food.count数量减少至0了则从state.cartFoods中删除相应的food
      // 【2】splice(删除下标,删除数量);indexOf(food)返回对应对象的下标
      if(food.count===0){
        state.cartFoods.splice(state.cartFoods.indexOf(food),1)
      }
    }
  }

2)设计一个新状态用于计算加入购物车食物总数量(totalCount)、总价格(totalPrice)

getters类似computed:处理vuex中现有的状态,生成返回一个所有地方都可调用的新状态
知识点:getters的生成
reduce()函数两种返回累加值写法=>和return

store/getters.js

// getters类似computed:处理vuex中现有的状态,生成返回一个所有地方都可调用的新状态
export default{
    //【1】返回根据cartFoods中的food.count生成:购物车中食物的总数量,用于结算时使用
    totalCount(state){
        return state.cartFoods.reduce((total,food)=>{
        //【1.1】此处是用非箭头写法,需要返回一下值外面才能收到
           return total+food.count
        },0)
    },

    //【2】返回根据cartFoods中的food.count生成:购物车中食物总价格
    totalPrice(state){
    //【2.1】此处内部直接用=>total+food...代替return
        return state.cartFoods.reduce((total,food)=>total+food.count*food.price,0)
    },
    //或: 【2.2】以上也可用totalCount生成的getter来计算购物车总价:
    totalPrice2(state,getters){
        return state.cartFoods.reduce((total,food)=>total+getters.totalCount*food.price,0)
    }

}

3)购物车组件src/components/shopCart/shopCart.vue

<template>
<div>
  <div class="shopcart">
    <div class="content">
      <div class="content-left">
        <div class="logo-wrapper">
          <!--5】如果购物车里商品有数量则加高亮显示类名highlight -->
          <div class="logo " :class="{highlight:totalCount}">
            <!--12】点击显示购物车列表 -->
            <i class="iconfont icon-shopping_cart" :class="{highlight:totalCount}"
            @click="showCarts"></i>
          </div>
          <div class="num">{{totalCount}}</div>
        </div>
        <div class="price" :class="{highlight:totalCount}">{{totalPrice}}</div>
        <div class="desc">另需配送费¥{{info.deliveryPrice}}</div>
      </div>
      <div class="content-right">
        <!-- 【6】结合计算属性控制结算按钮颜色(/绿),显示文字(还差xx元起送/去结算) -->
        <div class="pay" :class="payClass">
          {{payText}}
        </div>
      </div>
    </div>
    <!--7】显隐购物车列表 需满足2个条件:1.isShow为true;且2.购物车总数不能为0;
      它们有一个为false就不显示列表,因此要用到计算属性showList对是否显示重新计算 -->
    <div class="shopcart-list" v-show="showList">
      <div class="list-header">
        <h1 class="title">购物车</h1>
        <span class="empty">清空</span>
      </div>
      <div class="list-content">
        <ul>
          <li class="food" v-for="(food,index) in cartFoods" :key="index">
            <span class="name">{{food.name}}</span>
            <div class="price"><span>{{food.price}}</span></div>
            <div class="cartcontrol-wrapper">
              <div class="cartcontrol">
                <CartControl :food="food"/>
              </div>
            </div>
          </li>
        </ul>
      </div>
    </div>
  </div>
  <!--8】购物车列表蒙版:用计算属性原因同上 -->
  <div class="list-mask" v-show="showList" @click="showCarts"></div>
</div>
</template>

<script>
  //【1】引入状态读取,getters读取助手函数
  import {mapState,mapGetters} from 'vuex'
  import CartControl from '../CartControl/CartControl' //购物车加减数量组件
  export default{
    data(){
      return{
        isShow:false, //【9】显隐购物车列表
      }
    },
    computed:{
      // 【2】引入需要的state、getters
      ...mapState(['cartFoods','info']), //购物车里的食物对象,商家相关信息如:食物的起送金等其它信息
      ...mapGetters(['totalCount','totalPrice']), //购物车食物总数量,购物车食物总价
      
      // 【3】购物车付款结算按钮:购物车总额>起送时显示样式:enough;
      // 小于时显示样式:not-enough;
      payClass(){
        const {totalPrice}=this
        const {minPrice}=this.info
        return totalPrice>=minPrice ? 'enough' : 'not-enough'
      },
      // 【4】购物车付款结算按钮:购物车没东西时显示:xx元起送;
      // 未达金额时显示:还差xx元起送;达金额则显示:去结算;
      payText(){
        const {totalPrice}=this
        const {minPrice}=this.info
        if(totalPrice===0){
          return `${minPrice}元起送`
        }else if(totalPrice<minPrice){
          return `还差${minPrice-totalPrice}元起送`
        }else{     
          return '去结算'
        }
      },


      // 【11】重新计算isShow,控制是否显示隐藏购物车列表
      showList(){
        /* 显示购物车列表条件:列表总数不为0 且 isShow也为true,才会显示购物车列表*/
        if(this.totalCount===0){
          /*isShow置否,防止在商家详情列表里点加时满足:count不为0 且 isShow也为true
          自动显示购物车列表*/
          this.isShow=false 
          return false //直接返回false让
        }
        // 不为0则原样返回
        return this.isShow
      },
    },
    methods:{
      //【10】显隐购物车列表
      showCarts(){
        // 大于0才显示:防止显示空列表
        if(this.totalCount>0){       
        this.isShow=!this.isShow
        }
      }
    },
    components:{
      CartControl
    }
  }
</script>

<style lang="stylus" rel="stylesheet/stylus" scoped>
  @import "../../common/stylus/mixins.styl"

  .shopcart
    position fixed
    left 0
    bottom 0
    z-index 50
    width 100%
    height 48px
    .content
      display flex
      background #141d27
      font-size 0
      color rgba(255, 255, 255, 0.4)
      .content-left
        flex 1
        .logo-wrapper
          display inline-block
          vertical-align top
          position relative
          top -10px
          margin 0 12px
          padding 6px
          width 56px
          height 56px
          box-sizing border-box
          border-radius 50%
          background #141d27
          .logo
            width 100%
            height 100%
            border-radius 50%
            text-align center
            background #2b343c
            &.highlight
              background $green
            .icon-shopping_cart
              line-height 44px
              font-size 24px
              color #80858a
              &.highlight
                color #fff
          .num
            position absolute
            top 0
            right 0
            width 24px
            height 16px
            line-height 16px
            text-align center
            border-radius 16px
            font-size 9px
            font-weight 700
            color #ffffff
            background rgb(240, 20, 20)
            box-shadow 0 4px 8px 0 rgba(0, 0, 0, 0.4)
        .price
          display inline-block
          vertical-align top
          margin-top 5px
          line-height 24px
          padding-right 12px
          box-sizing border-box
          font-size 16px
          font-weight 700
          color #fff
          &.highlight
            color #fff
        .desc
          display inline-block
          vertical-align bottom
          margin-bottom 15px
          margin-left -45px
          font-size 10px
      .content-right
        flex 0 0 105px
        width 105px
        .pay
          height 48px
          line-height 48px
          text-align center
          font-size 12px
          font-weight 700
          color #fff
          &.not-enough
            background #2b333b
          &.enough
            background #00b43c
            color #fff
    .ball-container
      .ball
        position fixed
        left 32px
        bottom 22px
        z-index 200
        transition all 0.4s cubic-bezier(0.49, -0.29, 0.75, 0.41)
        .inner
          width 16px
          height 16px
          border-radius 50%
          background $green
          transition all 0.4s linear
    .shopcart-list
      position absolute
      left 0
      top 0
      z-index -1
      width 100%
      transform translateY(-100%)
      &.move-enter-active, &.move-leave-active
        transition transform .3s
      &.move-enter, &.move-leave-to
        transform translateY(0)
      .list-header
        height 40px
        line-height 40px
        padding 0 18px
        background #f3f5f7
        border-bottom 1px solid rgba(7, 17, 27, 0.1)
        .title
          float left
          font-size 14px
          color rgb(7, 17, 27)
        .empty
          float right
          font-size 12px
          color rgb(0, 160, 220)

      .list-content
        padding 0 18px
        max-height 217px
        overflow hidden
        background #fff
        .food
          position relative
          padding 12px 0
          box-sizing border-box
          bottom-border-1px(rgba(7, 17, 27, 0.1))
          .name
            line-height 24px
            font-size 14px
            color rgb(7, 17, 27)
          .price
            position absolute
            right 90px
            bottom 12px
            line-height 24px
            font-size 14px
            font-weight 700
            color rgb(240, 20, 20)
          .cartcontrol-wrapper
            position absolute
            right 0
            bottom 6px

  .list-mask
    position fixed
    top 0
    left 0
    width 100%
    height 100%
    z-index 40
    backdrop-filter blur(10px)
    opacity 1
    background rgba(7, 17, 27, 0.6)
    &.fade-enter-active, &.fade-leave-active
      transition all 0.5s
    &.fade-enter, &.fade-leave-to
      opacity 0
      background rgba(7, 17, 27, 0)
</style>

4)商家详情调用购物车组件src/pages/shop/goods/goods.vue

...略过
<!-- 底部购物车组件 -->
        <ShopCart /> 
      </div>
      <!-- ref是标识此子组件:用于调用其内部的toggleShow()展示隐藏食物详情;
        :food向子组件传当前food对象 -->
      <Food :food='food' ref="food" />
  </div>
</template>

import ShopCart from '../../../components/ShopCart/ShopCart.vue' //底购物车按钮

export default{
...
components:{
      CartControl,
      Food,
      ShopCart,
    }
}

5)效果:http://localhost:8080/#/shop/goods

0.点购物车图标显隐购物车列表
并能自动计算相关数值:

  1. 右结算按钮:满xx元配送 / 差xx配送 / 去结算
  2. 总费用计算
  3. 食品总数量计算 左侧红点
  4. 点蒙板也可关闭列表

在这里插入图片描述

二、清空购物车+滚动购物车

0.store/state.js

// 所有要管理的状态数据:从页面需求分析出来,最好和api/index.js里的命名相同
export default{
  cartFoods: [], // 购物车中食物的列表
}

1.store/mutation-types.js

export const CLEAR_CART = 'clear_cart' // 清空购物车

2.mutations.js

import {
	//【1】  引入类型
  CLEAR_CART,
} from './mutation-types.js'
import Vue from 'vue' //用于新增一个状态的属性,并能自动更新视图

export default{
	...略过
	//【2】清空购物车
  [CLEAR_CART](state){
    // 把购物车状态中的count全部置为0
    state.cartFoods.forEach(food=>food.count=0)
    // 把购物车清空
    state.cartFoods=[]
  }

}

3.actions.js

// 控制mutations
import {
略过...
  CLEAR_CART //【1】
} from './mutation-types.js'
import {
略过... //ajax请求
} from '../api/index.js'

export default{
	略过...

	// 【2】清空购物车
	clearCart({commit}){
		commit(CLEAR_CART)
	}
	
}

4.调用清空购物车components/shopCart/shopCart.vue

//【1】
<span class="empty" @click="clearCart">清空</span>


import { MessageBox } from 'mint-ui' //确认提示框组件

methods:{
      略过...
      //【1】调用vuex的action触发:mutation清空cartFoods[]和food.count数量[实现清空购物车]
      clearCart(){
        /*min-ui组件MessageBox.confirm('确定吗')
        .then(fn1,fn2)fn1是点确定时执行的操作,fn2:取消时操作*/
        MessageBox.confirm('确认清空购物车吗').then(action=>{
          this.$store.dispatch('clearCart')
        },()=>{})    
      }

5. 购物车列表的滚动better-scroll components/shopCart/shopCart.vue

import BScroll from '@better-scroll/core' //滑动库


methods:{
// 【11】重新计算isShow,控制是否显示隐藏购物车列表
      showList(){
        /* 显示购物车列表条件:列表总数不为0 且 isShow也为true,才会显示购物车列表*/
        if(this.totalCount===0){
          /*isShow置否,防止在商家详情列表里点加时满足:count不为0 且 isShow也为true
          自动显示购物车列表*/
          this.isShow=false 
          return false //直接返回false让
        }


        //【重点】如果显示了就创建一个Bscroll滑动对象
        if(this.isShow) {
          this.$nextTick(() => {
            // 实现BScroll的实例是一个单例,
            // 如果创建多个就会导致里面的加减按钮一次加减多个食物数量
            if(!this.scroll) { //如果滚动对象不存在执行:
              this.scroll = new BScroll('.list-content', {
                click: true
              })
            } else {
              // 让滚动条刷新一下: 重新统计内容的高度解决第一次无法滚动问题
              this.scroll.refresh() 
            }

          })
        }

        // 不为0则原样返回
        return this.isShow
      }
      
}

效果:购物车列表即可滚动

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值