项目总结:vue.js2.5饿了么APP(4)主要组件实现 - 购物车相关组件(下)

说明:本总结来源于慕课网 @ustbhuangyi老师的课程《Vue.js2.5+cube-ui重构饿了么App》课程,本博客做了项目总结梳理便于回顾,需要学习的伙伴可以移步学习。与君共勉!

之前章节传送: 

项目总结:vue.js2.5饿了么APP(1)概述+项目准备 

项目总结:vue.js2.5饿了么APP(2)主要组件实现 - 头部相关组件

项目总结:vue.js2.5饿了么APP(3)主要组件实现 - 购物车相关组件(上)

购物车组件主要包含(shop-cart ,shop-control , shop-cart-list , shop-cart-sticky)4个部分,本节主要讲解后两个,以及一些购物车相关优化设计 。


速看

shop-cart-list组件+shop-cart-sticky组件  当购物车中有商品时,点击购物车会弹出购物车详细内容,列表限制高度并可以滚动。(起初一期时没有将这一部分抽离出来,因此就在shop-cart组件的后半部分,通过一个标记来控制区块显示)二期,对于这种弹层类组件选择使用cube-ui的<popup>组件实现,并将其设置为API调用,设置hide() show()方法来控制蒙层显示。

但是有个问题:shop-cart-list是API组件,会动态的向body中挂载结点,因此层级较高,高过内层组件层级。但是购物车组件是一个子组件,因此会遮盖到原来的购物车组件(这个组件有个向上突出的部分)。由于无法调整层级,解决方法就是:使用shop-cart-sticky组件。动态向body中挂载一个购物车组件的副本。

并且这一部分还要加入的功能有:(加入购物车的小球动画,结算,清空购物车)对于小球下落动画,可以调用cart-control的drop方法,两个弹出dialog的操作都使用了cube-ui中的dialog组件。而对于切换tab的购物车联动问题,只需要在请求数据之前判断是否已经拿到数据。

最后,对于header和shop-cart-list的弹层组件中都用到的hide() show() 方法用mixin抽离出来做代码简化。


目录

一、购物车详情shop-cart-list组件(二期)

1. 概述

2. 布局

3. 实现

(1)popup组件使用

(2)过渡动画

 (3)列表滚动

(4)组件的使用

(5)添加弹出触发方法

(6)存在的问题

(7)商品购买时小球飞入动画

 (8)清空购物车

(9)结算按钮

(10)公共部分mixin抽离

(11)滑动页面购物车关联

二、购物车详情shopcart-list区块(一期)

1. 概述

2. 布局

3. 实现

(1)实现组件显示隐藏

(2)实现动画

 (3)蒙层的实现

三、shop-cart-sticky组件

1. 概括

2. 布局

3. 实现

四、createAPI详解

 


一、购物车详情shop-cart-list组件(二期)

1. 概述

当购物车中有商品时,点击购物车会弹出购物车详细内容,列表限制高度并可以滚动。(起初一期时没有将这一部分抽离出来,因此就在shop-cart组件的后半部分,通过一个标记来控制区块显示)二期,对于这种弹层类组件选择使用cube-ui的<popup>组件实现,并将其设置为API调用,设置hide() show()方法来控制蒙层显示。但是有个问题:shop-cart-list是API组件,会动态的向body中挂载结点,因此层级较高,高过内层组件层级。但是购物车组件是一个子组件,因此会遮盖到原来的购物车组件(这个组件有个向上突出的部分)。由于无法调整层级,解决方法就是:使用shop-cart-sticky组件。动态向body中挂载一个购物车组件的副本。

并且这一部分还要加入的功能有:(加入购物车的小球动画,结算,清空购物车)对于小球下落动画,可以调用cart-control的drop方法,两个弹出dialog的操作都使用了cube-ui中的dialog组件。而对于切换tab的购物车联动问题,只需要在请求数据之前判断是否已经拿到数据。

最后,对于header和shop-cart-list的弹层组件中都用到的hide() show() 方法用mixin抽离出来做代码简化。

