当前位置:   article > 正文

项目总结:vue.js2.5饿了么APP(5)主要组件实现 - 商品详情页部分_this.foodcomp || this.$createfood

this.foodcomp || this.$createfood

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

之前章节传送: 

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

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

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

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


速看

在商品页面中点击某一个商品,可以跳到商品详情页查看详情,包括:商品图片、名称价格、商品评价、加入购物车。该组件为全屏弹层组件,如果单纯作为goods的子组件调用会出现不能全屏显示的问题。因此将其注册为create-API模块,让good组件动态挂载到body下。(这样就可以使用fixed全屏布局以及层级的z-index来控制是否盖住下面的header部分。)(原因:food是fixed布局,外部有一个transform,fixed布局会受到transform影响,因此对于全屏类弹层组件,组件的样式很容易受到父元素影响,(fixed布局是全屏类的))同样的,该页面也需要控制shop-cart-sticky购物车组件显隐。

评论部分有两个维度实现过滤:首先可以查看评价类型(全部、推荐、吐槽)还有“只看有内容的评价”,将其该部分设置为rating-select组件,接收props(ratings是个数组(通过ratings可以计算各部分的评价个数);selectType是选择类型(有三种类型,定义三种常量POSITIVE、NEGATIVE、ALL)使用常量增加可读性;onlycontent是否有内容;desc描述(对象默认描述:all:"全部" positive:“满意” negative:“不满意”) )外部可以传入标签,从而实现复用。在food中添加计算属性computedRatings,定义数组并遍历将符合的ratings放入(当选中所有时push(rating)或者当前选择类型和这条评价类型相同,也push(rating))从而按情况显示ratings


目录

一、food组件-商品部分

1. 概述

2. 布居

3. 实现

(1)添加头图

(2)使用声明式调用food(未采用)

(3)注册api组件

(4)实现food 组件内部滚动

(5)使用sticky组件

(6)在商品详情页进行购物

二、food组件-评价部分

1. 布局

2. 实现

(1)显示整个评论列表

(2)时间部分

(3)评价过滤功能

三、food组件-rating-select组件

1. 布局

2. 实现

(1)rating-select组件接收数据

(2)rating-select组件使用

(3)添加点击事件

(4)控制列表显示变化


一、food组件-商品部分

1. 概述

在商品页面中点击某一个商品,可以跳到商品详情页查看详情,包括:商品图片、名称价格、商品评价、加入购物车。该组件为全屏弹层组件,如果单纯作为goods的子组件调用会出现不能全屏显示的问题。因此将其注册为create-API模块,让good组件动态挂载到body下。(这样就可以使用fixed全屏布局以及层级的z-index来控制是否盖住下面的header部分。)(原因:food是fixed布局,外部有一个transform,fixed布局会受到transform影响,因此对于全屏类弹层组件,组件的样式很容易受到父元素影响,(fixed布局是全屏类的))同样的,该页面也需要控制shop-cart-sticky购物车组件显隐。

评论部分有两个维度实现过滤:首先可以查看评价类型(全部、推荐、吐槽)还有“只看有内容的评价”,将其该部分设置为rating-select组件,接收props(ratings是个数组(通过ratings可以计算各部分的评价个数);selectType是选择类型(有三种类型,定义三种常量POSITIVE、NEGATIVE、ALL)使用常量增加可读性;onlycontent是否有内容;desc描述(对象默认描述:all:"全部" positive:“满意” negative:“不满意”) )外部可以传入标签,从而实现复用。在food中添加计算属性computedRatings,定义数组并遍历将符合的ratings放入(当选中所有时push(rating)或者当前选择类型和这条评价类型相同,也push(rating))从而按情况显示ratings.

(在rating-select中的每个选项添加点击事件select,toggle 。由于selectType是传递过来的props不能直接修改,因此把这个props的值派发到父组件food,让后让父组件做修改,响应式更新数据,通过props再影响子组件。)

2. 布居

当点击某一商品时点击可以查看详情,主要分为:头部图片+商品购物+商品信息+评价

商品评价可以有选项分类,有全部、推荐、吐槽,并且有一个按钮可以只看有内容的评价

