Fork me on GitHub
余鸢

饿了么45个页面重构(九):购物车页面开发

引入buyCart组件

回到shop.vue组件中,将buyCart.vue引入这个组件中,传入相关数据,以及要实现的监听小球是否进入购物车事件。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
<buy-cart :foods="food" :shopId="shopId" @moveInCart="listenInCart"></buy-cart>
methods: {
listenInCart () {
if (!this.receiveInCart) {
this.receiveInCart = true
this.$refs.cartContainer.addEventListener('animationend', () => {
this.receiveInCart = false
})
this.$refs.cartContainer.addEventListener('webkitAnimationEnd', () => {
this.receiveInCart = false
})
}
},
}

底部购物车结算

展示购物车商品数量和总价,同时购物车商品的数量和总价会随着购物车中的商品增加或减少改变而改变。计算购物车里的商品总数量。

1
2
3
4
5
6
7
8
9
computed: {
totalNum () {
let num = 0
this.cartFoodList.forEach(item => {
num += item.num
})
return num
},
}

同时购物车商品数量有值时改变class的状态,绑定class,data中定义totalPrice表示总价。

1
2
<div class="cart_icon_container"
:class="{cart_icon_activity: totalPrice > 0, move_in_cart:receiveInCart}">

当购物车商品有数量时,显示购物车商品总数量。

1
<span class="cart_list_length" v-if="totalNum">{{totalNum}}</span>

展示总价和配送费

1
2
3
4
5
6
7
8
9
10
11
12
13
14
<div class="cart_num">
<div>¥ {{totalPrice}}</div>
<div>配送费¥{{deliveryFee}}</div>
</div>
methods: {
deliveryFee () {
if (this.shopDetailData) {
return this.shopDetailData.float_delivery_fee
} else {
return null
}
},
}

还差多少元起送,在computed钩子中计算差价。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
<section class="goToPay" :class="{goToPay_activity: minimumOrderAmount < 0}">
<span class="goToPay_button_style" v-if="minimumOrderAmount > 0">还差¥{{minimumOrderAmount}}起送</span>
<!--<span class="goToPay_button_style" v-else >去结算</span>-->
<router-link :to="{path:'/confirmOrder', query:{geohash, shopId}}" class="goToPay_button_style" v-else>去结算</router-link>
</section>
computed: {
minimumOrderAmount () {
if (this.shopDetailData) {
return this.shopDetailData.float_minimum_order_amount - this.totalPrice
} else {
return null
}
},
}

当差价小于0时高亮去结算按钮的样式,绑定class :class="{goToPay_activity: minimumOrderAmount < 0}">

1
2
3
4
5
<style>
.goToPay_acitvity{
background-color: #4cd964;
}
</style>

监听购物车中商铺列表的变化,当length为0时将列表隐藏。

1
2
3
4
5
6
7
watch: {
cartFoodList (value) {
if (!value.length) {
this.showCartList = false
}
}
}

效果如图:

高亮去结算

购物车展示商品列表

点击购物车小图标,展示购物车中的商品列表。

1
2
3
4
5
6
7
8
9
<transition name="fade">
<div class="screen_cover" v-show="showCartList&&cartFoodList.length" @click="toggleCartList"> </div>
</transition>
methods: {
toggleCartList () {
this.showCartList = !this.showCartList
},
}

当然先要判断购物车里是否有商品v-show="showCartList&&cartFoodList.length"。如果有商品遍历它们依次展示商品详情,代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
<section class="cart_food_details">
<ul>
<li v-for="(item, index) in cartFoodList" :key="index" class="cart_food_li">
<div class="cart_list_num">
<p class="ellipsis">{{item.name}}</p>
<p class="ellipsis">{{item.specs}}</p>
</div>
<div class="cart_list_price">
<span>¥</span>
<span>{{item.price}}</span>
</div>
<section class="cart_list_control">
<svg @click="removeOutCart(item.category_id, item.item_id, item.food_id, item.name, item.price, item.specs)">
<use xmlns:xlink="http://www.w3.org/1999/xlink" xlink:href="#cart-minus"></use>
</svg>
<span class="cart_num">{{item.num}}</span>
<svg class="cart_add" @click="addToCart(item.category_id, item.item_id, item.food_id, item.name, item.price, item.specs)">
<use xmlns:xlink="http://www.w3.org/1999/xlink" xlink:href="#cart-add"></use>
</svg>
</section>
</li>
</ul>
</section>

