商品详情页(food组件)
Posted Emily
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了商品详情页(food组件)相关的知识,希望对你有一定的参考价值。
前言
本节分为四大块:
1. 商品信息(布局样式、第三方插件库better-scroll 的应用、split 组件)
2. 商品评价(ratingselect 组件)
3. 商品评价(评价列表)
PS:本节所有代码在文章底部。
商品信息
1. CSS 设置
1 <style lang="stylus" rel="stylesheet/stylus"> 2 @import "../../common/stylus/mixin.styl" 3 4 .food 5 position fixed 6 left 0 7 top 0 8 bottom 48px 9 z-index 30 10 width 100% 11 background #fff 12 transform translate3d(0, 0, 0) 13 &.move-enter-active, &.move-leave-active 14 transition all 0.2s linear 15 &.move-enter, &.move-leave-active 16 transform translate3d(100%, 0, 0) 17 .image-header 18 position relative 19 width 100% 20 height 0 21 padding-top 100% 22 img 23 position absolute 24 left 0 25 top 0 26 width 100% 27 height 100% 28 .back 29 position absolute 30 left 0 31 top 10px 32 .icon-arrow_lift 33 display block 34 /*点击区域变大*/ 35 padding 10px 36 font-size 20px 37 color #fff 38 .content 39 position relative 40 padding 18px 41 .title 42 margin-bottom 8px 43 line-height 14px 44 font-size 14px 45 font-weight 700 46 color rgb(7, 17, 27) 47 .detail 48 margin-bottom 18px 49 height 10px 50 line-height 10px 51 font-size 0 52 .sell-count, .rating 53 font-size 10px 54 color rgb(147, 153, 159) 55 .sell-count 56 margin-right 12px 57 .price 58 font-weight 700 59 line-height 24px 60 .now 61 margin-right 8px 62 font-size 14px 63 color rgb(240, 20, 20) 64 .old 65 text-decoration line-through 66 font-size 10px 67 color rgb(147, 153, 159) 68 .cartcontrol-wrapper 69 position absolute 70 right 12px 71 bottom 12px 72 .buy 73 position absolute 74 right 18px 75 bottom 18px 76 /*因为要盖住cartcontrol组件*/ 77 z-index 10 78 height 24px 79 line-height 24px 80 padding 0 12px 81 box-sizing border-box 82 border-radius 12px 83 font-size 10px 84 color #fff 85 background-color rgb(0, 160, 220) 86 opacity 1 87 &.fade-enter-active, &.fade-leave-active 88 transition all 0.2s linear 89 &.fade-enter, &.fade-leave-active 90 opacity 0 91 z-index -1 92 .info 93 padding: 18px 94 .title 95 line-height: 14px 96 margin-bottom: 6px 97 font-size: 14px 98 color: rgb(7, 17, 27) 99 .text 100 line-height: 24px 101 padding: 0 8px 102 font-size: 12px 103 color: rgb(77, 85, 93) 104 .rating 105 padding-top: 18px 106 .title 107 line-height: 14px 108 margin-left: 18px 109 font-size: 14px 110 color: rgb(7, 17, 27) 111 .rating-wrapper 112 padding 0 18px 113 .rating-item 114 position relative 115 padding 16px 0 116 border-1px(rgba(7, 17, 27, 0.1)) 117 .user 118 position absolute 119 top 16px 120 right 0 121 line-height 12px 122 font-size 0 123 .username 124 margin-right 6px 125 display inline-block 126 vertical-align top 127 font-size 10px 128 color rgb(147, 153, 159) 129 .avatar 130 border-radius 50% 131 .time 132 margin-bottom 6px 133 line-height 12px 134 font-size 10px 135 color rgb(147, 153, 159) 136 .text 137 line-height 16px 138 font-size 12px 139 color rgb(7, 17, 27) 140 .icon-thumb_up, .icon-thumb_down 141 margin-right 4px 142 line-height 16px 143 font-size 12px 144 .icon-thumb_up 145 color rgb(0, 160, 220) 146 .icon-thumb_down 147 color rgb(147, 153, 159) 148 .no-rating 149 padding 16px 0 150 font-size 12px 151 color rgb(147, 153, 159) 152 </style>
1)相对于屏幕进行定位,使用 fixed 布局。
2)底部有购物车,所以设置 bottom 为 48px。
3)z-index 的值应该小于购物车详情层的 z-index,因为购物车详情层弹出应该要遮盖住商品详情层。
4)加上 transition 动画
5)图片高度应该和屏幕宽度一样,是动态变化的。图片加载是异步过程,如果不设置图片高度,等到图片加载完毕,页面高度会突然被撑开。如何解决?先把宽高设好,设置 height 0,padding-top 100%。这是 W3C 的一个特定写法,当 padding 的 top 或 bottom 设置为100% 时,会相对于盒子的 width 100%,这就相当于有一个宽高相等的盒子。(同理,当 padding 的 left 或 right 设置为100% 时,会相对于盒子的 height 100%)
2. 数据获取
1 food.vue文件 2 props: { 3 food: { 4 type: Object 5 } 6 }, 7 8 9 goods.vue文件 10 <food :food="selectedFood" ref="food"></food> 11 12 import food from \'../../components/food/food\'; 13 data() { 14 return { 15 selectedFood: {} 16 }; 17 }, 18 methods: { 19 selectFood(food, event) { 20 if (!event._constructed) { 21 // eslint-disable-next-line 22 return; 23 } 24 this.selectedFood = food; 25 }, 26 }
1)商品详情页关联的是商品本身,也就是食品分类下的每个商品。使用 props 接收 food 对象。
2)在 goods 组件中引用 food 组件,并且传入food,设定变量为 selectedFood(即选中的 food),在 data 中定义该变量,为一个空对象。
3)selectedFood 是选中的 food,那么什么时候选中?当点击 food 时。如何实现:在 li 中添加点击事件 selectFood(food,$event),传入参数 food,因为这里是 foodWrapper,所以点击的时候也要拿到 event,接着在在 methods 中实现该方法。先 return 掉浏览器默认的点击事件,将参数 food 传递给变量 selectedFood。如何检验:在浏览器调试窗口中找到 food 层,取消display属性,看看布局有没有错。
3. show 方法的实现:点击商品时,商品详情层展开
1 food.vue文件 2 <div class="food" v-show="showFlag" ref="food"> 3 4 data() { 5 return { 6 showFlag: false 7 } 8 }; 9 }, 10 methods: { 11 show() { 12 this.showFlag = true; 13 }, 14 } 15 16 goods.vue文件 17 methods: { 18 selectFood(food, event) { 19 this.$refs.food.show(); 20 } 21 }
在 food 组件中定义 show 方法,show 方法通过改变 showFlag 的值来实现商品详情层的展开功能,在此之前,需要在 data 中先定义 showFlag。然后在 goods 组件中通过 ref 获取组件,并通过 $refs 调用子组件的 show 方法。
PS:1)父组件可以调用子组件方法,子组件不能调用父组件方法。2)设计规范:下划线开头是私有方法(_drop)。
4. 给返回按钮添加点击事件 hide
1 <div class="back" @click="hide"> 2 3 methods: { 4 hide() { 5 this.showFlag = false; 6 } 7 }
1)CSS设置:(display block,padding 10px)先设置display,才能设置padding,使点击区域变大。
2)hide 方法通过改变 showFlag 的值为 false 来实现返回功能。
设置其他样式
5. better-scroll 的使用
1 <div class="food" v-show="showFlag" ref="food"> 2 <div class="food-content"> 3 </div> 4 </div> 5 6 methods: { 7 show() { 8 this.$nextTick(() => { 9 if (!this.scroll) { 10 this.scroll = new BScroll(this.$refs.food, { 11 click: true 12 }); 13 } else { 14 this.scroll.refresh(); 15 } 16 }); 17 } 18 }
原理:当内部(content)的内容高度大于视口高度,则产生滚动效果
固定框架:外层是一个 wrapper,里面有一个 content,并且这个 content 高度是由其中的内容撑开的。
使用:better-scroll 应该绑定在最外层(.food)。依旧是通过 ref 获取组件,并通过 $refs 调用组件。better-scroll 应该在商品详情层展开时就进行初始化,所以其初始化应该在 show 方法中。
6. 购物车模块
1 food.vue文件: 2 <div class="cartcontrol-wrapper"> 3 <cartcontrol :food="food"></cartcontrol> 4 </div> 5 <transition name="fade"> 6 <div @click.stop.prevent="addFirst" class="buy" v-show="!food.count || food.count===0">加入购物车</div> 7 </transition> 8 9 import Vue from \'vue\'; 10 methods: { 11 addFirst(event) { 12 console.log(\'click\'); 13 if (!event._constructed) { 14 // eslint-disable-next-line 15 return; 16 } 17 this.$emit(\'add\', event.target); 18 console.log(event.target); 19 Vue.set(this.food, \'count\', 1); 20 } 21 } 22 23 cartcontrol.vue文件: 24 <div class="cart-decrease" v-show="food.count>0" @click.stop.prevent="decreaseCart"> 25 <div class="cart-add icon-add_circle" @click.stop.prevent="addCart"></div>
有两种样式:当加入商品时,“加入购物车” 文字消失,显示 cartcontrol 组件。所以应该加入两个层,这两个层平级,分别是 buy 层和 cartcontrol 层。
1)这两个层都是绝对定位。
2)buy 层加入 transition 动画及 v-show 指令(当 food.count 不存在或等于 0 时,该层显示)。
3)buy 层进行样式设计:z-index 10,因为要盖住 cartcontrol 组件。
4)添加点击按钮事件 addFirst。首先阻止浏览器默认点击事件,然后引入 vue,利用 Vue.set,将 this.count 置为1,最后使用 $emit
将事件事件传出去。
PS:1)不传参数的话,则默认参数为event。2)不传参数得写为addFirst,而不能写成addFirst()。
Q1:在 buy 层点击时,发现小球位下落置不正确。
A1:因为点击后,count>0,此时 buy 层因为 v-show 指令,会被隐藏,display 瞬间变成 none。所以在 $nextTick 中做动画时就找不到小球初始位置。
R1:将 buy 层的消失做成一个动画(transition,渐隐效果),这样 buy 层就不会立刻隐藏,display 属性不会立刻被设置 none,位置就能被计算。
Q2:点击商品页(goods 组件)的小球,商品详情页会弹出。
A2:这是因为事件冒泡,所以要阻止事件冒泡。
R2:给 cartcontro l组件的加号和减号小球的点击事件添加修饰符(@click.stop.prevent)。
6. 新建组件(components -> split ->split.vue),在 food.vue 引入注册并使用。
1 <template> 2 <div class="split"></div> 3 </template> 4 5 <script type="text/ecmascript-6"> 6 export default {}; 7 </script> 8 9 <style lang="stylus" rel="stylesheet/stylus"> 10 .split 11 width: 100% 12 height: 16px 13 border-top: 1px solid rgba(7, 17, 27, 0.1) 14 border-bottom: 1px solid rgba(7, 17, 27, 0.1) 15 background: #f3f5f7 16 </style>
7. food.info 不是每个数据都有,所以 food.info 要加 v-show 指令。
商品评价(ratingselect 组件)
1. 新建组件(components -> ratingselect->ratingselect.vue)。
2. ratingselect 组件需要在 props 中先接收四个参数:一个变量控制评价数组(ratings),一个变量维护描述(desc),一个变量确定是否只看内容(onlyContent),一个变量维护选择的评价类型(selectType)。
1 const POSITIVE = 0; // 正面评价 2 const NEGATIVE = 1; // 负面评价 3 const ALL = 2; // 所有评价 4 5 props: { 6 // 评价数组 7 ratings: { 8 type: Array, 9 default() { 10 return []; 11 } 12 }, 13 // 选择评论类型 14 selectType: { 15 type: Number, 16 default: ALL 17 }, 18 // 只看有内容评论 19 onlyContent: { 20 type: Boolean, 21 default: false 22 }, 23 // 评论描述 24 desc: { 25 type: Object, 26 default() { 27 return { 28 all: \'全部\', 29 positive: \'满意\', 30 negative: \'不满意\' 31 }; 32 } 33 } 34 },
评价类型一般分为所有评价,正面评价和负面评价,将这三个定义为常量。评价类型默认选所有评价。
3. 在 food.vue 引入注册并使用 ratingselect 组件,完成布局及CSS设置。
1 <div class="rating"> 2 <h1 class="title">商品评价</h1> 3 <ratingselect :selectType="selectType" :onlyContent="onlyContent" :desc="desc" :ratings="food.ratings" @select="selectRating" @toggle="toggleContent"></ratingselect> 4 </div> 5 6 .rating 7 padding-top: 18px 8 .title 9 line-height: 14px 10 margin-left: 18px 11 font-size: 14px 12 color: rgb(7, 17, 27) 13
1 <template> 2 <div class="ratingselect"> 3 <div class="rating-type border-1px"> 4 <span @click="select(2,$event)" class="block positive" :class="{\'active\':selectType===2}">{{desc.all}}<span 5 class="count">{{ratings.length}}</span></span> 6 <span @click="select(0,$event)" class="block positive" :class="{\'active\':selectType===0}">{{desc.positive}}<span 7 class="count">{{positives.length}}</span></span> 8 <span @click="select(1,$event)" class="block negative" :class="{\'active\':selectType===1}">{{desc.negative}}<span 9 class="count">{{negatives.length}}</span></span> 10 </div> 11 <div @click="toggleContent" class="switch" :class="{\'on\':onlyContent}"> 12 <span class="icon-check_circle"></span> 13 <span class="text">只看有内容的评价</span> 14 </div> 15 </div> 16 </template> 17 18 <style lang="stylus" rel="stylesheet/stylus"> 19 @import "../../common/stylus/mixin.styl" 20 21 .ratingselect 22 .rating-type 23 padding: 18px 0 24 margin: 0 18px 25 border-1px(rgba(7, 17, 27, 0.1)) 26 /* 消除空白字符影响 */ 27 font-size: 0 28 .block 29 display: inline-block 30 padding: 8px 12px 31 margin-right: 8px 32 Flutter 仿京东商品详情页嵌套滚动组件