一、前言
二、主要內容
1、實現效果(其實可以直接在父組件中操作子組件的顯示隱藏,但是這里通過在子組件定義自己的顯示隱藏效果,讓父組件調用,訓練一下這種方式)

2、分析:
(1)點擊父組件的某一個li項,跳出這個商品詳情(子組件項)
(2)子組件中還是需要接收到父組件中的food,但是這個food不像上一篇那樣是固定的,所以這里的這個food是根據我們點擊的不同的項,傳進去的
(3)為了實現上一步分析:我們需要在data中定義一個對象,點擊的時候,將當前對象的food傳進去,然后在傳給子組件,這樣就能實現動態傳遞food了
3、具體實現
(1)父組件中先引入並且使用子組件
(2)在父組件的每一項上添加一個點擊事件,並且將當前項的food通過定義的這個函數傳給子組件
<!--點擊的時候將當前的food傳進去--> <li class="food-item bottom-border-1px" v-for="(food, index) in good.foods" :key="index" @click="showFood(food)"> //省略 </div> </div> </li>
(3)metods:中定義這個方法接收並傳過去
<template> <Food :food="food" ref="food"></Food> </template> <script> data(){ return{ scrollY:0, tops:[], //存放每一個類的初始位置 food:{} } } components:{ Food } // 顯示點擊的food showFood (food) { // 設置food this.food = food } </script>
(4)在子組件中要定義該組件的顯示隱藏方法,先用一個標識來標記
export default { props: { food: Object }, data () { return { isShow: false } }, methods: { toggleShow () { this.isShow = !this.isShow } }, components: { CartControl } }
(5)父組件中用this.$refs.refname.method()來得到子組件的方法,並且執行
// 顯示點擊的food showFood (food) { // 設置food this.food = food // 顯示food組件 (在父組件中調用子組件對象的方法) this.$refs.food.toggleShow() }
4、完整代碼:
父組件:
<template>
<div>
<div class="goods">
<div class="menu-wrapper" >
<ul>
<!--current-->
<li class="menu-item " v-for="(good,index) in goods" :key="index" :class="{current:index===currentIndex}" @click="clickMenuItem(index)">
<span class="text bottom-border-1px">
<img class="icon" :src="good.icon" v-if="good.icon" >
{{good.name}}
</span>
</li>
</ul>
</div>
<div class="foods-wrapper">
<ul ref="foodsUl">
<li class="food-list-hook" v-for="(good, index) in goods" :key="index" >
<h1 class="title">{{good.name}}</h1>
<ul>
<li class="food-item bottom-border-1px" v-for="(food, index) in good.foods" :key="index" @click="showFood(food)">
<div class="icon">
<img width="57" height="57" :src="food.icon">
</div>
<div class="content">
<h2 class="name">{{food.name}}</h2>
<p class="desc">{{food.description}}</p>
<div class="extra">
<span class="count">月售{{food.sellCount}}份</span>
<span>好評率{{food.rating}}%</span>
</div>
<div class="price">
<span class="now">¥{{food.price}}</span>
<span class="old" v-if="food.oldPrice">¥{{food.oldPrice}}</span>
</div>
<div class="cartcontrol-wrapper">
<CartControl :food='food'></CartControl>
</div>
</div>
</li>
</ul>
</li>
</ul>
</div>
<Food :food="food" ref="food"></Food>
</div>
</div>
</template>
<script>
import {mapState} from 'vuex'
import BScroll from 'better-scroll'
import CartControl from '../../../components/CartControl/CartControl'
import Food from '../../../components/Food/Food'
export default {
data(){
return{
scrollY:0,
tops:[], //存放每一個類的初始位置
food:{}
}
},
components:{
CartControl,
Food
},
//這里的數據是異步顯示的,所以我們要等數據異步請求之后再創建這個滑動列表
mounted(){
//異步請求可以傳過去兩個參數,
this.$store.dispatch('getShopGoods',()=>{
//數據請求完之后再執行這里了
//初始化滾動
this.$nextTick(()=>{
//初始化,並且實時獲取滾動坐標
this._initScrollY()
//初始化右邊的數組
this._initTops();
})
})
},
methods:{
//初始化BScroll
_initScrollY(){
new BScroll('.menu-wrapper',{
click:true
})
//創建右邊的
this.foodswrapper = new BScroll('.foods-wrapper',{
click:true,
probeType:3
})
//給右側綁定的BScroll綁定監聽事件,但是你會發現並沒有調用
this.foodswrapper.on('scroll',({x,y})=>{
console.log(x,y)//默認沒有分發滾動事件
this.scrollY=Math.abs(y);
})
//獲取停下來的位置
//給右側綁定的BScroll綁定監聽事件,但是你會發現並沒有調用
this.foodswrapper.on('scrollEnd',({x,y})=>{
//console.log(x,y)//默認沒有分發滾動事件
this.scrollY=Math.abs(y);
})
}
//初始化數組,獲取到每個li 的坐標
,_initTops(){
var tops=[] //定義一個空數組
let top=0;
tops[0]=0 //第一個li的坐標為0
var lis = this.$refs.foodsUl.children; //獲取到了每個li
Array.prototype.slice.call(lis).forEach((li,index)=>{
top = top + li.clientHeight//當前的位置,等於上一個的位置,加上這一個的高度
tops.push(top)
})
this.tops=tops
console.log(tops)
},
//將當前的index傳進來
clickMenuItem(index){
//先得到目標位置scrollY
const top = this.tops[index];
// 立即更新scrollY,更新當前分類,點擊的分類項成為當前
this.scrollY=top
//平滑滾動右側列表
this.foodswrapper.scrollTo(0, -top, 3);
},
// 顯示點擊的food
showFood (food) {
// 設置food
this.food = food
// 顯示food組件 (在父組件中調用子組件對象的方法)
this.$refs.food.toggleShow()
}
},
computed:{
...mapState(['goods']),
currentIndex(){
return this.tops.findIndex((top,index)=>{
return this.scrollY>=top && this.scrollY<this.tops[index+1]
})
}
}
}
</script>
<style lang="stylus" rel="stylesheet/stylus">
@import "../../../common/stylus/mixins.styl"
.goods
display: flex
position: absolute
top: 195px
bottom: 46px
width: 100%
background: #fff;
overflow: hidden
.menu-wrapper
flex: 0 0 80px
width: 80px
background: #f3f5f7
.menu-item
display: table
height: 54px
width: 56px
padding: 0 12px
line-height: 14px
&.current
position: relative
z-index: 10
margin-top: -1px
background: #fff
color: $green
font-weight: 700
.text
border-none()
.icon
display: inline-block
vertical-align: top
width: 12px
height: 12px
margin-right: 2px
background-size: 12px 12px
background-repeat: no-repeat
.text
display: table-cell
width: 56px
vertical-align: middle
bottom-border-1px(rgba(7, 17, 27, 0.1))
font-size: 12px
.foods-wrapper
flex: 1
.title
padding-left: 14px
height: 26px
line-height: 26px
border-left: 2px solid #d9dde1
font-size: 12px
color: rgb(147, 153, 159)
background: #f3f5f7
.food-item
display: flex
margin: 18px
padding-bottom: 18px
bottom-border-1px(rgba(7, 17, 27, 0.1))
&:last-child
border-none()
margin-bottom: 0
.icon
flex: 0 0 57px
margin-right: 10px
.content
flex: 1
.name
margin: 2px 0 8px 0
height: 14px
line-height: 14px
font-size: 14px
color: rgb(7, 17, 27)
.desc, .extra
line-height: 10px
font-size: 10px
color: rgb(147, 153, 159)
.desc
line-height: 12px
margin-bottom: 8px
.extra
.count
margin-right: 12px
.price
font-weight: 700
line-height: 24px
.now
margin-right: 8px
font-size: 14px
color: rgb(240, 20, 20)
.old
text-decoration: line-through
font-size: 10px
color: rgb(147, 153, 159)
.cartcontrol-wrapper
position: absolute
right: 0
bottom: 12px
</style>
子組件:
<template> <div class="food" v-if="isShow"> <div class="food-content"> <div class="image-header"> <img :src="food.image"> <p class="foodpanel-desc">{{food.info}}</p> <div class="back" @click="toggleShow"> <i class="iconfont icon-arrow_left"></i> </div> </div> <div class="content"> <h1 class="title">{{food.name}}</h1> <div class="detail"> <span class="sell-count">月售{{food.sellCount}}份</span> <span class="rating">好評率{{food.rating}}%</span> </div> <div class="price"> <span class="now">¥{{food.price}}</span> <span class="old" v-show="food.oldPrice">¥{{food.oldPrice}}</span> </div> <div class="cartcontrol-wrapper"> <CartControl :food="food"/> </div> </div> </div> <div class="food-cover" @click="toggleShow"></div> </div> </template> <script> import CartControl from '../CartControl/CartControl.vue' export default { props: { food: Object }, data () { return { isShow: false } }, methods: { toggleShow () { this.isShow = !this.isShow } }, components: { CartControl } } </script> <style lang="stylus" rel="stylesheet/stylus" scoped> @import "../../common/stylus/mixins.styl" .food position: fixed left: 0 top: 0 bottom: 48px z-index: 101 width: 100% &.fade-enter-active, &.fade-leave-active transition opacity .5s &.fade-enter, &.fade-leave-to opacity 0 .food-content position absolute left 50% top 50% transform translate(-50%, -50%) width 80% height 65% z-index 66 background #fff border-radius 5px .image-header position: relative width: 100% height: 0 padding-top: 100% img position: absolute top: 0 left: 0 width: 100% height: 100% .foodpanel-desc font-size 10px color #ddd letter-spacing 0 position absolute bottom 0 left 0 right 0 padding 0 10px 10px .back position: absolute top: 10px left: 0 .icon-arrow_left display: block padding: 10px font-size: 20px color: #fff .content position: relative padding: 18px .title line-height: 14px margin-bottom: 8px font-size: 14px font-weight: 700 color: rgb(7, 17, 27) .detail margin-bottom: 18px line-height: 10px height: 10px font-size: 0 .sell-count, .rating font-size: 10px color: rgb(147, 153, 159) .sell-count margin-right: 12px .price font-weight: 700 line-height: 24px .now margin-right: 8px font-size: 14px color: rgb(240, 20, 20) .old text-decoration: line-through font-size: 10px color: rgb(147, 153, 159) .cartcontrol-wrapper position: absolute right: 12px bottom: 12px .buy position: absolute right: 18px bottom: 18px z-index: 10 height: 24px line-height: 24px padding: 0 12px box-sizing: border-box border-radius: 12px font-size: 10px color: #fff background: rgb(0, 160, 220) &.fade-transition transition: all 0.2s opacity: 1 &.fade-enter, &.fade-leave opacity: 0 .food-cover position absolute top 0 right 0 bottom -48px left 0 z-index 55 background-color rgba(0, 0, 0, 0.5) </style>
三、總結