二期:goods组件需要基于API实现

  1. <transition name="move" @after-leave="afterLeave">
  2. <div class="food" v-show="visible">
  3. <cube-scroll :data="computedRatings" ref="scroll">
  4. <div class="food-content">
  5. <div class="image-header"></div>
  6. <div class="content">
  7. <div class="cart-control-wrapper">
  8. <cart-control @add="addFood" :food="food"></cart-control>
  9. </div>
  10. <transition name="fade"><div class="buy">加入购物车</div> </transition>
  11. </div>
  12. <split v-show="food.info"></split>
  13. <div class="info" v-show="food.info"></div>
  14. <split></split>
  15. <div class="rating">
  16. <rating-select></rating-select>
  17. <div class="rating-wrapper"></div>
  18. </div>
  19. </div>
  20. </cube-scroll>
  21. </div>
  22. </transition>

需要接收food这个props,需要根据不同的food渲染不同内容,开始是隐藏的,通过通过使用visible控制显隐,其中的<transition>对应左滑的过渡动画,内部使用cube-scroll组件实现滚动.

3. 实现

(1)添加头图

使用height:0 padding-top:100%的方式,让图片可以达到等比效果

(2)使用声明式调用food(未采用)

即在goods组件中引入food组件。使用food组加需要传入:food="selectedFood",在goods组件中,当选中列表的一项时,添加点击事件selectFood(food),并实现方法(将food赋值给selectedFood)。在data中声明一个当前选中的商品selectedFood.并且给添加一个ref="food"

  1. selectFood(food){
  2. this.selectedFood = food
  3. this.$refs.food.show()
  4. }

发现效果是在下面区域显示整个food组件而没有遮盖头部部分。

原因:food是fixed布局,外部有一个transform,fixed布局会受到transform影响,因此对于全屏类弹层组件,组件的样式很容易受到父元素影响,(fixed布局是全屏类的)

解决方法:使用create-API模块,让good组件动态挂载到body下。这样就可以使用fixed全屏布局以及层级的z-index来控制是否盖住下面的header部分。

(3)注册api组件

定义私有方法_showFood(),当选择的food变化了,selectedFood响应式变化

  1. selectFood(food) {
  2. this.selectedFood = food
  3. this._showFood()
  4. this._showShopCartSticky()
  5. },
  6. _showFood() {
  7. this.foodComp = this.foodComp || this.$createFood({
  8. $props: {
  9. food: 'selectedFood'
  10. },
  11. $events: {
  12. leave: () => {
  13. this._hideShopCartList()
  14. },
  15. add: (el) => {
  16. this.shopCartStickyComp.drop(el)
  17. }
  18. }
  19. })
  20. this.foodComp.show()
  21. },

(4)实现food 组件内部滚动

scroll每次需要初始化,在food组件中添加created

  1. const EVENT_SHOW = 'show'
  2. created() {
  3. this.$on(EVENT_SHOW, () => {
  4. this.$nextTick(() => {
  5. this.$refs.scroll.refresh()
  6. })
  7. })
  8. },

(5)使用sticky组件

在goods组件中,打开food组件时,同时要将shop-cart-sticky组件显示出来。同时定义showSopCartSticky()方法

  1. selectFood(food) {
  2. this.selectedFood = food
  3. this._showFood()
  4. this._showShopCartSticky()
  5. },
  6. _showShopCartSticky() {
  7. this.shopCartStickyComp = this.shopCartStickyComp || this.$createShopCartSticky({
  8. $props: {
  9. selectFoods: 'selectFoods',
  10. deliveryPrice: this.seller.deliveryPrice,
  11. minPrice: this.seller.minPrice,
  12. fold: true
  13. }
  14. })
  15. this.shopCartStickyComp.show()
  16. },
  17. _hideShopCartSticky() {
  18. this.shopCartStickyComp.hide()
  19. }

当动画结束时隐藏sticky组件,在food组件添加过渡钩子函数after-leave=afterLeave

  1. afterLeave() {
  2. this.$emit(EVENT_LEAVE) //leave
  3. },

并且在goods组件中的——showFood中添加event(hideShopCartSticky)

  1. _showFood() {
  2. this.foodComp = this.foodComp || this.$createFood({
  3. $props: {
  4. food: 'selectedFood'
  5. },
  6. $events: {
  7. leave: () => {
  8. this._hideShopCartSticky()
  9. },
  10. add: (el) => {
  11. this.shopCartStickyComp.drop(el)
  12. }
  13. }
  14. })
  15. this.foodComp.show()
  16. },

(6)在商品详情页进行购物

比之前的cart-control多一步,在没有购买时会显示“加入购物车”,