购物车减少和增加按钮事件

对购物车商品的增加和减少图标绑定事件动态的控制。removeOutCart()减少事件,addToCart()增加事件。

依次添加所需要的参数,分别是商铺id,食品分类id,食品id,食品规格id,食品名字,食品价格,食品规格。

1
2
3
4
5
6
7
8
9
10
11
12
methods: {
addToCart (category_id, item_id, food_id, name, price, specs) {
this.ADD_CART({shopId:this.shopId, category_id, item_id, food_id, name, price, specs})
},
removeOutCart (category_id, item_id, food_id, name, price, specs) {
this.REDUCE_CART({shopId:this.shopId, category_id, item_id, food_id, name, price, specs})
},
...mapMutations([
'ADD_CART', 'REDUCE_CART', 'CLEAR_CART',
])
}

清空购物车列表

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
<header>
<h4>购物车</h4>
<div @click="clearCart">
<svg>
<use xmlns:xlink="http://www.w3.org/1999/xlink" xlink:href="#cart-remove"></use>
</svg>
<span class="clear_cart">清空</span>
</div>
</header>
methods: {
clearCart () {
this.toggleCartList()
this.CLEAR_CART(this.shopId)
},
}

评价

当点击评价时显示评价页v-show="changeShowType == 'rating'"

获取评价总体分数数据

shop.js

1
2
3
4
5
6
7
export function ratingScores (id) {
const url = '/restapi/ugc/v2/restaurants/' + id + '/ratings/scores'
return axios.get(url).then((res) => {
return Promise.resolve(res.data)
})
}

data中定义ratingScores表示评价总分数。

shop.vue

1
2
3
4
5
6
7
methods: {
getRatingScores () {
ratingScores(this.shopId).then(res => {
this.ratingScoresData = res
})
},
}

设置综合评价

1
2
3
4
5
<section class="rating_header_left">
<p>{{shopDetailData.rating}}</p>
<p>综合评价</p>
<p>高于周边商家{{(ratingScoresData.compare_rating*100).toFixed(1)}}%</p>
</section>

设置服务态度

1
2
3
4
5
<p>
<span>服务态度</span>
<rating-star :rating="ratingScoresData.service_score"></rating-star>
<span class="rating_num">{{ratingScoresData.service_score.toFixed(1)}}</span>
</p>

设置菜品评价

1
2
3
4
5
<p>
<span>菜品评价</span>
<rating-star :rating="ratingScoresData.overall_score"></rating-star>
<span class="rating_num">{{ratingScoresData.overall_score.toFixed(1)}}</span>
</p>

设置送达时间

1
2
3
4
<p>
<span>送达时间</span>
<span class="delivery_time">{{shopDetailData.order_lead_time}}分钟</span>
</p>

效果如下:
e24

获取评价分类

shop.js

1
2
3
4
5
6
7
export function ratingTags (id) {
const url = '/restapi/ugc/v2/restaurants/' + id + '/ratings/tags'
return axios.get(url).then((res) => {
return Promise.resolve(res.data)
})
}

data中定义ratingTagsList表示评价分类列表。

shop.vue

1
2
3
4
5
6
7
methods: {
getRatingTags () {
ratingTags(this.shopId).then(res => {
this.ratingTagsList = res
})
},
}

遍历ratingTagsList,设置默认当前评价分类的index

1
2
3
4
5
6
7
8
<ul class="tag_list_ul">
<li v-for="(item, index) in ratingTagsList" :key="index"
:class="{unsatisfied: item.unsatisfied, tagActivity: ratingTagsIndex == index}"
@click="changeTagIndex(index, item.name)"
>
{{item.name}}({{item.count}})
</li>
</ul>