2. 布局

当购物车中有商品时,点击购物车会弹出购物车详细内容。

布局:顶部是一个标题区+列表(有一个最大高度,超过最大高度列表可以滚动,如果列表高度不满足最大高度,只能被列表自身的高度撑高)

 <transition name="fade">
        <cube-popup>
            <transition name="move" @after-leave="onLeave">
                <div v-show="visible">
                    <div class="list-header">
                    <cube-scroll class="list-content" ref="listContent">
                        <ul>
                            <li class="food" v-for="food in selectFoods" :key="food.name">
                                <span class="name"></span>
                                <div class="price"></div>
                                <div class="cart-control-wrapper">
                                    <cart-control @add="onAdd" :food="food"></cart-control>
                                </div>
                            </li>
                        </ul>
                    </cube-scroll>
                </div>
            </transition>
        </cube-popup>
    </transition>

使用cube-scroll组件实现列表滚动,并且其中添加shop-control组件做商品的购买。

使用cube-ui将购物车组件变成一个createAPI模块的调用,并使用cube-popup弹层组件

3. 实现

(1)popup组件使用

<cube-popup>组件是所有弹层组件的基础组件. position是弹窗出现的置,:mask-closable=true点击蒙层会派发click=maskclick事件,而达到关闭的效果,z-index弹窗高这里的type类似于class,可以改变样式(设置bottom样式达到我们的效果),并通过v-show控制弹层显隐,以及驱动过渡动画<transition>。

<cube-popup
            v-show="visible"
            :mask-closable=true
            :z-index=90
            position="bottom"
            type="shop-cart-list"
            @mask-click="maskClick">
</cube-popup>

 (2)过渡动画

.cube-shop-cart-list
        bottom: 48px
        &.fade-enter, &.fade-leave-active
            opacity: 0
        &.fade-enter-active, &.fade-leave-active
            transition: all 0.3s ease-in-out
        .move-enter, .move-leave-active
            transform: translate3d(0, 100%, 0)
        .move-enter-active, .move-leave-active
            transition: all 0.3 ease-in-out

 (3)列表滚动

使用cube-scroll组件实现列表滚动,并且其中添加shop-control组件做商品的购买。

Data中添加visiable控制显示。提供两个方法show() hide()修改visiablemaskClick()方法控制其蒙层隐藏

(4)组件的使用

定义props有select Foods,从而使购物车中选择的商品通过props传递到shop-cart-list组件,

组件使用:将其变成API组件。 在register.js中修改,之后就可以通过API的方式调用。

import ShopCartList from 'components/shop-cart-list/shop-cart-list'
createAPI(Vue, ShopCartList)

(5)添加弹出触发方法

 当我们点击底部购物车时会显示这个组件。在shop-cart的content中添加一个点击事件toggleList,并添加该方法。

(添加辅助变量listfold默认为收起状态)在函数中,只有是收起状态才会展开,并且判断商品件数是否为0,不为零显示lsit(_showShopCartList),为零隐藏list(_hidwShopCartList)

toggleList() {
            if (this.listFold) {
                if (!this.totalCount) {
                    return
                }
                this.listFold = false
                this._showShopCartList()
                this._showShopCartSticky()
            } else {
                this.listFold = true
                this._hideShopCartList()
            }
        },

并且实现这两个私有方法,使用api调用组件(由于每次都是单例,因此做缓存)

_showShopCartList() {
            this.shopCartListComp = this.shopCartListComp || this.$createShopCartList({
                $props: {
                    selectFoods: 'selectFoods'
                },
                $events: {
                    hide: () => {
                        this.listFold = true
                    },
                    leave: () => {
                        this._hideShopCartSticky()
                    },
                    add: (el) => {
                        this.shopCartStickyComp.drop(el)
                    }
                }
            })
            this.shopCartListComp.show()
        },
 _hideShopCartList() {
            const comp = this.sticky ? this.$parent.list : this.shopCartListComp
            comp.hide && comp.hide()
        },