实现:使用两层,一层是使用cat-control组件,另一层是一个按钮,并且使用<tansition>组件包裹,驱动动画的变化,通过food.count控制显隐,当count是0或者undefined,就是显示的,否则相反。都是用绝对定位,让“加入购物车”部分盖住cart-control

  1. <div class="cart-control-wrapper">
  2. <cart-control @add="addFood" :food="food"></cart-control>
  3. </div>
  4. <transition name="fade">
  5. <div @click="addFirst" class="buy" v-show="!food.count">加入购物车</div>
  6. </transition>

并且在“加入购物车”添加addFirst方法,(没有count时设置为1)点击按钮驱动外层的shop-cart-sticky组件,因此需要派发EVENT_ADD;在cart-control添加addFood方法(事件向父组件传递,food组件的父组件是goods,里面含有shopCartStickyComp,它的drop()方法才是真正调用小球动画的,因此事件需要一层层派发)

  1. addFirst(event) {
  2. this.$set(this.food, 'count', 1)
  3. this.$emit(EVENT_ADD, event.target)
  4. },
  5. addFood(target) {
  6. this.$emit(EVENT_ADD, target)
  7. },

这是在goods中监听add方法完成动画

  1. _showFood() {
  2. this.foodComp = this.foodComp || this.$createFood({
  3. $props: {
  4. food: 'selectedFood'
  5. },
  6. $events: {
  7. leave: () => {
  8. this._hideShopCartSticky()
  9. },
  10. add: (el) => {
  11. this.shopCartStickyComp.drop(el)
  12. }
  13. }
  14. })
  15. this.foodComp.show()
  16. },

二、food组件-评价部分

1. 布局

除了有对评价的显示,还有对评价的过滤,可以对评价列表进行切换。

  1. <div class="rating">
  2. <h1 class="title">商品评价</h1>
  3. <rating-select></rating-select>
  4. <div class="rating-wrapper">
  5. <ul>
  6. <li class="rating-item border-bottom-1px">
  7. <div class="user">
  8. <span class="name">{{rating.username}}</span>
  9. <img class="avatar" :src="rating.avatar">
  10. </div>
  11. <div class="time">{{format(rating.rateTime)}}</div>
  12. <p class="text"></p>
  13. </li>
  14. </ul>
  15. <div class="no-rating" v-show="!computedRatings || !computedRatings.length">暂无评价</div>
  16. </div>
  17. </div>

2. 实现

(1)显示整个评论列表

ratings使用计算属性(简化书写)

  1. <div class="rating-wrapper">
  2. <ul v-show="computedRatings && computedRatings.length">
  3. <li
  4. v-for="(rating, index) in computedRatings"
  5. class="rating-item border-bottom-1px"
  6. :key="index">
  7. <div class="user"><div>
  8. <div class="time">{{format(rating.rateTime)}}</div>
  9. <p class="text"> </p>
  10. </li>
  11. </ul>
  12. </div>
  13. computed: {
  14. ratings() {
  15. return this.food.ratings
  16. }
  17. },

遍历整个列表,渲染出所有的ratings,包含时间,评价,用户,手指图标

(2)时间部分

通常日期的显示服务端不会显示时间,而是时间戳。而前端可以按照自己的要求改变为字符串。这里使用了moment的开源组件(Moment.js)可以添加format()方法,使用moment的方法即可

  1. format(time) {
  2. return moment(time).format('YYYY-MM-DD hh:mm')
  3. }

至此完成了评价列表

(3)评价过滤功能

有两个维度实现过滤:首先可以查看评价类型(全部、推荐、吐槽)过滤评论的同时模块激活状态颜色改变;还有“只看有内容的评价”,将其设置为rating-select组件,在rating-select中的每个选项添加点击事件select,toggle,由于不能直接修改selectType,(传递过来的props)因此把这个props的值派发到父组件food,让后让父组件做修改,响应式更新数据再影响子组件。

因此在food组件中可以监听这两个方法onselect() ontoggle()控制改变。

三、food组件-rating-select组件

1. 布局

主要是(全部、推荐、吐糟)+只看有内容的评论

  1. <div class="rating-select">
  2. <div class="rating-type border-bottom-1px">
  3. <span @click="select(2)" class="block positive" :class="{'active': selectType===2}">{{desc.all}}
  4. <span class="count">{{ratings.length}}</span>
  5. </span>
  6. <span @click="select(0)" class="block positive" :class="{'active': selectType===0}">{{desc.positive}}
  7. <span class="count">{{positives.length}}</span>
  8. </span>
  9. <span @click="select(1)" class="block positive" :class="{'active': selectType===1}">{{desc.negative}}
  10. <span class="count">{{negatives.length}}</span>
  11. </span>
  12. </div>
  13. <div @click="toggle" class="switch" :class="{'on':onlyContent}">
  14. <span class="icon-check_circle"></span>
  15. <span class="text">只看有内容的评价</span>
  16. </div>
  17. </div>