效果如图:

e25

获取商铺评价列表

shop.js

1
2
3
4
5
6
7
8
9
10
11
12
export function ratings (id) {
const url = '/restapi/ugc/v3/restaurants/' + id + '/ratings'
const data = Object.assign({}, {
has_content: true,
offset: 0,
limit: 8
})
return axios.get(url, {params: data}).then((res) => {
return Promise.resolve(res.data)
})
}

data中定义ratingList表示评价列表。

shop.vue

1
2
3
4
5
6
7
methods: {
getRatingList () {
ratings(this.shopId).then(res => {
this.ratingList = res
})
},
}

遍历ratingList:<li v-for="(item, index) in ratingList" :key="index" class="rating_list_li">

设置头像:<img :src="getImgPath(item.avatar)" class="user_avatar">

设置用户名称:<p class="username"></p>

设置星级和日期:

1
2
3
4
5
<p class="star_desc">
<rating-star :rating="item.rating"></rating-star>
<span class="time_spent_desc">{{item.time_spent_desc}}</span>
</p>
<time class="rated_at">{{item.rated_at}}</time>

设置商品图片,图片有多个需要做遍历:

1
2
3
4
5
<ul class="food_img_ul">
<li v-for="(foodItem, index) in item.order_images" :key="index">
<img :src="getImgPath(foodItem.image_hash)" v-if="foodItem.image_hash">
</li>
</ul>

设置商品名称,和图片一样也需要做遍历:

1
2
3
4
5
<ul class="food_name_ul">
<li class="ellipsis" v-for="(foodItem, index) in item.food_ratings" :key="index">
{{foodItem.rate_name}}
</li>
</ul>

效果如图:

e26

初始化数据

页面初始化时会获取到从本地缓存购物车数据,重新获取购物车改变过的数据。初始化数据同样我也通过vuex管理。

mutations.js

1
2
3
4
5
6
[types.INIT_BUYCART] (state) {
let initCart = getStore('buyCart')
if (initCart) {
state.cartList = JSON.parse(initCart)
}
},

shop.vue

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
methods: {
initCategoryNum () {
//左侧食品列表当前分类中已加入购物车的商品数量
let newArr = []
let cartFoodNum = 0
//购物车总共的价格
this.totalPrice = 0
//购物车中所有商品的详细信息列表
this.cartFoodList = []
this.menuList.forEach((item, index) => {
if (this.shopCart && this.shopCart[item.foods[0].category_id]) {
let num = 0
Object.keys(this.shopCart[item.foods[0].category_id]).forEach(itemId => {
Object.keys(this.shopCart[item.foods[0].category_id][itemId]).forEach(foodId => {
let foodItem = this.shopCart[item.foods[0].category_id][itemId][foodId]
num += foodItem.num
if (item.type === 1) {
this.totalPrice += foodItem.num * foodItem.price
if (foodItem.num > 0) {
this.cartFoodList[cartFoodNum] = {}
this.cartFoodList[cartFoodNum].category_id = item.foods[0].category_id
this.cartFoodList[cartFoodNum].item_id = itemId
this.cartFoodList[cartFoodNum].food_id = foodId
this.cartFoodList[cartFoodNum].num = foodItem.num
this.cartFoodList[cartFoodNum].price = foodItem.price
this.cartFoodList[cartFoodNum].name = foodItem.name
this.cartFoodList[cartFoodNum].specs = foodItem.specs
cartFoodNum ++
}
}
})
})
newArr[index] = num
} else {
newArr[index] = 0
}
})
this.categoryNum = newArr.concat([])
},
}

监听购物车列表的变化

1
2
3
4
5
watch: {
shopCart (value) {
this.initCategoryNum()
}
}

具体代码见:

https://github.com/kakajing/vue-elmm

如有什么疑问或者不懂之处,可以加这个群交流:

QQ群:157028081