(6)存在的问题

但是有个问题就是,shop-cart-list是API组件,会动态的向body中挂载结点,因此层级较高,高过内层组件层级。但是购物车组件是一个子组件,因此会遮盖到原来的购物车组件。但是无法调整层级,

因此解决方法就是:使用shop-cart-sticky组件。动态向body中挂载一个购物车组件的副本。 shop-cart-sticky组件。

(7)商品购买时小球飞入动画

购物车小球,在shop-cat-list的cart-control点击需要向其父组件shop-cart组件派发onAdd()方法

<div class="cart-control-wrapper">
            <cart-control @add="onAdd" :food="food"></cart-control>
</div>

 onAdd(target) {
            this.$emit(EVENT_ADD, target)
        },

在shop-cart组件中监听该事件,在add中调用shop-cart-sticky组件的drop()方法,(其中需要在shop-cart-sticky中添加drop方法,并调用shop-cart的drop方法。)

_showShopCartList() {
            this.shopCartListComp = this.shopCartListComp || this.$createShopCartList({
                $props: {
                    selectFoods: 'selectFoods'
                },
                $events: {
                    hide: () => {
                        this.listFold = true
                    },
                    leave: () => {
                        this._hideShopCartSticky()
                    },
                    add: (el) => {
                        this.shopCartStickyComp.drop(el)
                    }
                }
            })
            this.shopCartListComp.show()
        },

 (8)清空购物车

点击清空按钮,添加click事件empty(), 调用cube-ui的dialog,遍历food将所有的count设置为0

empty() {
            this.$createDialog({
                type: 'confirm',
                content: '确认清空购物车吗?',
                $events: {
                    confirm: () => {
                        this.selectFoods.forEach((food) => {
                            food.count = 0
                        })
                        this.hide()
                    }
                }
            }).show()
        }
}

 当商品数量为0时,也要收回list,因此在shop-cart组件中添加watch,对于totalcount,当展开情况,并且值为0,将其关闭。

watch: {
        fold(newVal) {
            this.listFold = newVal
        },
        totalCount(newVal) {
            if (!this.listFold && !newVal) {
                this._hideShopCartList()
            }
        }
    },

(9)结算按钮

对于去结算按钮,添加点击事件pay(),使用cube-ui提供的dialog,当价格大于起送价,计算价格并显示 

pay(e) {
            if (this.totalPrice < this.minPrice) {
                return
            }
            this.$createDialog({
                title: '支付',
                content: `您需要支付${this.totalPrice}元`
            }).show()
            e.stopPropagation()
        },

(10)公共部分mixin抽离

     所有我们使用的弹层组件都包含了show() hide() 主要用途是修改visible的true false,因此可以抽象出这部分代码,在common中添加mixins文件夹popup.js,在shop-cart-list等中添加引用mixin:[popupMixin] 

const EVENT_SHOW = 'show'
const EVENT_HIDE = 'hide'

export default {
    data() {
        return {
            visible: false
        }
    },
    methods: {
        show() {
            this.visible = true
            this.$emit(EVENT_SHOW)
        },
        hide() {
            this.visible = false
            this.$emit(EVENT_HIDE)
        }
    }
}

(11)滑动页面购物车关联

对于滑动页面也可以实现购物车的联动问题:在tab组件中我们定义了一个onChange()方法

onChange(current) {
            this.index = current
            const component = this.$refs.component[current]
            component.fetch && component.fetch()
        },

 其中会执行组件的fetch方法,在goods组件中定义了这样一个fetch方法,每次都会getGoods,因此添加一个标记,只有当没有定义过时才会getGoods,如果已经获取过一次时就不用再次获取了。

fetch() {
            if (!this.fetched) {
                this.fetched = true
                getGoods().then((goods) => {
                this.goods = goods
            })
            }
        },

二、购物车详情shopcart-list区块(一期)

1. 概述