2. 实现

(1)rating-select组件接收数据

设置props,外部可以传入的变量。

接收ratings是个数组(通过ratings可以计算各部分的评价个数);selectType是选择类型(有三种类型,定义三种常量POSITIVE、NEGATIVE、ALL)使用常量增加可读性;onlycontent是否有内容;desc描述(对象默认描述:all:"全部" positive:“满意” negative:“不满意”)

  1. const POSITIVE = 0
  2. const NEGATIVE = 1
  3. const ALL = 2
  4. props: {
  5. ratings: {
  6. type: Array,
  7. default() {
  8. return []
  9. }
  10. },
  11. selectType: {
  12. type: Number,
  13. default: ALL
  14. },
  15. onlyContent: {
  16. type: Boolean,
  17. default: false
  18. },
  19. desc: {
  20. type: Object,
  21. default() {
  22. return {
  23. all: '全部',
  24. positive: '满意',
  25. negative: '不满意'
  26. }
  27. }
  28. }
  29. },

添加计算属性positive negative,根据服务端约定数据得到,计算每个部分的数量

  1. computed: {
  2. positives() {
  3. return this.ratings.filter((rating) => {
  4. return rating.rateType === POSITIVE
  5. })
  6. },
  7. negatives() {
  8. return this.ratings.filter((rating) => {
  9. return rating.rateType === NEGATIVE
  10. })
  11. }
  12. },

(2)rating-select组件使用

在food组件写入date,包括onlyContent,selectType,desc(“全部、推荐、吐槽”)

  1. data() {
  2. return {
  3. onlyContent: true,
  4. selectType: ALL
  5. desc: {
  6. all: '全部',
  7. positive: '推荐',
  8. negative: '吐槽'
  9. }
  10. }
  11. },

然后在rating-select中绑定数据

  1. <rating-select
  2. :ratings="ratings"
  3. :onlyContent="onlyContent"
  4. :selectType="selectType"
  5. :desc="desc"
  6. @select="onSelect"
  7. @toggle="onToggle">
  8. </rating-select>

(3)添加点击事件

在rating-select中的每个选项添加点击事件(如上代码),并写方法select,toggle

  1. const EVENT_SELECT = 'select'
  2. const EVENT_TOGGLE = 'toggle'
  3. methods: {
  4. select(type) {
  5. this.$emit(EVENT_SELECT, type)
  6. },
  7. toggle() {
  8. this.$emit(EVENT_TOGGLE)
  9. }
  10. }

思路:这时不能直接修改selectType,不能修改传递过来的props,因此把这个props的值派发到父组件food,让后让父组件做修改,响应式更新数据,这个数据通过props再影响子组件。

因此在food组件中可以监听这两个方法onselect() ontoggle()控制改变。

  1. methods: {
  2. onSelect(type) {
  3. this.selectType = type
  4. },
  5. onToggle() {
  6. this.onlyContent = !this.onlyContent
  7. }
  8. }

(在这个地方使用v-model不是很合适)

至此只是完成了交互效果,所有交互的效果应该影响列表,根据onlycontent 和 select计算而来

(4)控制列表显示变化

在food中添加计算属性computedRatings,定义数组遍历,

解释:当选中所有时push(rating)或者当前选择类型和这条评价类型相同,也push(rating)最后返回数组

  1. computed: {
  2. computedRatings() {
  3. const ret = []
  4. this.ratings.forEach((rating) => {
  5. if (this.onlyContent && !rating.text) {
  6. return
  7. }
  8. if (this.selectType === ALL || this.selectType === rating.rateType) {
  9. ret.push(rating)
  10. }
  11. })
  12. return ret
  13. }
  14. },

使用这个计算属性, 在每一个li中遍历computedRatings,并且当没有评价时显示为“暂无评价”

  1. <ul v-show="computedRatings && computedRatings.length">
  2. <li
  3. v-for="(rating, index) in computedRatings"
  4. class="rating-item border-bottom-1px"
  5. :key="index">
  6. </li>
  7. </ul>

后续章节传送:

项目总结:vue.js2.5饿了么APP(6)主要组件实现 - 评价页+商家页部分

项目总结:vue.js2.5饿了么APP(7)项目部署与总结

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/天景科技苑/article/detail/871940
推荐阅读
相关标签
  

闽ICP备14008679号