创建购物车组件,引入并注册到goods组件中,在模板中添加shopCart购物车的初等样式。
先写结构:分左右两侧,左侧自适应,右侧部分为20元起送状态,固定宽度,使用flex布局
因为后来给shopcart添加了一个同级罩层mask,这样一个template下边就有两个div了,所以在这两个div的外边添加一个父层div
<template>
<!-- shopcart是goods组件的一部分,要将其引入到goods组件中,template下只能有一个div,所以添加一个div做最外层 -->
<div>
<div class="shopcart">
<div class="content" @click="toggleList"> <!-- 详情界面-->
<div class="content-left"> </div>
<div class="content-right" @click.stop.prevent="pay"> </div>
</div>
</div>
</div>
</template>
<div class="content-left">
<div class="logo-wrapper"> <!-- logo 区块-->
<div class="logo" :class="{'highlight': totalCount > 0}">
<span class="icon-shopping_cart" :class="{'highlight': totalCount > 0}"></span> <!-- icon.stylus中引入-->
</div>
<!-- 购物车中有东西时才出现-->
<div class="num" v-show="totalCount > 0">{{totalCount}}</div>
</div>
<div class="price" :class="{'highlight': totalCount > 0}">¥{{totalPrice}}元</div>
<div class="desc">另需配送费¥{{deliveryPrice}}元</div>
</div>
上边是左侧,下边是右侧
<!-- 点击去结算,结算过后因为事件冒泡的原因(pay按钮是content详情界面的子节点),详情列表会被展开,用stop.prevent阻止事件冒泡-->
<div class="content-right" @click.stop.prevent="pay">
<div class="pay" :class="payClass"> <!-- 在这里做一个状态的判断-->
{{payDesc}}
</div>
</div>
css样式:
.shopcart
position fixed
left 0
bottom 0
z-index 50
width 100%
height 48px
.content
display flex
background #141d27
font-size 0 //解决inline-block的缝隙问题
.content-left
flex 1 //自适应
.content-right
flex 0 0 105
width 105px
接着写content-left部分的css,写样式之前先把购物车样式中用到的配送费和起送价通过父组件goods中传过来,并在购物车组件中用props接收
<!-- props将seller的属性传进来,数据都存在data.json中 -->
<shopcart :delivery-price="seller.deliveryPrice" :min-price="seller.minPrice"></shopcart>
props: {
deliveryPrice: {
type: Number,
default: 0
},
minPrice: {
type: Number,
default: 0
}
}
其中,seller数据也是从goods的父组件App.vue中传递过来的,在App.vue中,商品、评论、商家谁被渲染就把seller的值传递给那个组件
<!-- 路由出口 -->
<!-- 路由匹配到的组件将渲染在这里 -->
<router-view :seller="seller"></router-view>
此时,接着写购物车的content-left部分,左侧部分的logo-wrapper,price和desc都是inliinline-block布局,
.content-left
flex 1 //自适应
.logo-wrapper // 包裹着logo图片和上侧num的一个wrapper,圆形,相对于content有一个高出的部分
display inline-block
vertical-align top
position relative
top -10px
margin 0 12px
padding 6px
width 56px
height 56px
box-sizing border-box // 宽度包括了padding在内
border-radius 50%
background #141d27 //不设置颜色默认透明不显示
.logo
width 100%
height 100%
border-radius 50%
text-align center
background #2b343c
&.highlight
background rgb(0,160,220)
.icon-shopping_cart
line-height 44px //88/2=44
font-size 24px
color #80858a
&.highlight
color #ffffff
.price
display inline-block
vertical-align top
margin-top 12px
line-height 24px
box-sizing border-box
padding-right 12px
border-right 1px solid rgba(255,255,255,.1)
font-size 16px
font-weight 700px
color rgba(255,255,255,.4)
&.highlight
color #ffffff
.desc
display inline-block
vertical-align top
margin 12px 0 0 12px
line-height 24px
font-weight 700
font-size 10px
color rgba(255,255,255,.4)
右侧,多少元起送部分,够不够起送费部分是要改变颜色的,在之后的部分会写到
.content-right
flex 0 0 105
width 105px
.pay
height 48px
line-height 48px
text-align center
font-size 12px
color rgba(255,255,255,.4)
font-weight 700px
background #2b333b
&.not-enough
background #2b333b
&.enough
background #00b43c
color #ffffff
购物车中的数据应该来自goods组件中,商品在goods组件中被选中就将数据传递到购物车组件中,所以我们要在props中委会一个来自goods组件的selectedFoods数组,用来存放在goods组件中被选中的商品,这里先假设了里面的值,包括单价和数量
props: {
selectFoods: { //保存一个被选择商品的数组,包括被选择商品的单价和数量
type: Array,
default() { // 当type是一个数组时,defalut就是一个函数
return [{
// price: 10,
// count: 2
}];
}
}
购物车中totalPrince的计算是依赖于selectFoods,我们使用计算属性计算总价格,并更新html中的值
computed: {
totalPrice() { //计算总价格
let total = 0;
this.selectFoods.forEach((food) => {
total += food.price * food.count;
});
return total;
}
}
<div class="price" :class="{'highlight': totalCount > 0}">¥{{totalPrice}}元</div>
当有商品选入购物车后,首先购物车颜色变化,右上角出现显示数量的num,totalPrince颜色加深,还差()元起送的值发生变化,当达到起送价格时会变为去结算的状态
首先计算totalCount
totalCount() { //计算被选中商品的数量
let count = 0;
this.selectFoods.forEach((food) => {
count += food.count;
});
return count;
}
计算到商品总数之后,回到购物车DOM部分添加数字角标
<div class="logo-wrapper"> <!-- logo 区块-->
<div class="logo" :class="{'highlight': totalCount > 0}">
<span class="icon-shopping_cart" :class="{'highlight': totalCount > 0}"></span> <!-- icon.stylus中引入-->
</div>
<!-- 购物车中有东西时才出现-->
<div class="num" v-show="totalCount > 0">{{totalCount}}</div>
</div>
添加num的样式,当total>0时,购物车logo背景色和小车的颜色改变
.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 700px
color #ffffff
background rgb(240,20,20)
box-shadow 0 4px 8px 0 rgba(0,0,0,.4)
当total>0时,购物车totalPrice颜色改变,在css中添加相应的highlight样式
<div class="price" :class="{'highlight': totalCount > 0}">¥{{totalPrice}}元</div>
右侧结算部分有三种状态,()元起送,还差()元起送,结算,状态的描述依赖于selectFoods的变化,用payDesc来描述,返回一个描述
payDesc() {
if (this.totalCount === 0) { //不要忘了加this
return `¥${this.minPrice}元起送`;
} else if (this.totalPrice < this.minPrice) {
let diff = this.minPrice - this.totalPrice;//忘了加this
return `还差¥${diff}元起送`;//不要忘了加$
} else {
return '去结算';
}
}
反映到模板里,而且达到起送状态时会改变颜色,添加动态的payClass类(返回css类名)
<div class="content-right" @click.stop.prevent="pay">
<div class="pay" :class="payClass"> <!-- 在这里做一个状态的判断-->
{{payDesc}}
</div>
</div>
payClass() {
if (this.totalPrice < this.minPrice) {
return 'not-enough';
} else {
return 'enough';
}
}
&.not-enough
background #2b333b
&.enough
background #00b43c
color #ffffff
接下来我们应该去获取真实的selectFoods的数据,但是我们希望selectFoods数据是通过一个加减号组件来实现