一期实现没有很复杂的部分就是需要添加计算属性listShow,判断totalCount没有商品时将fold = true,否则为true。点击触发togglelist方法控制该部分显示。

2. 布局

组件部分没有抽离出来,而是直接在shop-cart组件中写入,放在整个组件的最下部。

<div class="shopcart-list" v-show="listShow" transition="fold">
      <div class="list-header">
        <h1 class="title">购物车</h1>
        <span class="empty" @click="empty">清空</span>
      </div>
      <div class="list-content" v-el:list-content>
        <ul>
          <li class="food" v-for="food in selectFoods">
            <span class="name">{{food.name}}</span>
            <div class="price"><span>¥{{food.price*food.count}}</span></div>
            <div class="cartcontrol-wrapper">
              <cartcontrol :food="food"></cartcontrol>
            </div>
          </li>
        </ul>
      </div>
    </div>

3. 实现

(1)实现组件显示隐藏

添加一个fold默认值为true,添加计算属性listShow。判断this.totalCount没有商品fold = true,否则为true。

listShow() {
        if (!this.totalCount) {
          this.fold = true;
          return false;
        }
        let show = !this.fold;
        if (show) {
          this.$nextTick(() => {
            if (!this.scroll) {
              this.scroll = new BScroll(this.$els.listContent, {
                click: true
              });
            } else {
              this.scroll.refresh();
            }
          });
        }
        return show;
      }
    },

在点击content时触发事件togglelist,并实现方法,当点击list-mask可以收回shop-cart,添加事件listShow事件

toggleList() {
        if (!this.totalCount) {
          return;
        }
        this.fold = !this.fold;
      },

(2)实现动画

动画:向上滑动,添加transition=fold,此处点击事件是better-scroll组件派发的事件。

&.fold-transition
        transition: all 0.5s
        transform: translate3d(0, -100%, 0)
 &.fold-enter, &.fold-leave
        transform: translate3d(0, 0, 0)

 (3)蒙层的实现

该组件出现时背后是一个蒙层,因此在shop-cart组件同级下定义list-mask

<div class="list-mask" @click="hideList" v-show="listShow" transition="fade"></div>

并添加渐变效果和样式。模糊效果使用 backdrop-filter: blur(10px)

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

三、shop-cart-sticky组件

1. 概括

shop-cart-list是API组件,会动态的向body中挂载结点,因此层级较高,高过内层组件层级。但是购物车组件是一个子组件,因此会遮盖到原来的购物车组件。但是无法调整层级,因此解决方法就是:使用shop-cart-sticky组件。动态向body中挂载一个购物车组件的副本。而Sticky组件就是对chop-cart组件的简单包装。

2. 布局

Sticky组件就是对chop-cart组件的包装,把它需要的数据通过props传入,因此可以把sticky组件作为API调用就可以了。并且也包含hide() show()函数控制visible显隐。

<div class="shop-cart-sticky" v-show="visible">
        <shop-cart
            ref="shopCart"
            :selectFoods="selectFoods"
            :deliveryPrice="deliveryPrice"
            :minPrice="minPrice"
            :fold="fold"
            :sticky=true>
        </shop-cart>
    </div>

3. 实现

sticky组件要可以跟着页面的切换而保存,因此调用要添加在shop-cart组件的toggleList函数中(如上代码)并实现该私有函数,并接收props

_showShopCartSticky() {
            this.shopCartStickyComp = this.shopCartStickyComp || this.$createShopCartSticky({
                $props: {
                    selectFoods: 'selectFoods',
                    deliveryPrice: 'deliveryPrice',
                    minPrice: 'minPrice',
                    fold: 'listFold',
                    list: this.shopCartListComp
                }
            })
            this.shopCartStickyComp.show()
        },
_hideShopCartSticky() {
            this.shopCartStickyComp.hide()
        }

四、createAPI详解

 

注:购物车部分还有一些问题的解决还没有整理出来(特别是sticky部分)之后还会细致整理。

猜你喜欢

转载自blog.csdn.net/Sabrina_cc/article/details/106860347